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.List; 021import java.util.concurrent.ExecutorService; 022import java.util.concurrent.TimeUnit; 023import javax.xml.bind.annotation.XmlAccessType; 024import javax.xml.bind.annotation.XmlAccessorType; 025import javax.xml.bind.annotation.XmlAttribute; 026import javax.xml.bind.annotation.XmlRootElement; 027import javax.xml.bind.annotation.XmlTransient; 028import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 029 030import org.apache.camel.Processor; 031import org.apache.camel.ThreadPoolRejectedPolicy; 032import org.apache.camel.builder.ThreadPoolProfileBuilder; 033import org.apache.camel.builder.xml.TimeUnitAdapter; 034import org.apache.camel.processor.Pipeline; 035import org.apache.camel.processor.ThreadsProcessor; 036import org.apache.camel.spi.ExecutorServiceManager; 037import org.apache.camel.spi.Metadata; 038import org.apache.camel.spi.RouteContext; 039import org.apache.camel.spi.ThreadPoolProfile; 040 041/** 042 * Specifies that all steps after this node are processed asynchronously 043 * 044 * @version 045 */ 046@Metadata(label = "eip,routing") 047@XmlRootElement(name = "threads") 048@XmlAccessorType(XmlAccessType.FIELD) 049public class ThreadsDefinition extends OutputDefinition<ThreadsDefinition> implements ExecutorServiceAwareDefinition<ThreadsDefinition> { 050 051 // TODO: Camel 3.0 Should extend NoOutputDefinition 052 053 @XmlTransient 054 private ExecutorService executorService; 055 @XmlAttribute 056 private String executorServiceRef; 057 @XmlAttribute 058 private Integer poolSize; 059 @XmlAttribute 060 private Integer maxPoolSize; 061 @XmlAttribute 062 private Long keepAliveTime; 063 @XmlAttribute 064 @XmlJavaTypeAdapter(TimeUnitAdapter.class) 065 private TimeUnit timeUnit; 066 @XmlAttribute 067 private Integer maxQueueSize; 068 @XmlAttribute 069 private Boolean allowCoreThreadTimeOut; 070 @XmlAttribute @Metadata(defaultValue = "Threads") 071 private String threadName; 072 @XmlAttribute 073 private ThreadPoolRejectedPolicy rejectedPolicy; 074 @XmlAttribute @Metadata(defaultValue = "true") 075 private Boolean callerRunsWhenRejected; 076 077 public ThreadsDefinition() { 078 this.threadName = "Threads"; 079 } 080 081 @Override 082 public Processor createProcessor(RouteContext routeContext) throws Exception { 083 // the threads name 084 String name = getThreadName() != null ? getThreadName() : "Threads"; 085 // prefer any explicit configured executor service 086 boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, true); 087 ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, name, this, false); 088 089 // resolve what rejected policy to use 090 ThreadPoolRejectedPolicy policy = resolveRejectedPolicy(routeContext); 091 if (policy == null) { 092 if (callerRunsWhenRejected == null || callerRunsWhenRejected) { 093 // should use caller runs by default if not configured 094 policy = ThreadPoolRejectedPolicy.CallerRuns; 095 } else { 096 policy = ThreadPoolRejectedPolicy.Abort; 097 } 098 } 099 log.debug("Using ThreadPoolRejectedPolicy: {}", policy); 100 101 // if no explicit then create from the options 102 if (threadPool == null) { 103 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 104 // create the thread pool using a builder 105 ThreadPoolProfile profile = new ThreadPoolProfileBuilder(name) 106 .poolSize(getPoolSize()) 107 .maxPoolSize(getMaxPoolSize()) 108 .keepAliveTime(getKeepAliveTime(), getTimeUnit()) 109 .maxQueueSize(getMaxQueueSize()) 110 .rejectedPolicy(policy) 111 .allowCoreThreadTimeOut(getAllowCoreThreadTimeOut()) 112 .build(); 113 threadPool = manager.newThreadPool(this, name, profile); 114 shutdownThreadPool = true; 115 } else { 116 if (getThreadName() != null && !getThreadName().equals("Threads")) { 117 throw new IllegalArgumentException("ThreadName and executorServiceRef options cannot be used together."); 118 } 119 if (getPoolSize() != null) { 120 throw new IllegalArgumentException("PoolSize and executorServiceRef options cannot be used together."); 121 } 122 if (getMaxPoolSize() != null) { 123 throw new IllegalArgumentException("MaxPoolSize and executorServiceRef options cannot be used together."); 124 } 125 if (getKeepAliveTime() != null) { 126 throw new IllegalArgumentException("KeepAliveTime and executorServiceRef options cannot be used together."); 127 } 128 if (getTimeUnit() != null) { 129 throw new IllegalArgumentException("TimeUnit and executorServiceRef options cannot be used together."); 130 } 131 if (getMaxQueueSize() != null) { 132 throw new IllegalArgumentException("MaxQueueSize and executorServiceRef options cannot be used together."); 133 } 134 if (getRejectedPolicy() != null) { 135 throw new IllegalArgumentException("RejectedPolicy and executorServiceRef options cannot be used together."); 136 } 137 if (getAllowCoreThreadTimeOut() != null) { 138 throw new IllegalArgumentException("AllowCoreThreadTimeOut and executorServiceRef options cannot be used together."); 139 } 140 } 141 142 ThreadsProcessor thread = new ThreadsProcessor(routeContext.getCamelContext(), threadPool, shutdownThreadPool, policy); 143 144 List<Processor> pipe = new ArrayList<Processor>(2); 145 pipe.add(thread); 146 pipe.add(createChildProcessor(routeContext, true)); 147 // wrap in nested pipeline so this appears as one processor 148 // (recipient list definition does this as well) 149 return new Pipeline(routeContext.getCamelContext(), pipe) { 150 @Override 151 public String toString() { 152 return "Threads[" + getOutputs() + "]"; 153 } 154 }; 155 } 156 157 protected ThreadPoolRejectedPolicy resolveRejectedPolicy(RouteContext routeContext) { 158 if (getExecutorServiceRef() != null && getRejectedPolicy() == null) { 159 ThreadPoolProfile threadPoolProfile = routeContext.getCamelContext().getExecutorServiceManager().getThreadPoolProfile(getExecutorServiceRef()); 160 if (threadPoolProfile != null) { 161 return threadPoolProfile.getRejectedPolicy(); 162 } 163 } 164 return getRejectedPolicy(); 165 } 166 167 @Override 168 public String getLabel() { 169 return "threads"; 170 } 171 172 @Override 173 public String toString() { 174 return "Threads[" + getOutputs() + "]"; 175 } 176 177 /** 178 * To use a custom thread pool 179 */ 180 public ThreadsDefinition executorService(ExecutorService executorService) { 181 setExecutorService(executorService); 182 return this; 183 } 184 185 /** 186 * To refer to a custom thread pool or use a thread pool profile (as overlay) 187 */ 188 public ThreadsDefinition executorServiceRef(String executorServiceRef) { 189 setExecutorServiceRef(executorServiceRef); 190 return this; 191 } 192 193 /** 194 * Sets the core pool size 195 * 196 * @param poolSize the core pool size to keep minimum in the pool 197 * @return the builder 198 */ 199 public ThreadsDefinition poolSize(int poolSize) { 200 setPoolSize(poolSize); 201 return this; 202 } 203 204 /** 205 * Sets the maximum pool size 206 * 207 * @param maxPoolSize the maximum pool size 208 * @return the builder 209 */ 210 public ThreadsDefinition maxPoolSize(int maxPoolSize) { 211 setMaxPoolSize(maxPoolSize); 212 return this; 213 } 214 215 /** 216 * Sets the keep alive time for idle threads 217 * 218 * @param keepAliveTime keep alive time 219 * @return the builder 220 */ 221 public ThreadsDefinition keepAliveTime(long keepAliveTime) { 222 setKeepAliveTime(keepAliveTime); 223 return this; 224 } 225 226 /** 227 * Sets the keep alive time unit. 228 * By default SECONDS is used. 229 * 230 * @param keepAliveTimeUnits time unit 231 * @return the builder 232 */ 233 public ThreadsDefinition timeUnit(TimeUnit keepAliveTimeUnits) { 234 setTimeUnit(keepAliveTimeUnits); 235 return this; 236 } 237 238 /** 239 * Sets the maximum number of tasks in the work queue. 240 * <p/> 241 * Use <tt>-1</tt> or <tt>Integer.MAX_VALUE</tt> for an unbounded queue 242 * 243 * @param maxQueueSize the max queue size 244 * @return the builder 245 */ 246 public ThreadsDefinition maxQueueSize(int maxQueueSize) { 247 setMaxQueueSize(maxQueueSize); 248 return this; 249 } 250 251 /** 252 * Sets the handler for tasks which cannot be executed by the thread pool. 253 * 254 * @param rejectedPolicy the policy for the handler 255 * @return the builder 256 */ 257 public ThreadsDefinition rejectedPolicy(ThreadPoolRejectedPolicy rejectedPolicy) { 258 setRejectedPolicy(rejectedPolicy); 259 return this; 260 } 261 262 /** 263 * Sets the thread name to use. 264 * 265 * @param threadName the thread name 266 * @return the builder 267 */ 268 public ThreadsDefinition threadName(String threadName) { 269 setThreadName(threadName); 270 return this; 271 } 272 273 /** 274 * Whether or not to use as caller runs as <b>fallback</b> when a task is rejected being added to the thread pool (when its full). 275 * This is only used as fallback if no rejectedPolicy has been configured, or the thread pool has no configured rejection handler. 276 * <p/> 277 * Is by default <tt>true</tt> 278 * 279 * @param callerRunsWhenRejected whether or not the caller should run 280 * @return the builder 281 */ 282 public ThreadsDefinition callerRunsWhenRejected(boolean callerRunsWhenRejected) { 283 setCallerRunsWhenRejected(callerRunsWhenRejected); 284 return this; 285 } 286 287 /** 288 * Whether idle core threads is allowed to timeout and therefore can shrink the pool size below the core pool size 289 * <p/> 290 * Is by default <tt>false</tt> 291 * 292 * @param allowCoreThreadTimeOut <tt>true</tt> to allow timeout 293 * @return the builder 294 */ 295 public ThreadsDefinition allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { 296 setAllowCoreThreadTimeOut(allowCoreThreadTimeOut); 297 return this; 298 } 299 300 public ExecutorService getExecutorService() { 301 return executorService; 302 } 303 304 public void setExecutorService(ExecutorService executorService) { 305 this.executorService = executorService; 306 } 307 308 public String getExecutorServiceRef() { 309 return executorServiceRef; 310 } 311 312 public void setExecutorServiceRef(String executorServiceRef) { 313 this.executorServiceRef = executorServiceRef; 314 } 315 316 public Integer getPoolSize() { 317 return poolSize; 318 } 319 320 public void setPoolSize(Integer poolSize) { 321 this.poolSize = poolSize; 322 } 323 324 public Integer getMaxPoolSize() { 325 return maxPoolSize; 326 } 327 328 public void setMaxPoolSize(Integer maxPoolSize) { 329 this.maxPoolSize = maxPoolSize; 330 } 331 332 public Long getKeepAliveTime() { 333 return keepAliveTime; 334 } 335 336 public void setKeepAliveTime(Long keepAliveTime) { 337 this.keepAliveTime = keepAliveTime; 338 } 339 340 public TimeUnit getTimeUnit() { 341 return timeUnit; 342 } 343 344 public void setTimeUnit(TimeUnit timeUnit) { 345 this.timeUnit = timeUnit; 346 } 347 348 public Integer getMaxQueueSize() { 349 return maxQueueSize; 350 } 351 352 public void setMaxQueueSize(Integer maxQueueSize) { 353 this.maxQueueSize = maxQueueSize; 354 } 355 356 public String getThreadName() { 357 return threadName; 358 } 359 360 public void setThreadName(String threadName) { 361 this.threadName = threadName; 362 } 363 364 public ThreadPoolRejectedPolicy getRejectedPolicy() { 365 return rejectedPolicy; 366 } 367 368 public void setRejectedPolicy(ThreadPoolRejectedPolicy rejectedPolicy) { 369 this.rejectedPolicy = rejectedPolicy; 370 } 371 372 public Boolean getCallerRunsWhenRejected() { 373 return callerRunsWhenRejected; 374 } 375 376 public void setCallerRunsWhenRejected(Boolean callerRunsWhenRejected) { 377 this.callerRunsWhenRejected = callerRunsWhenRejected; 378 } 379 380 public Boolean getAllowCoreThreadTimeOut() { 381 return allowCoreThreadTimeOut; 382 } 383 384 public void setAllowCoreThreadTimeOut(Boolean allowCoreThreadTimeOut) { 385 this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; 386 } 387}