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 // if no explicit then create from the options 089 if (threadPool == null) { 090 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 091 // create the thread pool using a builder 092 ThreadPoolProfile profile = new ThreadPoolProfileBuilder(name) 093 .poolSize(getPoolSize()) 094 .maxPoolSize(getMaxPoolSize()) 095 .keepAliveTime(getKeepAliveTime(), getTimeUnit()) 096 .maxQueueSize(getMaxQueueSize()) 097 .rejectedPolicy(getRejectedPolicy()) 098 .allowCoreThreadTimeOut(getAllowCoreThreadTimeOut()) 099 .build(); 100 threadPool = manager.newThreadPool(this, name, profile); 101 shutdownThreadPool = true; 102 } else { 103 if (getThreadName() != null && !getThreadName().equals("Threads")) { 104 throw new IllegalArgumentException("ThreadName and executorServiceRef options cannot be used together."); 105 } 106 if (getPoolSize() != null) { 107 throw new IllegalArgumentException("PoolSize and executorServiceRef options cannot be used together."); 108 } 109 if (getMaxPoolSize() != null) { 110 throw new IllegalArgumentException("MaxPoolSize and executorServiceRef options cannot be used together."); 111 } 112 if (getKeepAliveTime() != null) { 113 throw new IllegalArgumentException("KeepAliveTime and executorServiceRef options cannot be used together."); 114 } 115 if (getMaxQueueSize() != null) { 116 throw new IllegalArgumentException("MaxQueueSize and executorServiceRef options cannot be used together."); 117 } 118 if (getRejectedPolicy() != null) { 119 throw new IllegalArgumentException("RejectedPolicy and executorServiceRef options cannot be used together."); 120 } 121 if (getAllowCoreThreadTimeOut() != null) { 122 throw new IllegalArgumentException("AllowCoreThreadTimeOut and executorServiceRef options cannot be used together."); 123 } 124 } 125 126 ThreadsProcessor thread = new ThreadsProcessor(routeContext.getCamelContext(), threadPool, shutdownThreadPool); 127 if (getCallerRunsWhenRejected() == null) { 128 // should be true by default 129 thread.setCallerRunsWhenRejected(true); 130 } else { 131 thread.setCallerRunsWhenRejected(getCallerRunsWhenRejected()); 132 } 133 thread.setRejectedPolicy(resolveRejectedPolicy(routeContext)); 134 135 List<Processor> pipe = new ArrayList<Processor>(2); 136 pipe.add(thread); 137 pipe.add(createChildProcessor(routeContext, true)); 138 // wrap in nested pipeline so this appears as one processor 139 // (recipient list definition does this as well) 140 return new Pipeline(routeContext.getCamelContext(), pipe) { 141 @Override 142 public String toString() { 143 return "Threads[" + getOutputs() + "]"; 144 } 145 }; 146 } 147 148 protected ThreadPoolRejectedPolicy resolveRejectedPolicy(RouteContext routeContext) { 149 if (getExecutorServiceRef() != null && getRejectedPolicy() == null) { 150 ThreadPoolProfile threadPoolProfile = routeContext.getCamelContext().getExecutorServiceManager().getThreadPoolProfile(getExecutorServiceRef()); 151 if (threadPoolProfile != null) { 152 return threadPoolProfile.getRejectedPolicy(); 153 } 154 } 155 return getRejectedPolicy(); 156 } 157 158 @Override 159 public String getLabel() { 160 return "threads"; 161 } 162 163 @Override 164 public String toString() { 165 return "Threads[" + getOutputs() + "]"; 166 } 167 168 /** 169 * To use a custom thread pool 170 */ 171 public ThreadsDefinition executorService(ExecutorService executorService) { 172 setExecutorService(executorService); 173 return this; 174 } 175 176 /** 177 * To refer to a custom thread pool or use a thread pool profile (as overlay) 178 */ 179 public ThreadsDefinition executorServiceRef(String executorServiceRef) { 180 setExecutorServiceRef(executorServiceRef); 181 return this; 182 } 183 184 /** 185 * Sets the core pool size 186 * 187 * @param poolSize the core pool size to keep minimum in the pool 188 * @return the builder 189 */ 190 public ThreadsDefinition poolSize(int poolSize) { 191 setPoolSize(poolSize); 192 return this; 193 } 194 195 /** 196 * Sets the maximum pool size 197 * 198 * @param maxPoolSize the maximum pool size 199 * @return the builder 200 */ 201 public ThreadsDefinition maxPoolSize(int maxPoolSize) { 202 setMaxPoolSize(maxPoolSize); 203 return this; 204 } 205 206 /** 207 * Sets the keep alive time for idle threads 208 * 209 * @param keepAliveTime keep alive time 210 * @return the builder 211 */ 212 public ThreadsDefinition keepAliveTime(long keepAliveTime) { 213 setKeepAliveTime(keepAliveTime); 214 return this; 215 } 216 217 /** 218 * Sets the keep alive time unit. 219 * By default SECONDS is used. 220 * 221 * @param keepAliveTimeUnits time unit 222 * @return the builder 223 */ 224 public ThreadsDefinition timeUnit(TimeUnit keepAliveTimeUnits) { 225 setTimeUnit(keepAliveTimeUnits); 226 return this; 227 } 228 229 /** 230 * Sets the maximum number of tasks in the work queue. 231 * <p/> 232 * Use <tt>-1</tt> or <tt>Integer.MAX_VALUE</tt> for an unbounded queue 233 * 234 * @param maxQueueSize the max queue size 235 * @return the builder 236 */ 237 public ThreadsDefinition maxQueueSize(int maxQueueSize) { 238 setMaxQueueSize(maxQueueSize); 239 return this; 240 } 241 242 /** 243 * Sets the handler for tasks which cannot be executed by the thread pool. 244 * 245 * @param rejectedPolicy the policy for the handler 246 * @return the builder 247 */ 248 public ThreadsDefinition rejectedPolicy(ThreadPoolRejectedPolicy rejectedPolicy) { 249 setRejectedPolicy(rejectedPolicy); 250 return this; 251 } 252 253 /** 254 * Sets the thread name to use. 255 * 256 * @param threadName the thread name 257 * @return the builder 258 */ 259 public ThreadsDefinition threadName(String threadName) { 260 setThreadName(threadName); 261 return this; 262 } 263 264 /** 265 * Whether or not the caller should run the task when it was rejected by the thread pool. 266 * <p/> 267 * Is by default <tt>true</tt> 268 * 269 * @param callerRunsWhenRejected whether or not the caller should run 270 * @return the builder 271 */ 272 public ThreadsDefinition callerRunsWhenRejected(boolean callerRunsWhenRejected) { 273 setCallerRunsWhenRejected(callerRunsWhenRejected); 274 return this; 275 } 276 277 /** 278 * Whether idle core threads is allowed to timeout and therefore can shrink the pool size below the core pool size 279 * <p/> 280 * Is by default <tt>false</tt> 281 * 282 * @param allowCoreThreadTimeOut <tt>true</tt> to allow timeout 283 * @return the builder 284 */ 285 public ThreadsDefinition allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { 286 setAllowCoreThreadTimeOut(allowCoreThreadTimeOut); 287 return this; 288 } 289 290 public ExecutorService getExecutorService() { 291 return executorService; 292 } 293 294 public void setExecutorService(ExecutorService executorService) { 295 this.executorService = executorService; 296 } 297 298 public String getExecutorServiceRef() { 299 return executorServiceRef; 300 } 301 302 public void setExecutorServiceRef(String executorServiceRef) { 303 this.executorServiceRef = executorServiceRef; 304 } 305 306 public Integer getPoolSize() { 307 return poolSize; 308 } 309 310 public void setPoolSize(Integer poolSize) { 311 this.poolSize = poolSize; 312 } 313 314 public Integer getMaxPoolSize() { 315 return maxPoolSize; 316 } 317 318 public void setMaxPoolSize(Integer maxPoolSize) { 319 this.maxPoolSize = maxPoolSize; 320 } 321 322 public Long getKeepAliveTime() { 323 return keepAliveTime; 324 } 325 326 public void setKeepAliveTime(Long keepAliveTime) { 327 this.keepAliveTime = keepAliveTime; 328 } 329 330 public TimeUnit getTimeUnit() { 331 return timeUnit; 332 } 333 334 public void setTimeUnit(TimeUnit timeUnit) { 335 this.timeUnit = timeUnit; 336 } 337 338 public Integer getMaxQueueSize() { 339 return maxQueueSize; 340 } 341 342 public void setMaxQueueSize(Integer maxQueueSize) { 343 this.maxQueueSize = maxQueueSize; 344 } 345 346 public String getThreadName() { 347 return threadName; 348 } 349 350 public void setThreadName(String threadName) { 351 this.threadName = threadName; 352 } 353 354 public ThreadPoolRejectedPolicy getRejectedPolicy() { 355 return rejectedPolicy; 356 } 357 358 public void setRejectedPolicy(ThreadPoolRejectedPolicy rejectedPolicy) { 359 this.rejectedPolicy = rejectedPolicy; 360 } 361 362 public Boolean getCallerRunsWhenRejected() { 363 return callerRunsWhenRejected; 364 } 365 366 public void setCallerRunsWhenRejected(Boolean callerRunsWhenRejected) { 367 this.callerRunsWhenRejected = callerRunsWhenRejected; 368 } 369 370 public Boolean getAllowCoreThreadTimeOut() { 371 return allowCoreThreadTimeOut; 372 } 373 374 public void setAllowCoreThreadTimeOut(Boolean allowCoreThreadTimeOut) { 375 this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; 376 } 377}