001/* 002 * Copyright 2002-2015 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.concurrent; 018 019import java.util.concurrent.ExecutorService; 020import java.util.concurrent.Executors; 021import java.util.concurrent.RejectedExecutionHandler; 022import java.util.concurrent.ScheduledExecutorService; 023import java.util.concurrent.ScheduledThreadPoolExecutor; 024import java.util.concurrent.ThreadFactory; 025 026import org.springframework.beans.factory.FactoryBean; 027import org.springframework.lang.UsesJava7; 028import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable; 029import org.springframework.scheduling.support.TaskUtils; 030import org.springframework.util.Assert; 031import org.springframework.util.ClassUtils; 032import org.springframework.util.ObjectUtils; 033 034/** 035 * {@link org.springframework.beans.factory.FactoryBean} that sets up 036 * a {@link java.util.concurrent.ScheduledExecutorService} 037 * (by default: a {@link java.util.concurrent.ScheduledThreadPoolExecutor}) 038 * and exposes it for bean references. 039 * 040 * <p>Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks}, 041 * automatically starting the {@link ScheduledExecutorService} on initialization and 042 * cancelling it on destruction of the context. In scenarios that only require static 043 * registration of tasks at startup, there is no need to access the 044 * {@link ScheduledExecutorService} instance itself in application code at all; 045 * {@code ScheduledExecutorFactoryBean} is then just being used for lifecycle integration. 046 * 047 * <p>For an alternative, you may set up a {@link ScheduledThreadPoolExecutor} instance 048 * directly using constructor injection, or use a factory method definition that points 049 * to the {@link java.util.concurrent.Executors} class. 050 * <b>This is strongly recommended in particular for common {@code @Bean} methods in 051 * configuration classes, where this {@code FactoryBean} variant would force you to 052 * return the {@code FactoryBean} type instead of {@code ScheduledExecutorService}.</b> 053 * 054 * <p>Note that {@link java.util.concurrent.ScheduledExecutorService} 055 * uses a {@link Runnable} instance that is shared between repeated executions, 056 * in contrast to Quartz which instantiates a new Job for each execution. 057 * 058 * <p><b>WARNING:</b> {@link Runnable Runnables} submitted via a native 059 * {@link java.util.concurrent.ScheduledExecutorService} are removed from 060 * the execution schedule once they throw an exception. If you would prefer 061 * to continue execution after such an exception, switch this FactoryBean's 062 * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"} 063 * property to "true". 064 * 065 * @author Juergen Hoeller 066 * @since 2.0 067 * @see #setPoolSize 068 * @see #setRemoveOnCancelPolicy 069 * @see #setThreadFactory 070 * @see ScheduledExecutorTask 071 * @see java.util.concurrent.ScheduledExecutorService 072 * @see java.util.concurrent.ScheduledThreadPoolExecutor 073 */ 074@SuppressWarnings("serial") 075public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport 076 implements FactoryBean<ScheduledExecutorService> { 077 078 // ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(boolean) only available on JDK 7+ 079 private static final boolean setRemoveOnCancelPolicyAvailable = 080 ClassUtils.hasMethod(ScheduledThreadPoolExecutor.class, "setRemoveOnCancelPolicy", boolean.class); 081 082 083 private int poolSize = 1; 084 085 private ScheduledExecutorTask[] scheduledExecutorTasks; 086 087 private boolean removeOnCancelPolicy = false; 088 089 private boolean continueScheduledExecutionAfterException = false; 090 091 private boolean exposeUnconfigurableExecutor = false; 092 093 private ScheduledExecutorService exposedExecutor; 094 095 096 /** 097 * Set the ScheduledExecutorService's pool size. 098 * Default is 1. 099 */ 100 public void setPoolSize(int poolSize) { 101 Assert.isTrue(poolSize > 0, "'poolSize' must be 1 or higher"); 102 this.poolSize = poolSize; 103 } 104 105 /** 106 * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService 107 * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings, 108 * it will be registered via one of ScheduledExecutorService's schedule methods. 109 * @see java.util.concurrent.ScheduledExecutorService#schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit) 110 * @see java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit) 111 * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit) 112 */ 113 public void setScheduledExecutorTasks(ScheduledExecutorTask... scheduledExecutorTasks) { 114 this.scheduledExecutorTasks = scheduledExecutorTasks; 115 } 116 117 /** 118 * Set the remove-on-cancel mode on {@link ScheduledThreadPoolExecutor} (JDK 7+). 119 * <p>Default is {@code false}. If set to {@code true}, the target executor will be 120 * switched into remove-on-cancel mode (if possible, with a soft fallback otherwise). 121 */ 122 public void setRemoveOnCancelPolicy(boolean removeOnCancelPolicy) { 123 this.removeOnCancelPolicy = removeOnCancelPolicy; 124 } 125 126 /** 127 * Specify whether to continue the execution of a scheduled task 128 * after it threw an exception. 129 * <p>Default is "false", matching the native behavior of a 130 * {@link java.util.concurrent.ScheduledExecutorService}. 131 * Switch this flag to "true" for exception-proof execution of each task, 132 * continuing scheduled execution as in the case of successful execution. 133 * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate 134 */ 135 public void setContinueScheduledExecutionAfterException(boolean continueScheduledExecutionAfterException) { 136 this.continueScheduledExecutionAfterException = continueScheduledExecutionAfterException; 137 } 138 139 /** 140 * Specify whether this FactoryBean should expose an unconfigurable 141 * decorator for the created executor. 142 * <p>Default is "false", exposing the raw executor as bean reference. 143 * Switch this flag to "true" to strictly prevent clients from 144 * modifying the executor's configuration. 145 * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService 146 */ 147 public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { 148 this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; 149 } 150 151 152 @Override 153 @UsesJava7 154 protected ExecutorService initializeExecutor( 155 ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { 156 157 ScheduledExecutorService executor = 158 createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler); 159 160 if (this.removeOnCancelPolicy) { 161 if (setRemoveOnCancelPolicyAvailable && executor instanceof ScheduledThreadPoolExecutor) { 162 ((ScheduledThreadPoolExecutor) executor).setRemoveOnCancelPolicy(true); 163 } 164 else { 165 logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor"); 166 } 167 } 168 169 // Register specified ScheduledExecutorTasks, if necessary. 170 if (!ObjectUtils.isEmpty(this.scheduledExecutorTasks)) { 171 registerTasks(this.scheduledExecutorTasks, executor); 172 } 173 174 // Wrap executor with an unconfigurable decorator. 175 this.exposedExecutor = (this.exposeUnconfigurableExecutor ? 176 Executors.unconfigurableScheduledExecutorService(executor) : executor); 177 178 return executor; 179 } 180 181 /** 182 * Create a new {@link ScheduledExecutorService} instance. 183 * <p>The default implementation creates a {@link ScheduledThreadPoolExecutor}. 184 * Can be overridden in subclasses to provide custom {@link ScheduledExecutorService} instances. 185 * @param poolSize the specified pool size 186 * @param threadFactory the ThreadFactory to use 187 * @param rejectedExecutionHandler the RejectedExecutionHandler to use 188 * @return a new ScheduledExecutorService instance 189 * @see #afterPropertiesSet() 190 * @see java.util.concurrent.ScheduledThreadPoolExecutor 191 */ 192 protected ScheduledExecutorService createExecutor( 193 int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { 194 195 return new ScheduledThreadPoolExecutor(poolSize, threadFactory, rejectedExecutionHandler); 196 } 197 198 /** 199 * Register the specified {@link ScheduledExecutorTask ScheduledExecutorTasks} 200 * on the given {@link ScheduledExecutorService}. 201 * @param tasks the specified ScheduledExecutorTasks (never empty) 202 * @param executor the ScheduledExecutorService to register the tasks on. 203 */ 204 protected void registerTasks(ScheduledExecutorTask[] tasks, ScheduledExecutorService executor) { 205 for (ScheduledExecutorTask task : tasks) { 206 Runnable runnable = getRunnableToSchedule(task); 207 if (task.isOneTimeTask()) { 208 executor.schedule(runnable, task.getDelay(), task.getTimeUnit()); 209 } 210 else { 211 if (task.isFixedRate()) { 212 executor.scheduleAtFixedRate(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit()); 213 } 214 else { 215 executor.scheduleWithFixedDelay(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit()); 216 } 217 } 218 } 219 } 220 221 /** 222 * Determine the actual Runnable to schedule for the given task. 223 * <p>Wraps the task's Runnable in a 224 * {@link org.springframework.scheduling.support.DelegatingErrorHandlingRunnable} 225 * that will catch and log the Exception. If necessary, it will suppress the 226 * Exception according to the 227 * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"} 228 * flag. 229 * @param task the ScheduledExecutorTask to schedule 230 * @return the actual Runnable to schedule (may be a decorator) 231 */ 232 protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) { 233 return (this.continueScheduledExecutionAfterException ? 234 new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER) : 235 new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER)); 236 } 237 238 239 @Override 240 public ScheduledExecutorService getObject() { 241 return this.exposedExecutor; 242 } 243 244 @Override 245 public Class<? extends ScheduledExecutorService> getObjectType() { 246 return (this.exposedExecutor != null ? this.exposedExecutor.getClass() : ScheduledExecutorService.class); 247 } 248 249 @Override 250 public boolean isSingleton() { 251 return true; 252 } 253 254}