changes for 1.2 M1 release packaging.

This commit is contained in:
markpollack
2008-08-04 20:14:18 +00:00
parent 651ab8ef8c
commit ec5b3278f7
51 changed files with 10928 additions and 28 deletions

View File

@@ -717,13 +717,8 @@
<call target="set-debug-build-configuration"/>
<call target="package-release-files"/>
<copy todir="${current.package.dir}">
<fileset>
<include name="Spring.Net.sln"/>
</fileset>
</copy>
<property name="project.zip-path" value="${package.dir}/${project.name}-${package.version}.zip"/>
<!-- for some reason this isn't being properly excluded in src.to.copy -->
@@ -746,8 +741,9 @@
<mkdir dir="${current.package.dir}/build-support/tools"/>
<copy todir="${current.package.dir}/build-support/tools">
<fileset basedir="tools">
<include name="**/*.exe"/>
<include name="**/*.jar"/>
<include name="antlr-2.7.6/*"/>
<include name="NAnt.NUnit2OutProc.Task/*"/>
<include name="nunit/*"/>
</fileset>
</copy>
</target>
@@ -810,13 +806,15 @@
<echo message="Current package directory = ${current.package.dir}"/>
<copy todir="${current.package.dir}">
<fileset>
<include name="${solution.file.1.1.2002}"/>
<include name="${solution.file.1.1.2003}"/>
<include name="${solution.file.1.1.2005}"/>
<include name="${solution.file.2002}"/>
<include name="${solution.file.2003}"/>
<include name="${solution.file.2005}"/>
<include name="${solution.file.2008}"/>
<include name="readme.txt"/>
<include name="changelog.txt"/>
<include name="license.txt"/>
<include name="BreakingChanges-1.1.txt"/>
<include name="Spring.build"/>
<include name="Spring.include"/>
<!-- exclude VS.Net stuff -->
<exclude name="**/sdk-web/**"/>
<exclude name="**/*.suo"/>
@@ -882,6 +880,10 @@
<include name="**/Spring.Messaging.xml"/>
<include name="**/Spring.Messaging.pdb"/>
<include name="**/Spring.Scheduling.Quartz.dll"/>
<include name="**/Spring.Scheduling.Quartz.xml"/>
<include name="**/Spring.Scheduling.Quartz.pdb"/>
<include name="**/antlr.runtime.dll"/>
<include name="**/Common.Logging.dll"/>
<include name="**/Common.Logging.*.dll"/>
@@ -960,6 +962,7 @@
<include name="**/Rhino.Mocks.dll"/>
<include name="**/System.Web.Extensions.dll"/>
<include name="**/Apache.NMS.dll"/>
<include name="**/Apache.NMS.ActiveMQ.dll"/>
<include name="**/Apache.NMS.MSMQ.dll"/>
<include name="**/EULA.rtf"/>
</fileset>
@@ -987,6 +990,17 @@
</if>
<mkdir dir="${current.package.dir}/lib/NHibernate20"/>
<if test="${property::exists('build.allnamespaces') and not build.allnamespaces}">
<copy todir="${current.package.dir}/lib/NHibernate20">
<fileset basedir="lib/NHibernate20">
<include name="**/*.dll"/>
<include name="**/*.license.txt"/>
</fileset>
</copy>
</if>
<if test="${property::exists('build.allnamespaces') and build.allnamespaces}">
<copy todir="${current.package.dir}/lib">
<fileset basedir="lib">
@@ -997,14 +1011,22 @@
</if>
<!-- copy third-party icons -->
<mkdir dir="${current.package.dir}/icons"/>
<copy todir="${current.package.dir}/icons">
<fileset basedir="icons">
<mkdir dir="${current.package.dir}/build-support/icons"/>
<copy todir="${current.package.dir}/build-support/icons">
<fileset basedir="build-support/icons">
<include name="*.ico"/>
<include name="*.pal"/>
</fileset>
</copy>
<!-- copy additional convenience solution files -->
<mkdir dir="${current.package.dir}/build-support/solutions"/>
<copy todir="${current.package.dir}/build-support/solutions">
<fileset basedir="build-support/solutions">
<include name="*.sln"/>
</fileset>
</copy>
<!-- copy schema and dtd -->
<mkdir dir="${current.package.dir}/doc/schema"/>
<copy todir="${current.package.dir}/doc/schema"
@@ -1187,9 +1209,10 @@
<property name="build-winservices" value="true"/>
<property name="build-web" value="true"/>
<property name="build-examples" value="true"/>
<property name="solution.file.1.1.2002" value="Spring.Net.1.1.2002.sln"/>
<property name="solution.file.1.1.2003" value="Spring.Net.1.1.2003.sln"/>
<property name="solution.file.1.1.2005" value="Spring.Net.1.1.2005.sln"/>
<property name="solution.file.2002" value="Spring.Net.2002.sln"/>
<property name="solution.file.2003" value="Spring.Net.2003.sln"/>
<property name="solution.file.2005" value="Spring.Net.2005.sln"/>
<property name="solution.file.2008" value="Spring.Net.2008.sln"/>
<fileset id="src.to.copy">
<include name="src/Spring/Spring.Core/**"/>
<include name="src/Spring/Spring.Aop/**"/>
@@ -1200,11 +1223,13 @@
<include name="src/Spring/Spring.Services/Web/**"/>
<include name="src/Spring/Spring.Services/Spring.Services.2003.csproj"/>
<include name="src/Spring/Spring.Services/Spring.Services.2005.csproj"/>
<include name="src/Spring/Spring.Services/Spring.Services.2008.csproj"/>
<include name="src/Spring/Spring.Services/*.build"/>
<include name="src/Spring/Spring.Services/AssemblyInfo.cs"/>
<include name="src/Spring/Spring.Data/**"/>
<include name="src/Spring/Spring.Data.NHibernate/**"/>
<include name="src/Spring/Spring.Data.NHibernate12/**"/>
<include name="src/Spring/Spring.Data.NHibernate20/**"/>
<include name="src/Spring/Spring.Testing.NUnit/**"/>
<include name="src/Spring/CommonAssemblyInfo.cs"/>
@@ -1217,6 +1242,7 @@
<include name="test/Spring/Spring.Services.Tests/Web/**"/>
<include name="test/Spring/Spring.Services.Tests/Spring.Services.Tests.2003.csproj"/>
<include name="test/Spring/Spring.Services.Tests/Spring.Services.Tests.2005.csproj"/>
<include name="test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj"/>
<include name="test/Spring/Spring.Services.Tests/Spring.Services.Tests.build"/>
<include name="test/Spring/Spring.Services.Tests/*.config"/>
<include name="test/Spring/Spring.Services.Tests/Spring.Services.Tests.dll.config"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
Check Application Requirements
Check/Install Application Pre-Requisites
Check Application Pre-Requisites
Install Application Pre-Requisites
Define Setup Globals
Setup User Interview
Perform Uninstallation
Finish Setup

