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