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