View File

@@ -0,0 +1,504 @@
Spring.NET-1.2.mia
componentstree.dfm
componentstree.dfm.miaf
destination.dfm
destination.dfm.miaf
finish.dfm
finish.dfm.miaf
licensecheck.dfm
licensecheck.dfm.miaf
maintenance.dfm
maintenance.dfm.miaf
prereq.dfm
prereq.dfm.miaf
progress.dfm
progress.dfm.miaf
progressprereq.dfm
progressprereq.dfm.miaf
readme.dfm
readme.dfm.miaf
registration.dfm
registration.dfm.miaf
registrationwithserial.dfm
registrationwithserial.dfm.miaf
setuptype.dfm
setuptype.dfm.miaf
startinstallation.dfm
startinstallation.dfm.miaf
startmenu.dfm
startmenu.dfm.miaf
welcome.dfm
welcome.dfm.miaf
wizard.dfm
wizard.dfm.miaf
$
icon.ico
$
$
$
1
0
FALSE
L:\projects\spring-net\msi
SpringSource
Spring.NET 1.2 M1
{36A8EE3D-7025-4FAE-BF84-0523EADA7082}
{1399F2CB-026A-4995-ADC7-9A921EEFB4E9}
1.2
English
Spring.NET 1.2 M1
Spring.NET 1.2 M1
Spring.NET authors
All rights reserved
{3A73E939-3C01-4C93-8CAB-39C3C40CFC05}
Spring.NET
Spring.NET
http://forum.springframework.net
http://www.springframework.net
All rights reserved
FALSE
Spring.NET-1.2-M1
TRUE
FALSE
http://timestamp.verisign.com/scripts/timstamp.dll
$
$
Service Pack
FALSE
FALSE
FALSE
TRUE
TRUE
FALSE
FALSE
FALSE
$
FALSE

View File

@@ -0,0 +1,504 @@
Spring.NET-1.2.mia
componentstree.dfm
componentstree.dfm.miaf
destination.dfm
destination.dfm.miaf
finish.dfm
finish.dfm.miaf
licensecheck.dfm
licensecheck.dfm.miaf
maintenance.dfm
maintenance.dfm.miaf
prereq.dfm
prereq.dfm.miaf
progress.dfm
progress.dfm.miaf
progressprereq.dfm
progressprereq.dfm.miaf
readme.dfm
readme.dfm.miaf
registration.dfm
registration.dfm.miaf
registrationwithserial.dfm
registrationwithserial.dfm.miaf
setuptype.dfm
setuptype.dfm.miaf
startinstallation.dfm
startinstallation.dfm.miaf
startmenu.dfm
startmenu.dfm.miaf
welcome.dfm
welcome.dfm.miaf
wizard.dfm
wizard.dfm.miaf
$
icon.ico
$
$
$
1
0
FALSE
L:\projects\spring-net\msi
SpringSource
Spring.NET 1.2 M1
{36A8EE3D-7025-4FAE-BF84-0523EADA7082}
{1399F2CB-026A-4995-ADC7-9A921EEFB4E9}
1.2
English
Spring.NET 1.2 M1
Spring.NET 1.2 M1
Spring.NET authors
All rights reserved
{CA310973-F1EA-49C3-B9B0-2071DAB16AF8}
Spring.NET
Spring.NET
http://forum.springframework.net
http://www.springframework.net
All rights reserved
FALSE
Spring.NET-1.2-M1
TRUE
FALSE
http://timestamp.verisign.com/scripts/timstamp.dll
$
$
Service Pack
FALSE
FALSE
FALSE
TRUE
TRUE
FALSE
FALSE
FALSE
$
FALSE

View File

@@ -0,0 +1,17 @@
IF (checkSuccess.Caption = COMPLETE) THEN textComplete.Visible := True;
IF (checkSuccess.Caption = REBOOT) THEN textReboot.Visible := True;
IF (checkSuccess.Caption = CANCEL) THEN textCancelled.Visible := True;
IF (checkSuccess.Caption = ERROR) THEN textError.Visible := True;
IF (checkRemove.Caption = TRUE) THEN textRemove.Visible := True;
IF (checkSuccess.Caption <> COMPLETE) THEN textComplete.Visible := False;
IF (checkSuccess.Caption <> REBOOT) THEN textReboot.Visible := False;
IF (checkSuccess.Caption <> CANCEL) THEN textCancelled.Visible := False;
IF (checkSuccess.Caption <> ERROR) THEN textError.Visible := False;
IF (checkRemove.Caption <> TRUE) THEN textRemove.Visible := False;
IF (checkRemove.Caption = TRUE) THEN textComplete.Visible := False;
IF (checkSuccess.Caption = CANCEL) THEN textRemove.Visible := False;
IF (textReboot.Visible = True) THEN textRemove.Visible := false;
IF (textComplete.Visible = True) THEN textRemove.Visible := false;
IF (textError.Visible = True) THEN textRemove.Visible := false;
IF (textCancelled.Visible = True) THEN textRemove.Visible := false;
IF (checkSuccess.Caption = ERROR) THEN textRemove.Visible := False;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,2 @@
IF (LicenseCheck.Checked = True) THEN Next.Enabled := True;
IF (LicenseCheck.Checked = False) THEN Next.Enabled := False;

View File

