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