001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.scheduling.config;
018
019import java.util.concurrent.RejectedExecutionHandler;
020
021import org.springframework.beans.factory.BeanNameAware;
022import org.springframework.beans.factory.DisposableBean;
023import org.springframework.beans.factory.FactoryBean;
024import org.springframework.beans.factory.InitializingBean;
025import org.springframework.core.task.TaskExecutor;
026import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
027import org.springframework.util.StringUtils;
028
029/**
030 * {@link FactoryBean} for creating {@link ThreadPoolTaskExecutor} instances,
031 * primarily used behind the XML task namespace.
032 *
033 * @author Mark Fisher
034 * @author Juergen Hoeller
035 * @since 3.0
036 */
037public class TaskExecutorFactoryBean implements
038                FactoryBean<TaskExecutor>, BeanNameAware, InitializingBean, DisposableBean {
039
040        private String poolSize;
041
042        private Integer queueCapacity;
043
044        private RejectedExecutionHandler rejectedExecutionHandler;
045
046        private Integer keepAliveSeconds;
047
048        private String beanName;
049
050        private ThreadPoolTaskExecutor target;
051
052
053        public void setPoolSize(String poolSize) {
054                this.poolSize = poolSize;
055        }
056
057        public void setQueueCapacity(int queueCapacity) {
058                this.queueCapacity = queueCapacity;
059        }
060
061        public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
062                this.rejectedExecutionHandler = rejectedExecutionHandler;
063        }
064
065        public void setKeepAliveSeconds(int keepAliveSeconds) {
066                this.keepAliveSeconds = keepAliveSeconds;
067        }
068
069        @Override
070        public void setBeanName(String beanName) {
071                this.beanName = beanName;
072        }
073
074
075        @Override
076        public void afterPropertiesSet() {
077                this.target = new ThreadPoolTaskExecutor();
078                determinePoolSizeRange();
079                if (this.queueCapacity != null) {
080                        this.target.setQueueCapacity(this.queueCapacity);
081                }
082                if (this.keepAliveSeconds != null) {
083                        this.target.setKeepAliveSeconds(this.keepAliveSeconds);
084                }
085                if (this.rejectedExecutionHandler != null) {
086                        this.target.setRejectedExecutionHandler(this.rejectedExecutionHandler);
087                }
088                if (this.beanName != null) {
089                        this.target.setThreadNamePrefix(this.beanName + "-");
090                }
091                this.target.afterPropertiesSet();
092        }
093
094        private void determinePoolSizeRange() {
095                if (StringUtils.hasText(this.poolSize)) {
096                        try {
097                                int corePoolSize;
098                                int maxPoolSize;
099                                int separatorIndex = this.poolSize.indexOf('-');
100                                if (separatorIndex != -1) {
101                                        corePoolSize = Integer.parseInt(this.poolSize.substring(0, separatorIndex));
102                                        maxPoolSize = Integer.parseInt(this.poolSize.substring(separatorIndex + 1));
103                                        if (corePoolSize > maxPoolSize) {
104                                                throw new IllegalArgumentException(
105                                                                "Lower bound of pool-size range must not exceed the upper bound");
106                                        }
107                                        if (this.queueCapacity == null) {
108                                                // No queue-capacity provided, so unbounded
109                                                if (corePoolSize == 0) {
110                                                        // Actually set 'corePoolSize' to the upper bound of the range
111                                                        // but allow core threads to timeout...
112                                                        this.target.setAllowCoreThreadTimeOut(true);
113                                                        corePoolSize = maxPoolSize;
114                                                }
115                                                else {
116                                                        // Non-zero lower bound implies a core-max size range...
117                                                        throw new IllegalArgumentException(
118                                                                        "A non-zero lower bound for the size range requires a queue-capacity value");
119                                                }
120                                        }
121                                }
122                                else {
123                                        Integer value = Integer.valueOf(this.poolSize);
124                                        corePoolSize = value;
125                                        maxPoolSize = value;
126                                }
127                                this.target.setCorePoolSize(corePoolSize);
128                                this.target.setMaxPoolSize(maxPoolSize);
129                        }
130                        catch (NumberFormatException ex) {
131                                throw new IllegalArgumentException("Invalid pool-size value [" + this.poolSize + "]: only single " +
132                                                "maximum integer (e.g. \"5\") and minimum-maximum range (e.g. \"3-5\") are supported", ex);
133                        }
134                }
135        }
136
137
138        @Override
139        public TaskExecutor getObject() {
140                return this.target;
141        }
142
143        @Override
144        public Class<? extends TaskExecutor> getObjectType() {
145                return (this.target != null ? this.target.getClass() : ThreadPoolTaskExecutor.class);
146        }
147
148        @Override
149        public boolean isSingleton() {
150                return true;
151        }
152
153
154        @Override
155        public void destroy() {
156                this.target.destroy();
157        }
158
159}