@@ -0,0 +1,6 @@
IF (checkWINST.Caption <> TRUE) THEN WINST.Visible := True;
IF (checkJS.Caption <> TRUE) THEN JS.Visible := True;
IF (checkDotNET.Caption <> TRUE) THEN dotNET.Visible := True;
IF (checkWINST.Caption = TRUE) THEN WINST.Visible := False;
IF (checkDotNET.Caption = TRUE) THEN dotNET.Visible := False;
IF (checkJS.Caption = TRUE) THEN JS.Visible := False;

View File

@@ -0,0 +1,4 @@
IF (TestRemove.Caption <> TRUE) THEN CaptionInstall.Visible := True;
IF (TestRemove.Caption = TRUE) THEN CaptionUninstall.Visible := True;
IF (TestRemove.Caption <> TRUE) THEN CaptionUninstall.Visible := False;
IF (TestRemove.Caption = TRUE) THEN CaptionInstall.Visible := False;

View File

@@ -0,0 +1,2 @@
IF (LicenseCheck.Checked = True) THEN Next.Enabled := True;
IF (LicenseCheck.Checked = False) THEN Next.Enabled := False;

View File

@@ -0,0 +1,4 @@
IF (Name.Text <> ) THEN Next.Enabled := True;
IF (Company.Text <> ) THEN Next.Enabled := True;
IF (Name.Text = ) THEN Next.Enabled := False;
IF (Company.Text = ) THEN Next.Enabled := False;

View File

@@ -0,0 +1,14 @@
IF (Name.Text <> ) THEN Next.Enabled := True;
IF (Company.Text <> ) THEN Next.Enabled := True;
IF (Serial1.Text <> ) THEN Next.Enabled := True;
IF (Serial2.Text <> ) THEN Next.Enabled := True;
IF (Serial3.Text <> ) THEN Next.Enabled := True;
IF (Serial4.Text <> ) THEN Next.Enabled := True;
IF (Serial5.Text <> ) THEN Next.Enabled := True;
IF (Name.Text = ) THEN Next.Enabled := False;
IF (Company.Text = ) THEN Next.Enabled := False;
IF (Serial1.Text = ) THEN Next.Enabled := False;
IF (Serial2.Text = ) THEN Next.Enabled := False;
IF (Serial3.Text = ) THEN Next.Enabled := False;
IF (Serial4.Text = ) THEN Next.Enabled := False;
IF (Serial5.Text = ) THEN Next.Enabled := False;

View File

@@ -0,0 +1,4 @@
IF (MenuGroup.Text <> ) THEN Next.Enabled := True;
IF (MenuGroup.Text = ) THEN Next.Enabled := False;
IF (ISNT.Caption = TRUE) THEN AllUsers.Enabled := True;
IF (ISNT.Caption <> TRUE) THEN AllUsers.Enabled := False;

View File

@@ -1,2 +0,0 @@
nunit-console-*

View File

@@ -16,8 +16,9 @@
<!ENTITY overview SYSTEM "overview.xml">
<!ENTITY psa-intro SYSTEM "psa-intro.xml">
<!ENTITY remoting SYSTEM "remoting.xml">
<!ENTITY nms SYSTEM "nms.xml">
<!ENTITY messaging SYSTEM "messaging.xml">
<!ENTITY msmq SYSTEM "msmq.xml">
<!ENTITY scheduling SYSTEM "scheduling.xml">
<!ENTITY web SYSTEM "web.xml">
<!ENTITY ajax SYSTEM "ajax.xml">
<!ENTITY services SYSTEM "services.xml">
@@ -300,23 +301,19 @@
</para>
<itemizedlist>
<listitem>
<para><xref linkend="nms"/></para>
<para><xref linkend="messaging"/></para>
</listitem>
<listitem>
<para><xref linkend="msmq"/></para>
</listitem>
<!--
<listitem>
<para><xref linkend="scheduling"/></para>
</listitem>
-->
</itemizedlist>
</partintro>
&nms;
&messaging;
&msmq;
<!--
&scheduling;
-->
</part>
<part id="index-vsnet">

View File

