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;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.stream.Collectors;
022
023import javax.xml.bind.annotation.XmlAccessType;
024import javax.xml.bind.annotation.XmlAccessorType;
025import javax.xml.bind.annotation.XmlElement;
026import javax.xml.bind.annotation.XmlElementRef;
027import javax.xml.bind.annotation.XmlElements;
028import javax.xml.bind.annotation.XmlRootElement;
029
030import org.apache.camel.Expression;
031import org.apache.camel.model.loadbalancer.CustomLoadBalancerDefinition;
032import org.apache.camel.model.loadbalancer.FailoverLoadBalancerDefinition;
033import org.apache.camel.model.loadbalancer.RandomLoadBalancerDefinition;
034import org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition;
035import org.apache.camel.model.loadbalancer.StickyLoadBalancerDefinition;
036import org.apache.camel.model.loadbalancer.TopicLoadBalancerDefinition;
037import org.apache.camel.model.loadbalancer.WeightedLoadBalancerDefinition;
038import org.apache.camel.processor.loadbalancer.LoadBalancer;
039import org.apache.camel.spi.Metadata;
040
041/**
042 * Balances message processing among a number of nodes
043 */
044@Metadata(label = "eip,routing")
045@XmlRootElement(name = "loadBalance")
046@XmlAccessorType(XmlAccessType.FIELD)
047public class LoadBalanceDefinition extends OutputDefinition<LoadBalanceDefinition> {
048    @XmlElements({@XmlElement(required = false, name = "failover", type = FailoverLoadBalancerDefinition.class),
049                  @XmlElement(required = false, name = "random", type = RandomLoadBalancerDefinition.class),
050                  @XmlElement(required = false, name = "customLoadBalancer", type = CustomLoadBalancerDefinition.class),
051                  @XmlElement(required = false, name = "roundRobin", type = RoundRobinLoadBalancerDefinition.class),
052                  @XmlElement(required = false, name = "sticky", type = StickyLoadBalancerDefinition.class),
053                  @XmlElement(required = false, name = "topic", type = TopicLoadBalancerDefinition.class),
054                  @XmlElement(required = false, name = "weighted", type = WeightedLoadBalancerDefinition.class)})
055    private LoadBalancerDefinition loadBalancerType;
056
057    public LoadBalanceDefinition() {
058    }
059
060    @Override
061    public List<ProcessorDefinition<?>> getOutputs() {
062        return outputs;
063    }
064
065    @XmlElementRef
066    @Override
067    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
068        super.setOutputs(outputs);
069    }
070
071    public LoadBalancerDefinition getLoadBalancerType() {
072        return loadBalancerType;
073    }
074
075    /**
076     * The load balancer to be used
077     */
078    public void setLoadBalancerType(LoadBalancerDefinition loadbalancer) {
079        if (loadBalancerType != null) {
080            throw new IllegalArgumentException("Loadbalancer already configured to: " + loadBalancerType + ". Cannot set it to: " + loadbalancer);
081        }
082        loadBalancerType = loadbalancer;
083    }
084
085    // Fluent API
086    // -------------------------------------------------------------------------
087
088    /**
089     * Uses a custom load balancer
090     *
091     * @param loadBalancer the load balancer
092     * @return the builder
093     */
094    @Override
095    public LoadBalanceDefinition loadBalance(LoadBalancer loadBalancer) {
096        CustomLoadBalancerDefinition def = new CustomLoadBalancerDefinition();
097        def.setCustomLoadBalancer(loadBalancer);
098        setLoadBalancerType(def);
099        return this;
100    }
101
102    /**
103     * Uses fail over load balancer
104     * <p/>
105     * Will not round robin and inherit the error handler.
106     *
107     * @return the builder
108     */
109    public LoadBalanceDefinition failover() {
110        return failover(-1, true, false);
111    }
112
113    /**
114     * Uses fail over load balancer
115     * <p/>
116     * Will not round robin and inherit the error handler.
117     *
118     * @param exceptions exception classes which we want to failover if one of
119     *            them was thrown
120     * @return the builder
121     */
122    public LoadBalanceDefinition failover(Class<?>... exceptions) {
123        return failover(-1, true, false, exceptions);
124    }
125
126    /**
127     * Uses fail over load balancer
128     *
129     * @param maximumFailoverAttempts maximum number of failover attempts before
130     *            exhausting. Use -1 to newer exhaust when round robin is also
131     *            enabled. If round robin is disabled then it will exhaust when
132     *            there are no more endpoints to failover
133     * @param inheritErrorHandler whether or not to inherit error handler. If
134     *            <tt>false</tt> then it will failover immediately in case of an
135     *            exception
136     * @param roundRobin whether or not to use round robin (which keeps state)
137     * @param exceptions exception classes which we want to failover if one of
138     *            them was thrown
139     * @return the builder
140     */
141    public LoadBalanceDefinition failover(int maximumFailoverAttempts, boolean inheritErrorHandler, boolean roundRobin, Class<?>... exceptions) {
142        return failover(maximumFailoverAttempts, inheritErrorHandler, roundRobin, false, exceptions);
143    }
144
145    /**
146     * Uses fail over load balancer
147     *
148     * @param maximumFailoverAttempts maximum number of failover attempts before
149     *            exhausting. Use -1 to newer exhaust when round robin is also
150     *            enabled. If round robin is disabled then it will exhaust when
151     *            there are no more endpoints to failover
152     * @param inheritErrorHandler whether or not to inherit error handler. If
153     *            <tt>false</tt> then it will failover immediately in case of an
154     *            exception
155     * @param roundRobin whether or not to use round robin (which keeps state)
156     * @param sticky whether or not to use sticky (which keeps state)
157     * @param exceptions exception classes which we want to failover if one of
158     *            them was thrown
159     * @return the builder
160     */
161    public LoadBalanceDefinition failover(int maximumFailoverAttempts, boolean inheritErrorHandler, boolean roundRobin, boolean sticky, Class<?>... exceptions) {
162        FailoverLoadBalancerDefinition def = new FailoverLoadBalancerDefinition();
163        def.setExceptionTypes(Arrays.asList(exceptions));
164        def.setMaximumFailoverAttempts(Integer.toString(maximumFailoverAttempts));
165        def.setRoundRobin(Boolean.toString(roundRobin));
166        def.setSticky(Boolean.toString(sticky));
167        setLoadBalancerType(def);
168        this.setInheritErrorHandler(inheritErrorHandler);
169        return this;
170    }
171
172    /**
173     * Uses weighted load balancer
174     *
175     * @param roundRobin used to set the processor selection algorithm.
176     * @param distributionRatio String of weighted ratios for distribution of
177     *            messages.
178     * @return the builder
179     */
180    public LoadBalanceDefinition weighted(boolean roundRobin, String distributionRatio) {
181        return weighted(roundRobin, distributionRatio, ",");
182    }
183
184    /**
185     * Uses weighted load balancer
186     *
187     * @param roundRobin used to set the processor selection algorithm.
188     * @param distributionRatio String of weighted ratios for distribution of
189     *            messages.
190     * @param distributionRatioDelimiter String containing delimiter to be used
191     *            for ratios
192     * @return the builder
193     */
194    public LoadBalanceDefinition weighted(boolean roundRobin, String distributionRatio, String distributionRatioDelimiter) {
195        WeightedLoadBalancerDefinition def = new WeightedLoadBalancerDefinition();
196        def.setRoundRobin(Boolean.toString(roundRobin));
197        def.setDistributionRatio(distributionRatio);
198        def.setDistributionRatioDelimiter(distributionRatioDelimiter);
199        setLoadBalancerType(def);
200        return this;
201    }
202
203    /**
204     * Uses round robin load balancer
205     *
206     * @return the builder
207     */
208    public LoadBalanceDefinition roundRobin() {
209        setLoadBalancerType(new RoundRobinLoadBalancerDefinition());
210        return this;
211    }
212
213    /**
214     * Uses random load balancer
215     *
216     * @return the builder
217     */
218    public LoadBalanceDefinition random() {
219        setLoadBalancerType(new RandomLoadBalancerDefinition());
220        return this;
221    }
222
223    /**
224     * Uses the custom load balancer
225     *
226     * @param ref reference to lookup a custom load balancer from the
227     *            {@link org.apache.camel.spi.Registry} to be used.
228     * @return the builder
229     */
230    public LoadBalanceDefinition custom(String ref) {
231        CustomLoadBalancerDefinition balancer = new CustomLoadBalancerDefinition();
232        balancer.setRef(ref);
233        setLoadBalancerType(balancer);
234        return this;
235    }
236
237    /**
238     * Uses sticky load balancer
239     *
240     * @param correlationExpression the expression for correlation
241     * @return the builder
242     */
243    public LoadBalanceDefinition sticky(Expression correlationExpression) {
244        StickyLoadBalancerDefinition def = new StickyLoadBalancerDefinition();
245        def.setCorrelationExpression(correlationExpression);
246        setLoadBalancerType(def);
247        return this;
248    }
249
250    /**
251     * Uses topic load balancer
252     * 
253     * @return the builder
254     */
255    public LoadBalanceDefinition topic() {
256        setLoadBalancerType(new TopicLoadBalancerDefinition());
257        return this;
258    }
259
260    @Override
261    public String getShortName() {
262        return "loadBalance";
263    }
264
265    @Override
266    public String getLabel() {
267        return getOutputs().stream().map(ProcessorDefinition::getLabel)
268                .collect(Collectors.joining(",", getShortName() + "[", "]"));
269    }
270
271    @Override
272    public String toString() {
273        return "LoadBalanceType[" + loadBalancerType + ", " + getOutputs() + "]";
274    }
275}