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