001/* 002 * Copyright 2002-2020 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.lang.Nullable; 027import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 028import org.springframework.util.StringUtils; 029 030/** 031 * {@link FactoryBean} for creating {@link ThreadPoolTaskExecutor} instances, 032 * primarily used behind the XML task namespace. 033 * 034 * @author Mark Fisher 035 * @author Juergen Hoeller 036 * @since 3.0 037 */ 038public class TaskExecutorFactoryBean implements 039 FactoryBean<TaskExecutor>, BeanNameAware, InitializingBean, DisposableBean { 040 041 @Nullable 042 private String poolSize; 043 044 @Nullable 045 private Integer queueCapacity; 046 047 @Nullable 048 private RejectedExecutionHandler rejectedExecutionHandler; 049 050 @Nullable 051 private Integer keepAliveSeconds; 052 053 @Nullable 054 private String beanName; 055 056 @Nullable 057 private ThreadPoolTaskExecutor target; 058 059 060 public void setPoolSize(String poolSize) { 061 this.poolSize = poolSize; 062 } 063 064 public void setQueueCapacity(int queueCapacity) { 065 this.queueCapacity = queueCapacity; 066 } 067 068 public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { 069 this.rejectedExecutionHandler = rejectedExecutionHandler; 070 } 071 072 public void setKeepAliveSeconds(int keepAliveSeconds) { 073 this.keepAliveSeconds = keepAliveSeconds; 074 } 075 076 @Override 077 public void setBeanName(String beanName) { 078 this.beanName = beanName; 079 } 080 081 082 @Override 083 public void afterPropertiesSet() { 084 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 085 determinePoolSizeRange(executor); 086 if (this.queueCapacity != null) { 087 executor.setQueueCapacity(this.queueCapacity); 088 } 089 if (this.keepAliveSeconds != null) { 090 executor.setKeepAliveSeconds(this.keepAliveSeconds); 091 } 092 if (this.rejectedExecutionHandler != null) { 093 executor.setRejectedExecutionHandler(this.rejectedExecutionHandler); 094 } 095 if (this.beanName != null) { 096 executor.setThreadNamePrefix(this.beanName + "-"); 097 } 098 executor.afterPropertiesSet(); 099 this.target = executor; 100 } 101 102 private void determinePoolSizeRange(ThreadPoolTaskExecutor executor) { 103 if (StringUtils.hasText(this.poolSize)) { 104 try { 105 int corePoolSize; 106 int maxPoolSize; 107 int separatorIndex = this.poolSize.indexOf('-'); 108 if (separatorIndex != -1) { 109 corePoolSize = Integer.parseInt(this.poolSize.substring(0, separatorIndex)); 110 maxPoolSize = Integer.parseInt(this.poolSize.substring(separatorIndex + 1)); 111 if (corePoolSize > maxPoolSize) { 112 throw new IllegalArgumentException( 113 "Lower bound of pool-size range must not exceed the upper bound"); 114 } 115 if (this.queueCapacity == null) { 116 // No queue-capacity provided, so unbounded 117 if (corePoolSize == 0) { 118 // Actually set 'corePoolSize' to the upper bound of the range 119 // but allow core threads to timeout... 120 executor.setAllowCoreThreadTimeOut(true); 121 corePoolSize = maxPoolSize; 122 } 123 else { 124 // Non-zero lower bound implies a core-max size range... 125 throw new IllegalArgumentException( 126 "A non-zero lower bound for the size range requires a queue-capacity value"); 127 } 128 } 129 } 130 else { 131 Integer value = Integer.valueOf(this.poolSize); 132 corePoolSize = value; 133 maxPoolSize = value; 134 } 135 executor.setCorePoolSize(corePoolSize); 136 executor.setMaxPoolSize(maxPoolSize); 137 } 138 catch (NumberFormatException ex) { 139 throw new IllegalArgumentException("Invalid pool-size value [" + this.poolSize + "]: only single " + 140 "maximum integer (e.g. \"5\") and minimum-maximum range (e.g. \"3-5\") are supported", ex); 141 } 142 } 143 } 144 145 146 @Override 147 @Nullable 148 public TaskExecutor getObject() { 149 return this.target; 150 } 151 152 @Override 153 public Class<? extends TaskExecutor> getObjectType() { 154 return (this.target != null ? this.target.getClass() : ThreadPoolTaskExecutor.class); 155 } 156 157 @Override 158 public boolean isSingleton() { 159 return true; 160 } 161 162 163 @Override 164 public void destroy() { 165 if (this.target != null) { 166 this.target.destroy(); 167 } 168 } 169 170}