diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java index a8d47eda29..0e63369c15 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable; import org.springframework.scheduling.support.TaskUtils; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; /** @@ -66,12 +67,17 @@ import org.springframework.util.ObjectUtils; public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport implements FactoryBean { + // ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(boolean) only available on JDK 7+ + private static final boolean setRemoveOnCancelPolicyAvailable = + ClassUtils.hasMethod(ScheduledThreadPoolExecutor.class, "setRemoveOnCancelPolicy", boolean.class); + + private int poolSize = 1; - private Boolean removeOnCancelPolicy; - private ScheduledExecutorTask[] scheduledExecutorTasks; + private boolean removeOnCancelPolicy = false; + private boolean continueScheduledExecutionAfterException = false; private boolean exposeUnconfigurableExecutor = false; @@ -88,14 +94,6 @@ public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport this.poolSize = poolSize; } - /** - * Set the same property on ScheduledExecutorService (JDK 1.7+). - * There is no default. If not set, the executor property is not set. - */ - public void setRemoveOnCancelPolicy(boolean removeOnCancelPolicy) { - this.removeOnCancelPolicy = removeOnCancelPolicy; - } - /** * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings, @@ -108,6 +106,15 @@ public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport this.scheduledExecutorTasks = scheduledExecutorTasks; } + /** + * Set the remove-on-cancel mode on {@link ScheduledThreadPoolExecutor} (JDK 7+). + *

Default is {@code false}. If set to {@code true}, the target executor will be + * switched into remove-on-cancel mode (if possible, with a soft fallback otherwise). + */ + public void setRemoveOnCancelPolicy(boolean removeOnCancelPolicy) { + this.removeOnCancelPolicy = removeOnCancelPolicy; + } + /** * Specify whether to continue the execution of a scheduled task * after it threw an exception. @@ -141,8 +148,13 @@ public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport ScheduledExecutorService executor = createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler); - if (executor instanceof ScheduledThreadPoolExecutor && this.removeOnCancelPolicy != null) { - ((ScheduledThreadPoolExecutor) executor).setRemoveOnCancelPolicy(this.removeOnCancelPolicy); + if (this.removeOnCancelPolicy) { + if (setRemoveOnCancelPolicyAvailable && executor instanceof ScheduledThreadPoolExecutor) { + ((ScheduledThreadPoolExecutor) executor).setRemoveOnCancelPolicy(true); + } + else { + logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor"); + } } // Register specified ScheduledExecutorTasks, if necessary. diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java index a4c6479087..0a3395311a 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java @@ -36,6 +36,7 @@ import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.TaskUtils; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ErrorHandler; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureTask; @@ -56,9 +57,14 @@ import org.springframework.util.concurrent.ListenableFutureTask; public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler { + // ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(boolean) only available on JDK 7+ + private static final boolean setRemoveOnCancelPolicyAvailable = + ClassUtils.hasMethod(ScheduledThreadPoolExecutor.class, "setRemoveOnCancelPolicy", boolean.class); + + private volatile int poolSize = 1; - private volatile Boolean removeOnCancelPolicy; + private volatile boolean removeOnCancelPolicy = false; private volatile ScheduledExecutorService scheduledExecutor; @@ -79,33 +85,42 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport } /** - * Set the same property on ScheduledExecutorService (JDK 1.7+). - * There is no default. If not set, the executor property is not set. + * Set the remove-on-cancel mode on {@link ScheduledThreadPoolExecutor} (JDK 7+). + *

Default is {@code false}. If set to {@code true}, the target executor will be + * switched into remove-on-cancel mode (if possible, with a soft fallback otherwise). *

This setting can be modified at runtime, for example through JMX. */ public void setRemoveOnCancelPolicy(boolean removeOnCancelPolicy) { this.removeOnCancelPolicy = removeOnCancelPolicy; - if (this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) { + if (setRemoveOnCancelPolicyAvailable && this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) { ((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(removeOnCancelPolicy); } + else if (removeOnCancelPolicy && this.scheduledExecutor != null) { + logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor"); + } } /** * Set a custom {@link ErrorHandler} strategy. */ public void setErrorHandler(ErrorHandler errorHandler) { - Assert.notNull(errorHandler, "'errorHandler' must not be null"); this.errorHandler = errorHandler; } + @Override protected ExecutorService initializeExecutor( ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { this.scheduledExecutor = createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler); - if (this.scheduledExecutor instanceof ScheduledThreadPoolExecutor && this.removeOnCancelPolicy != null) { - ((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(this.removeOnCancelPolicy); + if (this.removeOnCancelPolicy) { + if (setRemoveOnCancelPolicyAvailable && this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) { + ((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(true); + } + else { + logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor"); + } } return this.scheduledExecutor; @@ -166,14 +181,17 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport } /** - * Return the current setting of removeOnCancelPolicy. - *

Requires an underlying {@link ScheduledThreadPoolExecutor} and JDK 1.7+. + * Return the current setting for the remove-on-cancel mode. + *

Requires an underlying {@link ScheduledThreadPoolExecutor}. */ public boolean isRemoveOnCancelPolicy() { - if (this.scheduledExecutor == null) { - // Not initialized yet: return false (the default of the executor) + if (!setRemoveOnCancelPolicyAvailable) { return false; } + if (this.scheduledExecutor == null) { + // Not initialized yet: return our setting for the time being. + return this.removeOnCancelPolicy; + } return getScheduledThreadPoolExecutor().getRemoveOnCancelPolicy(); }