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