Files
spring-net/doc/reference/src/quartz-quickstart.xml

248 lines
12 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2008 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.
*/
-->
<chapter xml:id="quartz-quickstart" xmlns="http://docbook.org/ns/docbook" version="5">
<title>Quartz QuickStart</title>
<section>
<title>Introduction</title>
<para>In many applications the need arises to perform a certain action at
a given time without any user interaction, usually to perform some
administrative tasks. These tasks need to be scheduled, say to perform a
job in the early hours of the morning before the start of business. This
functionality is provided by using job scheduling software. Quartz.NET is
an excellent open source job scheduler that can be used for these
purposes. It provides a wealth of features, such as persistent jobs and
clustering. To find out more about Quartz.NET visit their <ulink
url="http://quartznet.sourceforge.net/">web site</ulink>. Spring
integration allows you to use Spring to configure Quartz jobs, triggers,
and schedulers and also provides integration with Spring's transaction
management features.</para>
<para>The full details of Quartz are outside the scope of this quickstart
but here is 'quick tour for the impatient' of the main classes and
interfaces used in Quartz so you can get your sea legs. A Quartz
<literal>IJob</literal> interface represents the task you would like
to execute. You either directly implement Quartz's
<literal>IJob</literal> interface or a convenience base class. The
Quartz <literal>Trigger</literal> controls when a job is executed, for
example in the wee hours of the morning every weekday . This would be done
using Quartz's <literal>CronTrigger</literal> implementation.
Instances of your job are created every time the trigger fires. As such,
in order to pass information between different job instances you stash
data away in a hashtable that gets passed to the each Job instance upon
its creation. Quartz's <literal>JobDetail</literal> class combines the
<literal>IJob</literal> and this hashtable of data. Instead of the
standard <literal>System.Collections.Hashtable</literal> the class
<literal>JobDataMap</literal> is used. Triggers are registered with a
Quartz <literal>IScheduler</literal> implementation that manages the
overall execution of the triggers and jobs. The
<literal>StdSchedulerFactory</literal> implementation is generally
used.</para>
</section>
<section>
<title>Application Overview</title>
<para>The sample application has two types of Jobs. One that inherits from
Spring's convenience base class <literal>QuartzJobObject</literal> and
another which does not inherit from any base class. The latter class is
adapted by Spring to be a Job. Two triggers, one for each of the jobs, are
created. These triggers are in turn registered with a scheduler. In each
case the job implementation will write information to the console when it
is executed.</para>
</section>
<section>
<title>Standard job scheduling</title>
<para>The Spring base class <literal>QuartzJobObject</literal>
implements <literal>IJob</literal> and allows for your object's
properties to be set via values that are stored inside Quartz's
<literal>JobDataMap</literal> that is passed along each time your job
is instantiated due a trigger firing. This class is shown below</para>
<programlisting language="csharp"> public class ExampleJob : QuartzJobObject
{
private string userName;
public string UserName
{
set { userName = value; }
}
protected override void ExecuteInternal(JobExecutionContext context)
{
Console.WriteLine("{0}: ExecuteInternal called, user name: {1}, next fire time {2}",
DateTime.Now, userName, context.NextFireTimeUtc.Value.ToLocalTime());
}
}</programlisting>
<para>The method <literal>ExecuteInternal</literal> is called when the
trigger fires and is where you would put your business logic. The
<literal>JobExecutionContext</literal> passed in lets you access
various pieces of information about the current job execution, such as the
JobDataMap or information on when the next time the trigger will fire. The
<literal>ExampleJob</literal> is configured by creating a
<literal>JobDetail</literal> object as shown below in the following
XML snippet taken from spring-objects.xml</para>
<programlisting language="myxml"> &lt;object name="exampleJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="JobType" value="Spring.Scheduling.Quartz.Example.ExampleJob, Spring.Scheduling.Quartz.Example" /&gt;
&lt;!-- We can inject values through JobDataMap --&gt;
&lt;property name="JobDataAsMap"&gt;
&lt;dictionary&gt;
&lt;entry key="UserName" value="Alexandre" /&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The dictionary property of the
<literal>JobDetailObject</literal>,
<literal>JobDataAsMap</literal>, is used to set the values of the
ExampleJob's properties. This will result in the ExampleJob being
instantiated with it's UserName property value set to 'Alexandre' the
first time the trigger fires.</para>
<para>We then will schedule this job to be executed on 20 second
increments of every minute as shown below using Spring's
<literal>CronTriggerObject</literal> which creates a Quartz
CronTrigger.</para>
<programlisting language="myxml"> &lt;object id="cronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="jobDetail" ref="exampleJob" /&gt;
&lt;!-- run every 20 second of minute --&gt;
&lt;property name="cronExpressionString" value="0/20 * * * * ?" /&gt;
&lt;/object&gt;</programlisting>
<para>Lastly, we schedule this trigger with the scheduler as shown
below</para>
<programlisting language="myxml"> &lt;object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="triggers"&gt;
&lt;list&gt;
&lt;ref object="cronTrigger" /&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Running this configuration will produce the following output</para>
<programlisting>8/8/2008 1:29:40 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:30:00 PM
8/8/2008 1:30:00 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:30:20 PM
8/8/2008 1:30:20 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:30:40 PM</programlisting>
</section>
<section>
<title>Scheduling arbitrary methods as jobs</title>
<para>It is very convenient to schedule the execution of method as a job.
The AdminService class in the example demonstrates this functionality and
is listed below.</para>
<programlisting language="csharp"> public class AdminService
{
private string userName;
public string UserName
{
set { userName = value; }
}
public void DoAdminWork()
{
Console.WriteLine("{0}: DoAdminWork called, user name: {1}", DateTime.Now, userName);
}
}</programlisting>
<para>Note that it does not inherit from any base class. To instruct
Spring to create a <literal>JobDetail</literal> object for this method
we use Spring's factory object class
<literal>MethodInvokingJobDetailFactoryObject</literal> as shown
below</para>
<programlisting language="myxml"> &lt;object id="adminService" type="Spring.Scheduling.Quartz.Example.AdminService, Spring.Scheduling.Quartz.Example"&gt;
&lt;!-- we inject straight to target object --&gt;
&lt;property name="UserName" value="admin-service" /&gt;
&lt;/object&gt;
&lt;object id="jobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"&gt;
&lt;!-- We don't actually need to implement IJob as we can use delegation --&gt;
&lt;property name="TargetObject" ref="adminService" /&gt;
&lt;property name="TargetMethod" value="DoAdminWork" /&gt;
&lt;/object&gt;
</programlisting>
<para>Note that <literal>AdminService</literal> object is configured
using Spring as you would do normally, without consideration for Quartz.
The trigger associated with the jobDetail object is listed below. Also
note that when using MethodInvokingJobDetailFactoryObject you can't use
database persistence for Jobs. See the class documentation for additional
details.</para>
<programlisting language="myxml"> &lt;object id="simpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz"&gt;
&lt;!-- see the example of method invoking job above --&gt;
&lt;property name="jobDetail" ref="jobDetail" /&gt;
&lt;!-- 5 seconds --&gt;
&lt;property name="startDelay" value="5s" /&gt;
&lt;!-- repeat every 5 seconds --&gt;
&lt;property name="repeatInterval" value="5s" /&gt;
&lt;/object&gt;</programlisting>
<para>This creates an instances of Quartz's SimpleTrigger class (as
compared to its CronTrigger class used in the previous section).
<literal>StartDelay</literal> and <literal>RepeatInterval</literal>
properties are TimeSpan objects than can be set using the convenient
strings such as 10s, 1h, etc, as supported by Spring's custom
TypeConverter for TimeSpans.</para>
<para>This trigger can then be added to the scheduler's list of registered
triggers as shown below.</para>
<programlisting language="myxml"> &lt;object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="triggers"&gt;
&lt;list&gt;
&lt;ref object="cronTrigger" /&gt;
&lt;ref object="simpleTrigger" /&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
</programlisting>
<para>The interleaved output of both these jobs being triggered is shown
below.</para>
<programlisting>8/8/2008 1:40:18 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:20 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:40:40 PM
8/8/2008 1:40:23 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:28 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:33 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:38 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:40 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:41:00 PM
8/8/2008 1:40:43 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:48 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:53 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:40:58 PM: DoAdminWork called, user name: Gabriel
8/8/2008 1:41:00 PM: ExecuteInternal called, user name: Alexandre, next fire time 8/8/2008 1:41:20 PM
8/8/2008 1:41:03 PM: DoAdminWork called, user name: Gabriel
</programlisting>
</section>
</chapter>