862 lines
40 KiB
XML
862 lines
40 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.5/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> |