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