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");
|
||||
* 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
|
||||
* <code>cron</code>, <code>fixedDelay</code>, or <code>fixedRate</code>
|
||||
* attributes must be provided.
|
||||
* {@link #cron()}, {@link #fixedDelay()}, or {@link #fixedRate()}
|
||||
* attributes must be specified.
|
||||
*
|
||||
* <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
|
||||
* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
|
||||
@@ -37,6 +37,7 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see EnableScheduling
|
||||
* @see ScheduledAnnotationBeanPostProcessor
|
||||
@@ -49,9 +50,10 @@ public @interface Scheduled {
|
||||
/**
|
||||
* 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
|
||||
* and day of week. e.g. <code>"0 * * * * MON-FRI"</code> means once
|
||||
* per minute on weekdays (at the top of the minute - the 0th second).
|
||||
* and day of week. e.g. {@code "0 * * * * MON-FRI"} means once per minute on
|
||||
* weekdays (at the top of the minute - the 0th second).
|
||||
* @return an expression that can be parsed to a cron schedule
|
||||
* @see org.springframework.scheduling.support.CronSequenceGenerator
|
||||
*/
|
||||
String cron() default "";
|
||||
|
||||
@@ -68,4 +70,12 @@ public @interface Scheduled {
|
||||
*/
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -33,6 +34,8 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
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.support.ScheduledMethodRunnable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -74,13 +77,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ScheduledTaskRegistrar registrar;
|
||||
|
||||
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>();
|
||||
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
|
||||
|
||||
|
||||
/**
|
||||
@@ -104,7 +101,6 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
return LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
@@ -124,9 +120,11 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
// found a @Scheduled method on the target class for this JDK proxy -> is it
|
||||
// also present on the proxy itself?
|
||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
} catch (SecurityException ex) {
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"@Scheduled method '%s' found on bean target class '%s', " +
|
||||
"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);
|
||||
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();
|
||||
if (!"".equals(cron)) {
|
||||
processedSchedule = true;
|
||||
if (embeddedValueResolver != null) {
|
||||
cron = embeddedValueResolver.resolveStringValue(cron);
|
||||
}
|
||||
cronTasks.put(runnable, cron);
|
||||
registrar.addCronTask(new CronTask(runnable, cron));
|
||||
}
|
||||
long initialDelay = annotation.initialDelay();
|
||||
long fixedDelay = annotation.fixedDelay();
|
||||
if (fixedDelay >= 0) {
|
||||
Assert.isTrue(!processedSchedule, errorMessage);
|
||||
processedSchedule = true;
|
||||
fixedDelayTasks.put(runnable, fixedDelay);
|
||||
registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
|
||||
}
|
||||
long fixedRate = annotation.fixedRate();
|
||||
if (fixedRate >= 0) {
|
||||
Assert.isTrue(!processedSchedule, errorMessage);
|
||||
processedSchedule = true;
|
||||
fixedRateTasks.put(runnable, fixedRate);
|
||||
registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
|
||||
}
|
||||
Assert.isTrue(processedSchedule, errorMessage);
|
||||
}
|
||||
@@ -170,17 +169,8 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, SchedulingConfigurer> configurers = applicationContext.getBeansOfType(SchedulingConfigurer.class);
|
||||
|
||||
if (this.cronTasks.isEmpty() && this.fixedDelayTasks.isEmpty() &&
|
||||
this.fixedRateTasks.isEmpty() && configurers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.registrar = new ScheduledTaskRegistrar();
|
||||
this.registrar.setCronTasks(this.cronTasks);
|
||||
this.registrar.setFixedDelayTasks(this.fixedDelayTasks);
|
||||
this.registrar.setFixedRateTasks(this.fixedRateTasks);
|
||||
Map<String, SchedulingConfigurer> configurers =
|
||||
this.applicationContext.getBeansOfType(SchedulingConfigurer.class);
|
||||
|
||||
if (this.scheduler != null) {
|
||||
this.registrar.setScheduler(this.scheduler);
|
||||
@@ -190,19 +180,23 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
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>();
|
||||
schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class));
|
||||
schedulers.putAll(applicationContext.getBeansOfType(ScheduledExecutorService.class));
|
||||
if (schedulers.size() == 0) {
|
||||
// do nothing -> fall back to default scheduler
|
||||
} else if (schedulers.size() == 1) {
|
||||
}
|
||||
else if (schedulers.size() == 1) {
|
||||
this.registrar.setScheduler(schedulers.values().iterator().next());
|
||||
} else if (schedulers.size() >= 2){
|
||||
throw new IllegalStateException("More than one TaskScheduler and/or ScheduledExecutorService " +
|
||||
"exist within the context. Remove all but one of the beans; or implement the " +
|
||||
"SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler " +
|
||||
"explicitly within the configureTasks() callback. Found the following beans: " + schedulers.keySet());
|
||||
}
|
||||
else if (schedulers.size() >= 2){
|
||||
throw new IllegalStateException(
|
||||
"More than one TaskScheduler and/or ScheduledExecutorService " +
|
||||
"exist within the context. Remove all but one of the beans; or " +
|
||||
"implement the SchedulingConfigurer interface and call " +
|
||||
"ScheduledTaskRegistrar#setScheduler explicitly within the " +
|
||||
"configureTasks() callback. Found the following beans: " + schedulers.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration}
|
||||
* classes annotated with @{@link EnableScheduling} that wish to register scheduled tasks
|
||||
* in a <em>programmatic</em> 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.
|
||||
* Optional interface to be implemented by @{@link
|
||||
* org.springframework.context.annotation.Configuration Configuration} classes annotated
|
||||
* with @{@link EnableScheduling}. Typically used for setting a specific
|
||||
* {@link org.springframework.scheduling.TaskScheduler TaskScheduler} bean to be used when
|
||||
* executing scheduled tasks or for registering scheduled tasks in a <em>programmatic</em>
|
||||
* 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.
|
||||
*
|
||||
@@ -35,6 +38,12 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -33,8 +35,8 @@ import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper bean for registering tasks with a {@link TaskScheduler},
|
||||
* typically using cron expressions.
|
||||
* Helper bean for registering tasks with a {@link TaskScheduler}, typically using cron
|
||||
* expressions.
|
||||
*
|
||||
* <p>As of Spring 3.1, {@code ScheduledTaskRegistrar} has a more prominent user-facing
|
||||
* role when used in conjunction with the @{@link
|
||||
@@ -43,6 +45,7 @@ import org.springframework.util.Assert;
|
||||
* SchedulingConfigurer} callback interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see org.springframework.scheduling.annotation.EnableAsync
|
||||
* @see org.springframework.scheduling.annotation.SchedulingConfigurer
|
||||
@@ -53,19 +56,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
||||
|
||||
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<?>>();
|
||||
|
||||
|
||||
/**
|
||||
* Set the TaskScheduler to register scheduled tasks with.
|
||||
* Set the {@link TaskScheduler} to register scheduled tasks with.
|
||||
*/
|
||||
public void setTaskScheduler(TaskScheduler taskScheduler) {
|
||||
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
|
||||
* tasks with, or a {@link java.util.concurrent.ScheduledExecutorService} to be
|
||||
* wrapped as a TaskScheduler.
|
||||
* Set the {@link TaskScheduler} to register scheduled tasks with, or a
|
||||
* {@link java.util.concurrent.ScheduledExecutorService} to be wrapped as a
|
||||
* {@code TaskScheduler}.
|
||||
*/
|
||||
public void setScheduler(Object scheduler) {
|
||||
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() {
|
||||
return this.taskScheduler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects
|
||||
* (typically custom implementations of the {@link Trigger} interface).
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -110,6 +127,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
||||
* @see CronTrigger
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -118,89 +148,183 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
|
||||
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (this.taskScheduler == null) {
|
||||
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
|
||||
}
|
||||
if (this.triggerTasks != null) {
|
||||
for (Map.Entry<Runnable, Trigger> entry : this.triggerTasks.entrySet()) {
|
||||
this.scheduledFutures.add(this.taskScheduler.schedule(entry.getKey(), entry.getValue()));
|
||||
for (TriggerTask task : triggerTasks) {
|
||||
this.scheduledFutures.add(this.taskScheduler.schedule(
|
||||
task.getRunnable(), task.getTrigger()));
|
||||
}
|
||||
}
|
||||
if (this.cronTasks != null) {
|
||||
for (Map.Entry<Runnable, String> entry : this.cronTasks.entrySet()) {
|
||||
this.scheduledFutures.add(this.taskScheduler.schedule(entry.getKey(), new CronTrigger(entry.getValue())));
|
||||
for (CronTask task : cronTasks) {
|
||||
this.scheduledFutures.add(this.taskScheduler.schedule(
|
||||
task.getRunnable(), task.getTrigger()));
|
||||
}
|
||||
}
|
||||
if (this.fixedRateTasks != null) {
|
||||
for (Map.Entry<Runnable, Long> entry : this.fixedRateTasks.entrySet()) {
|
||||
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(entry.getKey(), entry.getValue()));
|
||||
for (IntervalTask task : fixedRateTasks) {
|
||||
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) {
|
||||
for (Map.Entry<Runnable, Long> entry : this.fixedDelayTasks.entrySet()) {
|
||||
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(entry.getKey(), entry.getValue()));
|
||||
for (IntervalTask task : fixedDelayTasks) {
|
||||
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() {
|
||||
for (ScheduledFuture<?> future : this.scheduledFutures) {
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,27 +16,29 @@
|
||||
|
||||
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.parsing.BeanComponentDefinition;
|
||||
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.ParserContext;
|
||||
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.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String ELEMENT_SCHEDULED = "scheduled";
|
||||
private static final long ZERO_INITIAL_DELAY = 0;
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
@@ -51,10 +53,10 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.setLazyInit(false); // lazy scheduled tasks are a contradiction in terms -> force to false
|
||||
ManagedMap<RuntimeBeanReference, String> cronTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
||||
ManagedMap<RuntimeBeanReference, String> fixedDelayTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
||||
ManagedMap<RuntimeBeanReference, String> fixedRateTaskMap = new ManagedMap<RuntimeBeanReference, String>();
|
||||
ManagedMap<RuntimeBeanReference, RuntimeBeanReference> triggerTaskMap = new ManagedMap<RuntimeBeanReference, RuntimeBeanReference>();
|
||||
ManagedList<RuntimeBeanReference> cronTaskList = new ManagedList<RuntimeBeanReference>();
|
||||
ManagedList<RuntimeBeanReference> fixedDelayTaskList = new ManagedList<RuntimeBeanReference>();
|
||||
ManagedList<RuntimeBeanReference> fixedRateTaskList = new ManagedList<RuntimeBeanReference>();
|
||||
ManagedList<RuntimeBeanReference> triggerTaskList = new ManagedList<RuntimeBeanReference>();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
@@ -64,55 +66,67 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
||||
Element taskElement = (Element) child;
|
||||
String ref = taskElement.getAttribute("ref");
|
||||
String method = taskElement.getAttribute("method");
|
||||
|
||||
|
||||
// Check that 'ref' and 'method' are specified
|
||||
if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) {
|
||||
parserContext.getReaderContext().error("Both 'ref' and 'method' are required", taskElement);
|
||||
// Continue with the possible next task element
|
||||
continue;
|
||||
}
|
||||
|
||||
RuntimeBeanReference runnableBeanRef = new RuntimeBeanReference(
|
||||
createRunnableBean(ref, method, taskElement, parserContext));
|
||||
|
||||
String cronAttribute = taskElement.getAttribute("cron");
|
||||
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
|
||||
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
|
||||
String triggerAttribute = taskElement.getAttribute("trigger");
|
||||
String initialDelayAttribute = taskElement.getAttribute("initial-delay");
|
||||
|
||||
boolean hasCronAttribute = StringUtils.hasText(cronAttribute);
|
||||
boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute);
|
||||
boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute);
|
||||
boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute);
|
||||
boolean hasInitialDelayAttribute = StringUtils.hasText(initialDelayAttribute);
|
||||
|
||||
if (!(hasCronAttribute | hasFixedDelayAttribute | hasFixedRateAttribute | hasTriggerAttribute)) {
|
||||
if (!(hasCronAttribute || hasFixedDelayAttribute || hasFixedRateAttribute || hasTriggerAttribute)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"exactly one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement);
|
||||
// Continue with the possible next task element
|
||||
continue;
|
||||
"one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement);
|
||||
continue; // with the possible next task element
|
||||
}
|
||||
|
||||
if (hasCronAttribute) {
|
||||
cronTaskMap.put(runnableBeanRef, cronAttribute);
|
||||
if (hasInitialDelayAttribute && (hasCronAttribute || hasTriggerAttribute)) {
|
||||
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) {
|
||||
fixedDelayTaskMap.put(runnableBeanRef, fixedDelayAttribute);
|
||||
fixedDelayTaskList.add(intervalTaskReference(runnableName,
|
||||
initialDelayAttribute, fixedDelayAttribute, taskElement, parserContext));
|
||||
}
|
||||
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) {
|
||||
triggerTaskMap.put(runnableBeanRef, new RuntimeBeanReference(triggerAttribute));
|
||||
String triggerName = new RuntimeBeanReference(triggerAttribute).getBeanName();
|
||||
triggerTaskList.add(triggerTaskReference(runnableName, triggerName,
|
||||
taskElement, parserContext));
|
||||
}
|
||||
}
|
||||
String schedulerRef = element.getAttribute("scheduler");
|
||||
if (StringUtils.hasText(schedulerRef)) {
|
||||
builder.addPropertyReference("taskScheduler", schedulerRef);
|
||||
}
|
||||
builder.addPropertyValue("cronTasks", cronTaskMap);
|
||||
builder.addPropertyValue("fixedDelayTasks", fixedDelayTaskMap);
|
||||
builder.addPropertyValue("fixedRateTasks", fixedRateTaskMap);
|
||||
builder.addPropertyValue("triggerTasks", triggerTaskMap);
|
||||
builder.addPropertyValue("cronTasksList", cronTaskList);
|
||||
builder.addPropertyValue("fixedDelayTasksList", fixedDelayTaskList);
|
||||
builder.addPropertyValue("fixedRateTasksList", fixedRateTaskList);
|
||||
builder.addPropertyValue("triggerTasksList", triggerTaskList);
|
||||
}
|
||||
|
||||
private boolean isScheduledElement(Node node, ParserContext parserContext) {
|
||||
@@ -120,16 +134,49 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
|
||||
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(
|
||||
"org.springframework.scheduling.support.ScheduledMethodRunnable");
|
||||
builder.addConstructorArgReference(ref);
|
||||
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
|
||||
builder.getRawBeanDefinition().setSource(parserContext.extractSource(taskElement));
|
||||
String generatedName = parserContext.getReaderContext().generateBeanName(builder.getRawBeanDefinition());
|
||||
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;
|
||||
}
|
||||
|
||||
String getExpression() {
|
||||
return this.expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof CronSequenceGenerator)) {
|
||||
|
||||
@@ -72,6 +72,9 @@ public class CronTrigger implements Trigger {
|
||||
return this.sequenceGenerator.next(date);
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
return this.sequenceGenerator.getExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
@@ -229,6 +229,14 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</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:annotation>
|
||||
<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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,15 +16,11 @@
|
||||
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -32,8 +28,13 @@ import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.config.IntervalTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void withTriggerTask() throws InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
@@ -421,4 +461,34 @@ public class EnableSchedulingTests {
|
||||
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");
|
||||
* 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.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
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.support.RootBeanDefinition;
|
||||
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.support.ScheduledMethodRunnable;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
|
||||
@@ -56,15 +60,18 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, Long> fixedDelayTasks = (Map<Runnable, Long>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<IntervalTask> fixedDelayTasks = (List<IntervalTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("fixedDelay", targetMethod.getName());
|
||||
assertEquals(new Long(5000), fixedDelayTasks.values().iterator().next());
|
||||
assertEquals(0L, task.getInitialDelay());
|
||||
assertEquals(5000L, task.getInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,15 +87,45 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, Long> fixedRateTasks = (Map<Runnable, Long>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
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
|
||||
@@ -104,15 +141,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<CronTask> cronTasks = (List<CronTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("cron", targetMethod.getName());
|
||||
assertEquals("*/7 * * * * ?", cronTasks.values().iterator().next());
|
||||
assertEquals("*/7 * * * * ?", task.getExpression());
|
||||
Thread.sleep(10000);
|
||||
}
|
||||
|
||||
@@ -129,15 +168,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, Long> fixedRateTasks = (Map<Runnable, Long>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("checkForUpdates", targetMethod.getName());
|
||||
assertEquals(new Long(5000), fixedRateTasks.values().iterator().next());
|
||||
assertEquals(5000L, task.getInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,15 +194,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<CronTask> cronTasks = (List<CronTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("generateReport", targetMethod.getName());
|
||||
assertEquals("0 0 * * * ?", cronTasks.values().iterator().next());
|
||||
assertEquals("0 0 * * * ?", task.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -183,15 +226,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<CronTask> cronTasks = (List<CronTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("x", targetMethod.getName());
|
||||
assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next());
|
||||
assertEquals(businessHoursCronExpression, task.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -213,15 +258,17 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
Map<Runnable, String> cronTasks = (Map<Runnable, String>)
|
||||
@SuppressWarnings("unchecked")
|
||||
List<CronTask> cronTasks = (List<CronTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
|
||||
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();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertEquals(target, targetObject);
|
||||
assertEquals("y", targetMethod.getName());
|
||||
assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next());
|
||||
assertEquals(businessHoursCronExpression, task.getExpression());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
@@ -236,14 +283,19 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void invalidCron() {
|
||||
public void invalidCron() throws Throwable {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
||||
ScheduledAnnotationBeanPostProcessorTests.InvalidCronTestBean.class);
|
||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||
context.registerBeanDefinition("target", targetDefinition);
|
||||
context.refresh();
|
||||
try {
|
||||
context.refresh();
|
||||
fail("expected exception");
|
||||
} catch (BeanCreationException ex) {
|
||||
throw ex.getRootCause();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
@@ -269,7 +321,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class FixedDelayTestBean {
|
||||
static class FixedDelayTestBean {
|
||||
|
||||
@Scheduled(fixedDelay=5000)
|
||||
public void fixedDelay() {
|
||||
@@ -277,7 +329,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class FixedRateTestBean {
|
||||
static class FixedRateTestBean {
|
||||
|
||||
@Scheduled(fixedRate=3000)
|
||||
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 * * * * ?")
|
||||
public void cron() throws IOException {
|
||||
@@ -295,7 +355,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class EmptyAnnotationTestBean {
|
||||
static class EmptyAnnotationTestBean {
|
||||
|
||||
@Scheduled
|
||||
public void invalid() {
|
||||
@@ -304,7 +364,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class InvalidCronTestBean {
|
||||
static class InvalidCronTestBean {
|
||||
|
||||
@Scheduled(cron="abc")
|
||||
public void invalid() {
|
||||
@@ -313,7 +373,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class NonVoidReturnTypeTestBean {
|
||||
static class NonVoidReturnTypeTestBean {
|
||||
|
||||
@Scheduled(fixedRate=3000)
|
||||
public String invalid() {
|
||||
@@ -323,7 +383,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class NonEmptyParamListTestBean {
|
||||
static class NonEmptyParamListTestBean {
|
||||
|
||||
@Scheduled(fixedRate=3000)
|
||||
public void invalid(String oops) {
|
||||
@@ -344,7 +404,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
private static @interface Hourly {}
|
||||
|
||||
|
||||
private static class MetaAnnotationFixedRateTestBean {
|
||||
static class MetaAnnotationFixedRateTestBean {
|
||||
|
||||
@EveryFiveSeconds
|
||||
public void checkForUpdates() {
|
||||
@@ -352,7 +412,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class MetaAnnotationCronTestBean {
|
||||
static class MetaAnnotationCronTestBean {
|
||||
|
||||
@Hourly
|
||||
public void generateReport() {
|
||||
@@ -360,7 +420,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private static class PropertyPlaceholderTestBean {
|
||||
static class PropertyPlaceholderTestBean {
|
||||
|
||||
@Scheduled(cron = "${schedules.businessHours}")
|
||||
public void x() {
|
||||
@@ -370,11 +430,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||
|
||||
@Scheduled(cron = "${schedules.businessHours}")
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private static @interface BusinessHours {}
|
||||
|
||||
|
||||
private static class PropertyPlaceholderMetaAnnotationTestBean {
|
||||
static class PropertyPlaceholderMetaAnnotationTestBean {
|
||||
|
||||
@BusinessHours
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,11 +17,9 @@
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -32,8 +30,13 @@ import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Chris Beams
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ScheduledTasksBeanDefinitionParserTests {
|
||||
@@ -63,9 +66,9 @@ public class ScheduledTasksBeanDefinitionParserTests {
|
||||
|
||||
@Test
|
||||
public void checkTarget() {
|
||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
||||
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||
this.registrar).getPropertyValue("fixedRateTasks");
|
||||
Runnable runnable = tasks.keySet().iterator().next();
|
||||
Runnable runnable = tasks.get(0).getRunnable();
|
||||
assertEquals(ScheduledMethodRunnable.class, runnable.getClass());
|
||||
Object targetObject = ((ScheduledMethodRunnable) runnable).getTarget();
|
||||
Method targetMethod = ((ScheduledMethodRunnable) runnable).getMethod();
|
||||
@@ -75,39 +78,39 @@ public class ScheduledTasksBeanDefinitionParserTests {
|
||||
|
||||
@Test
|
||||
public void fixedRateTasks() {
|
||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
||||
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||
this.registrar).getPropertyValue("fixedRateTasks");
|
||||
assertEquals(2, tasks.size());
|
||||
Collection<Long> values = tasks.values();
|
||||
assertTrue(values.contains(new Long(1000)));
|
||||
assertTrue(values.contains(new Long(2000)));
|
||||
assertEquals(3, tasks.size());
|
||||
assertEquals(1000L, tasks.get(0).getInterval());
|
||||
assertEquals(2000L, tasks.get(1).getInterval());
|
||||
assertEquals(4000L, tasks.get(2).getInterval());
|
||||
assertEquals(500, tasks.get(2).getInitialDelay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fixedDelayTasks() {
|
||||
Map<Runnable, Long> tasks = (Map<Runnable, Long>) new DirectFieldAccessor(
|
||||
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
|
||||
this.registrar).getPropertyValue("fixedDelayTasks");
|
||||
assertEquals(1, tasks.size());
|
||||
Long value = tasks.values().iterator().next();
|
||||
assertEquals(new Long(3000), value);
|
||||
assertEquals(2, tasks.size());
|
||||
assertEquals(3000L, tasks.get(0).getInterval());
|
||||
assertEquals(3500L, tasks.get(1).getInterval());
|
||||
assertEquals(250, tasks.get(1).getInitialDelay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cronTasks() {
|
||||
Map<Runnable, String> tasks = (Map<Runnable, String>) new DirectFieldAccessor(
|
||||
List<CronTask> tasks = (List<CronTask>) new DirectFieldAccessor(
|
||||
this.registrar).getPropertyValue("cronTasks");
|
||||
assertEquals(1, tasks.size());
|
||||
String expression = tasks.values().iterator().next();
|
||||
assertEquals("*/4 * 9-17 * * MON-FRI", expression);
|
||||
assertEquals("*/4 * 9-17 * * MON-FRI", tasks.get(0).getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void triggerTasks() {
|
||||
Map<Runnable, Trigger> tasks = (Map<Runnable, Trigger>) new DirectFieldAccessor(
|
||||
List<TriggerTask> tasks = (List<TriggerTask>) new DirectFieldAccessor(
|
||||
this.registrar).getPropertyValue("triggerTasks");
|
||||
assertEquals(1, tasks.size());
|
||||
Trigger trigger = tasks.values().iterator().next();
|
||||
assertEquals(TestTrigger.class, trigger.getClass());
|
||||
assertThat(tasks.get(0).getTrigger(), instanceOf(TestTrigger.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
<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-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" trigger="customTrigger"/>
|
||||
<task:scheduled ref="testBean" method="test" fixed-rate="4000" initial-delay="500"/>
|
||||
</task:scheduled-tasks>
|
||||
|
||||
<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
|
||||
* support executor qualification with @Async#value (SPR-6847)
|
||||
* 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)
|
||||
-------------------------------------
|
||||
|
||||
@@ -472,7 +472,7 @@ public class TaskExecutorExample {
|
||||
example.</para>
|
||||
|
||||
<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: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,
|
||||
and each individual task includes the configuration of its trigger
|
||||
metadata. In the preceding example, that metadata defines a periodic
|
||||
trigger with a fixed delay. It could also be configured with a
|
||||
"fixed-rate", or for more control, a "cron" attribute could be provided
|
||||
instead. Here's an example featuring these other options.</para>
|
||||
trigger with a fixed delay indicating the number of milliseconds to wait
|
||||
after each task execution has completed. Another option is 'fixed-rate',
|
||||
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">
|
||||
<task:scheduled ref="someObject" method="someMethod" fixed-rate="5000"/>
|
||||
<task:scheduled ref="anotherObject" method="anotherMethod" cron="*/5 * * * * MON-FRI"/>
|
||||
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
|
||||
<task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
|
||||
<task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
|
||||
</task:scheduled-tasks>
|
||||
|
||||
<task:scheduler id="myScheduler" pool-size="10"/></programlisting>
|
||||
@@ -523,6 +529,16 @@ public void doSomething() {
|
||||
// something that should execute periodically
|
||||
}</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
|
||||
cron expression may be provided. For example, the following will only
|
||||
execute on weekdays.</para>
|
||||
|
||||
Reference in New Issue
Block a user