Merge branch cbeams/SPR-7022
* SPR-7022: Support initial delay attribute for scheduled tasks Polish scheduled task execution infrastructure
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,11 +24,11 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation that marks a method to be scheduled. Exactly one of the
|
* Annotation that marks a method to be scheduled. Exactly one of the
|
||||||
* <code>cron</code>, <code>fixedDelay</code>, or <code>fixedRate</code>
|
* {@link #cron()}, {@link #fixedDelay()}, or {@link #fixedRate()}
|
||||||
* attributes must be provided.
|
* attributes must be specified.
|
||||||
*
|
*
|
||||||
* <p>The annotated method must expect no arguments and have a
|
* <p>The annotated method must expect no arguments and have a
|
||||||
* <code>void</code> return type.
|
* {@code void} return type.
|
||||||
*
|
*
|
||||||
* <p>Processing of {@code @Scheduled} annotations is performed by
|
* <p>Processing of {@code @Scheduled} annotations is performed by
|
||||||
* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
|
* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
|
||||||
@@ -37,6 +37,7 @@ import java.lang.annotation.Target;
|
|||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see EnableScheduling
|
* @see EnableScheduling
|
||||||
* @see ScheduledAnnotationBeanPostProcessor
|
* @see ScheduledAnnotationBeanPostProcessor
|
||||||
@@ -49,9 +50,10 @@ public @interface Scheduled {
|
|||||||
/**
|
/**
|
||||||
* A cron-like expression, extending the usual UN*X definition to include
|
* A cron-like expression, extending the usual UN*X definition to include
|
||||||
* triggers on the second as well as minute, hour, day of month, month
|
* triggers on the second as well as minute, hour, day of month, month
|
||||||
* and day of week. e.g. <code>"0 * * * * MON-FRI"</code> means once
|
* and day of week. e.g. {@code "0 * * * * MON-FRI"} means once per minute on
|
||||||
* per minute on weekdays (at the top of the minute - the 0th second).
|
* weekdays (at the top of the minute - the 0th second).
|
||||||
* @return an expression that can be parsed to a cron schedule
|
* @return an expression that can be parsed to a cron schedule
|
||||||
|
* @see org.springframework.scheduling.support.CronSequenceGenerator
|
||||||
*/
|
*/
|
||||||
String cron() default "";
|
String cron() default "";
|
||||||
|
|
||||||
@@ -68,4 +70,12 @@ public @interface Scheduled {
|
|||||||
*/
|
*/
|
||||||
long fixedRate() default -1;
|
long fixedRate() default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of milliseconds to delay before the first execution of a
|
||||||
|
* {@link #fixedRate()} or {@link #fixedDelay()} task.
|
||||||
|
* @return the initial delay in milliseconds
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
long initialDelay() default 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.scheduling.annotation;
|
package org.springframework.scheduling.annotation;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@@ -33,6 +34,8 @@ import org.springframework.core.Ordered;
|
|||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.scheduling.TaskScheduler;
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
import org.springframework.scheduling.Trigger;
|
import org.springframework.scheduling.Trigger;
|
||||||
|
import org.springframework.scheduling.config.CronTask;
|
||||||
|
import org.springframework.scheduling.config.IntervalTask;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@@ -74,13 +77,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private ScheduledTaskRegistrar registrar;
|
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
|
||||||
|
|
||||||
private final Map<Runnable, String> cronTasks = new HashMap<Runnable, String>();
|
|
||||||
|
|
||||||
private final Map<Runnable, Long> fixedDelayTasks = new HashMap<Runnable, Long>();
|
|
||||||
|
|
||||||
private final Map<Runnable, Long> fixedRateTasks = new HashMap<Runnable, Long>();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +101,6 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
return LOWEST_PRECEDENCE;
|
return LOWEST_PRECEDENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
@@ -124,9 +120,11 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
// found a @Scheduled method on the target class for this JDK proxy -> is it
|
// found a @Scheduled method on the target class for this JDK proxy -> is it
|
||||||
// also present on the proxy itself?
|
// also present on the proxy itself?
|
||||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||||
} catch (SecurityException ex) {
|
}
|
||||||
|
catch (SecurityException ex) {
|
||||||
ReflectionUtils.handleReflectionException(ex);
|
ReflectionUtils.handleReflectionException(ex);
|
||||||
} catch (NoSuchMethodException ex) {
|
}
|
||||||
|
catch (NoSuchMethodException ex) {
|
||||||
throw new IllegalStateException(String.format(
|
throw new IllegalStateException(String.format(
|
||||||
"@Scheduled method '%s' found on bean target class '%s', " +
|
"@Scheduled method '%s' found on bean target class '%s', " +
|
||||||
"but not found in any interface(s) for bean JDK proxy. Either " +
|
"but not found in any interface(s) for bean JDK proxy. Either " +
|
||||||
@@ -137,26 +135,27 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
}
|
}
|
||||||
Runnable runnable = new ScheduledMethodRunnable(bean, method);
|
Runnable runnable = new ScheduledMethodRunnable(bean, method);
|
||||||
boolean processedSchedule = false;
|
boolean processedSchedule = false;
|
||||||
String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
|
String errorMessage = "Exactly one of the 'cron', 'fixedDelay', or 'fixedRate' attributes is required.";
|
||||||
String cron = annotation.cron();
|
String cron = annotation.cron();
|
||||||
if (!"".equals(cron)) {
|
if (!"".equals(cron)) {
|
||||||
processedSchedule = true;
|
processedSchedule = true;
|
||||||
if (embeddedValueResolver != null) {
|
if (embeddedValueResolver != null) {
|
||||||
cron = embeddedValueResolver.resolveStringValue(cron);
|
cron = embeddedValueResolver.resolveStringValue(cron);
|
||||||
}
|
}
|
||||||
cronTasks.put(runnable, cron);
|
registrar.addCronTask(new CronTask(runnable, cron));
|
||||||
}
|
}
|
||||||
|
long initialDelay = annotation.initialDelay();
|
||||||
long fixedDelay = annotation.fixedDelay();
|
long fixedDelay = annotation.fixedDelay();
|
||||||
if (fixedDelay >= 0) {
|
if (fixedDelay >= 0) {
|
||||||
Assert.isTrue(!processedSchedule, errorMessage);
|
Assert.isTrue(!processedSchedule, errorMessage);
|
||||||
processedSchedule = true;
|
processedSchedule = true;
|
||||||
fixedDelayTasks.put(runnable, fixedDelay);
|
registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
|
||||||
}
|
}
|
||||||
long fixedRate = annotation.fixedRate();
|
long fixedRate = annotation.fixedRate();
|
||||||
if (fixedRate >= 0) {
|
if (fixedRate >= 0) {
|
||||||
Assert.isTrue(!processedSchedule, errorMessage);
|
Assert.isTrue(!processedSchedule, errorMessage);
|
||||||
processedSchedule = true;
|
processedSchedule = true;
|
||||||
fixedRateTasks.put(runnable, fixedRate);
|
registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
|
||||||
}
|
}
|
||||||
Assert.isTrue(processedSchedule, errorMessage);
|
Assert.isTrue(processedSchedule, errorMessage);
|
||||||
}
|
}
|
||||||
@@ -170,17 +169,8 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, SchedulingConfigurer> configurers = applicationContext.getBeansOfType(SchedulingConfigurer.class);
|
Map<String, SchedulingConfigurer> configurers =
|
||||||
|
this.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) {
|
if (this.scheduler != null) {
|
||||||
this.registrar.setScheduler(this.scheduler);
|
this.registrar.setScheduler(this.scheduler);
|
||||||
@@ -190,19 +180,23 @@ public class ScheduledAnnotationBeanPostProcessor
|
|||||||
configurer.configureTasks(this.registrar);
|
configurer.configureTasks(this.registrar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registrar.getScheduler() == null) {
|
if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
|
||||||
Map<String, ? super Object> schedulers = new HashMap<String, Object>();
|
Map<String, ? super Object> schedulers = new HashMap<String, Object>();
|
||||||
schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class));
|
schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class));
|
||||||
schedulers.putAll(applicationContext.getBeansOfType(ScheduledExecutorService.class));
|
schedulers.putAll(applicationContext.getBeansOfType(ScheduledExecutorService.class));
|
||||||
if (schedulers.size() == 0) {
|
if (schedulers.size() == 0) {
|
||||||
// do nothing -> fall back to default scheduler
|
// do nothing -> fall back to default scheduler
|
||||||
} else if (schedulers.size() == 1) {
|
}
|
||||||
|
else if (schedulers.size() == 1) {
|
||||||
this.registrar.setScheduler(schedulers.values().iterator().next());
|
this.registrar.setScheduler(schedulers.values().iterator().next());
|
||||||
} else if (schedulers.size() >= 2){
|
}
|
||||||
throw new IllegalStateException("More than one TaskScheduler and/or ScheduledExecutorService " +
|
else if (schedulers.size() >= 2){
|
||||||
"exist within the context. Remove all but one of the beans; or implement the " +
|
throw new IllegalStateException(
|
||||||
"SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler " +
|
"More than one TaskScheduler and/or ScheduledExecutorService " +
|
||||||
"explicitly within the configureTasks() callback. Found the following beans: " + schedulers.keySet());
|
"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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,12 +19,15 @@ package org.springframework.scheduling.annotation;
|
|||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration}
|
* Optional interface to be implemented by @{@link
|
||||||
* classes annotated with @{@link EnableScheduling} that wish to register scheduled tasks
|
* org.springframework.context.annotation.Configuration Configuration} classes annotated
|
||||||
* in a <em>programmatic</em> fashion as opposed to the <em>declarative</em> approach of
|
* with @{@link EnableScheduling}. Typically used for setting a specific
|
||||||
* using the @{@link Scheduled} annotation. For example, this may be necessary when
|
* {@link org.springframework.scheduling.TaskScheduler TaskScheduler} bean to be used when
|
||||||
* implementing {@link org.springframework.scheduling.Trigger Trigger}-based tasks, which
|
* executing scheduled tasks or for registering scheduled tasks in a <em>programmatic</em>
|
||||||
* are not supported by the {@code @Scheduled} annotation.
|
* fashion as opposed to the <em>declarative</em> approach of using the @{@link Scheduled}
|
||||||
|
* annotation. For example, this may be necessary when implementing {@link
|
||||||
|
* org.springframework.scheduling.Trigger Trigger}-based tasks, which are not supported by
|
||||||
|
* the {@code @Scheduled} annotation.
|
||||||
*
|
*
|
||||||
* <p>See @{@link EnableScheduling} for detailed usage examples.
|
* <p>See @{@link EnableScheduling} for detailed usage examples.
|
||||||
*
|
*
|
||||||
@@ -35,6 +38,12 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||||||
*/
|
*/
|
||||||
public interface SchedulingConfigurer {
|
public interface SchedulingConfigurer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback allowing a {@link org.springframework.scheduling.TaskScheduler
|
||||||
|
* TaskScheduler} and specific {@link org.springframework.scheduling.config.Task Task}
|
||||||
|
* instances to be registered against the given the {@link ScheduledTaskRegistrar}
|
||||||
|
* @param taskRegistrar the registrar to be configured.
|
||||||
|
*/
|
||||||
void configureTasks(ScheduledTaskRegistrar taskRegistrar);
|
void configureTasks(ScheduledTaskRegistrar taskRegistrar);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 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.config;
|
||||||
|
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.scheduling.support.CronTrigger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link TriggerTask} implementation defining a {@code Runnable} to be executed according
|
||||||
|
* to a {@linkplain org.springframework.scheduling.support.CronSequenceGenerator standard
|
||||||
|
* cron expression}.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.2
|
||||||
|
* @see Scheduled#cron()
|
||||||
|
* @see ScheduledTaskRegistrar#setCronTasksList(java.util.List)
|
||||||
|
* @see org.springframework.scheduling.TaskScheduler
|
||||||
|
*/
|
||||||
|
public class CronTask extends TriggerTask {
|
||||||
|
|
||||||
|
private String expression;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code CronTask}.
|
||||||
|
* @param runnable the underlying task to execute
|
||||||
|
* @param expression cron expression defining when the task should be executed
|
||||||
|
*/
|
||||||
|
public CronTask(Runnable runnable, String expression) {
|
||||||
|
this(runnable, new CronTrigger(expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code CronTask}.
|
||||||
|
* @param runnable the underlying task to execute
|
||||||
|
* @param cronTrigger the cron trigger defining when the task should be executed
|
||||||
|
*/
|
||||||
|
public CronTask(Runnable runnable, CronTrigger cronTrigger) {
|
||||||
|
super(runnable, cronTrigger);
|
||||||
|
this.expression = cronTrigger.getExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 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.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} implementation defining a {@code Runnable} to be executed at a given
|
||||||
|
* millisecond interval which may be treated as fixed-rate or fixed-delay depending on
|
||||||
|
* context.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.2
|
||||||
|
* @see org.springframework.scheduling.annotation.Scheduled#fixedRate()
|
||||||
|
* @see org.springframework.scheduling.annotation.Scheduled#fixedDelay()
|
||||||
|
* @see ScheduledTaskRegistrar#setFixedRateTasksList(java.util.List)
|
||||||
|
* @see ScheduledTaskRegistrar#setFixedDelayTasksList(java.util.List)
|
||||||
|
* @see org.springframework.scheduling.TaskScheduler
|
||||||
|
*/
|
||||||
|
public class IntervalTask extends Task {
|
||||||
|
|
||||||
|
private final long interval;
|
||||||
|
|
||||||
|
private final long initialDelay;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code IntervalTask}.
|
||||||
|
* @param runnable the underlying task to execute
|
||||||
|
* @param interval how often in milliseconds the task should be executed
|
||||||
|
* @param initialDelay initial delay before first execution of the task
|
||||||
|
*/
|
||||||
|
public IntervalTask(Runnable runnable, long interval, long initialDelay) {
|
||||||
|
super(runnable);
|
||||||
|
this.initialDelay = initialDelay;
|
||||||
|
this.interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code IntervalTask} with no initial delay.
|
||||||
|
* @param runnable the underlying task to execute
|
||||||
|
* @param interval how often in milliseconds the task should be executed
|
||||||
|
*/
|
||||||
|
public IntervalTask(Runnable runnable, long interval) {
|
||||||
|
this(runnable, interval, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public long getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getInitialDelay() {
|
||||||
|
return initialDelay;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.scheduling.config;
|
package org.springframework.scheduling.config;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -33,8 +35,8 @@ import org.springframework.scheduling.support.CronTrigger;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper bean for registering tasks with a {@link TaskScheduler},
|
* Helper bean for registering tasks with a {@link TaskScheduler}, typically using cron
|
||||||
* typically using cron expressions.
|
* expressions.
|
||||||
*
|
*
|
||||||
* <p>As of Spring 3.1, {@code ScheduledTaskRegistrar} has a more prominent user-facing
|
* <p>As of Spring 3.1, {@code ScheduledTaskRegistrar} has a more prominent user-facing
|
||||||
* role when used in conjunction with the @{@link
|
* role when used in conjunction with the @{@link
|
||||||
@@ -43,6 +45,7 @@ import org.springframework.util.Assert;
|
|||||||
* SchedulingConfigurer} callback interface.
|
* SchedulingConfigurer} callback interface.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see org.springframework.scheduling.annotation.EnableAsync
|
* @see org.springframework.scheduling.annotation.EnableAsync
|
||||||
* @see org.springframework.scheduling.annotation.SchedulingConfigurer
|
* @see org.springframework.scheduling.annotation.SchedulingConfigurer
|
||||||
@@ -53,19 +56,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
|||||||
|
|
||||||
private ScheduledExecutorService localExecutor;
|
private ScheduledExecutorService localExecutor;
|
||||||
|
|
||||||
private Map<Runnable, Trigger> triggerTasks;
|
private List<TriggerTask> triggerTasks;
|
||||||
|
|
||||||
private Map<Runnable, String> cronTasks;
|
private List<CronTask> cronTasks;
|
||||||
|
|
||||||
private Map<Runnable, Long> fixedRateTasks;
|
private List<IntervalTask> fixedRateTasks;
|
||||||
|
|
||||||
private Map<Runnable, Long> fixedDelayTasks;
|
private List<IntervalTask> fixedDelayTasks;
|
||||||
|
|
||||||
private final Set<ScheduledFuture<?>> scheduledFutures = new LinkedHashSet<ScheduledFuture<?>>();
|
private final Set<ScheduledFuture<?>> scheduledFutures = new LinkedHashSet<ScheduledFuture<?>>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the TaskScheduler to register scheduled tasks with.
|
* Set the {@link TaskScheduler} to register scheduled tasks with.
|
||||||
*/
|
*/
|
||||||
public void setTaskScheduler(TaskScheduler taskScheduler) {
|
public void setTaskScheduler(TaskScheduler taskScheduler) {
|
||||||
Assert.notNull(taskScheduler, "TaskScheduler must not be null");
|
Assert.notNull(taskScheduler, "TaskScheduler must not be null");
|
||||||
@@ -73,9 +76,9 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link org.springframework.scheduling.TaskScheduler} to register scheduled
|
* Set the {@link TaskScheduler} to register scheduled tasks with, or a
|
||||||
* tasks with, or a {@link java.util.concurrent.ScheduledExecutorService} to be
|
* {@link java.util.concurrent.ScheduledExecutorService} to be wrapped as a
|
||||||
* wrapped as a TaskScheduler.
|
* {@code TaskScheduler}.
|
||||||
*/
|
*/
|
||||||
public void setScheduler(Object scheduler) {
|
public void setScheduler(Object scheduler) {
|
||||||
Assert.notNull(scheduler, "Scheduler object must not be null");
|
Assert.notNull(scheduler, "Scheduler object must not be null");
|
||||||
@@ -91,17 +94,31 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the scheduler instance for this registrar (may be null)
|
* Return the {@link TaskScheduler} instance for this registrar (may be {@code null}).
|
||||||
*/
|
*/
|
||||||
public TaskScheduler getScheduler() {
|
public TaskScheduler getScheduler() {
|
||||||
return this.taskScheduler;
|
return this.taskScheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects
|
* Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects
|
||||||
* (typically custom implementations of the {@link Trigger} interface).
|
* (typically custom implementations of the {@link Trigger} interface).
|
||||||
*/
|
*/
|
||||||
public void setTriggerTasks(Map<Runnable, Trigger> triggerTasks) {
|
public void setTriggerTasks(Map<Runnable, Trigger> triggerTasks) {
|
||||||
|
this.triggerTasks = new ArrayList<TriggerTask>();
|
||||||
|
for (Map.Entry<Runnable, Trigger> task : triggerTasks.entrySet()) {
|
||||||
|
this.triggerTasks.add(new TriggerTask(task.getKey(), task.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify triggered tasks as a list of {@link TriggerTask} objects. Primarily used
|
||||||
|
* by {@code <task:*>} namespace parsing.
|
||||||
|
* @since 3.2
|
||||||
|
* @see ScheduledTasksBeanDefinitionParser
|
||||||
|
*/
|
||||||
|
public void setTriggerTasksList(List<TriggerTask> triggerTasks) {
|
||||||
this.triggerTasks = triggerTasks;
|
this.triggerTasks = triggerTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +127,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
|||||||
* @see CronTrigger
|
* @see CronTrigger
|
||||||
*/
|
*/
|
||||||
public void setCronTasks(Map<Runnable, String> cronTasks) {
|
public void setCronTasks(Map<Runnable, String> cronTasks) {
|
||||||
|
this.cronTasks = new ArrayList<CronTask>();
|
||||||
|
for (Map.Entry<Runnable, String> task : cronTasks.entrySet()) {
|
||||||
|
this.addCronTask(task.getKey(), task.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify triggered tasks as a list of {@link CronTask} objects. Primarily used by
|
||||||
|
* {@code <task:*>} namespace parsing.
|
||||||
|
* @since 3.2
|
||||||
|
* @see ScheduledTasksBeanDefinitionParser
|
||||||
|
*/
|
||||||
|
public void setCronTasksList(List<CronTask> cronTasks) {
|
||||||
this.cronTasks = cronTasks;
|
this.cronTasks = cronTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,89 +148,183 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
|||||||
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
||||||
*/
|
*/
|
||||||
public void setFixedRateTasks(Map<Runnable, Long> fixedRateTasks) {
|
public void setFixedRateTasks(Map<Runnable, Long> fixedRateTasks) {
|
||||||
|
this.fixedRateTasks = new ArrayList<IntervalTask>();
|
||||||
|
for (Map.Entry<Runnable, Long> task : fixedRateTasks.entrySet()) {
|
||||||
|
this.addFixedRateTask(task.getKey(), task.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify fixed-rate tasks as a list of {@link IntervalTask} objects. Primarily used
|
||||||
|
* by {@code <task:*>} namespace parsing.
|
||||||
|
* @since 3.2
|
||||||
|
* @see ScheduledTasksBeanDefinitionParser
|
||||||
|
*/
|
||||||
|
public void setFixedRateTasksList(List<IntervalTask> fixedRateTasks) {
|
||||||
this.fixedRateTasks = fixedRateTasks;
|
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<Runnable, Trigger>();
|
|
||||||
}
|
|
||||||
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<Runnable, String>();
|
|
||||||
}
|
|
||||||
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<Runnable, Long>();
|
|
||||||
}
|
|
||||||
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<Runnable, Long>();
|
|
||||||
}
|
|
||||||
this.fixedRateTasks.put(task, period);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values.
|
* Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values.
|
||||||
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long)
|
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long)
|
||||||
*/
|
*/
|
||||||
public void setFixedDelayTasks(Map<Runnable, Long> fixedDelayTasks) {
|
public void setFixedDelayTasks(Map<Runnable, Long> fixedDelayTasks) {
|
||||||
|
this.fixedDelayTasks = new ArrayList<IntervalTask>();
|
||||||
|
for (Map.Entry<Runnable, Long> task : fixedDelayTasks.entrySet()) {
|
||||||
|
this.addFixedDelayTask(task.getKey(), task.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify fixed-delay tasks as a list of {@link IntervalTask} objects. Primarily used
|
||||||
|
* by {@code <task:*>} namespace parsing.
|
||||||
|
* @since 3.2
|
||||||
|
* @see ScheduledTasksBeanDefinitionParser
|
||||||
|
*/
|
||||||
|
public void setFixedDelayTasksList(List<IntervalTask> fixedDelayTasks) {
|
||||||
this.fixedDelayTasks = fixedDelayTasks;
|
this.fixedDelayTasks = fixedDelayTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Runnable task to be triggered per the given {@link Trigger}.
|
||||||
|
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
||||||
|
*/
|
||||||
|
public void addTriggerTask(Runnable task, Trigger trigger) {
|
||||||
|
this.addTriggerTask(new TriggerTask(task, trigger));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a {@code TriggerTask}.
|
||||||
|
* @since 3.2
|
||||||
|
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
||||||
|
*/
|
||||||
|
public void addTriggerTask(TriggerTask task) {
|
||||||
|
if (this.triggerTasks == null) {
|
||||||
|
this.triggerTasks = new ArrayList<TriggerTask>();
|
||||||
|
}
|
||||||
|
this.triggerTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Runnable task to be triggered per the given cron expression
|
||||||
|
*/
|
||||||
|
public void addCronTask(Runnable task, String expression) {
|
||||||
|
this.addCronTask(new CronTask(task, expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a {@link CronTask}.
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public void addCronTask(CronTask task) {
|
||||||
|
if (this.cronTasks == null) {
|
||||||
|
this.cronTasks = new ArrayList<CronTask>();
|
||||||
|
}
|
||||||
|
this.cronTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
this.addFixedRateTask(new IntervalTask(task, period, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fixed-rate {@link IntervalTask}.
|
||||||
|
* @since 3.2
|
||||||
|
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
||||||
|
*/
|
||||||
|
public void addFixedRateTask(IntervalTask task) {
|
||||||
|
if (this.fixedRateTasks == null) {
|
||||||
|
this.fixedRateTasks = new ArrayList<IntervalTask>();
|
||||||
|
}
|
||||||
|
this.fixedRateTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Runnable task to be triggered with the given fixed delay.
|
||||||
|
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long)
|
||||||
|
*/
|
||||||
|
public void addFixedDelayTask(Runnable task, long delay) {
|
||||||
|
this.addFixedDelayTask(new IntervalTask(task, delay, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fixed-delay {@link IntervalTask}.
|
||||||
|
* @since 3.2
|
||||||
|
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long)
|
||||||
|
*/
|
||||||
|
public void addFixedDelayTask(IntervalTask task) {
|
||||||
|
if (this.fixedDelayTasks == null) {
|
||||||
|
this.fixedDelayTasks = new ArrayList<IntervalTask>();
|
||||||
|
}
|
||||||
|
this.fixedDelayTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this {@code ScheduledTaskRegistrar} has any tasks registered.
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public boolean hasTasks() {
|
||||||
|
return (this.fixedRateTasks != null && !this.fixedRateTasks.isEmpty()) ||
|
||||||
|
(this.fixedDelayTasks != null && !this.fixedDelayTasks.isEmpty()) ||
|
||||||
|
(this.cronTasks != null && !this.cronTasks.isEmpty()) ||
|
||||||
|
(this.triggerTasks != null && !this.triggerTasks.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule all registered tasks against the underlying {@linkplain
|
||||||
|
* #setTaskScheduler(TaskScheduler) task scheduler.
|
||||||
|
*/
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
if (this.taskScheduler == null) {
|
if (this.taskScheduler == null) {
|
||||||
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
|
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
|
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
|
||||||
}
|
}
|
||||||
if (this.triggerTasks != null) {
|
if (this.triggerTasks != null) {
|
||||||
for (Map.Entry<Runnable, Trigger> entry : this.triggerTasks.entrySet()) {
|
for (TriggerTask task : triggerTasks) {
|
||||||
this.scheduledFutures.add(this.taskScheduler.schedule(entry.getKey(), entry.getValue()));
|
this.scheduledFutures.add(this.taskScheduler.schedule(
|
||||||
|
task.getRunnable(), task.getTrigger()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.cronTasks != null) {
|
if (this.cronTasks != null) {
|
||||||
for (Map.Entry<Runnable, String> entry : this.cronTasks.entrySet()) {
|
for (CronTask task : cronTasks) {
|
||||||
this.scheduledFutures.add(this.taskScheduler.schedule(entry.getKey(), new CronTrigger(entry.getValue())));
|
this.scheduledFutures.add(this.taskScheduler.schedule(
|
||||||
|
task.getRunnable(), task.getTrigger()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.fixedRateTasks != null) {
|
if (this.fixedRateTasks != null) {
|
||||||
for (Map.Entry<Runnable, Long> entry : this.fixedRateTasks.entrySet()) {
|
for (IntervalTask task : fixedRateTasks) {
|
||||||
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(entry.getKey(), entry.getValue()));
|
if (task.getInitialDelay() > 0) {
|
||||||
|
Date startTime = new Date(now + task.getInitialDelay());
|
||||||
|
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
|
||||||
|
task.getRunnable(), startTime, task.getInterval()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
|
||||||
|
task.getRunnable(), task.getInterval()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.fixedDelayTasks != null) {
|
if (this.fixedDelayTasks != null) {
|
||||||
for (Map.Entry<Runnable, Long> entry : this.fixedDelayTasks.entrySet()) {
|
for (IntervalTask task : fixedDelayTasks) {
|
||||||
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(entry.getKey(), entry.getValue()));
|
if (task.getInitialDelay() > 0) {
|
||||||
|
Date startTime = new Date(now + task.getInitialDelay());
|
||||||
|
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
|
||||||
|
task.getRunnable(), startTime, task.getInterval()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
|
||||||
|
task.getRunnable(), task.getInterval()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
for (ScheduledFuture<?> future : this.scheduledFutures) {
|
for (ScheduledFuture<?> future : this.scheduledFutures) {
|
||||||
future.cancel(true);
|
future.cancel(true);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,27 +16,29 @@
|
|||||||
|
|
||||||
package org.springframework.scheduling.config;
|
package org.springframework.scheduling.config;
|
||||||
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.ManagedMap;
|
import org.springframework.beans.factory.support.ManagedList;
|
||||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parser for the 'scheduled-tasks' element of the scheduling namespace.
|
* Parser for the 'scheduled-tasks' element of the scheduling namespace.
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||||
|
|
||||||
private static final String ELEMENT_SCHEDULED = "scheduled";
|
private static final String ELEMENT_SCHEDULED = "scheduled";
|
||||||
|
private static final long ZERO_INITIAL_DELAY = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldGenerateId() {
|
protected boolean shouldGenerateId() {
|
||||||
@@ -51,10 +53,10 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
|||||||
@Override
|
@Override
|
||||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||||
builder.setLazyInit(false); // lazy scheduled tasks are a contradiction in terms -> force to false
|
builder.setLazyInit(false); // lazy scheduled tasks are a contradiction in terms -> force to false
|
||||||
ManagedMap<RuntimeBeanReference, String> cronTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
ManagedList<RuntimeBeanReference> cronTaskList = new ManagedList<RuntimeBeanReference>();
|
||||||
ManagedMap<RuntimeBeanReference, String> fixedDelayTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
ManagedList<RuntimeBeanReference> fixedDelayTaskList = new ManagedList<RuntimeBeanReference>();
|
||||||
ManagedMap<RuntimeBeanReference, String> fixedRateTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
ManagedList<RuntimeBeanReference> fixedRateTaskList = new ManagedList<RuntimeBeanReference>();
|
||||||
ManagedMap<RuntimeBeanReference, RuntimeBeanReference> triggerTaskMap = new ManagedMap<RuntimeBeanReference, RuntimeBeanReference>();
|
ManagedList<RuntimeBeanReference> triggerTaskList = new ManagedList<RuntimeBeanReference>();
|
||||||
NodeList childNodes = element.getChildNodes();
|
NodeList childNodes = element.getChildNodes();
|
||||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
Node child = childNodes.item(i);
|
Node child = childNodes.item(i);
|
||||||
@@ -64,55 +66,67 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
|||||||
Element taskElement = (Element) child;
|
Element taskElement = (Element) child;
|
||||||
String ref = taskElement.getAttribute("ref");
|
String ref = taskElement.getAttribute("ref");
|
||||||
String method = taskElement.getAttribute("method");
|
String method = taskElement.getAttribute("method");
|
||||||
|
|
||||||
// Check that 'ref' and 'method' are specified
|
// Check that 'ref' and 'method' are specified
|
||||||
if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) {
|
if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) {
|
||||||
parserContext.getReaderContext().error("Both 'ref' and 'method' are required", taskElement);
|
parserContext.getReaderContext().error("Both 'ref' and 'method' are required", taskElement);
|
||||||
// Continue with the possible next task element
|
// Continue with the possible next task element
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeBeanReference runnableBeanRef = new RuntimeBeanReference(
|
|
||||||
createRunnableBean(ref, method, taskElement, parserContext));
|
|
||||||
|
|
||||||
String cronAttribute = taskElement.getAttribute("cron");
|
String cronAttribute = taskElement.getAttribute("cron");
|
||||||
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
|
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
|
||||||
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
|
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
|
||||||
String triggerAttribute = taskElement.getAttribute("trigger");
|
String triggerAttribute = taskElement.getAttribute("trigger");
|
||||||
|
String initialDelayAttribute = taskElement.getAttribute("initial-delay");
|
||||||
|
|
||||||
boolean hasCronAttribute = StringUtils.hasText(cronAttribute);
|
boolean hasCronAttribute = StringUtils.hasText(cronAttribute);
|
||||||
boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute);
|
boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute);
|
||||||
boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute);
|
boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute);
|
||||||
boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute);
|
boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute);
|
||||||
|
boolean hasInitialDelayAttribute = StringUtils.hasText(initialDelayAttribute);
|
||||||
|
|
||||||
if (!(hasCronAttribute | hasFixedDelayAttribute | hasFixedRateAttribute | hasTriggerAttribute)) {
|
if (!(hasCronAttribute || hasFixedDelayAttribute || hasFixedRateAttribute || hasTriggerAttribute)) {
|
||||||
parserContext.getReaderContext().error(
|
parserContext.getReaderContext().error(
|
||||||
"exactly one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement);
|
"one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement);
|
||||||
// Continue with the possible next task element
|
continue; // with the possible next task element
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCronAttribute) {
|
if (hasInitialDelayAttribute && (hasCronAttribute || hasTriggerAttribute)) {
|
||||||
cronTaskMap.put(runnableBeanRef, cronAttribute);
|
parserContext.getReaderContext().error(
|
||||||
|
"the 'initial-delay' attribute may not be used with cron and trigger tasks", taskElement);
|
||||||
|
continue; // with the possible next task element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String runnableName =
|
||||||
|
runnableReference(ref, method, taskElement, parserContext).getBeanName();
|
||||||
|
|
||||||
if (hasFixedDelayAttribute) {
|
if (hasFixedDelayAttribute) {
|
||||||
fixedDelayTaskMap.put(runnableBeanRef, fixedDelayAttribute);
|
fixedDelayTaskList.add(intervalTaskReference(runnableName,
|
||||||
|
initialDelayAttribute, fixedDelayAttribute, taskElement, parserContext));
|
||||||
}
|
}
|
||||||
if (hasFixedRateAttribute) {
|
if (hasFixedRateAttribute) {
|
||||||
fixedRateTaskMap.put(runnableBeanRef, fixedRateAttribute);
|
fixedRateTaskList.add(intervalTaskReference(runnableName,
|
||||||
|
initialDelayAttribute, fixedRateAttribute, taskElement, parserContext));
|
||||||
|
}
|
||||||
|
if (hasCronAttribute) {
|
||||||
|
cronTaskList.add(cronTaskReference(runnableName, cronAttribute,
|
||||||
|
taskElement, parserContext));
|
||||||
}
|
}
|
||||||
if (hasTriggerAttribute) {
|
if (hasTriggerAttribute) {
|
||||||
triggerTaskMap.put(runnableBeanRef, new RuntimeBeanReference(triggerAttribute));
|
String triggerName = new RuntimeBeanReference(triggerAttribute).getBeanName();
|
||||||
|
triggerTaskList.add(triggerTaskReference(runnableName, triggerName,
|
||||||
|
taskElement, parserContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String schedulerRef = element.getAttribute("scheduler");
|
String schedulerRef = element.getAttribute("scheduler");
|
||||||
if (StringUtils.hasText(schedulerRef)) {
|
if (StringUtils.hasText(schedulerRef)) {
|
||||||
builder.addPropertyReference("taskScheduler", schedulerRef);
|
builder.addPropertyReference("taskScheduler", schedulerRef);
|
||||||
}
|
}
|
||||||
builder.addPropertyValue("cronTasks", cronTaskMap);
|
builder.addPropertyValue("cronTasksList", cronTaskList);
|
||||||
builder.addPropertyValue("fixedDelayTasks", fixedDelayTaskMap);
|
builder.addPropertyValue("fixedDelayTasksList", fixedDelayTaskList);
|
||||||
builder.addPropertyValue("fixedRateTasks", fixedRateTaskMap);
|
builder.addPropertyValue("fixedRateTasksList", fixedRateTaskList);
|
||||||
builder.addPropertyValue("triggerTasks", triggerTaskMap);
|
builder.addPropertyValue("triggerTasksList", triggerTaskList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isScheduledElement(Node node, ParserContext parserContext) {
|
private boolean isScheduledElement(Node node, ParserContext parserContext) {
|
||||||
@@ -120,16 +134,49 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
|||||||
ELEMENT_SCHEDULED.equals(parserContext.getDelegate().getLocalName(node));
|
ELEMENT_SCHEDULED.equals(parserContext.getDelegate().getLocalName(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createRunnableBean(String ref, String method, Element taskElement, ParserContext parserContext) {
|
private RuntimeBeanReference runnableReference(String ref, String method, Element taskElement, ParserContext parserContext) {
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||||
"org.springframework.scheduling.support.ScheduledMethodRunnable");
|
"org.springframework.scheduling.support.ScheduledMethodRunnable");
|
||||||
builder.addConstructorArgReference(ref);
|
builder.addConstructorArgReference(ref);
|
||||||
builder.addConstructorArgValue(method);
|
builder.addConstructorArgValue(method);
|
||||||
|
return beanReference(taskElement, parserContext, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RuntimeBeanReference intervalTaskReference(String runnableBeanName,
|
||||||
|
String initialDelay, String interval, Element taskElement, ParserContext parserContext) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||||
|
"org.springframework.scheduling.config.IntervalTask");
|
||||||
|
builder.addConstructorArgReference(runnableBeanName);
|
||||||
|
builder.addConstructorArgValue(interval);
|
||||||
|
builder.addConstructorArgValue(StringUtils.hasLength(initialDelay) ? initialDelay : ZERO_INITIAL_DELAY);
|
||||||
|
return beanReference(taskElement, parserContext, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RuntimeBeanReference cronTaskReference(String runnableBeanName,
|
||||||
|
String cronExpression, Element taskElement, ParserContext parserContext) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||||
|
"org.springframework.scheduling.config.CronTask");
|
||||||
|
builder.addConstructorArgReference(runnableBeanName);
|
||||||
|
builder.addConstructorArgValue(cronExpression);
|
||||||
|
return beanReference(taskElement, parserContext, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RuntimeBeanReference triggerTaskReference(String runnableBeanName,
|
||||||
|
String triggerBeanName, Element taskElement, ParserContext parserContext) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||||
|
"org.springframework.scheduling.config.TriggerTask");
|
||||||
|
builder.addConstructorArgReference(runnableBeanName);
|
||||||
|
builder.addConstructorArgReference(triggerBeanName);
|
||||||
|
return beanReference(taskElement, parserContext, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RuntimeBeanReference beanReference(Element taskElement,
|
||||||
|
ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||||
// Extract the source of the current task
|
// Extract the source of the current task
|
||||||
builder.getRawBeanDefinition().setSource(parserContext.extractSource(taskElement));
|
builder.getRawBeanDefinition().setSource(parserContext.extractSource(taskElement));
|
||||||
String generatedName = parserContext.getReaderContext().generateBeanName(builder.getRawBeanDefinition());
|
String generatedName = parserContext.getReaderContext().generateBeanName(builder.getRawBeanDefinition());
|
||||||
parserContext.registerBeanComponent(new BeanComponentDefinition(builder.getBeanDefinition(), generatedName));
|
parserContext.registerBeanComponent(new BeanComponentDefinition(builder.getBeanDefinition(), generatedName));
|
||||||
return generatedName;
|
return new RuntimeBeanReference(generatedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 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.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder class defining a {@code Runnable} to be executed as a task, typically at a
|
||||||
|
* scheduled time or interval. See subclass hierarchy for various scheduling approaches.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public class Task {
|
||||||
|
|
||||||
|
private final Runnable runnable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code Task}.
|
||||||
|
* @param runnable the underlying task to execute.
|
||||||
|
*/
|
||||||
|
public Task(Runnable runnable) {
|
||||||
|
this.runnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Runnable getRunnable() {
|
||||||
|
return runnable;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 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.config;
|
||||||
|
|
||||||
|
import org.springframework.scheduling.Trigger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} implementation defining a {@code Runnable} to be executed according to a
|
||||||
|
* given {@link Trigger}.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.2
|
||||||
|
* @see Trigger#nextExecutionTime(org.springframework.scheduling.TriggerContext)
|
||||||
|
* @see ScheduledTaskRegistrar#setTriggerTasksList(java.util.List)
|
||||||
|
* @see org.springframework.scheduling.TaskScheduler#schedule(Runnable, Trigger)
|
||||||
|
*/
|
||||||
|
public class TriggerTask extends Task {
|
||||||
|
|
||||||
|
private final Trigger trigger;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link TriggerTask}.
|
||||||
|
* @param runnable the underlying task to execute
|
||||||
|
* @param trigger specifies when the task should be executed
|
||||||
|
*/
|
||||||
|
public TriggerTask(Runnable runnable, Trigger trigger) {
|
||||||
|
super(runnable);
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Trigger getTrigger() {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -339,6 +339,10 @@ public class CronSequenceGenerator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getExpression() {
|
||||||
|
return this.expression;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof CronSequenceGenerator)) {
|
if (!(obj instanceof CronSequenceGenerator)) {
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ public class CronTrigger implements Trigger {
|
|||||||
return this.sequenceGenerator.next(date);
|
return this.sequenceGenerator.next(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getExpression() {
|
||||||
|
return this.sequenceGenerator.getExpression();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
|||||||
@@ -229,6 +229,14 @@
|
|||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="initial-delay" type="xsd:int" use="optional">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Number of milliseconds to delay before the first execution of a 'fixed-rate' or
|
||||||
|
'fixed-delay' task.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
<xsd:attribute name="ref" type="xsd:string" use="required">
|
<xsd:attribute name="ref" type="xsd:string" use="required">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,15 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.scheduling.annotation;
|
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.Date;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -32,8 +28,13 @@ import org.springframework.scheduling.TaskScheduler;
|
|||||||
import org.springframework.scheduling.Trigger;
|
import org.springframework.scheduling.Trigger;
|
||||||
import org.springframework.scheduling.TriggerContext;
|
import org.springframework.scheduling.TriggerContext;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
|
import org.springframework.scheduling.config.IntervalTask;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests use of @EnableScheduling on @Configuration classes.
|
* Tests use of @EnableScheduling on @Configuration classes.
|
||||||
*
|
*
|
||||||
@@ -383,6 +384,45 @@ public class EnableSchedulingTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withTaskAddedVia_configureTasks() throws InterruptedException {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(SchedulingEnabled_withTaskAddedVia_configureTasks.class);
|
||||||
|
ctx.refresh();
|
||||||
|
Thread.sleep(20);
|
||||||
|
ThreadAwareWorker worker = ctx.getBean(ThreadAwareWorker.class);
|
||||||
|
ctx.close();
|
||||||
|
assertThat(worker.executedByThread, startsWith("taskScheduler-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
static class SchedulingEnabled_withTaskAddedVia_configureTasks implements SchedulingConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ThreadAwareWorker worker() {
|
||||||
|
return new ThreadAwareWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TaskScheduler taskScheduler() {
|
||||||
|
return new ThreadPoolTaskScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||||
|
taskRegistrar.setScheduler(taskScheduler());
|
||||||
|
taskRegistrar.addFixedRateTask(new IntervalTask(
|
||||||
|
new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
worker().executedByThread = Thread.currentThread().getName();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
10, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withTriggerTask() throws InterruptedException {
|
public void withTriggerTask() throws InterruptedException {
|
||||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
@@ -421,4 +461,34 @@ public class EnableSchedulingTests {
|
|||||||
return scheduler;
|
return scheduler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withInitiallyDelayedFixedRateTask() throws InterruptedException {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(FixedRateTaskConfig_withInitialDelay.class);
|
||||||
|
ctx.refresh();
|
||||||
|
|
||||||
|
Thread.sleep(1950);
|
||||||
|
AtomicInteger counter = ctx.getBean(AtomicInteger.class);
|
||||||
|
ctx.close();
|
||||||
|
|
||||||
|
assertThat(counter.get(), greaterThan(0)); // the @Scheduled method was called
|
||||||
|
assertThat(counter.get(), lessThanOrEqualTo(10)); // but not more than times the delay allows
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@EnableScheduling @Configuration
|
||||||
|
static class FixedRateTaskConfig_withInitialDelay {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AtomicInteger counter() {
|
||||||
|
return new AtomicInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(initialDelay=1000, fixedRate=100)
|
||||||
|
public void task() {
|
||||||
|
counter().incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,10 +22,9 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.DirectFieldAccessor;
|
import org.springframework.beans.DirectFieldAccessor;
|
||||||
@@ -34,12 +33,17 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.support.StaticApplicationContext;
|
import org.springframework.context.support.StaticApplicationContext;
|
||||||
|
import org.springframework.scheduling.config.CronTask;
|
||||||
|
import org.springframework.scheduling.config.IntervalTask;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public class ScheduledAnnotationBeanPostProcessorTests {
|
public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
@@ -56,15 +60,18 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, Long> fixedDelayTasks = (Map<Runnable, Long>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IntervalTask> fixedDelayTasks = (List<IntervalTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
|
||||||
assertEquals(1, fixedDelayTasks.size());
|
assertEquals(1, fixedDelayTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedDelayTasks.keySet().iterator().next();
|
IntervalTask task = fixedDelayTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("fixedDelay", targetMethod.getName());
|
assertEquals("fixedDelay", targetMethod.getName());
|
||||||
assertEquals(new Long(5000), fixedDelayTasks.values().iterator().next());
|
assertEquals(0L, task.getInitialDelay());
|
||||||
|
assertEquals(5000L, task.getInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,15 +87,45 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, Long> fixedRateTasks = (Map<Runnable, Long>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
||||||
assertEquals(1, fixedRateTasks.size());
|
assertEquals(1, fixedRateTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedRateTasks.keySet().iterator().next();
|
IntervalTask task = fixedRateTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("fixedRate", targetMethod.getName());
|
assertEquals("fixedRate", targetMethod.getName());
|
||||||
assertEquals(new Long(3000), fixedRateTasks.values().iterator().next());
|
assertEquals(0L, task.getInitialDelay());
|
||||||
|
assertEquals(3000L, task.getInterval());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixedRateTaskWithInitialDelay() {
|
||||||
|
StaticApplicationContext context = new StaticApplicationContext();
|
||||||
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
|
BeanDefinition targetDefinition = new RootBeanDefinition(
|
||||||
|
ScheduledAnnotationBeanPostProcessorTests.FixedRateWithInitialDelayTestBean.class);
|
||||||
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
|
context.refresh();
|
||||||
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
|
Object target = context.getBean("target");
|
||||||
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
|
||||||
|
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
||||||
|
assertEquals(1, fixedRateTasks.size());
|
||||||
|
IntervalTask task = fixedRateTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
|
Object targetObject = runnable.getTarget();
|
||||||
|
Method targetMethod = runnable.getMethod();
|
||||||
|
assertEquals(target, targetObject);
|
||||||
|
assertEquals("fixedRate", targetMethod.getName());
|
||||||
|
assertEquals(1000L, task.getInitialDelay());
|
||||||
|
assertEquals(3000L, task.getInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -104,15 +141,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<CronTask> cronTasks = (List<CronTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||||
assertEquals(1, cronTasks.size());
|
assertEquals(1, cronTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next();
|
CronTask task = cronTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("cron", targetMethod.getName());
|
assertEquals("cron", targetMethod.getName());
|
||||||
assertEquals("*/7 * * * * ?", cronTasks.values().iterator().next());
|
assertEquals("*/7 * * * * ?", task.getExpression());
|
||||||
Thread.sleep(10000);
|
Thread.sleep(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,15 +168,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, Long> fixedRateTasks = (Map<Runnable, Long>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
||||||
assertEquals(1, fixedRateTasks.size());
|
assertEquals(1, fixedRateTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedRateTasks.keySet().iterator().next();
|
IntervalTask task = fixedRateTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("checkForUpdates", targetMethod.getName());
|
assertEquals("checkForUpdates", targetMethod.getName());
|
||||||
assertEquals(new Long(5000), fixedRateTasks.values().iterator().next());
|
assertEquals(5000L, task.getInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -153,15 +194,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<CronTask> cronTasks = (List<CronTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||||
assertEquals(1, cronTasks.size());
|
assertEquals(1, cronTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next();
|
CronTask task = cronTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("generateReport", targetMethod.getName());
|
assertEquals("generateReport", targetMethod.getName());
|
||||||
assertEquals("0 0 * * * ?", cronTasks.values().iterator().next());
|
assertEquals("0 0 * * * ?", task.getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -183,15 +226,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<CronTask> cronTasks = (List<CronTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||||
assertEquals(1, cronTasks.size());
|
assertEquals(1, cronTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next();
|
CronTask task = cronTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("x", targetMethod.getName());
|
assertEquals("x", targetMethod.getName());
|
||||||
assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next());
|
assertEquals(businessHoursCronExpression, task.getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -213,15 +258,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
@SuppressWarnings("unchecked")
|
||||||
|
List<CronTask> cronTasks = (List<CronTask>)
|
||||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||||
assertEquals(1, cronTasks.size());
|
assertEquals(1, cronTasks.size());
|
||||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next();
|
CronTask task = cronTasks.get(0);
|
||||||
|
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||||
Object targetObject = runnable.getTarget();
|
Object targetObject = runnable.getTarget();
|
||||||
Method targetMethod = runnable.getMethod();
|
Method targetMethod = runnable.getMethod();
|
||||||
assertEquals(target, targetObject);
|
assertEquals(target, targetObject);
|
||||||
assertEquals("y", targetMethod.getName());
|
assertEquals("y", targetMethod.getName());
|
||||||
assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next());
|
assertEquals(businessHoursCronExpression, task.getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
@@ -236,14 +283,19 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void invalidCron() {
|
public void invalidCron() throws Throwable {
|
||||||
StaticApplicationContext context = new StaticApplicationContext();
|
StaticApplicationContext context = new StaticApplicationContext();
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(
|
||||||
ScheduledAnnotationBeanPostProcessorTests.InvalidCronTestBean.class);
|
ScheduledAnnotationBeanPostProcessorTests.InvalidCronTestBean.class);
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
try {
|
||||||
|
context.refresh();
|
||||||
|
fail("expected exception");
|
||||||
|
} catch (BeanCreationException ex) {
|
||||||
|
throw ex.getRootCause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
@@ -269,7 +321,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class FixedDelayTestBean {
|
static class FixedDelayTestBean {
|
||||||
|
|
||||||
@Scheduled(fixedDelay=5000)
|
@Scheduled(fixedDelay=5000)
|
||||||
public void fixedDelay() {
|
public void fixedDelay() {
|
||||||
@@ -277,7 +329,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class FixedRateTestBean {
|
static class FixedRateTestBean {
|
||||||
|
|
||||||
@Scheduled(fixedRate=3000)
|
@Scheduled(fixedRate=3000)
|
||||||
public void fixedRate() {
|
public void fixedRate() {
|
||||||
@@ -285,7 +337,15 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class CronTestBean {
|
static class FixedRateWithInitialDelayTestBean {
|
||||||
|
|
||||||
|
@Scheduled(initialDelay=1000, fixedRate=3000)
|
||||||
|
public void fixedRate() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class CronTestBean {
|
||||||
|
|
||||||
@Scheduled(cron="*/7 * * * * ?")
|
@Scheduled(cron="*/7 * * * * ?")
|
||||||
public void cron() throws IOException {
|
public void cron() throws IOException {
|
||||||
@@ -295,7 +355,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class EmptyAnnotationTestBean {
|
static class EmptyAnnotationTestBean {
|
||||||
|
|
||||||
@Scheduled
|
@Scheduled
|
||||||
public void invalid() {
|
public void invalid() {
|
||||||
@@ -304,7 +364,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class InvalidCronTestBean {
|
static class InvalidCronTestBean {
|
||||||
|
|
||||||
@Scheduled(cron="abc")
|
@Scheduled(cron="abc")
|
||||||
public void invalid() {
|
public void invalid() {
|
||||||
@@ -313,7 +373,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class NonVoidReturnTypeTestBean {
|
static class NonVoidReturnTypeTestBean {
|
||||||
|
|
||||||
@Scheduled(fixedRate=3000)
|
@Scheduled(fixedRate=3000)
|
||||||
public String invalid() {
|
public String invalid() {
|
||||||
@@ -323,7 +383,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class NonEmptyParamListTestBean {
|
static class NonEmptyParamListTestBean {
|
||||||
|
|
||||||
@Scheduled(fixedRate=3000)
|
@Scheduled(fixedRate=3000)
|
||||||
public void invalid(String oops) {
|
public void invalid(String oops) {
|
||||||
@@ -344,7 +404,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
private static @interface Hourly {}
|
private static @interface Hourly {}
|
||||||
|
|
||||||
|
|
||||||
private static class MetaAnnotationFixedRateTestBean {
|
static class MetaAnnotationFixedRateTestBean {
|
||||||
|
|
||||||
@EveryFiveSeconds
|
@EveryFiveSeconds
|
||||||
public void checkForUpdates() {
|
public void checkForUpdates() {
|
||||||
@@ -352,7 +412,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class MetaAnnotationCronTestBean {
|
static class MetaAnnotationCronTestBean {
|
||||||
|
|
||||||
@Hourly
|
@Hourly
|
||||||
public void generateReport() {
|
public void generateReport() {
|
||||||
@@ -360,7 +420,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class PropertyPlaceholderTestBean {
|
static class PropertyPlaceholderTestBean {
|
||||||
|
|
||||||
@Scheduled(cron = "${schedules.businessHours}")
|
@Scheduled(cron = "${schedules.businessHours}")
|
||||||
public void x() {
|
public void x() {
|
||||||
@@ -370,11 +430,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
|||||||
|
|
||||||
@Scheduled(cron = "${schedules.businessHours}")
|
@Scheduled(cron = "${schedules.businessHours}")
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
private static @interface BusinessHours {}
|
private static @interface BusinessHours {}
|
||||||
|
|
||||||
|
|
||||||
private static class PropertyPlaceholderMetaAnnotationTestBean {
|
static class PropertyPlaceholderMetaAnnotationTestBean {
|
||||||
|
|
||||||
@BusinessHours
|
@BusinessHours
|
||||||
public void y() {
|
public void y() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,11 +17,9 @@
|
|||||||
package org.springframework.scheduling.config;
|
package org.springframework.scheduling.config;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -32,8 +30,13 @@ import org.springframework.scheduling.Trigger;
|
|||||||
import org.springframework.scheduling.TriggerContext;
|
import org.springframework.scheduling.TriggerContext;
|
||||||
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class ScheduledTasksBeanDefinitionParserTests {
|
public class ScheduledTasksBeanDefinitionParserTests {
|
||||||
@@ -63,9 +66,9 @@ public class ScheduledTasksBeanDefinitionParserTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkTarget() {
|
public void checkTarget() {
|
||||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||||
this.registrar).getPropertyValue("fixedRateTasks");
|
this.registrar).getPropertyValue("fixedRateTasks");
|
||||||
Runnable runnable = tasks.keySet().iterator().next();
|
Runnable runnable = tasks.get(0).getRunnable();
|
||||||
assertEquals(ScheduledMethodRunnable.class, runnable.getClass());
|
assertEquals(ScheduledMethodRunnable.class, runnable.getClass());
|
||||||
Object targetObject = ((ScheduledMethodRunnable) runnable).getTarget();
|
Object targetObject = ((ScheduledMethodRunnable) runnable).getTarget();
|
||||||
Method targetMethod = ((ScheduledMethodRunnable) runnable).getMethod();
|
Method targetMethod = ((ScheduledMethodRunnable) runnable).getMethod();
|
||||||
@@ -75,39 +78,39 @@ public class ScheduledTasksBeanDefinitionParserTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fixedRateTasks() {
|
public void fixedRateTasks() {
|
||||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||||
this.registrar).getPropertyValue("fixedRateTasks");
|
this.registrar).getPropertyValue("fixedRateTasks");
|
||||||
assertEquals(2, tasks.size());
|
assertEquals(3, tasks.size());
|
||||||
Collection<Long> values = tasks.values();
|
assertEquals(1000L, tasks.get(0).getInterval());
|
||||||
assertTrue(values.contains(new Long(1000)));
|
assertEquals(2000L, tasks.get(1).getInterval());
|
||||||
assertTrue(values.contains(new Long(2000)));
|
assertEquals(4000L, tasks.get(2).getInterval());
|
||||||
|
assertEquals(500, tasks.get(2).getInitialDelay());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fixedDelayTasks() {
|
public void fixedDelayTasks() {
|
||||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||||
this.registrar).getPropertyValue("fixedDelayTasks");
|
this.registrar).getPropertyValue("fixedDelayTasks");
|
||||||
assertEquals(1, tasks.size());
|
assertEquals(2, tasks.size());
|
||||||
Long value = tasks.values().iterator().next();
|
assertEquals(3000L, tasks.get(0).getInterval());
|
||||||
assertEquals(new Long(3000), value);
|
assertEquals(3500L, tasks.get(1).getInterval());
|
||||||
|
assertEquals(250, tasks.get(1).getInitialDelay());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cronTasks() {
|
public void cronTasks() {
|
||||||
Map<Runnable, String> tasks = (Map<Runnable, String>) new DirectFieldAccessor(
|
List<CronTask> tasks = (List<CronTask>) new DirectFieldAccessor(
|
||||||
this.registrar).getPropertyValue("cronTasks");
|
this.registrar).getPropertyValue("cronTasks");
|
||||||
assertEquals(1, tasks.size());
|
assertEquals(1, tasks.size());
|
||||||
String expression = tasks.values().iterator().next();
|
assertEquals("*/4 * 9-17 * * MON-FRI", tasks.get(0).getExpression());
|
||||||
assertEquals("*/4 * 9-17 * * MON-FRI", expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void triggerTasks() {
|
public void triggerTasks() {
|
||||||
Map<Runnable, Trigger> tasks = (Map<Runnable, Trigger>) new DirectFieldAccessor(
|
List<TriggerTask> tasks = (List<TriggerTask>) new DirectFieldAccessor(
|
||||||
this.registrar).getPropertyValue("triggerTasks");
|
this.registrar).getPropertyValue("triggerTasks");
|
||||||
assertEquals(1, tasks.size());
|
assertEquals(1, tasks.size());
|
||||||
Trigger trigger = tasks.values().iterator().next();
|
assertThat(tasks.get(0).getTrigger(), instanceOf(TestTrigger.class));
|
||||||
assertEquals(TestTrigger.class, trigger.getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
<task:scheduled ref="testBean" method="test" fixed-rate="1000"/>
|
<task:scheduled ref="testBean" method="test" fixed-rate="1000"/>
|
||||||
<task:scheduled ref="testBean" method="test" fixed-rate="2000"/>
|
<task:scheduled ref="testBean" method="test" fixed-rate="2000"/>
|
||||||
<task:scheduled ref="testBean" method="test" fixed-delay="3000"/>
|
<task:scheduled ref="testBean" method="test" fixed-delay="3000"/>
|
||||||
|
<task:scheduled ref="testBean" method="test" fixed-delay="3500" initial-delay="250"/>
|
||||||
<task:scheduled ref="testBean" method="test" cron="*/4 * 9-17 * * MON-FRI"/>
|
<task:scheduled ref="testBean" method="test" cron="*/4 * 9-17 * * MON-FRI"/>
|
||||||
<task:scheduled ref="testBean" method="test" trigger="customTrigger"/>
|
<task:scheduled ref="testBean" method="test" trigger="customTrigger"/>
|
||||||
|
<task:scheduled ref="testBean" method="test" fixed-rate="4000" initial-delay="500"/>
|
||||||
</task:scheduled-tasks>
|
</task:scheduled-tasks>
|
||||||
|
|
||||||
<task:scheduler id="testScheduler"/>
|
<task:scheduler id="testScheduler"/>
|
||||||
|
|||||||
1
src/dist/changelog.txt
vendored
1
src/dist/changelog.txt
vendored
@@ -31,6 +31,7 @@ Changes in version 3.2 M1
|
|||||||
* add required flag to @RequestBody annotation
|
* add required flag to @RequestBody annotation
|
||||||
* support executor qualification with @Async#value (SPR-6847)
|
* support executor qualification with @Async#value (SPR-6847)
|
||||||
* add convenient WebAppInitializer base classes (SPR-9300)
|
* add convenient WebAppInitializer base classes (SPR-9300)
|
||||||
|
* support initial delay attribute for scheduled tasks (SPR-7022)
|
||||||
|
|
||||||
Changes in version 3.1.1 (2012-02-16)
|
Changes in version 3.1.1 (2012-02-16)
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|||||||
@@ -472,7 +472,7 @@ public class TaskExecutorExample {
|
|||||||
example.</para>
|
example.</para>
|
||||||
|
|
||||||
<programlisting language="xml"><task:scheduled-tasks scheduler="myScheduler">
|
<programlisting language="xml"><task:scheduled-tasks scheduler="myScheduler">
|
||||||
<task:scheduled ref="someObject" method="someMethod" fixed-delay="5000"/>
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
|
||||||
</task:scheduled-tasks>
|
</task:scheduled-tasks>
|
||||||
|
|
||||||
<task:scheduler id="myScheduler" pool-size="10"/></programlisting>
|
<task:scheduler id="myScheduler" pool-size="10"/></programlisting>
|
||||||
@@ -480,13 +480,19 @@ public class TaskExecutorExample {
|
|||||||
<para>As you can see, the scheduler is referenced by the outer element,
|
<para>As you can see, the scheduler is referenced by the outer element,
|
||||||
and each individual task includes the configuration of its trigger
|
and each individual task includes the configuration of its trigger
|
||||||
metadata. In the preceding example, that metadata defines a periodic
|
metadata. In the preceding example, that metadata defines a periodic
|
||||||
trigger with a fixed delay. It could also be configured with a
|
trigger with a fixed delay indicating the number of milliseconds to wait
|
||||||
"fixed-rate", or for more control, a "cron" attribute could be provided
|
after each task execution has completed. Another option is 'fixed-rate',
|
||||||
instead. Here's an example featuring these other options.</para>
|
indicating how often the method should be executed regardless of how long
|
||||||
|
any previous execution takes. Additionally, for both fixed-delay and
|
||||||
|
fixed-rate tasks an 'initial-delay' parameter may be specified indicating
|
||||||
|
the number of milliseconds to wait before the first execution of the
|
||||||
|
method. For more control, a "cron" attribute may be provided instead.
|
||||||
|
Here is an example demonstrating these other options.</para>
|
||||||
|
|
||||||
<programlisting language="xml"><task:scheduled-tasks scheduler="myScheduler">
|
<programlisting language="xml"><task:scheduled-tasks scheduler="myScheduler">
|
||||||
<task:scheduled ref="someObject" method="someMethod" fixed-rate="5000"/>
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
|
||||||
<task:scheduled ref="anotherObject" method="anotherMethod" cron="*/5 * * * * MON-FRI"/>
|
<task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
|
||||||
|
<task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
|
||||||
</task:scheduled-tasks>
|
</task:scheduled-tasks>
|
||||||
|
|
||||||
<task:scheduler id="myScheduler" pool-size="10"/></programlisting>
|
<task:scheduler id="myScheduler" pool-size="10"/></programlisting>
|
||||||
@@ -523,6 +529,16 @@ public void doSomething() {
|
|||||||
// something that should execute periodically
|
// something that should execute periodically
|
||||||
}</programlisting>
|
}</programlisting>
|
||||||
|
|
||||||
|
<para>For fixed-delay and fixed-rate tasks, an initial delay may be
|
||||||
|
specified indicating the number of milliseconds to wait before the first
|
||||||
|
execution of the method.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting language="java">@Scheduled(initialDelay=1000, fixedRate=5000)
|
||||||
|
public void doSomething() {
|
||||||
|
// something that should execute periodically
|
||||||
|
}</programlisting>
|
||||||
|
|
||||||
<para>If simple periodic scheduling is not expressive enough, then a
|
<para>If simple periodic scheduling is not expressive enough, then a
|
||||||
cron expression may be provided. For example, the following will only
|
cron expression may be provided. For example, the following will only
|
||||||
execute on weekdays.</para>
|
execute on weekdays.</para>
|
||||||
|
|||||||
Reference in New Issue
Block a user