001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model.cloud;
018
019import java.util.Optional;
020import java.util.function.Function;
021import java.util.function.Supplier;
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlElement;
026import javax.xml.bind.annotation.XmlElements;
027import javax.xml.bind.annotation.XmlRootElement;
028import javax.xml.bind.annotation.XmlTransient;
029
030import org.apache.camel.CamelContext;
031import org.apache.camel.CamelContextAware;
032import org.apache.camel.ExchangePattern;
033import org.apache.camel.Expression;
034import org.apache.camel.Processor;
035import org.apache.camel.builder.ExpressionClause;
036import org.apache.camel.cloud.ServiceChooser;
037import org.apache.camel.cloud.ServiceChooserAware;
038import org.apache.camel.cloud.ServiceDiscovery;
039import org.apache.camel.cloud.ServiceDiscoveryAware;
040import org.apache.camel.cloud.ServiceExpressionFactory;
041import org.apache.camel.cloud.ServiceFilter;
042import org.apache.camel.cloud.ServiceFilterAware;
043import org.apache.camel.cloud.ServiceLoadBalancer;
044import org.apache.camel.impl.cloud.DefaultServiceCallExpression;
045import org.apache.camel.impl.cloud.DefaultServiceCallProcessor;
046import org.apache.camel.impl.cloud.DefaultServiceLoadBalancer;
047import org.apache.camel.impl.cloud.HealthyServiceFilter;
048import org.apache.camel.impl.cloud.PassThroughServiceFilter;
049import org.apache.camel.impl.cloud.RandomServiceChooser;
050import org.apache.camel.impl.cloud.RoundRobinServiceChooser;
051import org.apache.camel.model.NoOutputDefinition;
052import org.apache.camel.spi.Metadata;
053import org.apache.camel.spi.RouteContext;
054import org.apache.camel.util.CamelContextHelper;
055import org.apache.camel.util.ObjectHelper;
056import org.apache.camel.util.function.Suppliers;
057
058import static org.apache.camel.util.CamelContextHelper.findByType;
059import static org.apache.camel.util.CamelContextHelper.lookup;
060
061/**
062 * To call remote services
063 */
064@Metadata(label = "eip,routing")
065@XmlRootElement(name = "serviceCall")
066@XmlAccessorType(XmlAccessType.FIELD)
067public class ServiceCallDefinition extends NoOutputDefinition<ServiceCallDefinition> {
068    @XmlAttribute @Metadata(required = "true")
069    private String name;
070    @XmlAttribute
071    private String uri;
072    @XmlAttribute @Metadata(defaultValue = ServiceCallDefinitionConstants.DEFAULT_COMPONENT)
073    private String component;
074    @XmlAttribute
075    private ExchangePattern pattern;
076    @XmlAttribute
077    private String configurationRef;
078    @XmlAttribute
079    private String serviceDiscoveryRef;
080    @XmlTransient
081    private ServiceDiscovery serviceDiscovery;
082    @XmlAttribute
083    private String serviceFilterRef;
084    @XmlTransient
085    private ServiceFilter serviceFilter;
086    @XmlAttribute
087    private String serviceChooserRef;
088    @XmlTransient
089    private ServiceChooser serviceChooser;
090    @XmlAttribute
091    private String loadBalancerRef;
092    @XmlTransient
093    private ServiceLoadBalancer loadBalancer;
094    @XmlAttribute
095    private String expressionRef;
096    @XmlTransient
097    private Expression expression;
098
099    @XmlElements({
100        @XmlElement(name = "cachingServiceDiscovery", type = CachingServiceCallServiceDiscoveryConfiguration.class),
101        @XmlElement(name = "aggregatingServiceDiscovery", type = AggregatingServiceCallServiceDiscoveryConfiguration.class),
102        @XmlElement(name = "consulServiceDiscovery", type = ConsulServiceCallServiceDiscoveryConfiguration.class),
103        @XmlElement(name = "dnsServiceDiscovery", type = DnsServiceCallServiceDiscoveryConfiguration.class),
104        @XmlElement(name = "etcdServiceDiscovery", type = EtcdServiceCallServiceDiscoveryConfiguration.class),
105        @XmlElement(name = "kubernetesServiceDiscovery", type = KubernetesServiceCallServiceDiscoveryConfiguration.class),
106        @XmlElement(name = "staticServiceDiscovery", type = StaticServiceCallServiceDiscoveryConfiguration.class)}
107    )
108    private ServiceCallServiceDiscoveryConfiguration serviceDiscoveryConfiguration;
109
110    @XmlElements({
111        @XmlElement(name = "blacklistServiceFilter", type = BlacklistServiceCallServiceFilterConfiguration.class),
112        @XmlElement(name = "chainedServiceFilter", type = ChainedServiceCallServiceFilterConfiguration.class),
113        @XmlElement(name = "customServiceFilter", type = CustomServiceCallServiceFilterConfiguration.class),
114        @XmlElement(name = "healthyServiceFilter", type = HealthyServiceCallServiceFilterConfiguration.class),
115        @XmlElement(name = "passThroughServiceFilter", type = PassThroughServiceCallServiceFilterConfiguration.class)}
116    )
117    private ServiceCallServiceFilterConfiguration serviceFilterConfiguration;
118
119    @XmlElements({
120        @XmlElement(name = "ribbonLoadBalancer", type = RibbonServiceCallServiceLoadBalancerConfiguration.class),
121        @XmlElement(name = "defaultLoadBalancer", type = DefaultServiceCallServiceLoadBalancerConfiguration.class) }
122    )
123    private ServiceCallServiceLoadBalancerConfiguration loadBalancerConfiguration;
124
125    @XmlElements({
126        @XmlElement(name = "expressionConfiguration", type = ServiceCallExpressionConfiguration.class)}
127    )
128    private ServiceCallExpressionConfiguration expressionConfiguration;
129
130    public ServiceCallDefinition() {
131    }
132
133    @Override
134    public String toString() {
135        return "ServiceCall[" + name + "]";
136    }
137
138    @Override
139    public String getLabel() {
140        return "serviceCall";
141    }
142
143    // *****************************
144    // Properties
145    // *****************************
146
147    public String getName() {
148        return name;
149    }
150
151    /**
152     * Sets the name of the service to use
153     */
154    public void setName(String name) {
155        this.name = name;
156    }
157
158    public ExchangePattern getPattern() {
159        return pattern;
160    }
161
162    /**
163     * Sets the optional {@link ExchangePattern} used to invoke this endpoint
164     */
165    public void setPattern(ExchangePattern pattern) {
166        this.pattern = pattern;
167    }
168
169    public String getConfigurationRef() {
170        return configurationRef;
171    }
172
173    /**
174     * Refers to a ServiceCall configuration to use
175     */
176    public void setConfigurationRef(String configurationRef) {
177        this.configurationRef = configurationRef;
178    }
179
180    public String getUri() {
181        return uri;
182    }
183
184    /**
185     * The uri of the endpoint to send to.
186     * The uri can be dynamic computed using the {@link org.apache.camel.language.simple.SimpleLanguage} expression.
187     */
188    public void setUri(String uri) {
189        this.uri = uri;
190    }
191
192    public String getComponent() {
193        return component;
194    }
195
196    /**
197     * The component to use.
198     */
199    public void setComponent(String component) {
200        this.component = component;
201    }
202
203    public String getServiceDiscoveryRef() {
204        return serviceDiscoveryRef;
205    }
206
207    /**
208     * Sets a reference to a custom {@link ServiceDiscovery} to use.
209     */
210    public void setServiceDiscoveryRef(String serviceDiscoveryRef) {
211        this.serviceDiscoveryRef = serviceDiscoveryRef;
212    }
213
214    public ServiceDiscovery getServiceDiscovery() {
215        return serviceDiscovery;
216    }
217
218    /**
219     * Sets a custom {@link ServiceDiscovery} to use.
220     */
221    public void setServiceDiscovery(ServiceDiscovery serviceDiscovery) {
222        this.serviceDiscovery = serviceDiscovery;
223    }
224
225    public String getServiceFilterRef() {
226        return serviceFilterRef;
227    }
228
229    /**
230     * Sets a reference to a custom {@link ServiceFilter} to use.
231     */
232    public void setServiceFilterRef(String serviceFilterRef) {
233        this.serviceFilterRef = serviceFilterRef;
234    }
235
236    public ServiceFilter getServiceFilter() {
237        return serviceFilter;
238    }
239
240    /**
241     * Sets a custom {@link ServiceFilter} to use.
242     */
243    public void setServiceFilter(ServiceFilter serviceFilter) {
244        this.serviceFilter = serviceFilter;
245    }
246
247    public String getServiceChooserRef() {
248        return serviceChooserRef;
249    }
250
251    /**
252     * Sets a reference to a custom {@link ServiceChooser} to use.
253     */
254    public void setServiceChooserRef(String serviceChooserRef) {
255        this.serviceChooserRef = serviceChooserRef;
256    }
257
258    public ServiceChooser getServiceChooser() {
259        return serviceChooser;
260    }
261
262    /**
263     * Sets a custom {@link ServiceChooser} to use.
264     */
265    public void setServiceChooser(ServiceChooser serviceChooser) {
266        this.serviceChooser = serviceChooser;
267    }
268
269    public String getLoadBalancerRef() {
270        return loadBalancerRef;
271    }
272
273    /**
274     * Sets a reference to a custom {@link ServiceLoadBalancer} to use.
275     */
276    public void setLoadBalancerRef(String loadBalancerRef) {
277        this.loadBalancerRef = loadBalancerRef;
278    }
279
280    public ServiceLoadBalancer getLoadBalancer() {
281        return loadBalancer;
282    }
283
284    /**
285     * Sets a custom {@link ServiceLoadBalancer} to use.
286     */
287    public void setLoadBalancer(ServiceLoadBalancer loadBalancer) {
288        this.loadBalancer = loadBalancer;
289    }
290
291    public String getExpressionRef() {
292        return expressionRef;
293    }
294
295    /**
296     * Set a reference to a custom {@link Expression} to use.
297     */
298    public void setExpressionRef(String expressionRef) {
299        this.expressionRef = expressionRef;
300    }
301
302    public Expression getExpression() {
303        return expression;
304    }
305
306    /**
307     * Set a custom {@link Expression} to use.
308     */
309    public void setExpression(Expression expression) {
310        this.expression = expression;
311    }
312
313    public ServiceCallServiceDiscoveryConfiguration getServiceDiscoveryConfiguration() {
314        return serviceDiscoveryConfiguration;
315    }
316
317    /**
318     * Configures the ServiceDiscovery using the given configuration.
319     */
320    public void setServiceDiscoveryConfiguration(ServiceCallServiceDiscoveryConfiguration serviceDiscoveryConfiguration) {
321        this.serviceDiscoveryConfiguration = serviceDiscoveryConfiguration;
322    }
323
324    public ServiceCallServiceFilterConfiguration getServiceFilterConfiguration() {
325        return serviceFilterConfiguration;
326    }
327
328    /**
329     * Configures the ServiceFilter using the given configuration.
330     */
331    public void setServiceFilterConfiguration(ServiceCallServiceFilterConfiguration serviceFilterConfiguration) {
332        this.serviceFilterConfiguration = serviceFilterConfiguration;
333    }
334
335    public ServiceCallServiceLoadBalancerConfiguration getLoadBalancerConfiguration() {
336        return loadBalancerConfiguration;
337    }
338
339    /**
340     * Configures the LoadBalancer using the given configuration.
341     */
342    public void setLoadBalancerConfiguration(ServiceCallServiceLoadBalancerConfiguration loadBalancerConfiguration) {
343        this.loadBalancerConfiguration = loadBalancerConfiguration;
344    }
345
346    public ServiceCallExpressionConfiguration getExpressionConfiguration() {
347        return expressionConfiguration;
348    }
349
350    /**
351     * Configures the Expression using the given configuration.
352     */
353    public void setExpressionConfiguration(ServiceCallExpressionConfiguration expressionConfiguration) {
354        this.expressionConfiguration = expressionConfiguration;
355    }
356
357    // *****************************
358    // Fluent API
359    // *****************************
360
361    /**
362     * Sets the optional {@link ExchangePattern} used to invoke this endpoint
363     */
364    public ServiceCallDefinition pattern(ExchangePattern pattern) {
365        setPattern(pattern);
366        return this;
367    }
368
369    /**
370     * Sets the name of the service to use
371     */
372    public ServiceCallDefinition name(String name) {
373        setName(name);
374        return this;
375    }
376
377    /**
378     * Sets the uri of the service to use
379     */
380    public ServiceCallDefinition uri(String uri) {
381        setUri(uri);
382        return this;
383    }
384
385    /**
386     * Sets the component to use
387     */
388    public ServiceCallDefinition component(String component) {
389        setComponent(component);
390        return this;
391    }
392
393    /**
394     * Refers to a ServiceCall configuration to use
395     */
396    public ServiceCallDefinition serviceCallConfiguration(String ref) {
397        configurationRef = ref;
398        return this;
399    }
400
401    /**
402     * Sets a reference to a custom {@link ServiceDiscovery} to use.
403     */
404    public ServiceCallDefinition serviceDiscovery(String serviceDiscoveryRef) {
405        setServiceDiscoveryRef(serviceDiscoveryRef);
406        return this;
407    }
408
409    /**
410     * Sets a custom {@link ServiceDiscovery} to use.
411     */
412    public ServiceCallDefinition serviceDiscovery(ServiceDiscovery serviceDiscovery) {
413        setServiceDiscovery(serviceDiscovery);
414        return this;
415    }
416
417    /**
418     * Sets a reference to a custom {@link ServiceFilter} to use.
419     */
420    public ServiceCallDefinition serviceFilter(String serviceFilterRef) {
421        setServiceDiscoveryRef(serviceDiscoveryRef);
422        return this;
423    }
424
425    /**
426     * Sets a custom {@link ServiceFilter} to use.
427     */
428    public ServiceCallDefinition serviceFilter(ServiceFilter serviceFilter) {
429        setServiceFilter(serviceFilter);
430        return this;
431    }
432
433    /**
434     * Sets a reference to a custom {@link ServiceChooser} to use.
435     */
436    public ServiceCallDefinition serviceChooser(String serviceChooserRef) {
437        setServiceChooserRef(serviceChooserRef);
438        return this;
439    }
440
441    /**
442     * Sets a custom {@link ServiceChooser} to use.
443     */
444    public ServiceCallDefinition serviceChooser(ServiceChooser serviceChooser) {
445        setServiceChooser(serviceChooser);
446        return this;
447    }
448
449    /**
450     * Sets a reference to a custom {@link ServiceLoadBalancer} to use.
451     */
452    public ServiceCallDefinition loadBalancer(String loadBalancerRef) {
453        setLoadBalancerRef(loadBalancerRef);
454        return this;
455    }
456
457    /**
458     * Sets a custom {@link ServiceLoadBalancer} to use.
459     */
460    public ServiceCallDefinition loadBalancer(ServiceLoadBalancer loadBalancer) {
461        setLoadBalancer(loadBalancer);
462        return this;
463    }
464
465    /**
466     * Sets a reference to a custom {@link Expression} to use.
467     */
468    public ServiceCallDefinition expression(String expressionRef) {
469        setExpressionRef(loadBalancerRef);
470        return this;
471    }
472
473    /**
474     * Sets a custom {@link Expression} to use.
475     */
476    public ServiceCallDefinition expression(Expression expression) {
477        setExpression(expression);
478        return this;
479    }
480
481    /**
482     * Sets a custom {@link Expression} to use through an expression builder clause.
483     *
484     * @return a expression builder clause to set the body
485     */
486    public ExpressionClause<ServiceCallDefinition> expression() {
487        ExpressionClause<ServiceCallDefinition> clause = new ExpressionClause<>(this);
488        setExpression(clause);
489
490        return clause;
491    }
492
493    /**
494     * Configures the ServiceDiscovery using the given configuration.
495     */
496    public ServiceCallDefinition serviceDiscoveryConfiguration(ServiceCallServiceDiscoveryConfiguration serviceDiscoveryConfiguration) {
497        setServiceDiscoveryConfiguration(serviceDiscoveryConfiguration);
498        return this;
499    }
500
501    /**
502     * Configures the ServiceFilter using the given configuration.
503     */
504    public ServiceCallDefinition serviceFilterConfiguration(ServiceCallServiceFilterConfiguration serviceFilterConfiguration) {
505        setServiceFilterConfiguration(serviceFilterConfiguration);
506        return this;
507    }
508
509    /**
510     * Configures the LoadBalancer using the given configuration.
511     */
512    public ServiceCallDefinition loadBalancerConfiguration(ServiceCallServiceLoadBalancerConfiguration loadBalancerConfiguration) {
513        setLoadBalancerConfiguration(loadBalancerConfiguration);
514        return this;
515    }
516
517    /**
518     * Configures the Expression using the given configuration.
519     */
520    public ServiceCallDefinition expressionConfiguration(ServiceCallExpressionConfiguration expressionConfiguration) {
521        setExpressionConfiguration(expressionConfiguration);
522        return this;
523    }
524
525    // *****************************
526    // Shortcuts - ServiceDiscovery
527    // *****************************
528
529    public CachingServiceCallServiceDiscoveryConfiguration cachingServiceDiscovery() {
530        CachingServiceCallServiceDiscoveryConfiguration conf = new CachingServiceCallServiceDiscoveryConfiguration(this);
531        setServiceDiscoveryConfiguration(conf);
532
533        return conf;
534    }
535
536    public ConsulServiceCallServiceDiscoveryConfiguration consulServiceDiscovery() {
537        ConsulServiceCallServiceDiscoveryConfiguration conf = new ConsulServiceCallServiceDiscoveryConfiguration(this);
538        setServiceDiscoveryConfiguration(conf);
539
540        return conf;
541    }
542
543    public ServiceCallDefinition consulServiceDiscovery(String url) {
544        ConsulServiceCallServiceDiscoveryConfiguration conf = new ConsulServiceCallServiceDiscoveryConfiguration(this);
545        conf.setUrl(url);
546
547        setServiceDiscoveryConfiguration(conf);
548
549        return this;
550    }
551
552    public DnsServiceCallServiceDiscoveryConfiguration dnsServiceDiscovery() {
553        DnsServiceCallServiceDiscoveryConfiguration conf = new DnsServiceCallServiceDiscoveryConfiguration(this);
554        setServiceDiscoveryConfiguration(conf);
555
556        return conf;
557    }
558
559    public ServiceCallDefinition dnsServiceDiscovery(String domain) {
560        DnsServiceCallServiceDiscoveryConfiguration conf = new DnsServiceCallServiceDiscoveryConfiguration(this);
561        conf.setDomain(domain);
562
563        setServiceDiscoveryConfiguration(conf);
564
565        return this;
566    }
567
568    public ServiceCallDefinition dnsServiceDiscovery(String domain, String protocol) {
569        DnsServiceCallServiceDiscoveryConfiguration conf = new DnsServiceCallServiceDiscoveryConfiguration(this);
570        conf.setDomain(domain);
571        conf.setProto(protocol);
572
573        setServiceDiscoveryConfiguration(conf);
574
575        return this;
576    }
577
578    public EtcdServiceCallServiceDiscoveryConfiguration etcdServiceDiscovery() {
579        EtcdServiceCallServiceDiscoveryConfiguration conf = new EtcdServiceCallServiceDiscoveryConfiguration(this);
580        setServiceDiscoveryConfiguration(conf);
581
582        return conf;
583    }
584
585    public ServiceCallDefinition etcdServiceDiscovery(String uris) {
586        EtcdServiceCallServiceDiscoveryConfiguration conf = new EtcdServiceCallServiceDiscoveryConfiguration(this);
587        conf.setUris(uris);
588
589        setServiceDiscoveryConfiguration(conf);
590
591        return this;
592    }
593
594    public ServiceCallDefinition etcdServiceDiscovery(String uris, String servicePath) {
595        EtcdServiceCallServiceDiscoveryConfiguration conf = new EtcdServiceCallServiceDiscoveryConfiguration(this);
596        conf.setUris(uris);
597        conf.setServicePath(servicePath);
598
599        setServiceDiscoveryConfiguration(conf);
600
601        return this;
602    }
603
604    public KubernetesServiceCallServiceDiscoveryConfiguration kubernetesServiceDiscovery() {
605        KubernetesServiceCallServiceDiscoveryConfiguration conf = new KubernetesServiceCallServiceDiscoveryConfiguration(this);
606        setServiceDiscoveryConfiguration(conf);
607
608        return conf;
609    }
610
611    public KubernetesServiceCallServiceDiscoveryConfiguration kubernetesClientServiceDiscovery() {
612        KubernetesServiceCallServiceDiscoveryConfiguration conf = new KubernetesServiceCallServiceDiscoveryConfiguration(this);
613        conf.setLookup("client");
614
615        setServiceDiscoveryConfiguration(conf);
616
617        return conf;
618    }
619
620    public ServiceCallDefinition kubernetesEnvServiceDiscovery() {
621        KubernetesServiceCallServiceDiscoveryConfiguration conf = new KubernetesServiceCallServiceDiscoveryConfiguration(this);
622        conf.setLookup("environment");
623
624        setServiceDiscoveryConfiguration(conf);
625
626        return this;
627    }
628
629    public ServiceCallDefinition kubernetesDnsServiceDiscovery(String namespace, String domain) {
630        KubernetesServiceCallServiceDiscoveryConfiguration conf = new KubernetesServiceCallServiceDiscoveryConfiguration(this);
631        conf.setLookup("dns");
632        conf.setNamespace(namespace);
633        conf.setDnsDomain(domain);
634
635        setServiceDiscoveryConfiguration(conf);
636
637        return this;
638    }
639
640    public AggregatingServiceCallServiceDiscoveryConfiguration multiServiceDiscovery() {
641        AggregatingServiceCallServiceDiscoveryConfiguration conf = new AggregatingServiceCallServiceDiscoveryConfiguration(this);
642        setServiceDiscoveryConfiguration(conf);
643
644        return conf;
645    }
646
647    public StaticServiceCallServiceDiscoveryConfiguration staticServiceDiscovery() {
648        StaticServiceCallServiceDiscoveryConfiguration conf = new StaticServiceCallServiceDiscoveryConfiguration(this);
649        setServiceDiscoveryConfiguration(conf);
650
651        return conf;
652    }
653
654    // *****************************
655    // Shortcuts - ServiceFilter
656    // *****************************
657
658    public ServiceCallDefinition healthyFilter() {
659        HealthyServiceCallServiceFilterConfiguration conf = new HealthyServiceCallServiceFilterConfiguration(this);
660        setServiceFilterConfiguration(conf);
661
662        return this;
663    }
664
665    public ServiceCallDefinition passThroughFilter() {
666        PassThroughServiceCallServiceFilterConfiguration conf = new PassThroughServiceCallServiceFilterConfiguration(this);
667        setServiceFilterConfiguration(conf);
668
669        return this;
670    }
671
672    public ChainedServiceCallServiceFilterConfiguration multiFilter() {
673        ChainedServiceCallServiceFilterConfiguration conf = new ChainedServiceCallServiceFilterConfiguration(this);
674        setServiceFilterConfiguration(conf);
675
676        return conf;
677    }
678
679    public BlacklistServiceCallServiceFilterConfiguration blacklistFilter() {
680        BlacklistServiceCallServiceFilterConfiguration conf = new BlacklistServiceCallServiceFilterConfiguration();
681        setServiceFilterConfiguration(conf);
682
683        return conf;
684    }
685
686    public ServiceCallDefinition customFilter(String serviceFilter) {
687        CustomServiceCallServiceFilterConfiguration conf = new CustomServiceCallServiceFilterConfiguration();
688        conf.setServiceFilterRef(serviceFilter);
689
690        setServiceFilterConfiguration(conf);
691
692        return this;
693    }
694
695    public ServiceCallDefinition customFilter(ServiceFilter serviceFilter) {
696        CustomServiceCallServiceFilterConfiguration conf = new CustomServiceCallServiceFilterConfiguration();
697        conf.setServiceFilter(serviceFilter);
698
699        setServiceFilterConfiguration(conf);
700
701        return this;
702    }
703
704    // *****************************
705    // Shortcuts - LoadBalancer
706    // *****************************
707
708    public ServiceCallDefinition defaultLoadBalancer() {
709        DefaultServiceCallServiceLoadBalancerConfiguration conf = new DefaultServiceCallServiceLoadBalancerConfiguration();
710        setLoadBalancerConfiguration(conf);
711
712        return this;
713    }
714
715    public ServiceCallDefinition ribbonLoadBalancer() {
716        RibbonServiceCallServiceLoadBalancerConfiguration conf = new RibbonServiceCallServiceLoadBalancerConfiguration(this);
717        setLoadBalancerConfiguration(conf);
718
719        return this;
720    }
721
722    public ServiceCallDefinition ribbonLoadBalancer(String clientName) {
723        RibbonServiceCallServiceLoadBalancerConfiguration conf = new RibbonServiceCallServiceLoadBalancerConfiguration(this);
724        conf.setClientName(clientName);
725
726        setLoadBalancerConfiguration(conf);
727
728        return this;
729    }
730
731    // *****************************
732    // Processor Factory
733    // *****************************
734
735    @Override
736    public Processor createProcessor(RouteContext routeContext) throws Exception {
737        final CamelContext camelContext = routeContext.getCamelContext();
738        final ServiceDiscovery serviceDiscovery = retrieveServiceDiscovery(camelContext);
739        final ServiceFilter serviceFilter = retrieveServiceFilter(camelContext);
740        final ServiceChooser serviceChooser = retrieveServiceChooser(camelContext);
741        final ServiceLoadBalancer loadBalancer = retrieveLoadBalancer(camelContext);
742
743        if (loadBalancer instanceof CamelContextAware) {
744            ((CamelContextAware) loadBalancer).setCamelContext(camelContext);
745        }
746        if (loadBalancer instanceof ServiceDiscoveryAware) {
747            ((ServiceDiscoveryAware) loadBalancer).setServiceDiscovery(serviceDiscovery);
748        }
749        if (loadBalancer instanceof ServiceFilterAware) {
750            ((ServiceFilterAware) loadBalancer).setServiceFilter(serviceFilter);
751        }
752        if (loadBalancer instanceof ServiceChooserAware) {
753            ((ServiceChooserAware) loadBalancer).setServiceChooser(serviceChooser);
754        }
755
756        // The component is used to configure the default scheme to use (eg camel component name).
757        // The component configured on EIP takes precedence vs configured on configuration.
758        String endpointScheme = this.component;
759        if (endpointScheme == null) {
760            ServiceCallConfigurationDefinition conf = retrieveConfig(camelContext);
761            if (conf != null) {
762                endpointScheme = conf.getComponent();
763            }
764        }
765        if (endpointScheme == null) {
766            ServiceCallConfigurationDefinition conf = retrieveDefaultConfig(camelContext);
767            if (conf != null) {
768                endpointScheme = conf.getComponent();
769            }
770        }
771
772        // The uri is used to tweak the uri.
773        // The uri configured on EIP takes precedence vs configured on configuration.
774        String endpointUri = this.uri;
775        if (endpointUri == null) {
776            ServiceCallConfigurationDefinition conf = retrieveConfig(camelContext);
777            if (conf != null) {
778                endpointUri = conf.getUri();
779            }
780        }
781        if (endpointUri == null) {
782            ServiceCallConfigurationDefinition conf = retrieveDefaultConfig(camelContext);
783            if (conf != null) {
784                endpointUri = conf.getUri();
785            }
786        }
787
788        // Service name is mandatory
789        ObjectHelper.notNull(name, "Service name");
790
791        endpointScheme = ObjectHelper.applyIfNotEmpty(endpointScheme, camelContext::resolvePropertyPlaceholders, () -> ServiceCallDefinitionConstants.DEFAULT_COMPONENT);
792        endpointUri = ObjectHelper.applyIfNotEmpty(endpointUri, camelContext::resolvePropertyPlaceholders, () -> null);
793
794        return new DefaultServiceCallProcessor(
795            camelContext,
796            camelContext.resolvePropertyPlaceholders(name),
797            endpointScheme,
798            endpointUri,
799            pattern,
800            loadBalancer,
801            retrieveExpression(camelContext, endpointScheme));
802    }
803
804    // *****************************
805    // Helpers
806    // *****************************
807
808    private ServiceCallConfigurationDefinition retrieveDefaultConfig(CamelContext camelContext) {
809        // check if a default configuration is bound to the registry
810        ServiceCallConfigurationDefinition config = camelContext.getServiceCallConfiguration(null);
811
812        if (config == null) {
813            // Or if it is in the registry
814            config = lookup(
815                camelContext,
816                ServiceCallDefinitionConstants.DEFAULT_SERVICE_CALL_CONFIG_ID,
817                ServiceCallConfigurationDefinition.class);
818        }
819
820        if (config == null) {
821            // If no default is set either by searching by name or bound to the
822            // camel context, assume that if there is a single instance in the
823            // registry, that is the default one
824            config = findByType(camelContext, ServiceCallConfigurationDefinition.class);
825        }
826
827        return config;
828    }
829
830    private ServiceCallConfigurationDefinition retrieveConfig(CamelContext camelContext) {
831        ServiceCallConfigurationDefinition config = null;
832        if (configurationRef != null) {
833            // lookup in registry firstNotNull
834            config = lookup(camelContext, configurationRef, ServiceCallConfigurationDefinition.class);
835            if (config == null) {
836                // and fallback as service configuration
837                config = camelContext.getServiceCallConfiguration(configurationRef);
838            }
839        }
840
841        return config;
842    }
843
844    // ******************************************
845    // ServiceDiscovery
846    // ******************************************
847
848    private ServiceDiscovery retrieveServiceDiscovery(CamelContext camelContext, Function<CamelContext, ServiceCallConfigurationDefinition> function) throws Exception {
849        ServiceDiscovery answer = null;
850
851        ServiceCallConfigurationDefinition config = function.apply(camelContext);
852        if (config != null) {
853            if (config.getServiceDiscoveryConfiguration() != null) {
854                answer = config.getServiceDiscoveryConfiguration().newInstance(camelContext);
855            } else {
856                answer = retrieve(
857                    ServiceDiscovery.class,
858                    camelContext,
859                    config::getServiceDiscovery,
860                    config::getServiceDiscoveryRef
861                );
862            }
863        }
864
865        return answer;
866    }
867
868    private ServiceDiscovery retrieveServiceDiscovery(CamelContext camelContext) throws Exception {
869        return Suppliers.firstNotNull(
870            () -> (serviceDiscoveryConfiguration != null) ? serviceDiscoveryConfiguration.newInstance(camelContext) : null,
871            // Local configuration
872            () -> retrieve(ServiceDiscovery.class, camelContext, this::getServiceDiscovery, this::getServiceDiscoveryRef),
873            // Linked configuration
874            () -> retrieveServiceDiscovery(camelContext, this::retrieveConfig),
875            // Default configuration
876            () -> retrieveServiceDiscovery(camelContext, this::retrieveDefaultConfig),
877            // Check if there is a single instance in the registry
878            () -> findByType(camelContext, ServiceDiscovery.class),
879            // From registry
880            () -> lookup(camelContext, ServiceCallDefinitionConstants.DEFAULT_SERVICE_DISCOVERY_ID, ServiceDiscovery.class)
881        ).orElseGet(
882            // Default, that's s little ugly but a load balancer may live without
883            // (i.e. the Ribbon one) so let's delegate the null check to the actual
884            // impl.
885            () -> null
886        );
887    }
888
889    // ******************************************
890    // ServiceFilter
891    // ******************************************
892
893    private ServiceFilter retrieveServiceFilter(CamelContext camelContext, Function<CamelContext, ServiceCallConfigurationDefinition> function) throws Exception {
894        ServiceFilter answer = null;
895
896        ServiceCallConfigurationDefinition config = function.apply(camelContext);
897        if (config != null) {
898            if (config.getServiceFilterConfiguration() != null) {
899                answer = config.getServiceFilterConfiguration().newInstance(camelContext);
900            } else {
901                answer = retrieve(
902                    ServiceFilter.class,
903                    camelContext,
904                    config::getServiceFilter,
905                    config::getServiceFilterRef
906                );
907            }
908
909            if (answer == null) {
910                String ref = config.getServiceFilterRef();
911                if (ObjectHelper.equal("healthy", ref, true)) {
912                    answer = new HealthyServiceFilter();
913                } else if (ObjectHelper.equal("pass-through", ref, true)) {
914                    answer = new PassThroughServiceFilter();
915                } else if (ObjectHelper.equal("passthrough", ref, true)) {
916                    answer = new PassThroughServiceFilter();
917                }
918            }
919        }
920
921        return answer;
922    }
923
924    private ServiceFilter retrieveServiceFilter(CamelContext camelContext) throws Exception {
925        return Suppliers.firstNotNull(
926            () -> (serviceFilterConfiguration != null) ? serviceFilterConfiguration.newInstance(camelContext) : null,
927            // Local configuration
928            () -> retrieve(ServiceFilter.class, camelContext, this::getServiceFilter, this::getServiceFilterRef),
929            // Linked configuration
930            () -> retrieveServiceFilter(camelContext, this::retrieveConfig),
931            // Default configuration
932            () -> retrieveServiceFilter(camelContext, this::retrieveDefaultConfig),
933            // Check if there is a single instance in the registry
934            () -> findByType(camelContext, ServiceFilter.class),
935            // From registry
936            () -> lookup(camelContext, ServiceCallDefinitionConstants.DEFAULT_SERVICE_FILTER_ID, ServiceFilter.class)
937        ).orElseGet(
938            // Default
939            () -> new HealthyServiceFilter()
940        );
941    }
942
943    // ******************************************
944    // ServiceChooser
945    // ******************************************
946
947    private ServiceChooser retrieveServiceChooser(CamelContext camelContext, Function<CamelContext, ServiceCallConfigurationDefinition> function) throws Exception {
948        ServiceChooser answer = null;
949
950        ServiceCallConfigurationDefinition config = function.apply(camelContext);
951        if (config != null) {
952            answer = retrieve(
953                ServiceChooser.class,
954                camelContext,
955                config::getServiceChooser,
956                config::getServiceChooserRef
957            );
958
959            if (answer == null) {
960                String ref = config.getServiceChooserRef();
961                if (ObjectHelper.equal("roundrobin", ref, true)) {
962                    answer = new RoundRobinServiceChooser();
963                } else if (ObjectHelper.equal("round-robin", ref, true)) {
964                    answer = new RoundRobinServiceChooser();
965                } else if (ObjectHelper.equal("random", ref, true)) {
966                    answer = new RandomServiceChooser();
967                }
968            }
969        }
970
971        return answer;
972    }
973
974    private ServiceChooser retrieveServiceChooser(CamelContext camelContext) throws Exception {
975        return Suppliers.firstNotNull(
976            // Local configuration
977            () -> retrieve(ServiceChooser.class, camelContext, this::getServiceChooser, this::getServiceChooserRef),
978            // Linked configuration
979            () -> retrieveServiceChooser(camelContext, this::retrieveConfig),
980            // Default configuration
981            () -> retrieveServiceChooser(camelContext, this::retrieveDefaultConfig),
982            // Check if there is a single instance in the registry
983            () -> findByType(camelContext, ServiceChooser.class),
984            // From registry
985            () -> lookup(camelContext, ServiceCallDefinitionConstants.DEFAULT_SERVICE_CHOOSER_ID, ServiceChooser.class)
986        ).orElseGet(
987            // Default
988            () -> new RoundRobinServiceChooser()
989        );
990    }
991
992    // ******************************************
993    // LoadBalancer
994    // ******************************************
995
996    private ServiceLoadBalancer retrieveLoadBalancer(CamelContext camelContext, Function<CamelContext, ServiceCallConfigurationDefinition> function) throws Exception {
997        ServiceLoadBalancer answer = null;
998
999        ServiceCallConfigurationDefinition config = function.apply(camelContext);
1000        if (config != null) {
1001            if (config.getLoadBalancerConfiguration() != null) {
1002                answer = config.getLoadBalancerConfiguration().newInstance(camelContext);
1003            } else {
1004                answer = retrieve(
1005                    ServiceLoadBalancer.class,
1006                    camelContext,
1007                    config::getLoadBalancer,
1008                    config::getLoadBalancerRef
1009                );
1010            }
1011        }
1012
1013        return answer;
1014    }
1015
1016    private ServiceLoadBalancer retrieveLoadBalancer(CamelContext camelContext) throws Exception {
1017        return Suppliers.firstNotNull(
1018            () -> (loadBalancerConfiguration != null) ? loadBalancerConfiguration.newInstance(camelContext) : null,
1019            // Local configuration
1020            () -> retrieve(ServiceLoadBalancer.class, camelContext, this::getLoadBalancer, this::getLoadBalancerRef),
1021            // Linked configuration
1022            () -> retrieveLoadBalancer(camelContext, this::retrieveConfig),
1023            // Default configuration
1024            () -> retrieveLoadBalancer(camelContext, this::retrieveDefaultConfig),
1025            // Check if there is a single instance in the registry
1026            () -> findByType(camelContext, ServiceLoadBalancer.class),
1027            // From registry
1028            () -> lookup(camelContext, ServiceCallDefinitionConstants.DEFAULT_LOAD_BALANCER_ID, ServiceLoadBalancer.class)
1029        ).orElseGet(
1030            // Default
1031            () -> new DefaultServiceLoadBalancer()
1032        );
1033    }
1034
1035    // ******************************************
1036    // Expression
1037    // ******************************************
1038
1039    private Expression retrieveExpression(CamelContext camelContext, Function<CamelContext, ServiceCallConfigurationDefinition> function) throws Exception {
1040        Expression answer = null;
1041
1042        ServiceCallConfigurationDefinition config = function.apply(camelContext);
1043        if (config != null) {
1044            if (config.getExpressionConfiguration() != null) {
1045                answer = config.getExpressionConfiguration().newInstance(camelContext);
1046            } else {
1047                answer = retrieve(
1048                    Expression.class,
1049                    camelContext,
1050                    config::getExpression,
1051                    config::getExpressionRef
1052                );
1053            }
1054        }
1055
1056        return answer;
1057    }
1058
1059    private Expression retrieveExpression(CamelContext camelContext, String component) throws Exception {
1060        Optional<Expression> expression = Suppliers.firstNotNull(
1061            () -> (expressionConfiguration != null) ? expressionConfiguration.newInstance(camelContext) : null,
1062            // Local configuration
1063            () -> retrieve(Expression.class, camelContext, this::getExpression, this::getExpressionRef),
1064            // Linked configuration
1065            () -> retrieveExpression(camelContext, this::retrieveConfig),
1066            // Default configuration
1067            () -> retrieveExpression(camelContext, this::retrieveDefaultConfig),
1068            // From registry
1069            () -> lookup(camelContext, ServiceCallDefinitionConstants.DEFAULT_SERVICE_CALL_EXPRESSION_ID, Expression.class)
1070        );
1071
1072        if (expression.isPresent()) {
1073            return expression.get();
1074        } else {
1075            String lookupName = component + "-service-expression";
1076            // First try to find the factory from the registry.
1077            ServiceExpressionFactory factory = CamelContextHelper.lookup(camelContext, lookupName, ServiceExpressionFactory.class);
1078            if (factory != null) {
1079                // If a factory is found in the registry do not re-configure it as
1080                // it should be pre-configured.
1081                return factory.newInstance(camelContext);
1082            } else {
1083
1084                Class<?> type = null;
1085
1086                try {
1087                    // Then use Service factory.
1088                    type = camelContext.getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(lookupName);
1089                } catch (Exception e) {
1090                }
1091
1092                if (ObjectHelper.isNotEmpty(type)) {
1093                    if (ServiceExpressionFactory.class.isAssignableFrom(type)) {
1094                        factory = (ServiceExpressionFactory) camelContext.getInjector().newInstance(type);
1095                    } else {
1096                        throw new IllegalArgumentException(
1097                            "Resolving Expression: " + lookupName + " detected type conflict: Not a ServiceExpressionFactory implementation. Found: " + type.getName());
1098                    }
1099                } else {
1100                    // If no factory is found, returns the default
1101                    factory = context -> new DefaultServiceCallExpression();
1102                }
1103
1104                return factory.newInstance(camelContext);
1105            }
1106        }
1107    }
1108
1109    // ************************************
1110    // Helpers
1111    // ************************************
1112
1113    private <T> T retrieve(Class<T> type, CamelContext camelContext, Supplier<T> instanceSupplier, Supplier<String> refSupplier) {
1114        T answer = null;
1115        if (instanceSupplier != null) {
1116            answer = instanceSupplier.get();
1117        }
1118
1119        if (answer == null && refSupplier != null) {
1120            String ref = refSupplier.get();
1121            if (ref != null) {
1122                answer = lookup(camelContext, ref, type);
1123            }
1124        }
1125
1126        return answer;
1127    }
1128}