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.loadbalancer;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlElement;
026import javax.xml.bind.annotation.XmlRootElement;
027import javax.xml.bind.annotation.XmlTransient;
028
029import org.apache.camel.model.LoadBalancerDefinition;
030import org.apache.camel.processor.loadbalancer.FailOverLoadBalancer;
031import org.apache.camel.processor.loadbalancer.LoadBalancer;
032import org.apache.camel.spi.Metadata;
033import org.apache.camel.spi.RouteContext;
034import org.apache.camel.util.ObjectHelper;
035
036/**
037 * Failover load balancer
038 *
039 * The failover load balancer is capable of trying the next processor in case an Exchange failed with an exception during processing.
040 * You can constrain the failover to activate only when one exception of a list you specify occurs.
041 * If you do not specify a list any exception will cause fail over to occur.
042 * This balancer uses the same strategy for matching exceptions as the Exception Clause does for the onException.
043 */
044@Metadata(label = "eip,routing,loadbalance")
045@XmlRootElement(name = "failover")
046@XmlAccessorType(XmlAccessType.FIELD)
047public class FailoverLoadBalancerDefinition extends LoadBalancerDefinition {
048    @XmlTransient
049    private List<Class<?>> exceptionTypes = new ArrayList<>();
050    @XmlElement(name = "exception")
051    private List<String> exceptions = new ArrayList<>();
052    @XmlAttribute
053    private Boolean roundRobin;
054    @XmlAttribute
055    private Boolean sticky;
056    @XmlAttribute @Metadata(defaultValue = "-1")
057    private Integer maximumFailoverAttempts;
058
059    public FailoverLoadBalancerDefinition() {
060    }
061
062    @Override
063    protected LoadBalancer createLoadBalancer(RouteContext routeContext) {
064        FailOverLoadBalancer answer;
065
066        List<Class<?>> classes = new ArrayList<>();
067        if (!exceptionTypes.isEmpty()) {
068            classes.addAll(exceptionTypes);
069        } else if (!exceptions.isEmpty()) {
070            for (String name : exceptions) {
071                Class<?> type = routeContext.getCamelContext().getClassResolver().resolveClass(name);
072                if (type == null) {
073                    throw new IllegalArgumentException("Cannot find class: " + name + " in the classpath");
074                }
075                if (!ObjectHelper.isAssignableFrom(Throwable.class, type)) {
076                    throw new IllegalArgumentException("Class is not an instance of Throwable: " + type);
077                }
078                classes.add(type);
079            }
080        }
081        if (classes.isEmpty()) {
082            answer = new FailOverLoadBalancer();
083        } else {
084            answer = new FailOverLoadBalancer(classes);
085        }
086
087        if (getMaximumFailoverAttempts() != null) {
088            answer.setMaximumFailoverAttempts(getMaximumFailoverAttempts());
089        }
090        if (roundRobin != null) {
091            answer.setRoundRobin(roundRobin);
092        }
093        if (sticky != null) {
094            answer.setSticky(sticky);
095        }
096
097        return answer;
098    }
099
100    public List<String> getExceptions() {
101        return exceptions;
102    }
103
104    /**
105     * A list of class names for specific exceptions to monitor.
106     * If no exceptions is configured then all exceptions is monitored
107     */
108    public void setExceptions(List<String> exceptions) {
109        this.exceptions = exceptions;
110    }
111
112    public List<Class<?>> getExceptionTypes() {
113        return exceptionTypes;
114    }
115
116    /**
117     * A list of specific exceptions to monitor.
118     * If no exceptions is configured then all exceptions is monitored
119     */
120    public void setExceptionTypes(List<Class<?>> exceptionTypes) {
121        this.exceptionTypes = exceptionTypes;
122    }
123
124    public Boolean getRoundRobin() {
125        return roundRobin;
126    }
127
128    /**
129     * Whether or not the failover load balancer should operate in round robin mode or not.
130     * If not, then it will always start from the first endpoint when a new message is to be processed.
131     * In other words it restart from the top for every message.
132     * If round robin is enabled, then it keeps state and will continue with the next endpoint in a round robin fashion.
133     * <p/>
134     * You can also enable sticky mode together with round robin, if so then it will pick the last known good endpoint
135     * to use when starting the load balancing (instead of using the next when starting).
136     */
137    public void setRoundRobin(Boolean roundRobin) {
138        this.roundRobin = roundRobin;
139    }
140
141    public Boolean getSticky() {
142        return sticky;
143    }
144
145    /**
146     * Whether or not the failover load balancer should operate in sticky mode or not.
147     * If not, then it will always start from the first endpoint when a new message is to be processed.
148     * In other words it restart from the top for every message.
149     * If sticky is enabled, then it keeps state and will continue with the last known good endpoint.
150     * <p/>
151     * You can also enable sticky mode together with round robin, if so then it will pick the last known good endpoint
152     * to use when starting the load balancing (instead of using the next when starting).
153     */
154    public void setSticky(Boolean sticky) {
155        this.sticky = sticky;
156    }
157
158    public Integer getMaximumFailoverAttempts() {
159        return maximumFailoverAttempts;
160    }
161
162    /**
163     * A value to indicate after X failover attempts we should exhaust (give up).
164     * Use -1 to indicate never give up and continuously try to failover. Use 0 to never failover.
165     * And use e.g. 3 to failover at most 3 times before giving up.
166     * his option can be used whether or not roundRobin is enabled or not.
167     */
168    public void setMaximumFailoverAttempts(Integer maximumFailoverAttempts) {
169        this.maximumFailoverAttempts = maximumFailoverAttempts;
170    }
171
172    @Override
173    public String toString() {
174        return "FailoverLoadBalancer";
175    }
176}