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}