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