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.impl;
018
019import java.util.concurrent.BlockingQueue;
020import java.util.concurrent.ExecutorService;
021import java.util.concurrent.Executors;
022import java.util.concurrent.LinkedBlockingQueue;
023import java.util.concurrent.RejectedExecutionHandler;
024import java.util.concurrent.ScheduledExecutorService;
025import java.util.concurrent.ScheduledThreadPoolExecutor;
026import java.util.concurrent.SynchronousQueue;
027import java.util.concurrent.ThreadFactory;
028import java.util.concurrent.ThreadPoolExecutor;
029import java.util.concurrent.TimeUnit;
030
031import org.apache.camel.spi.ThreadPoolFactory;
032import org.apache.camel.spi.ThreadPoolProfile;
033import org.apache.camel.util.concurrent.RejectableScheduledThreadPoolExecutor;
034import org.apache.camel.util.concurrent.RejectableThreadPoolExecutor;
035import org.apache.camel.util.concurrent.SizedScheduledExecutorService;
036
037/**
038 * Factory for thread pools that uses the JDK {@link Executors} for creating the thread pools.
039 */
040public class DefaultThreadPoolFactory implements ThreadPoolFactory {
041
042    public ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
043        return Executors.newCachedThreadPool(threadFactory);
044    }
045    
046    @Override
047    public ExecutorService newThreadPool(ThreadPoolProfile profile, ThreadFactory factory) {
048        // allow core thread timeout is default false if not configured
049        boolean allow = profile.getAllowCoreThreadTimeOut() != null ? profile.getAllowCoreThreadTimeOut() : false;
050        return newThreadPool(profile.getPoolSize(), 
051                             profile.getMaxPoolSize(), 
052                             profile.getKeepAliveTime(),
053                             profile.getTimeUnit(),
054                             profile.getMaxQueueSize(),
055                             allow,
056                             profile.getRejectedExecutionHandler(),
057                             factory);
058    }
059
060    public ExecutorService newThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit timeUnit, int maxQueueSize, boolean allowCoreThreadTimeOut,
061                                         RejectedExecutionHandler rejectedExecutionHandler, ThreadFactory threadFactory) throws IllegalArgumentException {
062
063        // the core pool size must be 0 or higher
064        if (corePoolSize < 0) {
065            throw new IllegalArgumentException("CorePoolSize must be >= 0, was " + corePoolSize);
066        }
067
068        // validate max >= core
069        if (maxPoolSize < corePoolSize) {
070            throw new IllegalArgumentException("MaxPoolSize must be >= corePoolSize, was " + maxPoolSize + " >= " + corePoolSize);
071        }
072
073        BlockingQueue<Runnable> workQueue;
074        if (corePoolSize == 0 && maxQueueSize <= 0) {
075            // use a synchronous queue for direct-handover (no tasks stored on the queue)
076            workQueue = new SynchronousQueue<>();
077            // and force 1 as pool size to be able to create the thread pool by the JDK
078            corePoolSize = 1;
079            maxPoolSize = 1;
080        } else if (maxQueueSize <= 0) {
081            // use a synchronous queue for direct-handover (no tasks stored on the queue)
082            workQueue = new SynchronousQueue<>();
083        } else {
084            // bounded task queue to store tasks on the queue
085            workQueue = new LinkedBlockingQueue<>(maxQueueSize);
086        }
087
088        ThreadPoolExecutor answer = new RejectableThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue);
089        answer.setThreadFactory(threadFactory);
090        answer.allowCoreThreadTimeOut(allowCoreThreadTimeOut);
091        if (rejectedExecutionHandler == null) {
092            rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
093        }
094        answer.setRejectedExecutionHandler(rejectedExecutionHandler);
095        return answer;
096    }
097    
098    @Override
099    public ScheduledExecutorService newScheduledThreadPool(ThreadPoolProfile profile, ThreadFactory threadFactory) {
100        RejectedExecutionHandler rejectedExecutionHandler = profile.getRejectedExecutionHandler();
101        if (rejectedExecutionHandler == null) {
102            rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
103        }
104
105        ScheduledThreadPoolExecutor answer = new RejectableScheduledThreadPoolExecutor(profile.getPoolSize(), threadFactory, rejectedExecutionHandler);
106        answer.setRemoveOnCancelPolicy(true);
107
108        // need to wrap the thread pool in a sized to guard against the problem that the
109        // JDK created thread pool has an unbounded queue (see class javadoc), which mean
110        // we could potentially keep adding tasks, and run out of memory.
111        if (profile.getMaxPoolSize() > 0) {
112            return new SizedScheduledExecutorService(answer, profile.getMaxQueueSize());
113        } else {
114            return answer;
115        }
116    }
117
118}