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