1 /* 2 * Copyright 2002-2013 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.scheduling.concurrent; 18 19 import java.util.concurrent.ExecutorService; 20 import java.util.concurrent.RejectedExecutionHandler; 21 import java.util.concurrent.ThreadFactory; 22 import java.util.concurrent.ThreadPoolExecutor; 23 import java.util.concurrent.TimeUnit; 24 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 28 import org.springframework.beans.factory.BeanNameAware; 29 import org.springframework.beans.factory.DisposableBean; 30 import org.springframework.beans.factory.InitializingBean; 31 32 /** 33 * Base class for classes that are setting up a 34 * {@code java.util.concurrent.ExecutorService} 35 * (typically a {@link java.util.concurrent.ThreadPoolExecutor}). 36 * Defines common configuration settings and common lifecycle handling. 37 * 38 * @author Juergen Hoeller 39 * @since 3.0 40 * @see java.util.concurrent.ExecutorService 41 * @see java.util.concurrent.Executors 42 * @see java.util.concurrent.ThreadPoolExecutor 43 */ 44 @SuppressWarnings("serial") 45 public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory 46 implements BeanNameAware, InitializingBean, DisposableBean { 47 48 protected final Log logger = LogFactory.getLog(getClass()); 49 50 private ThreadFactory threadFactory = this; 51 52 private boolean threadNamePrefixSet = false; 53 54 private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); 55 56 private boolean waitForTasksToCompleteOnShutdown = false; 57 58 private int awaitTerminationSeconds = 0; 59 60 private String beanName; 61 62 private ExecutorService executor; 63 64 65 /** 66 * Set the ThreadFactory to use for the ExecutorService's thread pool. 67 * Default is the underlying ExecutorService's default thread factory. 68 * <p>In a Java EE 7 or other managed environment with JSR-236 support, 69 * consider specifying a JNDI-located ManagedThreadFactory: by default, 70 * to be found at "java:comp/DefaultManagedThreadFactory". 71 * Use the "jee:jndi-lookup" namespace element in XML or the programmatic 72 * {@link org.springframework.jndi.JndiLocatorDelegate} for convenient lookup. 73 * Alternatively, consider using Spring's {@link DefaultManagedAwareThreadFactory} 74 * with its fallback to local threads in case of no managed thread factory found. 75 * @see java.util.concurrent.Executors#defaultThreadFactory() 76 * @see javax.enterprise.concurrent.ManagedThreadFactory 77 * @see DefaultManagedAwareThreadFactory 78 */ 79 public void setThreadFactory(ThreadFactory threadFactory) { 80 this.threadFactory = (threadFactory != null ? threadFactory : this); 81 } 82 83 @Override 84 public void setThreadNamePrefix(String threadNamePrefix) { 85 super.setThreadNamePrefix(threadNamePrefix); 86 this.threadNamePrefixSet = true; 87 } 88 89 /** 90 * Set the RejectedExecutionHandler to use for the ExecutorService. 91 * Default is the ExecutorService's default abort policy. 92 * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy 93 */ 94 public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { 95 this.rejectedExecutionHandler = 96 (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); 97 } 98 99 /** 100 * Set whether to wait for scheduled tasks to complete on shutdown, 101 * not interrupting running tasks and executing all tasks in the queue. 102 * <p>Default is "false", shutting down immediately through interrupting 103 * ongoing tasks and clearing the queue. Switch this flag to "true" if you 104 * prefer fully completed tasks at the expense of a longer shutdown phase. 105 * <p>Note that Spring's container shutdown continues while ongoing tasks 106 * are being completed. If you want this executor to block and wait for the 107 * termination of tasks before the rest of the container continues to shut 108 * down - e.g. in order to keep up other resources that your tasks may need -, 109 * set the {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} 110 * property instead of or in addition to this property. 111 * @see java.util.concurrent.ExecutorService#shutdown() 112 * @see java.util.concurrent.ExecutorService#shutdownNow() 113 */ 114 public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { 115 this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; 116 } 117 118 /** 119 * Set the maximum number of seconds that this executor is supposed to block 120 * on shutdown in order to wait for remaining tasks to complete their execution 121 * before the rest of the container continues to shut down. This is particularly 122 * useful if your remaining tasks are likely to need access to other resources 123 * that are also managed by the container. 124 * <p>By default, this executor won't wait for the termination of tasks at all. 125 * It will either shut down immediately, interrupting ongoing tasks and clearing 126 * the remaining task queue - or, if the 127 * {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"} 128 * flag has been set to {@code true}, it will continue to fully execute all 129 * ongoing tasks as well as all remaining tasks in the queue, in parallel to 130 * the rest of the container shutting down. 131 * <p>In either case, if you specify an await-termination period using this property, 132 * this executor will wait for the given time (max) for the termination of tasks. 133 * As a rule of thumb, specify a significantly higher timeout here if you set 134 * "waitForTasksToCompleteOnShutdown" to {@code true} at the same time, 135 * since all remaining tasks in the queue will still get executed - in contrast 136 * to the default shutdown behavior where it's just about waiting for currently 137 * executing tasks that aren't reacting to thread interruption. 138 * @see java.util.concurrent.ExecutorService#shutdown() 139 * @see java.util.concurrent.ExecutorService#awaitTermination 140 */ 141 public void setAwaitTerminationSeconds(int awaitTerminationSeconds) { 142 this.awaitTerminationSeconds = awaitTerminationSeconds; 143 } 144 145 @Override 146 public void setBeanName(String name) { 147 this.beanName = name; 148 } 149 150 151 /** 152 * Calls {@code initialize()} after the container applied all property values. 153 * @see #initialize() 154 */ 155 @Override 156 public void afterPropertiesSet() { 157 initialize(); 158 } 159 160 /** 161 * Set up the ExecutorService. 162 */ 163 public void initialize() { 164 if (logger.isInfoEnabled()) { 165 logger.info("Initializing ExecutorService " + (this.beanName != null ? " '" + this.beanName + "'" : "")); 166 } 167 if (!this.threadNamePrefixSet && this.beanName != null) { 168 setThreadNamePrefix(this.beanName + "-"); 169 } 170 this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler); 171 } 172 173 /** 174 * Create the target {@link java.util.concurrent.ExecutorService} instance. 175 * Called by {@code afterPropertiesSet}. 176 * @param threadFactory the ThreadFactory to use 177 * @param rejectedExecutionHandler the RejectedExecutionHandler to use 178 * @return a new ExecutorService instance 179 * @see #afterPropertiesSet() 180 */ 181 protected abstract ExecutorService initializeExecutor( 182 ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler); 183 184 185 /** 186 * Calls {@code shutdown} when the BeanFactory destroys 187 * the task executor instance. 188 * @see #shutdown() 189 */ 190 @Override 191 public void destroy() { 192 shutdown(); 193 } 194 195 /** 196 * Perform a shutdown on the underlying ExecutorService. 197 * @see java.util.concurrent.ExecutorService#shutdown() 198 * @see java.util.concurrent.ExecutorService#shutdownNow() 199 * @see #awaitTerminationIfNecessary() 200 */ 201 public void shutdown() { 202 if (logger.isInfoEnabled()) { 203 logger.info("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : "")); 204 } 205 if (this.waitForTasksToCompleteOnShutdown) { 206 this.executor.shutdown(); 207 } 208 else { 209 this.executor.shutdownNow(); 210 } 211 awaitTerminationIfNecessary(); 212 } 213 214 /** 215 * Wait for the executor to terminate, according to the value of the 216 * {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property. 217 */ 218 private void awaitTerminationIfNecessary() { 219 if (this.awaitTerminationSeconds > 0) { 220 try { 221 if (!this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)) { 222 if (logger.isWarnEnabled()) { 223 logger.warn("Timed out while waiting for executor" + 224 (this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate"); 225 } 226 } 227 } 228 catch (InterruptedException ex) { 229 if (logger.isWarnEnabled()) { 230 logger.warn("Interrupted while waiting for executor" + 231 (this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate"); 232 } 233 Thread.currentThread().interrupt(); 234 } 235 } 236 } 237 238 }