You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
862 lines
40 KiB
862 lines
40 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> |
|
|
|
<chapter id="scheduling"> |
|
<title>Task Execution and Scheduling</title> |
|
<section id="scheduling-introduction"> |
|
<title>Introduction</title> |
|
<para> |
|
The Spring Framework provides abstractions for asynchronous execution and scheduling of tasks |
|
with the <interfacename>TaskExecutor</interfacename> and <interfacename>TaskScheduler</interfacename> |
|
interfaces, respectively. Spring also features implementations of those interfaces that support |
|
thread pools or delegation to CommonJ within an application server environment. Ultimately |
|
the use of these implementations behind the common interfaces abstracts away the differences |
|
between Java SE 5, Java SE 6 and Java EE environments. |
|
</para> |
|
<para> |
|
Spring also features integration classes for supporting scheduling with the |
|
<classname>Timer</classname>, part of the JDK since 1.3, and the Quartz Scheduler |
|
(<ulink url="http://www.opensymphony.com/quartz/"/>). Both of those schedulers |
|
are set up using a <interfacename>FactoryBean</interfacename> with optional references |
|
to <classname>Timer</classname> or <classname>Trigger</classname> instances, respectively. |
|
Furthermore, a convenience class for both the Quartz Scheduler and the <classname>Timer</classname> is |
|
available that allows you to invoke a method of an existing target object |
|
(analogous to the normal <classname>MethodInvokingFactoryBean</classname> operation). |
|
</para> |
|
</section> |
|
|
|
<section id="scheduling-task-executor"> |
|
<title>The Spring <interfacename>TaskExecutor</interfacename> abstraction</title> |
|
|
|
<para>Spring 2.0 introduces a new abstraction for dealing with |
|
executors. Executors are the Java 5 name for the concept of |
|
thread pools. The "executor" naming is due to the fact that there |
|
is no guarantee that the underlying implementation is actually a |
|
pool; an executor may be single-threaded or even synchronous. |
|
Spring's abstraction hides implementation details between |
|
Java SE 1.4, Java SE 5 and Java EE environments.</para> |
|
|
|
<para>Spring's <interfacename>TaskExecutor</interfacename> interface is |
|
identical to the <classname>java.util.concurrent.Executor</classname> |
|
interface. In fact, its primary reason for existence is to abstract away |
|
the need for Java 5 when using thread pools. The interface has a single |
|
method <classname>execute(Runnable task)</classname> that accepts a task |
|
for execution based on the semantics and configuration of the thread pool.</para> |
|
<para>The <interfacename>TaskExecutor</interfacename> was originally |
|
created to give other Spring components an abstraction for thread pooling where |
|
needed. Components such as the <classname>ApplicationEventMulticaster</classname>, |
|
JMS's <classname>AbstractMessageListenerContainer</classname>, |
|
and Quartz integration all use the <interfacename>TaskExecutor</interfacename> |
|
abstraction to pool threads. However, if your beans need thread pooling behavior, |
|
it is possible to use this abstraction for your own needs.</para> |
|
|
|
<section id="scheduling-task-executor-types"> |
|
<title><interfacename>TaskExecutor</interfacename> types</title> |
|
<para>There are a number of pre-built implementations of |
|
<interfacename>TaskExecutor</interfacename> included with the |
|
Spring distribution. In all likelihood, you shouldn't ever |
|
need to implement your own.</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para> |
|
<classname>SimpleAsyncTaskExecutor</classname> |
|
</para> |
|
|
|
<para> |
|
This implementation does not reuse any threads, |
|
rather it starts up a new thread for each |
|
invocation. However, it does support a |
|
concurrency limit which will block any |
|
invocations that are over the limit until a slot |
|
has been freed up. If you're looking for true |
|
pooling, keep scrolling further down the page. |
|
</para> |
|
</listitem> |
|
|
|
<listitem id="syncTaskExecutor"> |
|
<para> |
|
<classname>SyncTaskExecutor</classname> |
|
</para> |
|
|
|
<para> |
|
This implementation doesn't execute |
|
invocations asynchronously. Instead, each |
|
invocation takes place in the calling thread. It |
|
is primarily used in situations where |
|
multithreading isn't necessary such as simple |
|
test cases. |
|
</para> |
|
</listitem> |
|
|
|
<listitem id="concurrentTaskExecutor"> |
|
<para> |
|
<classname>ConcurrentTaskExecutor</classname> |
|
</para> |
|
|
|
<para> |
|
This implementation is a wrapper for a Java 5 |
|
<classname>java.util.concurrent.Executor</classname>. |
|
There is an alternative, |
|
<classname>ThreadPoolTaskExecutor</classname>, |
|
that exposes the <classname>Executor</classname> |
|
configuration parameters as bean properties. It |
|
is rare to need to use the <classname>ConcurrentTaskExecutor</classname> |
|
but if the |
|
<link linkend="threadPoolTaskExecutor"><classname>ThreadPoolTaskExecutor</classname></link> |
|
isn't robust enough for your needs, the |
|
<classname>ConcurrentTaskExecutor</classname> |
|
is an alternative. |
|
</para> |
|
</listitem> |
|
|
|
<listitem id="simpleThreadPoolTaskExecutor"> |
|
<para> |
|
<classname>SimpleThreadPoolTaskExecutor</classname> |
|
</para> |
|
|
|
<para> |
|
This implementation is actually a subclass of |
|
Quartz's <classname>SimpleThreadPool</classname> |
|
which listens to Spring's lifecycle callbacks. |
|
This is typically used when you have a |
|
thread pool that may need to be shared by both |
|
Quartz and non-Quartz components. |
|
</para> |
|
</listitem> |
|
|
|
<listitem id="threadPoolTaskExecutor"> |
|
<para> |
|
<classname>ThreadPoolTaskExecutor</classname> |
|
</para> |
|
|
|
<sidebar> |
|
<para> |
|
It is not possible to use any backport or |
|
alternate versions of the |
|
<classname>java.util.concurrent</classname> |
|
package with this implementation. Both Doug |
|
Lea's and Dawid Kurzyniec's implementations |
|
use different package structures which will |
|
prevent them from working correctly. |
|
</para> |
|
</sidebar> |
|
|
|
<para> |
|
This implementation can only be used in a Java 5 |
|
environment but is also the most commonly used |
|
one in that environment. It exposes bean properties for |
|
configuring a |
|
<classname>java.util.concurrent.ThreadPoolExecutor</classname> |
|
and wraps it in a <interfacename>TaskExecutor</interfacename>. |
|
If you need something advanced such as a |
|
<classname>ScheduledThreadPoolExecutor</classname>, |
|
it is recommended that you use a |
|
<link linkend="concurrentTaskExecutor"><classname>ConcurrentTaskExecutor</classname></link> |
|
instead. |
|
</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para> |
|
<classname>TimerTaskExecutor</classname> |
|
</para> |
|
|
|
<para> |
|
This implementation uses a single |
|
<classname>TimerTask</classname> |
|
as its backing implementation. It's different |
|
from the |
|
<link linkend="syncTaskExecutor"><classname>SyncTaskExecutor</classname></link> |
|
in that the method invocations are executed in a |
|
separate thread, although they are synchronous |
|
in that thread. |
|
</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para> |
|
<classname>WorkManagerTaskExecutor</classname> |
|
</para> |
|
|
|
<sidebar><para> |
|
CommonJ is a set of specifications jointly |
|
developed between BEA and IBM. These |
|
specifications are not Java EE standards, but |
|
are standard across BEA's and IBM's |
|
Application Server implementations. |
|
</para></sidebar> |
|
|
|
<para> |
|
This implementation uses the CommonJ WorkManager |
|
as its backing implementation and is the central |
|
convenience class for setting up a CommonJ |
|
WorkManager reference in a Spring context. |
|
Similar to the |
|
<link linkend="simpleThreadPoolTaskExecutor"><classname>SimpleThreadPoolTaskExecutor</classname></link>, |
|
this class implements the WorkManager |
|
interface and therefore can be used directly as |
|
a WorkManager as well. |
|
</para> |
|
</listitem> |
|
|
|
</itemizedlist> |
|
</section> |
|
|
|
<section id="scheduling-task-executor-usage"> |
|
<title>Using a <interfacename>TaskExecutor</interfacename></title> |
|
<para>Spring's <interfacename>TaskExecutor</interfacename> implementations |
|
are used as simple JavaBeans. In the example below, we define |
|
a bean that uses the <classname>ThreadPoolTaskExecutor</classname> |
|
to asynchronously print out a set of messages.</para> |
|
|
|
<programlisting language="java"><![CDATA[import org.springframework.core.task.TaskExecutor; |
|
|
|
public class TaskExecutorExample { |
|
|
|
private class MessagePrinterTask implements Runnable { |
|
|
|
private String message; |
|
|
|
public MessagePrinterTask(String message) { |
|
this.message = message; |
|
} |
|
|
|
public void run() { |
|
System.out.println(message); |
|
} |
|
|
|
} |
|
|
|
private TaskExecutor taskExecutor; |
|
|
|
public TaskExecutorExample(TaskExecutor taskExecutor) { |
|
this.taskExecutor = taskExecutor; |
|
} |
|
|
|
public void printMessages() { |
|
for(int i = 0; i < 25; i++) { |
|
taskExecutor.execute(new MessagePrinterTask("Message" + i)); |
|
} |
|
} |
|
}]]></programlisting> |
|
|
|
<para>As you can see, rather than retrieving a thread from the |
|
pool and executing yourself, you add your <classname>Runnable</classname> |
|
to the queue and the <interfacename>TaskExecutor</interfacename> |
|
uses its internal rules to decide when the task gets executed.</para> |
|
|
|
<para>To configure the rules that the <interfacename>TaskExecutor</interfacename> |
|
will use, simple bean properties have been exposed.</para> |
|
|
|
<programlisting language="xml"><![CDATA[<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> |
|
<property name="corePoolSize" value="5" /> |
|
<property name="maxPoolSize" value="10" /> |
|
<property name="queueCapacity" value="25" /> |
|
</bean> |
|
|
|
<bean id="taskExecutorExample" class="TaskExecutorExample"> |
|
<constructor-arg ref="taskExecutor" /> |
|
</bean>]]></programlisting> |
|
|
|
</section> |
|
</section> |
|
|
|
<section id="scheduling-task-scheduler"> |
|
<title>The Spring <interfacename>TaskScheduler</interfacename> abstraction</title> |
|
|
|
<para>In addition to the <interfacename>TaskExecutor</interfacename> |
|
abstraction, Spring 3.0 introduces a <interfacename>TaskScheduler</interfacename> |
|
with a variety of methods for scheduling tasks to run at some point in the future. |
|
</para> |
|
<programlisting language="java"><![CDATA[public interface TaskScheduler { |
|
|
|
ScheduledFuture schedule(Runnable task, Trigger trigger); |
|
|
|
ScheduledFuture schedule(Runnable task, Date startTime); |
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); |
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, long period); |
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); |
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); |
|
|
|
}]]></programlisting> |
|
|
|
<para>The simplest method is the one named 'schedule' that takes a |
|
<interfacename>Runnable</interfacename> and <classname>Date</classname> |
|
only. That will cause the task to run once after the specified time. All of |
|
the other methods are capable of scheduling tasks to run repeatedly. The |
|
fixed-rate and fixed-delay methods are for simple, periodic execution, but |
|
the method that accepts a Trigger is much more flexible.</para> |
|
|
|
<section id="scheduling-trigger-interface"> |
|
<title>The <interfacename>Trigger</interfacename> interface</title> |
|
|
|
<para>The <interfacename>Trigger</interfacename> interface is |
|
essentially inspired by JSR-236, which, as of Spring 3.0, has not yet |
|
been officially implemented. The basic idea of the |
|
<interfacename>Trigger</interfacename> is that execution times may be |
|
determined based on past execution outcomes or even arbitrary |
|
conditions. If these determinations do take into account the outcome of |
|
the preceding execution, that information is available within a |
|
<interfacename>TriggerContext</interfacename>. The |
|
<interfacename>Trigger</interfacename> interface itself is quite |
|
simple:</para> |
|
<programlisting language="java"><![CDATA[public interface Trigger { |
|
|
|
Date nextExecutionTime(TriggerContext triggerContext); |
|
|
|
}]]></programlisting> |
|
|
|
<para>As you can see, the <interfacename>TriggerContext</interfacename> |
|
is the most important part. It encapsulates all of the relevant data, |
|
and is open for extension in the future if necessary. The |
|
<interfacename>TriggerContext</interfacename> is an interface (a |
|
<classname>SimpleTriggerContext</classname> implementation is used by |
|
default). Here you can see what methods are available for |
|
<interfacename>Trigger</interfacename> implementations.</para> |
|
<programlisting language="java"><![CDATA[public interface TriggerContext { |
|
|
|
Date lastScheduledExecutionTime(); |
|
|
|
Date lastActualExecutionTime(); |
|
|
|
Date lastCompletionTime(); |
|
|
|
}]]></programlisting> |
|
</section> |
|
|
|
<section id="scheduling-trigger-implementations"> |
|
<title><interfacename>Trigger</interfacename> implementations</title> |
|
|
|
<para>Spring provides two implementations of the |
|
<interfacename>Trigger</interfacename> interface. The most interesting |
|
one is the <classname>CronTrigger</classname>. It enables the |
|
scheduling of tasks based on cron expressions. For example the |
|
following task is being scheduled to run 15 minutes past each hour but |
|
only during the 9-to-5 "business hours" on weekdays.</para> |
|
<programlisting language="java"><![CDATA[scheduler.schedule(task, new CronTrigger("* 15 9-17 * * MON-FRI"));]]></programlisting> |
|
|
|
<para>The other out-of-the-box implementation is a |
|
<classname>PeriodicTrigger</classname> that accepts a fixed period, an |
|
optional initial delay value, and a boolean to indicate whether the |
|
period should be interpreted as a fixed-rate or a fixed-delay. Since |
|
the <interfacename>TaskScheduler</interfacename> interface already |
|
defines methods for scheduling tasks at a fixed-rate or with a |
|
fixed-delay, those methods should be used directly whenever possible. |
|
The value of the <classname>PeriodicTrigger</classname> implementation |
|
is that it can be used within components that rely on the |
|
<interfacename>Trigger</interfacename> abstraction. For example, it may |
|
be convenient to allow periodic triggers, cron-based triggers, and even |
|
custom trigger implementations to be used interchangeably. Such a |
|
component could take advantage of dependency injection so that such |
|
<interfacename>Triggers</interfacename> could be configured externally. |
|
</para> |
|
</section> |
|
|
|
<section id="scheduling-task-scheduler-implementations"> |
|
<title><interfacename>TaskScheduler</interfacename> implementations</title> |
|
|
|
<para>As with Spring's <interfacename>TaskExecutor</interfacename> |
|
abstraction, the primary benefit of the |
|
<interfacename>TaskScheduler</interfacename> is that code relying on |
|
scheduling behavior need not be coupled to a particular scheduler |
|
implementation. The flexibility this provides is particularly relevant |
|
when running within Application Server environments where threads |
|
should not be created directly by the application itself. For such |
|
cases, Spring provides a |
|
<classname>TimerManagerTaskScheduler</classname> that delegates to a |
|
CommonJ TimerManager instance, typically configured with a JNDI-lookup. |
|
</para> |
|
|
|
<para>A simpler alternative, the |
|
<classname>ThreadPoolTaskScheduler</classname>, can be used whenever |
|
external thread management is not a requirement. Internally, it |
|
delegates to a <interfacename>ScheduledExecutorService</interfacename> |
|
instance. <classname>ThreadPoolTaskScheduler</classname> actually |
|
implements Spring's <interfacename>TaskExecutor</interfacename> |
|
interface as well, so that a single instance can be used for |
|
asynchronous execution <emphasis>as soon as possible</emphasis> as well |
|
as scheduled, and potentially recurring, executions.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="scheduling-task-namespace"> |
|
<title>The Task Namespace</title> |
|
|
|
<para>Beginning with Spring 3.0, there is an XML namespace for configuring |
|
<interfacename>TaskExecutor</interfacename> and |
|
<interfacename>TaskScheduler</interfacename> instances. It also provides a |
|
convenient way to configure tasks to be scheduled with a trigger.</para> |
|
|
|
<section id="scheduling-task-namespace-scheduler"> |
|
<title>The 'scheduler' element</title> |
|
<para>The following element will create a |
|
<classname>ThreadPoolTaskScheduler</classname> instance with the |
|
specified thread pool size.</para> |
|
<programlisting language="xml"><![CDATA[<task:scheduler id="scheduler" pool-size="10"/>]]></programlisting> |
|
|
|
<para>The value provided for the 'id' attribute will be used as the |
|
prefix for thread names within the pool. The 'scheduler' element is |
|
relatively straightforward. If you do not provide a 'pool-size' attribute, |
|
the default thread pool will only have a single thread. There are no |
|
other configuration options for the scheduler.</para> |
|
</section> |
|
|
|
<section id="scheduling-task-namespace-executor"> |
|
<title>The 'executor' element</title> |
|
<para>The following will create a |
|
<classname>ThreadPoolTaskExecutor</classname> instance: |
|
<programlisting language="xml"><![CDATA[<task:executor id="executor" pool-size="10"/>]]></programlisting> |
|
</para> |
|
|
|
<para>As with the scheduler above, the value provided for the 'id' |
|
attribute will be used as the prefix for thread names within the pool. |
|
As far as the pool size is concerned, the 'executor' element supports |
|
more configuration options than the 'scheduler' element. For one thing, |
|
the thread pool for a <classname>ThreadPoolTaskExecutor</classname> is |
|
itself more configurable. Rather than just a single size, an executor's |
|
thread pool may have different values for the <emphasis>core</emphasis> |
|
and the <emphasis>max</emphasis> size. If a single value is provided |
|
then the executor will have a fixed-size thread pool (the core and max |
|
sizes are the same). However, the 'executor' element's 'pool-size' attribute |
|
also accepts a range in the form of "min-max". |
|
<programlisting language="xml"><![CDATA[<task:executor id="executorWithPoolSizeRange" |
|
pool-size="5-25" |
|
queue-capacity="100"/>]]></programlisting> |
|
</para> |
|
|
|
<para>As you can see from that configuration, a 'queue-capacity' value |
|
has also been provided. The configuration of the thread pool should |
|
also be considered in light of the executor's queue capacity. For the |
|
full description of the relationship between pool size and queue |
|
capacity, consult the documentation for |
|
<ulink url="http://java.sun.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html">ThreadPoolExecutor</ulink>. |
|
The main idea is that when a task is submitted, the executor will first |
|
try to use a free thread if the number of active threads is currently |
|
less than the core size. If the core size has been reached, then the |
|
task will be added to the queue as long as its capacity has not yet |
|
been reached. Only then, if the queue's capacity |
|
<emphasis>has</emphasis> been reached, will the executor create a new |
|
thread beyond the core size. If the max size has also been reached, |
|
then the executor will reject the task.</para> |
|
|
|
<para>By default, the queue is <emphasis>unbounded</emphasis>, but this |
|
is rarely the desired configuration, because it can lead to |
|
<classname>OutOfMemoryErrors</classname> if enough tasks are added to |
|
that queue while all pool threads are busy. Furthermore, if the queue |
|
is unbounded, then the max size has no effect at all. Since the |
|
executor will always try the queue before creating a new thread beyond |
|
the core size, a queue must have a finite capacity for the thread pool |
|
to grow beyond the core size (this is why a <emphasis>fixed size</emphasis> |
|
pool is the only sensible case when using an unbounded queue).</para> |
|
|
|
<para>In a moment, we will review the effects of the keep-alive setting |
|
which adds yet another factor to consider when providing a pool size |
|
configuration. First, let's consider the case, as mentioned above, when |
|
a task is rejected. By default, when a task is rejected, a thread pool |
|
executor will throw a <classname>TaskRejectedException</classname>. |
|
However, the rejection policy is actually configurable. The exception |
|
is thrown when using the default rejection policy which is the |
|
<classname>AbortPolicy</classname> implementation. For applications |
|
where some tasks can be skipped under heavy load, either the |
|
<classname>DiscardPolicy</classname> or |
|
<classname>DiscardOldestPolicy</classname> may be configured instead. |
|
Another option that works well for applications that need to throttle |
|
the submitted tasks under heavy load is the |
|
<classname>CallerRunsPolicy</classname>. Instead of throwing an |
|
exception or discarding tasks, that policy will simply force the thread |
|
that is calling the submit method to run the task itself. The idea is |
|
that such a caller will be busy while running that task and not able to |
|
submit other tasks immediately. Therefore it provides a simple way to |
|
throttle the incoming load while maintaining the limits of the thread |
|
pool and queue. Typically this allows the executor to "catch up" on the |
|
tasks it is handling and thereby frees up some capacity on the queue, |
|
in the pool, or both. Any of these options can be chosen from an |
|
enumeration of values available for the 'rejection-policy' attribute on |
|
the 'executor' element.</para> |
|
<programlisting language="xml"><![CDATA[<task:executor id="executorWithCallerRunsPolicy" |
|
pool-size="5-25" |
|
queue-capacity="100" |
|
rejection-policy="CALLER_RUNS"/>]]></programlisting> |
|
</section> |
|
|
|
<section id="scheduling-task-namespace-scheduled-tasks"> |
|
<title>The 'scheduled-tasks' element</title> |
|
|
|
<para>The most powerful feature of Spring's task namespace is the |
|
support for configuring tasks to be scheduled within a Spring |
|
Application Context. This follows an approach similar to other |
|
"method-invokers" in Spring, such as that provided by the JMS namespace |
|
for configuring Message-driven POJOs. Basically a "ref" attribute can |
|
point to any Spring-managed object, and the "method" attribute provides |
|
the name of a method to be invoked on that object. Here is a simple |
|
example.</para> |
|
<programlisting language="xml"><![CDATA[<task:scheduled-tasks scheduler="myScheduler"> |
|
<task:scheduled ref="someObject" method="someMethod" fixed-delay="5000"/> |
|
<task:scheduled-tasks/> |
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/>]]></programlisting> |
|
|
|
<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> |
|
<programlisting language="xml"><![CDATA[<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-tasks/> |
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/>]]></programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="scheduling-annotation-support"> |
|
<title>Annotation Support for Scheduling and Asynchronous Execution</title> |
|
|
|
<para>Spring 3.0 also adds annotation support for both task scheduling |
|
and asynchronous method execution.</para> |
|
|
|
<section id="scheduling-annotation-support-scheduled"> |
|
<title>The @Scheduled Annotation</title> |
|
|
|
<para>The @Scheduled annotation can be added to a method along with |
|
trigger metadata. For example, the following method would be invoked |
|
every 5 seconds with a fixed delay, meaning that the period will be |
|
measured from the completion time of each preceding invocation.</para> |
|
<programlisting language="java"><![CDATA[@Scheduled(fixedDelay=5000) |
|
public void doSomething() { |
|
// something that should execute periodically |
|
}]]></programlisting> |
|
<para>If a fixed rate execution is desired, simply change the property |
|
name specified within the annotation. The following would be executed |
|
every 5 seconds measured between the successive start times of each |
|
invocation.</para> |
|
<programlisting language="java"><![CDATA[@Scheduled(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> |
|
<programlisting language="java"><![CDATA[@Scheduled(cron="*/5 * * * * MON-FRI") |
|
public void doSomething() { |
|
// something that should execute on weekdays only |
|
}]]></programlisting> |
|
<para>Notice that the methods to be scheduled must have void returns |
|
and must not expect any arguments. If the method needs to interact with |
|
other objects from the Application Context, then those would typically |
|
have been provided through dependency injection.</para> |
|
</section> |
|
|
|
<section id="scheduling-annotation-support-async"> |
|
<title>The @Async Annotation</title> |
|
|
|
<para>The @Async annotation can be provided on a method so that |
|
invocation of that method will occur asynchronously. In other words, |
|
the caller will return immediately upon invocation and the actual |
|
execution of the method will occur in a task that has been submitted to |
|
a Spring TaskExecutor. In the simplest case, the annotation may be |
|
applied to a void-returning method.</para> |
|
|
|
<programlisting language="java"><![CDATA[@Async |
|
void doSomething() { |
|
// this will be executed asynchronously |
|
}]]></programlisting> |
|
|
|
<para>Unlike the methods annotated with the @Scheduled annotation, |
|
these methods can expect arguments, because they will be invoked |
|
in the "normal" way by callers at runtime rather than from a |
|
scheduled task being managed by the container. For example, the |
|
following is a legitimate application of the @Async annotation. |
|
</para> |
|
<programlisting language="java"><![CDATA[@Async |
|
void doSomething(String s) { |
|
// this will be executed asynchronously |
|
}]]></programlisting> |
|
|
|
<para>Even methods that return a value can be invoked asynchronously. |
|
However, such methods are required to have a Future typed return value. |
|
This still provides the benefit of asynchronous execution so that the |
|
caller can perform other tasks prior to calling 'get()' on that Future. |
|
</para> |
|
<programlisting language="java"><![CDATA[@Async |
|
Future<String> returnSomething(int i) { |
|
// this will be executed asynchronously |
|
}]]></programlisting> |
|
</section> |
|
|
|
<section id="scheduling-annotation-support-namespace"> |
|
<title>The <annotation-driven> Element</title> |
|
|
|
<para>To enable both @Scheduled and @Async annotations, simply |
|
include the 'annotation-driven' element from the task namespace in |
|
your configuration.</para> |
|
<programlisting language="xml"><![CDATA[<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/> |
|
|
|
<task:executor id="myExecutor" pool-size="5"/> |
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/>}]]></programlisting> |
|
|
|
<para>Notice that an executor reference is provided for handling |
|
those tasks that correspond to methods with the @Async annotation, |
|
and the scheduler reference is provided for managing those methods |
|
annotated with @Scheduled.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="scheduling-quartz"> |
|
<title>Using the OpenSymphony Quartz Scheduler</title> |
|
<para>Quartz uses <classname>Trigger</classname>, <classname>Job</classname> and |
|
<classname>JobDetail</classname> objects to realize scheduling of all kinds of jobs. |
|
For the basic concepts behind Quartz, have a look at |
|
<ulink url="http://www.opensymphony.com/quartz" />. For convenience purposes, |
|
Spring offers a couple of classes that simplify the usage of Quartz within |
|
Spring-based applications. |
|
</para> |
|
<section id="scheduling-quartz-jobdetail"> |
|
<title>Using the JobDetailBean</title> |
|
<para> |
|
<classname>JobDetail</classname> objects contain all information needed to |
|
run a job. The Spring Framework provides a <classname>JobDetailBean</classname> |
|
that makes the <classname>JobDetail</classname> more of an actual JavaBean |
|
with sensible defaults. Let's have a look at an example: |
|
</para> |
|
<programlisting language="xml"><![CDATA[ |
|
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> |
|
<property name="jobClass" value="example.ExampleJob" /> |
|
<property name="jobDataAsMap"> |
|
<map> |
|
<entry key="timeout" value="5" /> |
|
</map> |
|
</property> |
|
</bean>]]></programlisting> |
|
<para>The job detail bean has all information it needs to run the job (<classname>ExampleJob</classname>). |
|
The timeout is specified in the job data map. The job data map is |
|
available through the <classname>JobExecutionContext</classname> |
|
(passed to you at execution time), but the <classname>JobDetailBean</classname> |
|
also maps the properties from the job data map to properties of the actual job. |
|
So in this case, if the <classname>ExampleJob</classname> contains a property |
|
named <literal>timeout</literal>, the <classname>JobDetailBean</classname> will |
|
automatically apply it:</para> |
|
<programlisting language="java"><![CDATA[package example; |
|
|
|
public class ExampleJob extends QuartzJobBean { |
|
|
|
private int timeout; |
|
|
|
/** |
|
* Setter called after the ExampleJob is instantiated |
|
* with the value from the JobDetailBean (5) |
|
*/ |
|
public void setTimeout(int timeout) { |
|
this.timeout = timeout; |
|
} |
|
|
|
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { |
|
]]><lineannotation>// do the actual work</lineannotation><![CDATA[ |
|
} |
|
}]]></programlisting> |
|
<para>All additional settings from the job detail bean are of course available to you as well.</para> |
|
<para><emphasis>Note: Using the <literal>name</literal> and <literal>group</literal> properties, |
|
you can modify the name and the group of the job, respectively. By default, the name of |
|
the job matches the bean name of the job detail bean (in the example above, this is |
|
<literal>exampleJob</literal>).</emphasis></para> |
|
</section> |
|
<section id="scheduling-quartz-method-invoking-job"> |
|
<title>Using the <classname>MethodInvokingJobDetailFactoryBean</classname></title> |
|
<para>Often you just need to invoke a method on a specific object. Using the |
|
<classname>MethodInvokingJobDetailFactoryBean</classname> you can do exactly this:</para> |
|
<programlisting language="xml"><![CDATA[<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> |
|
<property name="targetObject" ref="exampleBusinessObject" /> |
|
<property name="targetMethod" value="doIt" /> |
|
</bean>]]></programlisting> |
|
<para>The above example will result in the <literal>doIt</literal> method being called on the |
|
<literal>exampleBusinessObject</literal> method (see below):</para> |
|
<programlisting language="java"><![CDATA[public class ExampleBusinessObject { |
|
|
|
]]><lineannotation>// properties and collaborators</lineannotation><![CDATA[ |
|
|
|
public void doIt() { |
|
]]><lineannotation>// do the actual work</lineannotation><![CDATA[ |
|
} |
|
}]]></programlisting> |
|
|
|
<programlisting language="xml"><![CDATA[ |
|
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>]]></programlisting> |
|
<para>Using the <classname>MethodInvokingJobDetailFactoryBean</classname>, you don't need to |
|
create one-line jobs that just invoke a method, and you only need to create the actual |
|
business object and wire up the detail object.</para> |
|
<para>By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering |
|
with each other. If you specify two triggers for the same <classname>JobDetail</classname>, |
|
it might be possible that before the first job has finished, the second one will start. |
|
If <classname>JobDetail</classname> classes implement the |
|
<interfacename>Stateful</interfacename> interface, this won't happen. The second job |
|
will not start before the first one has finished. To make jobs resulting from the |
|
<classname>MethodInvokingJobDetailFactoryBean</classname> non-concurrent, set the |
|
<literal>concurrent</literal> flag to <literal>false</literal>.</para> |
|
<programlisting language="xml"><![CDATA[ |
|
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> |
|
<property name="targetObject" ref="exampleBusinessObject" /> |
|
<property name="targetMethod" value="doIt" /> |
|
<property name="concurrent" value="false" /> |
|
</bean>]]></programlisting> |
|
<note> |
|
<para>By default, jobs will run in a concurrent fashion.</para> |
|
</note> |
|
</section> |
|
<section id="scheduling-quartz-cron"> |
|
<title>Wiring up jobs using triggers and the <classname>SchedulerFactoryBean</classname></title> |
|
<para> |
|
We've created job details and jobs. We've also reviewed the convenience bean |
|
that allows to you invoke a method on a specific object. Of course, we still need |
|
to schedule the jobs themselves. This is done using triggers and a |
|
<classname>SchedulerFactoryBean</classname>. Several triggers are available |
|
within Quartz. Spring offers two subclassed triggers with convenient defaults: |
|
<classname>CronTriggerBean</classname> and <classname>SimpleTriggerBean</classname>. |
|
</para> |
|
<para> |
|
Triggers need to be scheduled. Spring offers a <classname>SchedulerFactoryBean</classname> |
|
that exposes triggers to be set as properties. <classname>SchedulerFactoryBean</classname> |
|
schedules the actual jobs with those triggers. |
|
</para> |
|
<para>Find below a couple of examples:</para> |
|
<programlisting language="xml"><![CDATA[<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> |
|
<!-- see the example of method invoking job above --> |
|
<property name="jobDetail" ref="jobDetail" /> |
|
<!-- 10 seconds --> |
|
<property name="startDelay" value="10000" /> |
|
<!-- repeat every 50 seconds --> |
|
<property name="repeatInterval" value="50000" /> |
|
</bean> |
|
|
|
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> |
|
<property name="jobDetail" ref="exampleJob" /> |
|
<!-- run every morning at 6 AM --> |
|
<property name="cronExpression" value="0 0 6 * * ?" /> |
|
</bean>]]></programlisting> |
|
<para>Now we've set up two triggers, one running every 50 seconds with a starting delay of |
|
10 seconds and one every morning at 6 AM. To finalize everything, we need to set up the |
|
<classname>SchedulerFactoryBean</classname>:</para> |
|
<programlisting language="xml"><![CDATA[<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> |
|
<property name="triggers"> |
|
<list> |
|
<ref bean="cronTrigger" /> |
|
<ref bean="simpleTrigger" /> |
|
</list> |
|
</property> |
|
</bean>]]></programlisting> |
|
<para> |
|
More properties are available for the <classname>SchedulerFactoryBean</classname> for you |
|
to set, such as the calendars used by the job details, properties to customize Quartz with, |
|
etc. Have a look at the |
|
<ulink url="http://static.springframework.org/spring/docs/3.0.x/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html">SchedulerFactoryBean Javadoc</ulink> |
|
for more information. |
|
</para> |
|
</section> |
|
</section> |
|
|
|
<section id="scheduling-jdk-timer"> |
|
<title>Using JDK Timer support</title> |
|
<para> |
|
The other way to schedule jobs in Spring is to use JDK |
|
<classname>Timer</classname> objects. You can create custom timers or |
|
use the timer that invokes methods. Wiring timers is done using the |
|
<classname>TimerFactoryBean</classname>. |
|
</para> |
|
<section id="scheduling-jdk-timer-creating"> |
|
<title>Creating custom timers</title> |
|
<para> |
|
Using the <classname>TimerTask</classname> you can create customer |
|
timer tasks, similar to Quartz jobs: |
|
</para> |
|
<programlisting language="java"><![CDATA[public class CheckEmailAddresses extends TimerTask { |
|
|
|
private List emailAddresses; |
|
|
|
public void setEmailAddresses(List emailAddresses) { |
|
this.emailAddresses = emailAddresses; |
|
} |
|
|
|
public void run() { |
|
]]><lineannotation>// iterate over all email addresses and archive them</lineannotation><![CDATA[ |
|
} |
|
}]]></programlisting> |
|
<para> |
|
Wiring it up is simple: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<bean id="checkEmail" class="examples.CheckEmailAddress"> |
|
<property name="emailAddresses"> |
|
<list> |
|
<value>test@springframework.org</value> |
|
<value>foo@bar.com</value> |
|
<value>john@doe.net</value> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"> |
|
]]><lineannotation><!-- wait 10 seconds before starting repeated execution --></lineannotation><![CDATA[ |
|
<property name="delay" value="10000" /> |
|
]]><lineannotation><!-- run every 50 seconds --></lineannotation><![CDATA[ |
|
<property name="period" value="50000" /> |
|
<property name="timerTask" ref="checkEmail" /> |
|
</bean>]]></programlisting> |
|
<para> |
|
<emphasis> |
|
Note that letting the task only run once can be done by changing the |
|
<literal>period</literal> property to 0 (or a negative value). |
|
</emphasis> |
|
</para> |
|
</section> |
|
<section id="scheduling-jdk-timer-method-invoking-task"> |
|
<title>Using the <classname>MethodInvokingTimerTaskFactoryBean</classname></title> |
|
<para> |
|
Similar to the Quartz support, the <classname>Timer</classname> support also features |
|
a component that allows you to periodically invoke a method: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<bean id="doIt" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"> |
|
<property name="targetObject" ref="exampleBusinessObject" /> |
|
<property name="targetMethod" value="doIt" /> |
|
</bean>]]></programlisting> |
|
<para> |
|
The above example will result in the <literal>doIt</literal> method being called on the |
|
<literal>exampleBusinessObject</literal> (see below): |
|
</para> |
|
<programlisting language="java"><![CDATA[public class BusinessObject { |
|
|
|
]]><lineannotation>// properties and collaborators</lineannotation><![CDATA[ |
|
|
|
public void doIt() { |
|
]]><lineannotation>// do the actual work</lineannotation><![CDATA[ |
|
} |
|
}]]></programlisting> |
|
<para>Changing the <literal>timerTask</literal> reference of the |
|
<classname>ScheduledTimerTask</classname> example to the bean <literal>doIt</literal> |
|
will result in the <literal>doIt</literal> method being executed on a fixed schedule.</para> |
|
</section> |
|
<section id="scheduling-jdk-timer-factory-bean"> |
|
<title>Wrapping up: setting up the tasks using the <classname>TimerFactoryBean</classname></title> |
|
<para>The <classname>TimerFactoryBean</classname> is similar to the Quartz |
|
<classname>SchedulerFactoryBean</classname> in that it serves the same |
|
purpose: setting up the actual scheduling. The <classname>TimerFactoryBean</classname> |
|
sets up an actual <classname>Timer</classname> and schedules the tasks it has |
|
references to. You can specify whether or not daemon threads should be used.</para> |
|
<programlisting language="xml"><![CDATA[<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"> |
|
<property name="scheduledTimerTasks"> |
|
<list> |
|
]]><lineannotation><!-- see the example above --></lineannotation><![CDATA[ |
|
<ref bean="scheduledTask" /> |
|
</list> |
|
</property> |
|
</bean>]]></programlisting> |
|
</section> |
|
</section> |
|
|
|
</chapter> |