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 // TODO: Camel 3.0 - Should be named customLoadBalancer to avoid naming clash with custom dataformat 055 @XmlElement(required = false, name = "custom", type = CustomLoadBalancerDefinition.class), 056 @XmlElement(required = false, name = "roundRobin", type = RoundRobinLoadBalancerDefinition.class), 057 @XmlElement(required = false, name = "sticky", type = StickyLoadBalancerDefinition.class), 058 @XmlElement(required = false, name = "topic", type = TopicLoadBalancerDefinition.class), 059 @XmlElement(required = false, name = "weighted", type = WeightedLoadBalancerDefinition.class), 060 @XmlElement(required = false, name = "circuitBreaker", type = CircuitBreakerLoadBalancerDefinition.class)} 061 ) 062 private LoadBalancerDefinition loadBalancerType; 063 @XmlElementRef 064 private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>(); 065 066 public LoadBalanceDefinition() { 067 } 068 069 @Override 070 public List<ProcessorDefinition<?>> getOutputs() { 071 return outputs; 072 } 073 074 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 075 this.outputs = outputs; 076 if (outputs != null) { 077 for (ProcessorDefinition<?> output : outputs) { 078 configureChild(output); 079 } 080 } 081 } 082 083 public boolean isOutputSupported() { 084 return true; 085 } 086 087 public LoadBalancerDefinition getLoadBalancerType() { 088 return loadBalancerType; 089 } 090 091 /** 092 * The load balancer to be used 093 */ 094 public void setLoadBalancerType(LoadBalancerDefinition loadbalancer) { 095 if (loadBalancerType != null) { 096 throw new IllegalArgumentException("Loadbalancer already configured to: " + loadBalancerType + ". Cannot set it to: " + loadbalancer); 097 } 098 loadBalancerType = loadbalancer; 099 } 100 101 @Override 102 public Processor createProcessor(RouteContext routeContext) throws Exception { 103 // the load balancer is stateful so we should only create it once in case its used from a context scoped error handler 104 105 LoadBalancer loadBalancer = loadBalancerType.getLoadBalancer(routeContext); 106 if (loadBalancer == null) { 107 // then create it and reuse it 108 loadBalancer = loadBalancerType.createLoadBalancer(routeContext); 109 loadBalancerType.setLoadBalancer(loadBalancer); 110 111 // some load balancer can only support a fixed number of outputs 112 int max = loadBalancerType.getMaximumNumberOfOutputs(); 113 int size = getOutputs().size(); 114 if (size > max) { 115 throw new IllegalArgumentException("To many outputs configured on " + loadBalancerType + ": " + size + " > " + max); 116 } 117 118 for (ProcessorDefinition<?> processorType : getOutputs()) { 119 // output must not be another load balancer 120 // check for instanceof as the code below as there is compilation errors on earlier versions of JDK6 121 // on Windows boxes or with IBM JDKs etc. 122 if (LoadBalanceDefinition.class.isInstance(processorType)) { 123 throw new IllegalArgumentException("Loadbalancer already configured to: " + loadBalancerType + ". Cannot set it to: " + processorType); 124 } 125 Processor processor = createProcessor(routeContext, processorType); 126 processor = wrapChannel(routeContext, processor, processorType); 127 loadBalancer.addProcessor(processor); 128 } 129 } 130 131 Boolean inherit = inheritErrorHandler; 132 if (loadBalancerType instanceof FailoverLoadBalancerDefinition) { 133 // special for failover load balancer where you can configure it to not inherit error handler for its children 134 // but the load balancer itself should inherit so Camels error handler can react afterwards 135 inherit = true; 136 } 137 Processor target = wrapChannel(routeContext, loadBalancer, this, inherit); 138 return target; 139 } 140 141 // Fluent API 142 // ------------------------------------------------------------------------- 143 144 /** 145 * Uses a custom load balancer 146 * 147 * @param loadBalancer the load balancer 148 * @return the builder 149 */ 150 public LoadBalanceDefinition loadBalance(LoadBalancer loadBalancer) { 151 CustomLoadBalancerDefinition def = new CustomLoadBalancerDefinition(); 152 def.setLoadBalancer(loadBalancer); 153 setLoadBalancerType(def); 154 return this; 155 } 156 157 /** 158 * Uses fail over load balancer 159 * <p/> 160 * Will not round robin and inherit the error handler. 161 * 162 * @return the builder 163 */ 164 public LoadBalanceDefinition failover() { 165 return failover(-1, true, false); 166 } 167 168 /** 169 * Uses fail over load balancer 170 * <p/> 171 * Will not round robin and inherit the error handler. 172 * 173 * @param exceptions exception classes which we want to failover if one of them was thrown 174 * @return the builder 175 */ 176 public LoadBalanceDefinition failover(Class<?>... exceptions) { 177 return failover(-1, true, false, exceptions); 178 } 179 180 /** 181 * Uses fail over load balancer 182 * 183 * @param maximumFailoverAttempts maximum number of failover attempts before exhausting. 184 * Use -1 to newer exhaust when round robin is also enabled. 185 * If round robin is disabled then it will exhaust when there are no more endpoints to failover 186 * @param inheritErrorHandler whether or not to inherit error handler. 187 * If <tt>false</tt> then it will failover immediately in case of an exception 188 * @param roundRobin whether or not to use round robin (which keeps state) 189 * @param exceptions exception classes which we want to failover if one of them was thrown 190 * @return the builder 191 */ 192 public LoadBalanceDefinition failover(int maximumFailoverAttempts, boolean inheritErrorHandler, boolean roundRobin, Class<?>... exceptions) { 193 return failover(maximumFailoverAttempts, inheritErrorHandler, roundRobin, false, exceptions); 194 } 195 196 /** 197 * Uses fail over load balancer 198 * 199 * @param maximumFailoverAttempts maximum number of failover attempts before exhausting. 200 * Use -1 to newer exhaust when round robin is also enabled. 201 * If round robin is disabled then it will exhaust when there are no more endpoints to failover 202 * @param inheritErrorHandler whether or not to inherit error handler. 203 * If <tt>false</tt> then it will failover immediately in case of an exception 204 * @param roundRobin whether or not to use round robin (which keeps state) 205 * @param sticky whether or not to use sticky (which keeps state) 206 * @param exceptions exception classes which we want to failover if one of them was thrown 207 * @return the builder 208 */ 209 public LoadBalanceDefinition failover(int maximumFailoverAttempts, boolean inheritErrorHandler, boolean roundRobin, boolean sticky, Class<?>... exceptions) { 210 FailoverLoadBalancerDefinition def = new FailoverLoadBalancerDefinition(); 211 def.setExceptionTypes(Arrays.asList(exceptions)); 212 def.setMaximumFailoverAttempts(maximumFailoverAttempts); 213 def.setRoundRobin(roundRobin); 214 def.setSticky(sticky); 215 setLoadBalancerType(def); 216 this.setInheritErrorHandler(inheritErrorHandler); 217 return this; 218 } 219 220 /** 221 * Uses weighted load balancer 222 * 223 * @param roundRobin used to set the processor selection algorithm. 224 * @param distributionRatio String of weighted ratios for distribution of messages. 225 * @return the builder 226 */ 227 public LoadBalanceDefinition weighted(boolean roundRobin, String distributionRatio) { 228 return weighted(roundRobin, distributionRatio, ","); 229 } 230 231 /** 232 * Uses circuitBreaker load balancer 233 * 234 * @param threshold number of errors before failure. 235 * @param halfOpenAfter time interval in milliseconds for half open state. 236 * @param exceptions exception classes which we want to break if one of them was thrown 237 * @return the builder 238 * @deprecated use Hystrix EIP instead which is the popular Netflix implementation of circuit breaker 239 */ 240 @Deprecated 241 public LoadBalanceDefinition circuitBreaker(int threshold, long halfOpenAfter, Class<?>... exceptions) { 242 CircuitBreakerLoadBalancerDefinition def = new CircuitBreakerLoadBalancerDefinition(); 243 def.setExceptionTypes(Arrays.asList(exceptions)); 244 def.setThreshold(threshold); 245 def.setHalfOpenAfter(halfOpenAfter); 246 setLoadBalancerType(def); 247 return this; 248 } 249 250 /** 251 * Uses weighted load balancer 252 * 253 * @param roundRobin used to set the processor selection algorithm. 254 * @param distributionRatio String of weighted ratios for distribution of messages. 255 * @param distributionRatioDelimiter String containing delimiter to be used for ratios 256 * @return the builder 257 */ 258 public LoadBalanceDefinition weighted(boolean roundRobin, String distributionRatio, String distributionRatioDelimiter) { 259 WeightedLoadBalancerDefinition def = new WeightedLoadBalancerDefinition(); 260 def.setRoundRobin(roundRobin); 261 def.setDistributionRatio(distributionRatio); 262 def.setDistributionRatioDelimiter(distributionRatioDelimiter); 263 setLoadBalancerType(def); 264 return this; 265 } 266 267 /** 268 * Uses round robin load balancer 269 * 270 * @return the builder 271 */ 272 public LoadBalanceDefinition roundRobin() { 273 setLoadBalancerType(new RoundRobinLoadBalancerDefinition()); 274 return this; 275 } 276 277 /** 278 * Uses random load balancer 279 * 280 * @return the builder 281 */ 282 public LoadBalanceDefinition random() { 283 setLoadBalancerType(new RandomLoadBalancerDefinition()); 284 return this; 285 } 286 287 /** 288 * Uses the custom load balancer 289 * 290 * @param ref reference to lookup a custom load balancer from the {@link org.apache.camel.spi.Registry} to be used. 291 * @return the builder 292 */ 293 public LoadBalanceDefinition custom(String ref) { 294 CustomLoadBalancerDefinition balancer = new CustomLoadBalancerDefinition(); 295 balancer.setRef(ref); 296 setLoadBalancerType(balancer); 297 return this; 298 } 299 300 /** 301 * Uses sticky load balancer 302 * 303 * @param correlationExpression the expression for correlation 304 * @return the builder 305 */ 306 public LoadBalanceDefinition sticky(Expression correlationExpression) { 307 StickyLoadBalancerDefinition def = new StickyLoadBalancerDefinition(); 308 def.setCorrelationExpression(correlationExpression); 309 setLoadBalancerType(def); 310 return this; 311 } 312 313 /** 314 * Uses topic load balancer 315 * 316 * @return the builder 317 */ 318 public LoadBalanceDefinition topic() { 319 setLoadBalancerType(new TopicLoadBalancerDefinition()); 320 return this; 321 } 322 323 @Override 324 public String getLabel() { 325 CollectionStringBuffer buffer = new CollectionStringBuffer("loadBalance["); 326 List<ProcessorDefinition<?>> list = getOutputs(); 327 for (ProcessorDefinition<?> processorType : list) { 328 buffer.append(processorType.getLabel()); 329 } 330 buffer.append("]"); 331 return buffer.toString(); 332 } 333 334 @Override 335 public String toString() { 336 return "LoadBalanceType[" + loadBalancerType + ", " + getOutputs() + "]"; 337 } 338}