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