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