diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java new file mode 100644 index 0000000000000000000000000000000000000000..2c671fa3b75c5838cbc564796d8004f5853f4221 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * Enables Spring's scheduled task execution capability. + * + * @author Chris Beams + * @since 3.1 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import(SchedulingConfiguration.class) +@Documented +public @interface EnableScheduling { + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 9f638cc0f03d44193ae73057d89f85b73cfcafa3..da57390d9864b19a447637733324160ac9465a63 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -19,6 +19,7 @@ package org.springframework.scheduling.annotation; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.DisposableBean; @@ -30,6 +31,7 @@ import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.support.ScheduledMethodRunnable; import org.springframework.util.Assert; @@ -44,9 +46,12 @@ import org.springframework.util.StringValueResolver; * * @author Mark Fisher * @author Juergen Hoeller + * @author Chris Beams * @since 3.0 * @see Scheduled + * @see SchedulingConfigurer * @see org.springframework.scheduling.TaskScheduler + * @see org.springframework.scheduling.config.ScheduledTaskRegistrar */ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware, @@ -58,7 +63,7 @@ public class ScheduledAnnotationBeanPostProcessor private ApplicationContext applicationContext; - private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); + private ScheduledTaskRegistrar registrar; private final Map cronTasks = new HashMap(); @@ -134,15 +139,47 @@ public class ScheduledAnnotationBeanPostProcessor } public void onApplicationEvent(ContextRefreshedEvent event) { - if (event.getApplicationContext() == this.applicationContext) { - if (this.scheduler != null) { - this.registrar.setScheduler(this.scheduler); + if (event.getApplicationContext() != this.applicationContext) { + return; + } + + Map configurers = applicationContext.getBeansOfType(SchedulingConfigurer.class); + + if (this.cronTasks.isEmpty() && this.fixedDelayTasks.isEmpty() && + this.fixedRateTasks.isEmpty() && configurers.isEmpty()) { + return; + } + + this.registrar = new ScheduledTaskRegistrar(); + this.registrar.setCronTasks(this.cronTasks); + this.registrar.setFixedDelayTasks(this.fixedDelayTasks); + this.registrar.setFixedRateTasks(this.fixedRateTasks); + + if (this.scheduler != null) { + this.registrar.setScheduler(this.scheduler); + } + + for (SchedulingConfigurer configurer : configurers.values()) { + configurer.configureTasks(this.registrar); + } + + if (registrar.getScheduler() == null) { + Map schedulers = new HashMap(); + schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class)); + schedulers.putAll(applicationContext.getBeansOfType(ScheduledExecutorService.class)); + if (schedulers.size() == 0) { + // do nothing -> fall back to default scheduler + } else if (schedulers.size() == 1) { + this.registrar.setScheduler(schedulers.values().iterator().next()); + } else if (schedulers.size() >= 2){ + throw new IllegalStateException("More than one TaskScheduler and/or ScheduledExecutorService " + + "exist within the context. Remove all but one of the beans; or implement the " + + "SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler " + + "explicitly within the configureTasks() callback. Found the following beans: " + schedulers.keySet()); } - this.registrar.setCronTasks(this.cronTasks); - this.registrar.setFixedDelayTasks(this.fixedDelayTasks); - this.registrar.setFixedRateTasks(this.fixedRateTasks); - this.registrar.afterPropertiesSet(); } + + this.registrar.afterPropertiesSet(); } public void destroy() throws Exception { diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..ab08f1e2de8e7d591dfa767c0bf90fb73b66be9a --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.annotation; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; + +@Configuration +public class SchedulingConfiguration { + + @Bean(name=AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { + return new ScheduledAnnotationBeanPostProcessor(); + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfigurer.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfigurer.java new file mode 100644 index 0000000000000000000000000000000000000000..466c4eb14e784bcfd925baebb078d0e65ff8bbd4 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfigurer.java @@ -0,0 +1,25 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.annotation; + +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +public interface SchedulingConfigurer { + + void configureTasks(ScheduledTaskRegistrar taskRegistrar); + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java b/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java index 593223abb72bece1fa18372a773a928e010fba6d..0caa68aa10b282708c727d0ae227e8f79cc6e03c 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.scheduling.config; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -81,6 +82,13 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean } } + /** + * Return the scheduler instance for this registrar (may be null) + */ + public TaskScheduler getScheduler() { + return this.taskScheduler; + } + /** * Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects * (typically custom implementations of the {@link Trigger} interface). @@ -105,6 +113,49 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean this.fixedRateTasks = fixedRateTasks; } + /** + * Add a Runnable task to be triggered per the given {@link Trigger}. + * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) + */ + public void addTriggerTask(Runnable task, Trigger trigger) { + if (this.triggerTasks == null) { + this.triggerTasks = new HashMap(); + } + this.triggerTasks.put(task, trigger); + } + + /** + * Add a Runnable task to be triggered per the given cron expression + */ + public void addCronTask(Runnable task, String cronExpression) { + if (this.cronTasks == null) { + this.cronTasks = new HashMap(); + } + this.cronTasks.put(task, cronExpression); + } + + /** + * Add a Runnable task to be triggered with the given fixed delay. + * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) + */ + public void addFixedDelayTask(Runnable task, long delay) { + if (this.fixedDelayTasks == null) { + this.fixedDelayTasks = new HashMap(); + } + this.fixedDelayTasks.put(task, delay); + } + + /** + * Add a Runnable task to be triggered at the given fixed-rate period. + * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) + */ + public void addFixedRateTask(Runnable task, long period) { + if (this.fixedRateTasks == null) { + this.fixedRateTasks = new HashMap(); + } + this.fixedRateTasks.put(task, period); + } + /** * Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values. * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java new file mode 100644 index 0000000000000000000000000000000000000000..9b98b48929b4c1df81a946226d546501ac392a57 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java @@ -0,0 +1,424 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.annotation; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.TriggerContext; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +/** + * Tests use of @EnableScheduling on @Configuration classes. + * + * @author Chris Beams + * @since 3.1 + */ +public class EnableSchedulingTests { + + @Test + public void withFixedRateTask() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(FixedRateTaskConfig.class); + ctx.refresh(); + + Thread.sleep(100); + assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10)); + ctx.close(); + } + + + @EnableScheduling @Configuration + static class FixedRateTaskConfig { + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(); + } + + @Scheduled(fixedRate=10) + public void task() { + counter().incrementAndGet(); + } + } + + + @Test + public void withSubclass() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(FixedRateTaskConfigSubclass.class); + ctx.refresh(); + + Thread.sleep(100); + assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10)); + ctx.close(); + } + + + @Configuration + static class FixedRateTaskConfigSubclass extends FixedRateTaskConfig { + } + + + @Test + public void withExplicitScheduler() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ExplicitSchedulerConfig.class); + ctx.refresh(); + + Thread.sleep(100); + assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10)); + assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName, startsWith("explicitScheduler-")); + ctx.close(); + } + + + @EnableScheduling @Configuration + static class ExplicitSchedulerConfig { + + String threadName; + + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler-"); + return scheduler; + } + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(); + } + + @Scheduled(fixedRate=10) + public void task() { + threadName = Thread.currentThread().getName(); + counter().incrementAndGet(); + } + } + + + @Test(expected=IllegalStateException.class) + public void withExplicitSchedulerAmbiguity_andSchedulingEnabled() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AmbiguousExplicitSchedulerConfig.class); + try { + ctx.refresh(); + } catch (IllegalStateException ex) { + assertThat(ex.getMessage(), startsWith("More than one TaskScheduler")); + throw ex; + } + } + + @EnableScheduling @Configuration + static class AmbiguousExplicitSchedulerConfig { + + String threadName; + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2"); + return scheduler; + } + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(); + } + + @Scheduled(fixedRate=10) + public void task() { + threadName = Thread.currentThread().getName(); + counter().incrementAndGet(); + } + } + + + @Test + public void withExplicitScheduledTaskRegistrar() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ExplicitScheduledTaskRegistrarConfig.class); + ctx.refresh(); + + Thread.sleep(100); + assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10)); + assertThat(ctx.getBean(ExplicitScheduledTaskRegistrarConfig.class).threadName, startsWith("explicitScheduler1")); + ctx.close(); + } + + + @EnableScheduling @Configuration + static class ExplicitScheduledTaskRegistrarConfig implements SchedulingConfigurer { + + String threadName; + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2"); + return scheduler; + } + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(); + } + + @Scheduled(fixedRate=10) + public void task() { + threadName = Thread.currentThread().getName(); + counter().incrementAndGet(); + } + + public Object getScheduler() { + return null; + } + + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler1()); + } + + } + + + @Test + public void withAmbiguousTaskSchedulers_butNoActualTasks() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SchedulingEnabled_withAmbiguousTaskSchedulers_butNoActualTasks.class); + ctx.refresh(); + } + + + @Configuration + @EnableScheduling + static class SchedulingEnabled_withAmbiguousTaskSchedulers_butNoActualTasks { + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2"); + return scheduler; + } + } + + + @Test(expected=IllegalStateException.class) + public void withAmbiguousTaskSchedulers_andSingleTask() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask.class); + try { + ctx.refresh(); + } catch (IllegalStateException ex) { + assertThat(ex.getMessage(), startsWith("More than one TaskScheduler and/or")); + throw ex; + } + } + + + @Configuration + @EnableScheduling + static class SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask { + + @Scheduled(fixedRate=10L) + public void task() { + } + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2"); + return scheduler; + } + } + + @Test + public void withAmbiguousTaskSchedulers_andSingleTask_disambiguatedByScheduledTaskRegistrarBean() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedByScheduledTaskRegistrar.class); + ctx.refresh(); + Thread.sleep(20); + ThreadAwareWorker worker = ctx.getBean(ThreadAwareWorker.class); + ctx.close(); + assertThat(worker.executedByThread, startsWith("explicitScheduler2-")); + } + + + static class ThreadAwareWorker { + String executedByThread; + } + + + @Configuration + @EnableScheduling + static class SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedByScheduledTaskRegistrar implements SchedulingConfigurer { + + @Scheduled(fixedRate=10) + public void task() { + worker().executedByThread = Thread.currentThread().getName(); + } + + @Bean + public ThreadAwareWorker worker() { + return new ThreadAwareWorker(); + } + + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler2()); + } + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1-"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2-"); + return scheduler; + } + } + + + @Test + public void withAmbiguousTaskSchedulers_andSingleTask_disambiguatedBySchedulerNameAttribute() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedBySchedulerNameAttribute.class); + ctx.refresh(); + Thread.sleep(20); + ThreadAwareWorker worker = ctx.getBean(ThreadAwareWorker.class); + ctx.close(); + assertThat(worker.executedByThread, startsWith("explicitScheduler2-")); + } + + + @Configuration + @EnableScheduling + static class SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedBySchedulerNameAttribute implements SchedulingConfigurer { + + @Scheduled(fixedRate=10) + public void task() { + worker().executedByThread = Thread.currentThread().getName(); + } + + @Bean + public ThreadAwareWorker worker() { + return new ThreadAwareWorker(); + } + + @Bean + public TaskScheduler taskScheduler1() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler1-"); + return scheduler; + } + + @Bean + public TaskScheduler taskScheduler2() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("explicitScheduler2-"); + return scheduler; + } + + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler2()); + } + } + + + @Test + public void withTriggerTask() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(TriggerTaskConfig.class); + ctx.refresh(); + + Thread.sleep(100); + assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThan(1)); + ctx.close(); + } + + + @Configuration + static class TriggerTaskConfig { + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(); + } + + @Bean + public TaskScheduler scheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.initialize(); + scheduler.schedule( + new Runnable() { + public void run() { + counter().incrementAndGet(); + } + }, + new Trigger() { + public Date nextExecutionTime(TriggerContext triggerContext) { + return new Date(new Date().getTime()+10); + } + }); + return scheduler; + } + } +}