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