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     */
017    package org.apache.servicemix.executors.impl;
018    
019    import java.lang.reflect.Method;
020    import java.util.HashMap;
021    import java.util.Map;
022    import java.util.concurrent.ArrayBlockingQueue;
023    import java.util.concurrent.BlockingQueue;
024    import java.util.concurrent.LinkedBlockingQueue;
025    import java.util.concurrent.RejectedExecutionHandler;
026    import java.util.concurrent.SynchronousQueue;
027    import java.util.concurrent.ThreadFactory;
028    import java.util.concurrent.ThreadPoolExecutor;
029    import java.util.concurrent.TimeUnit;
030    import java.util.concurrent.atomic.AtomicInteger;
031    
032    import org.apache.servicemix.executors.Executor;
033    import org.apache.servicemix.executors.ExecutorFactory;
034    
035    /**
036     * Default implementation of the ExecutorFactory.
037     * 
038     * Configuration can be done hierachically.
039     * When an executor is created with an id of <code>foo.bar</code>,
040     * the factory will look for a configuration in the following
041     * way:
042     * <ul>
043     *    <li>configs.get("foo.bar")</li>
044     *    <li>configs.get("foo")</li>
045     *    <li>defaultConfig</li>
046     * </ul>
047     * 
048     * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
049     */
050    public class ExecutorFactoryImpl implements ExecutorFactory {
051    
052        private ExecutorConfig defaultConfig = new ExecutorConfig();
053    
054        private Map<String, ExecutorConfig> configs = new HashMap<String, ExecutorConfig>();
055    
056        public Executor createExecutor(String id) {
057            ExecutorConfig config = getConfig(id);
058            return new ExecutorImpl(createService(id, config), config.getShutdownDelay());
059        }
060    
061        protected ExecutorConfig getConfig(String id) {
062            ExecutorConfig config = null;
063            if (configs != null) {
064                config = configs.get(id);
065                while (config == null && id.indexOf('.') > 0) {
066                    id = id.substring(0, id.lastIndexOf('.'));
067                    config = configs.get(id);
068                }
069            }
070            if (config == null) {
071                config = defaultConfig;
072            }
073            return config;
074        }
075    
076        protected ThreadPoolExecutor createService(String id, ExecutorConfig config) {
077            if (config.getQueueSize() != 0 && config.getCorePoolSize() == 0) {
078                throw new IllegalArgumentException("CorePoolSize must be > 0 when using a capacity queue");
079            }
080            BlockingQueue<Runnable> queue;
081            if (config.getQueueSize() == 0) {
082                queue = new SynchronousQueue<Runnable>();
083            } else if (config.getQueueSize() < 0 || config.getQueueSize() == Integer.MAX_VALUE) {
084                queue = new LinkedBlockingQueue<Runnable>();
085            } else {
086                queue = new ArrayBlockingQueue<Runnable>(config.getQueueSize());
087            }
088            ThreadFactory factory = new DefaultThreadFactory(id, config.isThreadDaemon(), config.getThreadPriority());
089            RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
090            ThreadPoolExecutor service = new ThreadPoolExecutor(config.getCorePoolSize(),
091                    config.getMaximumPoolSize() < 0 ? Integer.MAX_VALUE : config.getMaximumPoolSize(), config
092                            .getKeepAliveTime(), TimeUnit.MILLISECONDS, queue, factory, handler);
093            if (config.isAllowCoreThreadsTimeout()) {
094                try {
095                    Method mth = service.getClass().getMethod("allowCoreThreadTimeOut", new Class[] {boolean.class });
096                    mth.invoke(service, new Object[] {Boolean.TRUE });
097                } catch (Throwable t) {
098                    // Do nothing
099                }
100            }
101            return service;
102        }
103    
104        /**
105         * The default thread factory
106         */
107        static class DefaultThreadFactory implements ThreadFactory {
108            final ThreadGroup group;
109    
110            final AtomicInteger threadNumber = new AtomicInteger(1);
111    
112            final String namePrefix;
113    
114            final boolean daemon;
115    
116            final int priority;
117    
118            DefaultThreadFactory(String id, boolean daemon, int priority) {
119                SecurityManager s = System.getSecurityManager();
120                group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
121                namePrefix = "pool-" + id + "-thread-";
122                this.daemon = daemon;
123                this.priority = priority;
124            }
125    
126            public Thread newThread(Runnable r) {
127                Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
128                if (t.isDaemon() != daemon) {
129                    t.setDaemon(daemon);
130                }
131                if (t.getPriority() != priority) {
132                    t.setPriority(priority);
133                }
134                return t;
135            }
136        }
137    
138        /**
139         * @return the configs
140         */
141        public Map<String, ExecutorConfig> getConfigs() {
142            return configs;
143        }
144    
145        /**
146         * @param configs the configs to set
147         */
148        public void setConfigs(Map<String, ExecutorConfig> configs) {
149            this.configs = configs;
150        }
151    
152        /**
153         * @return the defaultConfig
154         */
155        public ExecutorConfig getDefaultConfig() {
156            return defaultConfig;
157        }
158    
159        /**
160         * @param defaultConfig the defaultConfig to set
161         */
162        public void setDefaultConfig(ExecutorConfig defaultConfig) {
163            this.defaultConfig = defaultConfig;
164        }
165    
166    }