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}