@@ -0,0 +1,745 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="messaging">
<title>Message Oriented Middleware</title>
<section>
<title>Introduction</title>
<para>The goal of Spring's messaging is to increase your productiviity
when writing an enterprise strength messaging middleware applications.
Spring achieves these goals in several ways. First it provides several
helper classes that remove from the developer the incidental complexity
and resource management issues that arrise when using messaging APIs. In
particular, this chapter deals with messaging providers whose API is done
in the spirit of the Java Message Service (JMS) API. Vendors who provide a
JMS inspired API include TIBCO, IBM, and Sonic Software. (If you are using
Microsoft's Message Queue, please refer to the specific <link
linkend="msmq">MSMQ section</link>). Second, the design of these messaging
helper classes promote best practices in desigining a messaging
applicaiton by promoting a clear separation between the messaging
middleware specific code and business processing that is technology
agnostic. This is generally referred to a "plain old .NET object" (or
PONO) programming model. Lastly, as there is no defacto-standard common
API across messaging vendors, Spring provides an implementation for each
of the major messaging middleware vendors. Portability across each vendor
is promoted by providing a configuration schema that hides the actual
class types used for Spring's helper class as well as consistent naming of
the helper classes in different namespaces. The goal with vendor
portability is to get as far as possible by simply changing your 'using'
statements in code and schema name in the configuration. </para>
<para>JMS can be roughly divided into two areas of functionality, namely
the production and consumption of messages. The
<classname>MessageTemplate</classname> class is used for message
production and synchronous message reception. For asynchronous reception,
Spring provides a multi-threaded message listener container,
<classname>SimpleMessageListenerContainer</classname>, that can be sued to
to create Message-Driven PONOs (MDPs). The MessageConverter interface is
used by both the MessageTemplate class and the message listener container
to converte between provider message types and PONOs.</para>
<para>For each vendor there is a namespace of the type,
<literal>Spring.Messaging.&lt;VendorAcronym&gt;.Core</literal>. For
example, in the case of ActiveMQ, you would use
<literal>Spring.Messaging.Nms.Core</literal>, for TIBCO you would use
<literal>Spring.Messaging.Ems.Core</literal>. This namespace provides the
core functionality for messaging. It contains the
<literal>MessageTemplate</literal> class that simplifies the use of the
messaging APIs by handling the creation and release of resources, much
like the AdoTemplate does for ADO.NET. The design principle common to
Spring template classes is to provide helper methods to perform common
operations and for more sophisticated usage, delegate the essence of the
processing task to user implemented callback interfaces. The messaging
template follows the same design. The classes offer various convenience
methods for the sending of messages, consuming a message synchronously,
and exposing the message Session and MessageProducer to the user.</para>
<para>The namespace
<literal>Spring.Messaging.&lt;VendorAcronym&gt;.Support.Converter</literal>
provides a <literal>MessageConverter</literal> abstraction to convert
between .NET objects and messages. The namespace
<literal>Spring.Messaging.&lt;VendorAcronym&gt;.Support.Destinations</literal>
provides various strategies for managing destinations, such as providing a
service locator for destinations stored in a directory service.</para>
<para>Finally, the namespace
<literal>Spring.Messaging.&lt;VendorAcronym&gt;.Connections</literal>
provides an implementations of the ConnectionFactory suitable for use in
standalone applications. </para>
<para>This chapter starts with a 'Quick tour for the impatient' that shows
you how to get up and running quickly using Spring's message helper
classes. You can also refer to the sample application that ships with
Spring for additional hands-on usage. The rest of the sections in this
chapter discusses each of the major helper classes in detail. </para>
<section>
<title>Separation of Concerns</title>
<para>The use of MessageConverters and a PONO programming model promote
messaging best practices by applying the principal of Separation of
Concerns to messaging based architectures. The infrasructure concern of
publishing and consuming messages is separated from the concern of
business processing. These two concerns are reflected in the
architecture as two distinct layers, a message processing layer and a
business processing layer. The benefit of this approach is that your
business processing is decoupled from the technology, making it more
likely to survive technological changes over time. Spring's
MessageConverters provide first class support for mapping messaging data
types to PONOs. Aside from being the link between the two layers, having
a pluggable strategy for message conversion helps support a loosely
coupled architecture over time. Message formats will change over time,
typically by the addition of new fields. MessageConverters can detect
different versions of messages and perform the appropriate mapping logic
to PONOs such so that multiple versions of a message can be supported
simultaneously, a common requirement in enterprise messaging
architectures. In can loosely associate Spring's MessageConverters to
XML/Object mappers but with a messaging twist.</para>
</section>
<section>
<title>Interopability</title>
<para>Messaging is a traditional area of interopabiltity across
heterogenous systems with messaging vendors providing support on
multiple operating systems (Windows, UNIX, Mainframes OS's) as well as
multiple language bindings (C, C++, Java, .NET, Perl, etc.). In 199x the
Java Community Process came up with a specifcation to provide a common
API across messaing providers as well as define some common messaging
functionality. This specification is know as the Java Message Service.
From the API perspective, it can roughly be thought of as the messaging
counterpart to the ADO.NET or JDBC APIs that provide portability across
different database providers. Given this history, when messaging vendors
created their .NET APIs, many did so by creating their own JMS inspired
API in .NET. The NMS project's goal is to provide a common API for .NET
thereby giving portability to the various .NET messaging providers. One
downside of the NMS API is that it is a low-level API, much like ADO.NET
and JDBC. Even the simplist of operations requires 10s of lines of code
with the bulk of that code related to resource management of
intermediate API objects. Note that the 'core' of the JMS/NMS API is
much simplier than with ADO.NET/JDBC. So while NMS provides portability
it also brings with it this API 'noise'. Spring's messaging support,
both in Java and .NET, addresses the error-prone boiler plate coding
style one needs when using thtese APIs.</para>
</section>
<section>
<title>The role of Messaging API in a 'WCF world'</title>
<para>Windows Communication Foundation (WCF) also supports message
oriented middleware. Not surprisingly, a Microsoft Message Queuing
(MSMQ) binding is provided as part of WCF. The WCF programming model is
higher level than the traditional messaging APIs such as JMS and NMS
since you are programing to a sevice interface and use metadata (either
XML or attributes) to configure the messaging behavior. This is a big
improvement over using low-level vendor specific APIs. However, at the
time of this writing, it is not clear that other messaging providers
will provide WCF bindings. A Spring Extensions project, Spring-NMS,
provides a WCF binding for NMS. This will let you use the WCF
programming model but still retain portability across messaging
providers.</para>
</section>
</section>
<section>
<title>Using Spring JMS</title>
<section>
<title>JmsTemplate</title>
<para>Code that uses the JmsTemplate only needs to implement callback
interfaces giving them a clearly defined contract. The IMessageCreator
callback interface creates a message given a Session provided by the
calling code in JmsTemplate. In order to allow for more complex usage of
the JMS API, the callback ISessionCallback provides the user with the
JMS session and the callback IProducerCallback exposes a Session and
MessageProducer pair.</para>
<para>The JMS API exposes two types of send methods, one that takes
delivery mode, priority, and time-to-live as quality of service (QOS)
parameters and one that takes no QOS parameters which uses default
values. Since there are many send methods in JmsTemplate, the setting of
the QOS parameters have been exposed as bean properties to avoid
duplication in the number of send methods. Similarly, the timeout value
for synchronous receive calls is set using the property
ReceiveTimeout.</para>
</section>
<section>
<title>Connections</title>
<para>The JmsTemplate requires a reference to a ConnectionFactory. The
ConnectionFactory is part of the JMS specification and serves as the
entry point for working with JMS. It is used by the client application
as a factory to create connections with the JMS provider and
encapsulates various configuration parameters, many of which are vendor
specific such as SSL configuration options.</para>
<para>Note: The TIBCO implementation is not interface based and its
methods are not virtual so no additional functionality that may
otherewise be part of a ConnectionFactory 'Wrapper' are provided. This
type of functionality wil be available when Spring.NET uses the
implementation neutral NMS AP(s.</para>
</section>
<section>
<title>Destination Management</title>
<para>In Java implementations of JMS, Connections and Destinations are
'administered objects' accessible though JNDI. In .NET each vendor has
selected a different approach, generally JNDI inspired, to retrieve
Connections and Destinations that were configured administratively. You
can use these vendor specific APIs to perform dependency injection on
references to JMS Destination objects in Sprng's XML configuration file
by creating am implementation of IObjectFactory.</para>
<para>However, this approach of administerd objects can be quite
cumbersome if there are a large number of destinations in the
application or if there are advanced destination management features
unique to the JMS provider. Examples of such advanced destination
management would be the creation of dynamic destinations or support for
a hierarchical namespace of destinations. The JmsTemplate delegates the
resolution of a destination name to a JMS destination object to an
implementation of the interface IDestinationResolver.
DynamicDestinationResolver is the default implementation used by
JmsTemplate and accommodates resolving dynamic destinations. A
JndiDestinationResolver is also provided that acts as a service locator
for destinations contained in JNDI and optionally falls back to the
behavior contained in DynamicDestinationResolver.</para>
<para>Quite often the destinations used in a JMS application are only
known at runtime and therefore cannot be administratively created when
the application is deployed. This is often because there is shared
application logic between interacting system components that create
destinations at runtime according to a well-known naming convention.
Even though the creation of dynamic destinations are not part of the JMS
specification, most vendors have provided this functionality. Dynamic
destinations are created with a name defined by the user which
differentiates them from temporary destinations and are often not
registered in a JNDI-like directory.. The API used to create dynamic
destinations varies from provider to provider since the properties
associated with the destination are vendor specific. However, a simple
implementation choice that is sometimes made by vendors is to disregard
the warnings in the JMS specification and to use the TopicSession method
createTopic(String topicName) or the QueueSession method
createQueue(String queueName) to create a new destination with default
destination properties. Depending on the vendor implementation,
DynamicDestinationResolver may then also create a physical destination
instead of only resolving one.</para>
<para>The boolean property PubSubDomain determines the behavior of
dynamic destination resolution via implementations of the
DestinationResolver interface.</para>
<para>You can also configure the JmsTemplate with a default destination
via the property defaultDestination. The default destination will be
used with send and receive operations that do not refer to a specific
destination.</para>
</section>
<section>
<title>Message Listener Containers</title>
<para>One of the most common uses of JMS is to concurrently process
messages delivered asynchronously.</para>
<para>A subclass of
<classname>AbstractMessageListenerContainer</classname> is used to
receive messages from JMS and drive the Message-Driven POCOs (MDPs) that
are injected into it. The
<classname>AbstractMessageListenerContainer</classname> is responsible
for all threading of message reception and dispatch into the MDPs for
processing. A message listener container is the intermediary between an
MDP and a messaging provider, and takes care of registering to receive
messages, participating in transactions, resource acquisition and
release, exception conversion and suchlike. This allows you as an
application developer to write the (posssibly complex) business logic
associated with receiving a message (and possibly responding to it), and
delegates boilerplate JMS infrastructure concerns to the framework.
There are one subclasses of
<classname>AbstractMessageListenerContainer</classname> packaged with
Spring - <classname>SimpleMessageListenerContainer</classname>.</para>
<para>SimpleMessageListenerContainer creates a fixed number of JMS
sessions at startup and uses them throughout the lifespan of the
container. This subclass doesn't allow for dynamic adaption to runtime
demands or participate in transactional reception of messages.</para>
<para>Spring.Java provides two other subclasses, one to support
distributed transactions and the other to provide a dynamic session
management to optimize concurrent processing. Distributed transaction
support is not provided by .NET C# vendors (AFAIK) and neither is the
dynamic session management support which is based on the
ServerSessionPool SPI - an optional part of the JMS
specification.</para>
</section>
<section>
<title>Transaction Management</title>
<para>TBD. This relates to integration with Spring's transaction
management features, the ability to have transacted JMS sessions is
supported.</para>
</section>
</section>
<section>
<title>Sending a Message</title>
<para>The <classname>JmsTemplate</classname> contains three convenience
methods to send a message. The methods are listed below.</para>
<itemizedlist>
<listitem>
<para><literal>void Send(Destination destination, IMessageCreator
messageCreator)</literal></para>
</listitem>
<listitem>
<para><literal>void Send(string destinationName, IMessageCreator
messageCreator)</literal></para>
</listitem>
<listitem>
<para><literal>void Send(IMessageCreator
messageCreator)</literal></para>
</listitem>
</itemizedlist>
<para>Which differ in how the destination is specified. In first case the
JMS Destination object is specified directly. The second case specifies
the destination using a string that is then resolved to a JMS JMS
<classname>Destination</classname> object using the DestinationResolver
associated with the template. The last method sends the message to the
destination specified by JmsTemplates
<classname>DefaultDestination</classname> property.</para>
<para>All methods take as an argument an instance of IMessageCreator which
defines the API contract for you to create the JMS message. The interface
is show below</para>
<para><programlisting>public interface IMessageCreator {
Message CreateMessage(Session session);
}</programlisting>Intermediate JMS Sessions and MessageProducers needed to
send the message are managed by JmsTemplate. The session passed in to the
method is never null. There is a similar set methods that use a delegate
instead of the interface, which can be convenientwhen writing small
implementaitons in .NET 2.0 using anonymous delegates. Larger, more
complex implementations of the method 'CreateMessage' are better suited to
an interface based implementation.</para>
<itemizedlist>
<listitem>
<para><literal>void SendWithDelegate(Destination destination,
MessageCreatorDelegate messageCreatorDelegate)</literal></para>
</listitem>
<listitem>
<para><literal>void SendWithDelegate(string destinationName,
MessageCreatorDelegate messageCreatorDelegate)</literal></para>
</listitem>
<listitem>
<para><literal>void SendWithDelegate(MessageCreatorDelegate
messageCreatorDelegate)</literal></para>
</listitem>
</itemizedlist>
<para>The declaration of the delegate is</para>
<programlisting>public delegate Message MessageCreatorDelegate(Session session);</programlisting>
<para>The following class shows how to use the API with an anonymous
delegate, making for very terse syntax and easy access to local variables.
A more realistic example would create the JmsTemplate via dependency
injection, allowing for easy configuration of the connection string. A
convenience class, JmsGatewaySupport, already contains a property of type
JmsTemplate for you to use in</para>
<programlisting> public class SimplePublisher
{
private JmsTemplate template;
public SimplePublisher()
{
template = new JmsTemplate(new ConnectionFactory("tcp://localhost:7222"));
template.PubSubDomain = true;
}
public void Publish(string ticker, double price)
{
template.SendWithDelegate("APP.STOCK",
delegate(Session session)
{
MapMessage message = session.CreateMapMessage();
message.SetString("TICKER", ticker);
message.SetDouble("PRICE", price);
message.Priority = 2;
return message;
});
}
}
</programlisting>
<section>
<title>Using MessageConverters</title>
<para>In order to facilitate the sending of domain model objects, the
<classname>JmsTemplate</classname> has various send methods that take a
.NET object as an argument for a message's data content. The overloaded
methods ConvertAndSend and ReceiveAndConvert in
<classname>JmsTemplate</classname> delegate the conversion process to an
instance of the <interfacename>MessageConverter</interfacename>
interface. This interface defines a simple contract to convert between
.NET objects and JMS messages. The default implementation
<classname>SimpleMessageConverter</classname> supports conversion
between String and TextMessage, byte[] and BytesMesssage, and
System.Collections.IDictionary and MapMessage. By using the converter,
you and your application code can focus on the business object that is
being sent or received via JMS and not be concerned with the details of
how it is represented as a JMS message.</para>
<para>The family of ConvertAndSend messages are similar to that of the
Send method with the additional argument of type IMessagePostProcessor.
These methods are listed below.</para>
<itemizedlist>
<listitem>
<para><code>void ConvertAndSend(object message)</code></para>
</listitem>
<listitem>
<para><code>void ConvertAndSend(object message,
IMessagePostProcessor postProcessor)</code></para>
</listitem>
<listitem>
<para><code>void ConvertAndSend(string destinationName, object
message)</code></para>
</listitem>
<listitem>
<para><code>void ConvertAndSend(string destinationName, object
message, IMessagePostProcessor postProcessor);</code></para>
</listitem>
<listitem>
<para><code>void ConvertAndSend(Destination destination, object
message)</code></para>
</listitem>
<listitem>
<para><code>void ConvertAndSend(Destination destination, object
message, IMessagePostProcessor postProcessor)</code></para>
</listitem>
</itemizedlist>
<para>In the previous example the message priority was set inside the
callback. Generally speaking, converters should not be responsible for
setting Quality of Service parameters since they are not aware of the
context in which they are being called. The following code show this in
action.</para>
<para><programlisting>public void PublishUsingDict(string ticker, double price)
{
IDictionary marketData = new Hashtable();
marketData.Add("TICKER", ticker);
marketData.Add("PRICE", price);
template.ConvertAndSend("APP.STOCK", marketData);
}</programlisting>A reflection based converter that can converter arbitrary
objects is available as a seperate project.</para>
</section>
</section>
<section>
<title>Session and Producer Callback</title>
<para>While the send operations cover many common usage scenarios, there
are cases when you want to perform multiple operations on a JMS Session or
MessageProducer. The SessionCallback and ProducerCallback expose the JMS
Session and Session / MessageProducer pair respectfully. The Execute()
methods on JmsTemplate execute these callback methods.</para>
<itemizedlist>
<listitem>
<para><code>public object Execute(IProducerCallback
action)</code></para>
</listitem>
<listitem>
<para><code>public object Execute(ISessionCallback
action)</code></para>
</listitem>
</itemizedlist>
<para>Where ISessionCallback and IProducerCallback are</para>
<para><programlisting>public interface IProducerCallback
{
object DoInJms(Session session, MessageProducer producer);
}</programlisting>and</para>
<programlisting>public interface ISessionCallback
{
object DoInJms(Session session);
}</programlisting>
</section>
<section>
<title>Receiving a message</title>
<section>
<title>Synchronous Reception</title>
<para>While JMS is typically associated with asynchronous processing, it
is possible to consume messages synchronously. The overloaded
<code>Receive(..)</code> methods provide this functionality. During a
synchronous receive, the calling thread blocks until a message becomes
available. This can be a dangerous operation since the calling thread
can potentially be blocked indefinitely. The property
<code><property>ReceiveTimeout</property></code> specifies how long the
receiver should wait before giving up waiting for a message.</para>
<para>The <methodname>Receive</methodname> methods are listed
below</para>
<itemizedlist>
<listitem>
<para><code>public Message Receive()</code></para>
</listitem>
<listitem>
<para><code>public Message Receive(Destination
destination)</code></para>
</listitem>
<listitem>
<para><code>public Message Receive(string
destinationName)</code></para>
</listitem>
<listitem>
<para><code>public Message ReceiveSelected(string
messageSelector)</code></para>
</listitem>
<listitem>
<para><code>public Message ReceiveSelected(string destinationName,
string messageSelector)</code></para>
</listitem>
<listitem>
<para><code>public Message ReceiveSelected(Destination destination,
string messageSelector)</code></para>
</listitem>
</itemizedlist>
<para>The <methodname>Recieve</methodname> method without arguments used
the <property>DefaultDestination</property>. The
<methodname>RecieveSelected</methodname> methods apply the provided JMS
message selector string to the <classname>MessageConsumer</classname>
that is created.</para>
<para>The <methodname>ReceiveAndConvert</methodname> methods apply the
templates message converter when receiving a message. These methods are
listed below.</para>
<itemizedlist>
<listitem>
<para><code>public object ReceiveAndConvert()</code></para>
</listitem>
<listitem>
<para><code>public object ReceiveAndConvert(Destination
destination)</code></para>
</listitem>
<listitem>
<para><code>public object ReceiveAndConvert(string
destinationName)</code></para>
</listitem>
<listitem>
<para><code>public object ReceiveSelectedAndConvert(string
messageSelector)</code></para>
</listitem>
<listitem>
<para><code>public object ReceiveSelectedAndConvert(string
destinationName, string messageSelector)</code></para>
</listitem>
<listitem>
<para><code>public object ReceiveSelectedAndConvert(Destination
destination, string messageSelector)</code></para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Asynchronous Reception</title>
<para>You can register a class that implements the
<interfacename>IMessageListener</interfacename> interface. In the case
of TIBCO EMS this interface is defined as</para>
<programlisting>public interface IMessageListener
{
void OnMessage(Message message);
}</programlisting>
<para>Other vendors may provide a delegate based version of this
interface.</para>
<para>You register you listener with a message listener container that
specifies JMS configuration parameters and the number of concurrent
consumers to create. There is an abstract base class for message
listener containers,
<classname>AbstractMessageListenerContainer</classname>, and one
concrete implementation,
<classname>SimpleMessageListenerContainer</classname>.
<classname>SimpleMessageListenerContainer</classname> creates a fixed
number of JMS Sessions/MessageConsumer pairs as set by the property
<property>ConcurrentConsumers</property>. Here is a sample
configuration</para>
<programlisting> &lt;object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS"&gt;
&lt;constructor-arg index="0" value="tcp://localhost:7222"/&gt;
&lt;/object&gt;
&lt;object id="messageListener" type="MyApp.MyMessageListener, MyApp"/&gt;
&lt;object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems"&gt;
&lt;property name="ConnectionFactory" ref="connectionFactory"/&gt;
&lt;property name="DestinationName" value="APP.REQUEST"/&gt;
&lt;property name="ConcurrentConsumers" value="10"/&gt;
&lt;property name="MessageListener" ref="messageListener"/&gt;
&lt;/object&gt;</programlisting>
<para>The property <property>PubSubDomain</property> is by defalt false,
meaning point-to-point/Queue delivery semantics. The above configuration
will create 10 threads that process messages off of the queue named
"APP.REQUEST". The threads are those owned by the JMS provider as a
result of creating a JMS MessageConsumer. Other important properties are
<property>ClientID</property>, used to set the ClientID of the JMS
Connection and <property>MessageSelector</property> to specify the JMS
'sql-like' message selector string. Durable subscriptions are supported
via the properties <property>SubscriptionDurable</property> and
<property>DurableSubscriptionName</property>. You may also register a
listener using the property ExceptionListener.</para>
<section>
<title>MessageListenerAdapater</title>
<para>The MessageListenerAdapter allows methods of a class that does
not implement the IMessageListener interface to be invoked upon
message delivery. Lets call this class the 'message handler' class. To
achive this goal the MessageListenerAdapter implements the standard
IMessageListener interface to recieve a message and then delegates the
processing to the message handler class. Since the message handler
class does not contain methods that refer to JMS artifacts such as
Message,TextMessage etc, the MessageListenerAdapter uses a
MessageConverter to bridge the JMS and 'plain object' worlds. As a
reminder, the provided SimpleMessageConverter converts from
TextMessage to string, BytesMessage to byte[], and MapMessage to
IDictionary. Once the incoming message is converted to an IDictionary
(for example) a method with the name 'Handle' is invoked via
reflection passing in the IDictionary as an argument.</para>
<para>Using the SimpleMessageConverter, your "plain old object'
messaging callback implementation would look like this.</para>
<programlisting>public class SimpleMessageHandler
{
public void HandleObject(IDictionary dict)
{
...
}
public void HandleObject(string text)
{
...
}
public void HandleObject(byte[] data)
{
...
}
}</programlisting>
<para>Notice how the various message handling methods are strongly
typed according to the contents of the various Message types that they
can receive and handle. The following configuration shows how to hook
up this class to process incoming JMS messages.</para>
<para><programlisting>
&lt;object name="simpleMessageHandler, type="MyApp.SimpleMessageHandler, MyApp"/&gt;
&lt;object name="simpleMessageConverter"
type="Spring.Messaging.Tibco.Ems.Support.Converter.SimpleMessageConverter, Spring.Messaging.Tibco.Ems"/&gt;
&lt;object id="messageListenerAdapter" type="Spring.Messaging.Tibco.Ems.Listener.Adapter.MessageListenerAdapter, "&gt;
&lt;property name="DelegateObject" ref="simpleMessageHandler"/&gt;
&lt;property name="DefaultListenerMethod" value="HandleObject"/&gt;
&lt;property name="MessageConverter" ref="simpleMessageConverter"/&gt;
&lt;/object&gt;
&lt;object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS"&gt;
&lt;constructor-arg index="0" value="tcp://localhost:7222"/&gt;
&lt;/object&gt;
&lt;object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems"&gt;
&lt;property name="ConnectionFactory" ref="connectionFactory"/&gt;
&lt;property name="DestinationName" value="APP.REQUEST"/&gt;
&lt;property name="ConcurrentConsumers" value="10"/&gt;
&lt;property name="MessageListener" ref="messageListener"/&gt;
&lt;/object&gt;</programlisting></para>
<para>Another of the capabilities of the MessageListenerAdapter class
is the ability to automatically send back a response Message if a
handler method returns a non-void value. Any non-null value that is
returned from the execution of the handler method will (in the default
configuration) be converted into a TextMessage. The resulting
TextMessage will then be sent to the Destination (if one exists)
defined in the JMS Reply-To property of the original Message, or the
default Destination set on the MessageListenerAdapter (if one has been
configured); if no Destination is found then an
InvalidDestinationException will be thrown (and please note that this
exception will not be swallowed and will propagate up the call
stack).</para>
<para></para>
</section>
</section>
</section>
<section>
<title>TIBCO Specific Details</title>
<para>Caching of JMS resources is usually done by a wrapping the 'raw
provider' JMS implementation with an implementation that will cache JMS
resources. The resources that are candidates for caching are the JMS
Connection, Session, and MessageProducer. The JMS specification requires
that the Connection be thread safe. The Session and MessageProducer are
not required to be thread safe but they are in TIBCO's implementation. The
most important resource to cache is the JMS Connection since the flow of
events in JmsTemplate is to create/close a connection on each operation
and this is an expensive operation. In the Java version of JmsTemplate a
class, SingleConnectionFactory is provided in which the same Connection is
returned on calls to createConnection() and all calls to .close() on the
returned Connection are ignored. Since TIBCO's connection class does not
have an interface nor virtual methods this strategy is not possible. An
alternative strategy is to 'hard-code' the caching of these resources
within JmsTemplate and SimpleMessageContainer. This functionality is
controlled by the property <property>CacheJmsResources</property> and is
set to true by default, resulting in caching of Connection, Session, and
MessageProducer. When integration with NMS (a set of common interfaces for
.NET JMS providers) is completed this will not be necessary and we can use
a wrapper implementation of the NMS API that performs caching of JMS
resources in an appropriate manner for each vendor.</para>
</section>
</chapter>

View File

@@ -130,7 +130,7 @@
<para>Find below an example of the basic structure of XML-based
configuration metadata.</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="..." type="..."&gt;
&lt;!-- collaborators and configuration for this object go here --&gt;

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="scheduling">
<title>Scheduling and Thread Pooling</title>
<section id="scheduling-introduction">
<title>Introduction</title>
<para>The Spring Framework features integration classes for scheduling
support. Currently, Spring supports the Quartz Scheduler (<ulink
url="http://quartznet.sourceforge.net/"></ulink>). The scheduler is set up
using a <interfacename>IFactoryObject</interfacename> with optional
references to <classname>Trigger</classname> instances, respectively.
Furthermore, a convenience class for both the Quartz Scheduler is
available that allows you to invoke a method of an existing target
object.</para>
</section>
<section id="scheduling-quartz">
<title>Using the Quartz.NET 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://quartznet.sourceforge.net/"></ulink>. 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 JobDetailObject</title>
<para><classname>JobDetail</classname> objects contain all information
needed to run a job. The Spring Framework provides a
<classname>JobDetailObject</classname> that makes the
<classname>JobDetail</classname> easier to configure and with sensible
defaults. Let's have a look at an example:</para>
<programlisting>
&lt;object name="ExampleJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="JobType" value="Example.Quartz.ExampleJob, Example.Quartz" /&gt;
&lt;property name="JobDataAsMap"&gt;
&lt;dictionary&gt;
&lt;entry key="Timeout" value="5" /&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The job detail object has all information it needs to run the job
(<classname>ExampleJob</classname>). The timeout is specified in the job
data dictionary. The job data dictonary is available through the
<classname>JobExecutionContext</classname> (passed to you at execution
time), but the <classname>JobDetailObject</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>JobDetailObject</classname> will automatically apply
it:</para>
<programlisting>namespace Example.Quartz;
public class ExampleJob extends QuartzJobObject {
private int timeout;
/// &lt;summary&gt;
/// Setter called after the ExampleJob is instantiated
/// with the value from the JobDetailObject (5)
/// &lt;/summary&gt;
public int Timeout {
set { timeout = value; };
}
protected override void ExecuteInternal(JobExecutionContext context) {
<lineannotation>// do the actual work</lineannotation>
}
}</programlisting>
<para>All additional settings from the job detail object 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 object name of the job detail object (in the example above, this is
<literal>ExampleJob</literal>).</emphasis></para>
</section>
<section id="scheduling-quartz-method-invoking-job">
<title>Using the
<classname>MethodInvokingJobDetailFactoryObject</classname></title>
<para>Often you just need to invoke a method on a specific object. Using
the <classname>MethodInvokingJobDetailFactoryObject</classname> you can
do exactly this:</para>
<programlisting>&lt;object id="JobDetail" class="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="TargetObject" ref="ExampleBusinessObject" /&gt;
&lt;property name="TargetMethod" value="DoIt" /&gt;
&lt;/object&gt;</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>public class ExampleBusinessObject {
<lineannotation>// properties and collaborators</lineannotation>
public void DoIt() {
<lineannotation>// do the actual work</lineannotation>
}
}</programlisting>
<programlisting>
&lt;object id="ExampleBusinessObject" class="Examples.BusinessObjects.ExampleBusinessObject, Examples.BusinessObjects"/&gt;</programlisting>
<para>Using the
<classname>MethodInvokingJobDetailFactoryObject</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>MethodInvokingJobDetailFactoryObject</classname>
non-concurrent, set the <literal>concurrent</literal> flag to
<literal>false</literal>.</para>
<programlisting>&lt;object id="JobDetail" class="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="TargetObject" ref="ExampleBusinessObject" /&gt;
&lt;property name="TargetMethod" value="DoIt" /&gt;
&lt;property name="Concurrent" value="false" /&gt;
&lt;/object&gt;
</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>SchedulerFactoryObject</classname></title>
<para>We've created job details and jobs. We've also reviewed the
convenience class 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>SchedulerFactoryObject</classname>. Several triggers are
available within Quartz. Spring offers two subclassed triggers with
convenient defaults: <classname>CronTriggerObject</classname> and
<classname>SimpleTriggerObject</classname></para>
<para>Triggers need to be scheduled. Spring offers a
<classname>SchedulerFactoryObject</classname> that exposes triggers to
be set as properties. <classname>SchedulerFactoryObject</classname>
schedules the actual jobs with those triggers.</para>
<para>Find below a couple of examples:</para>
<programlisting>&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="ExampleJob" /&gt;
&lt;!-- 10 seconds --&gt;
&lt;property name="StartDelay" value="10s" /&gt;
&lt;!-- repeat every 50 seconds --&gt;
&lt;property name="RepeatInterval" value="50s" /&gt;
&lt;/object&gt;
&lt;object id="CronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz"&gt;
&lt;property name="JobDetail" ref="ExampleJob" /&gt;
&lt;!-- run every morning at 6 AM --&gt;
&lt;property name="CronExpression" value="0 0 6 * * ?" /&gt;
&lt;/object&gt;
</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>SchedulerFactoryObject</classname>:</para>
<programlisting>&lt;object id="quartzSchedulerFactory" 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>More properties are available for the
<classname>SchedulerFactoryObjecct</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/2.5.x/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html">SchedulerFactoryObject
SDK docs</ulink> for more information.</para>
</section>
</section>
</chapter>