Monday, 17 September 2012

Spring JMS Tutorial with ActiveMQ

In this post I'll look at Springs messaging support and how it can be used to integrate with Message Oriented Middleware (MOM) offerings such as Apache ActiveMQ. Although the sample application in this post will use ActiveMQ as its message broker, the application itself is vendor agnostic and can  integrate with any JMS compliant messaging platform. I've kept the application as loosely coupled from ActiveMQ as possible and will highlight the bits that would need to change if you were to choose another message platform such as IBM MQSeries.

Technologies Used
The sample code in this post will be built and deployed as a simple web application. Obviously this doesn't have to be the case but I've chosen to build a web app as these type of enterprise integration components tend to be deployed as web applications in the real world. The sample app will be built using the following stack.
  • Apache ActiveMQ - JMS Message Broker(not part of the actual application but used to test our applications JMS fucntionality)
  • Spring 3.1
  • Maven
  • Tomcat

Setting Up ActiveMQ
If you don't already have ActiveMQ you'll need to download it at http://activemq.apache.org/download.html The latest version at the time of writing is 5.6.0. To set up ActiveMQ follow the steps below.
  1. Copy the downloaded zip to C:\Program Files and unzip. 
  2. Open a command window and cd to C:\Program Files\apache-activemq-5.6.0-bin\apache-activemq-5.6.0\bin.
  3. Start ApacheMQ by calling activemq.bat
ActiveMQ should start up as shown in figure 1.0 below.
Figure 1.0 ActiveMQ Start-up
Now that ActiveMQ has started we can open the admin console by navigating to http://localhost:8161/admin/index.jsp. On the home page we'll see some information about the broker and some administration menu options.
Figure 2.0 ActiveMQ Admin Console
Next we need to create 2 new Queues, one that our sample application will consume messages from and another Queue that we'll write messages to. Click on the Queues link and create a new Queue by entering a Queue name and clicking submit. For our sample application we'll create 2 Queues, TestQueueOne and TestQueueTwo as shown in figure 3.0 below.
Figure 3.0 Create New Queues
Now that we have our message broker set up lets take start building an application to use it.

Creating the Project
We'll start off by creating a simple Spring web project like the one shown in figure 4.0 below.
Figure 4.0 Project Structure

Spring Configuration
We'll start by defining the Spring configuration for our application. I've commented the configuration below to help explain the main components. The most important parts are the DefaultMessageListenerContainer (used to consume messages) and the JMSTemplate (used to put message onto a queue).
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <beans xmlns="http://www.springframework.org/schema/beans"  
3:                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:                 xmlns:context="http://www.springframework.org/schema/context"  
5:                 xmlns:jee="http://www.springframework.org/schema/jee"  
6:                 xsi:schemaLocation="http://www.springframework.org/schema/beans  
7:                                       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
8:                                       http://www.springframework.org/schema/context  
9:                                       http://www.springframework.org/schema/context/spring-context-3.0.xsd  
10:                                      http://www.springframework.org/schema/beans  
11:                                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
12:                                      http://www.springframework.org/schema/jee  
13:                                      http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">  
14:    
15:    
16:       <!-- Use Springs JNDI support to look up JMS Connection Factory and Queue definitions from the  
17:            container. This means that specific connection details are not embedded in the application  
18:        -->  
19:       <jee:jndi-lookup id="mqConnectionFactory" jndi-name="java:comp/env/jms/mqConnectionFactory" />  
20:       <jee:jndi-lookup id="testQueueOne" jndi-name="java:comp/env/jms/testQueueOne" />  
21:       <jee:jndi-lookup id="testQueueTwo" jndi-name="java:comp/env/jms/testQueueTwo" />  
22:    
23:       <!-- Our message listener implementation that implements the JMS MessageListener interface and implements the  
24:             onMessage method to process incoming messages  
25:        -->  
26:       <bean id="testMessageListener" class="com.blog.spring.jms.TestMessageListener">  
27:          <property name="testMessageSender" ref ="testMessageSender" />  
28:       </bean>  
29:    
30:       <!-- DefaultMessageListenerConatiner is the Spring equivalent to an EJB Message Driven Bean.  
31:          It polls and consumes messages from a JMS queue. The configuration below is as follows  
32:    
33:          1. connectionFactory - the connection factory definition used to connect to the Message Broker  
34:             which in our case is Active MQ  
35:          2. destination - the Queue which the MessageListener container is listening on from incoming messages  
36:          3. messageListener - the implementation class that will actually handle the incoming messages. The  
37:             DeafultMesssageListener takes messages from the queue and passes them to the message listener for  
38:             processing. We've defined our message listener above (testMessageListener)  
39:          4. concurrentConsumers - this is the number of threads that the DeafultMesaegListenerContainer will  
40:             spawn to handle incoming messages. The default is 1 but in our application we'll have 2 separate  
41:             threads processing incoming messages.  
42:        -->  
43:       <bean id="poiMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
44:          <property name="connectionFactory" ref ="mqConnectionFactory" />  
45:          <property name="destination" ref ="testQueueOne"/>  
46:          <property name="messageListener" ref ="testMessageListener"/>  
47:          <property name="concurrentConsumers" value="2" />  
48:       </bean>  
49:    
50:       <!-- MessageSender is a simple POJO that we supply with a JMSTemplate and  
51:            the Queue that we want to send messages to  
52:        -->  
53:       <bean id="testMessageSender" class="com.blog.spring.jms.TestMessageSender">  
54:          <property name="jmsTemplate" ref="jmsTemplate"/>  
55:          <property name="testQueue" ref="testQueueTwo"/>  
56:       </bean>  
57:    
58:       <!-- JMSTemplate is a Spring template that allows us to communicate with  
59:            a message broker via JMS. JMSTemplate takes care of boiler plate code such as exception handling  
60:            and resource management such as connection pooling. This allows us concentrate on solving the 'business'  
61:            problem. We supply the JMS template with the connection factory mentioned above  
62:        -->  
63:       <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
64:          <property name="connectionFactory" ref="mqConnectionFactory" />  
65:       </bean>  
66:    
67:  </beans>  

JMS Resource Configuration
Next we'll configure the ActiveMQ Connection Factory and Message Queues which we referenced on lines 19 to 21 above. These resources are defined in context.xml, a configuration file that Tomcat uses to look up runtime resources. This approach allows us to define connection details on the Servlet Container and outside of the actual application, which ensures that our application is not tightly coupled to the the JMS implementation. The resource lookups in context.xml is the only place where we reference ActiveMQ specifics. The application itself does not contain any reference to ActiveMQ, which means that we could easily switch to another JMS broker without changing any code in our application. To integrate with a different JMS broker all we'd need to do is update the context.xml to lookup Connection Factory and Queue definitions specific to our new JMS implementation, IBM MQSeries for example. Our context.xml is defined below.
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <Context>  
3:    
4:    <!--  
5:          Active MQ Connection Factory manages pooled connections  
6:          to the ActiveMQ broker. Tomcat will connect with the  
7:          broker using a TCP connection on port 61616 - this is the  
8:          default port for ActiveMQ  
9:    -->  
10:    <Resource name="jms/mqConnectionFactory"  
11:              auth="Container"  
12:              type="org.apache.activemq.ActiveMQConnectionFactory"  
13:              description="JMS Connection Factory"  
14:              factory="org.apache.activemq.jndi.JNDIReferenceFactory"  
15:              brokerURL="tcp://localhost:61616" />  
16:    
17:    <!--  
18:          This is a reference to the first Queue we defined  
19:          earlier in the ActiveMQ admin console  
20:    -->  
21:    <Resource name="jms/testQueueOne"  
22:              auth="Container"  
23:              type="org.apache.activemq.command.ActiveMQQueue"  
24:              factory="org.apache.activemq.jndi.JNDIReferenceFactory"  
25:              physicalName="TestQueueOne"/>  
26:    
27:    <!--  
28:          This is a reference to the second Queue we defined  
29:          earlier in the ActiveMQ admin console  
30:    -->  
31:    <Resource name="jms/testQueueTwo"  
32:              auth="Container"  
33:              type="org.apache.activemq.command.ActiveMQQueue"  
34:              factory="org.apache.activemq.jndi.JNDIReferenceFactory"  
35:              physicalName="TestQueueTwo"/>  
36:    
37:  </Context>  
The final part of our application configuration is web.xml. Here we simply define the context loader listener and point it at the Spring configuration file we defined earlier. 
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:             xmlns="http://java.sun.com/xml/ns/javaee"  
4:             xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
5:             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
6:                                 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
7:             id="WebApp_ID"  
8:             version="2.5">  
9:    
10:       <!--  
11:            Main configuration file for this Spring web application.  
12:       -->  
13:       <context-param>  
14:            <param-name>contextConfigLocation</param-name>  
15:            <param-value>  
16:                 /WEB-INF/config/spring-config.xml  
17:            </param-value>  
18:       </context-param>  
19:    
20:       <!--  
21:            Loads the Spring web application context using the config file defined above.  
22:       -->  
23:       <listener>  
24:            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
25:       </listener>  
26:    
27:  </web-app>  

Message Listener
Now we're ready to start writing some code. You may be glad to know that you that don't have to write very much code to consume messages from a JMS broker. In fact all we need is a class that implements the MessageListener interface - we override the onMessage method which is then invoked by the DefaultMessageListenerContainer (defined earlier) when a message is read from the queue. Our implementation of onMessage is very straight forward - we simply log the incoming message and then pass it to the message sender (defined later). The Message Listener class is defined below.
1:    
2:  package com.blog.spring.jms;  
3:    
4:  import javax.jms.JMSException;  
5:  import javax.jms.Message;  
6:  import javax.jms.MessageListener;  
7:  import javax.jms.TextMessage;  
8:  import org.apache.log4j.Logger;  
9:    
10:  /**  
11:   * Class handles incoming messages  
12:   *  
13:   * @see PointOfIssueMessageEvent  
14:   */  
15:  public class TestMessageListener implements MessageListener  
16:  {  
17:    
18:       private TestMessageSender messageSender_i;  
19:       private static final Logger logger_c = Logger.getLogger(TestMessageListener.class);  
20:    
21:       /**  
22:        * Method implements JMS onMessage and acts as the entry  
23:        * point for messages consumed by Springs DefaultMessageListenerContainer.  
24:        * When DefaultMessageListenerContainer picks a message from the queue it  
25:        * invokes this method with the message payload.  
26:        */  
27:       public void onMessage(Message message)  
28:       {  
29:            logger_c.debug("Received message from queue [" + message +"]");  
30:    
31:            /* The message must be of type TextMessage */  
32:            if (message instanceof TextMessage)  
33:            {  
34:                 try  
35:                 {  
36:                      String msgText = ((TextMessage) message).getText();  
37:                      logger_c.debug("About to process message: " + msgText);  
38:    
39:                      /* call message sender to put message onto second queue */  
40:                      messageSender_i.sendMessage(msgText);  
41:    
42:                 }  
43:                 catch (JMSException jmsEx_p)  
44:                 {  
45:                      String errMsg = "An error occurred extracting message";  
46:                      logger_c.error(errMsg, jmsEx_p);  
47:                 }  
48:            }  
49:            else  
50:            {  
51:                 String errMsg = "Message is not of expected type TextMessage";  
52:                 logger_c.error(errMsg);  
53:                 throw new RuntimeException(errMsg);  
54:            }  
55:       }  
56:    
57:       /**  
58:        * Sets the message sender.  
59:        *  
60:        * @param messageSender_p the new message sender  
61:        */  
62:       public void setTestMessageSender(TestMessageSender messageSender_p)  
63:       {  
64:            this.messageSender_i = messageSender_p;  
65:       }  
66:  }  
67:    

MessageSender
Now that our application can consume messages lets take a look at how we push messages onto a queue. We defined a JMSTemplate in the Spring configuration earlier - this class provides a convenient way of pushing messages onto a queue and saves us from having to write boiler plate code for opening and closing connections, handling JMS exceptions etc. This allows the developer to simply specify a queue and the message payload that you want to push onto that queue.
1:  package com.blog.spring.jms;  
2:    
3:  import javax.jms.JMSException;  
4:  import javax.jms.Queue;  
5:  import org.apache.log4j.Logger;  
6:  import org.springframework.jms.core.JmsTemplate;  
7:  import org.springframework.stereotype.Service;  
8:    
9:  /**  
10:   * The TestMessageSender class uses the injected JMSTemplate to send a message  
11:   * to a specified Queue. In our case we're sending messages to 'TestQueueTwo'  
12:   */  
13:  @Service  
14:  public class TestMessageSender  
15:  {  
16:       private JmsTemplate jmsTemplate_i;  
17:       private Queue testQueue_i;  
18:       private static final Logger logger_c = Logger .getLogger(TestMessageSender.class);  
19:    
20:       /**  
21:        * Sends message using JMS Template.  
22:        *  
23:        * @param message_p the message_p  
24:        * @throws JMSException the jMS exception  
25:        */  
26:       public void sendMessage(String message_p) throws JMSException  
27:       {  
28:            logger_c.debug("About to put message on queue. Queue[" + testQueue_i.toString() + "] Message[" + message_p + "]");  
29:            jmsTemplate_i.convertAndSend(testQueue_i, message_p);  
30:       }  
31:    
32:       /**  
33:        * Sets the jms template.  
34:        *  
35:        * @param template the jms template  
36:        */  
37:       public void setJmsTemplate(JmsTemplate tmpl)  
38:       {  
39:            this.jmsTemplate_i = tmpl;  
40:       }  
41:    
42:       /**  
43:        * Sets the test queue.  
44:        *  
45:        * @param queue the new test queue  
46:        */  
47:       public void setTestQueue(Queue queue)  
48:       {  
49:            this.testQueue_i = queue;  
50:       }  
51:  }  

Testing the Application
Now that the application is complete lets test it. Using the ActiveMQ admin console we'll put a message onto TestQueueOne. Conveniently you can tell the broker to submit the message as may times as you like. We'll ask the broker to submit our message 100 times so that we have time to see  it get picked up and processed in the eclipse console. When we put a message onto TestQueueOne we should see the following
  1. DefaultMessageListenerContainer will pick up the next message from TestQueuOne and invoke the onMessage method in our Message listener class with the message payload.
  2. onMessage will log the message payload and then call the TestMessageSender sendMessage with the message we've just received.
  3. TestMessageSender will use the JMSTemplate to put the message onto TestQueueTwo. 
To test the application follow the steps below.
  • In the ActiveMQ admin console go to the TestQueueOne definition and click the SendTo link. Add a message and set the 'Number of Messages to Send' to 100. This will ensure that broker puts the message onto the queue 100 times.
Figure 5.0 Send JMS Message
  • Click Send. 100 messages will be put onto the queue and you should see the application log the consumption of each message as shown in the log extract below.
 DEBUG: [Sep-16 22:59:29,911] spring.jms.TestMessageListener - Received message from queue [ActiveMQTextMessage {commandId = 96, responseRequired = false, messageId = ID:motown-58007-1347813056932-3:4:1:1:92, originalDestination = null, originalTransactionId = null, producerId = ID:motown-58007-1347813056932-3:4:1:1, destination = queue://TestQueueOne, transactionId = null, expiration = 0, timestamp = 1347832725483, arrival = 0, brokerInTime = 1347832725510, brokerOutTime = 1347832766192, correlationId = , replyTo = null, persistent = false, type = , priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@855da06, marshalledProperties = org.apache.activemq.util.ByteSequence@3c5cc430, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, text = null}]  
 DEBUG: [Sep-16 22:59:29,911] spring.jms.TestMessageListener - About to process message: Test Message!!  
 DEBUG: [Sep-16 22:59:29,911] spring.jms.TestMessageSender - About to put message on queue. Queue[queue://TestQueueTwo] Message[Test Message!!]  
 DEBUG: [Sep-16 22:59:29,947] spring.jms.TestMessageListener - Received message from queue [ActiveMQTextMessage {commandId = 97, responseRequired = false, messageId = ID:motown-58007-1347813056932-3:4:1:1:93, originalDestination = null, originalTransactionId = null, producerId = ID:motown-58007-1347813056932-3:4:1:1, destination = queue://TestQueueOne, transactionId = null, expiration = 0, timestamp = 1347832725483, arrival = 0, brokerInTime = 1347832725510, brokerOutTime = 1347832766192, correlationId = , replyTo = null, persistent = false, type = , priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@6e544a45, marshalledProperties = org.apache.activemq.util.ByteSequence@5fd83099, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, text = null}]  
 DEBUG: [Sep-16 22:59:29,947] spring.jms.TestMessageListener - About to process message: Test Message!!  
 DEBUG: [Sep-16 22:59:29,948] spring.jms.TestMessageSender - About to put message on queue. Queue[queue://TestQueueTwo] Message[Test Message!!]  
 DEBUG: [Sep-16 22:59:29,986] spring.jms.TestMessageListener - Received message from queue [ActiveMQTextMessage {commandId = 98, responseRequired = false, messageId = ID:motown-58007-1347813056932-3:4:1:1:94, originalDestination = null, originalTransactionId = null, producerId = ID:motown-58007-1347813056932-3:4:1:1, destination = queue://TestQueueOne, transactionId = null, expiration = 0, timestamp = 1347832725483, arrival = 0, brokerInTime = 1347832725510, brokerOutTime = 1347832766192, correlationId = , replyTo = null, persistent = false, type = , priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@6a5ebdf7, marshalledProperties = org.apache.activemq.util.ByteSequence@7209d9af, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, text = null}]  
 DEBUG: [Sep-16 22:59:29,986] spring.jms.TestMessageListener - About to process message: Test Message!!  
 DEBUG: [Sep-16 22:59:29,986] spring.jms.TestMessageSender - About to put message on queue. Queue[queue://TestQueueTwo] Message[Test Message!!]  
 DEBUG: [Sep-16 22:59:30,022] spring.jms.TestMessageListener - Received message from queue [ActiveMQTextMessage {commandId = 99, responseRequired = false, messageId = ID:motown-58007-1347813056932-3:4:1:1:95, originalDestination = null, originalTransactionId = null, producerId = ID:motown-58007-1347813056932-3:4:1:1, destination = queue://TestQueueOne, transactionId = null, expiration = 0, timestamp = 1347832725483, arrival = 0, brokerInTime = 1347832725511, brokerOutTime = 1347832766192, correlationId = , replyTo = null, persistent = false, type = , priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@64b2aaa6, marshalledProperties = org.apache.activemq.util.ByteSequence@de1abf0, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, text = null}]  
 DEBUG: [Sep-16 22:59:30,022] spring.jms.TestMessageListener - About to process message: Test Message!!  
 DEBUG: [Sep-16 22:59:30,022] spring.jms.TestMessageSender - About to put message on queue. Queue[queue://TestQueueTwo] Message[Test Message!!]  
 DEBUG: [Sep-16 22:59:30,052] spring.jms.TestMessageListener - Received message from queue [ActiveMQTextMessage {commandId = 100, responseRequired = false, messageId = ID:motown-58007-1347813056932-3:4:1:1:96, originalDestination = null, originalTransactionId = null, producerId = ID:motown-58007-1347813056932-3:4:1:1, destination = queue://TestQueueOne, transactionId = null, expiration = 0, timestamp = 1347832725484, arrival = 0, brokerInTime = 1347832725511, brokerOutTime = 1347832766192, correlationId = , replyTo = null, persistent = false, type = , priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@5adf20ae, marshalledProperties = org.apache.activemq.util.ByteSequence@6edaae1d, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, text = null}]  
 DEBUG: [Sep-16 22:59:30,053] spring.jms.TestMessageListener - About to process message: Test Message!!  
 DEBUG: [Sep-16 22:59:30,053] spring.jms.TestMessageSender - About to put message on queue. Queue[queue://TestQueueTwo] Message[Test Message!!]  
  • Now take a look at the number of messages on TestQueueTwo - it should now have 100 pending messages. These are the 100 messages that were consumed from TestQueueOne and pushed onto TestQueueTwo by our application. See screenshot below.  
Figure 6.0 Messages pushed to TestQueueTwo
Summary
The sample code in this post should be enough to familiarise you with the fundamentals of JMS messaging with Spring. Although the code and sample configurations are very simple they should act as a basic guide and get you moving in the right direction. If you're interested in learning more I'd recommend you look at the Spring documentation so that you can read up on some of the more advanced messaging features available. If you want to run this tutorial locally you can grab the full source code here https://docs.google.com/folder/d/0B_SZOyniHfc1YXE0M3BER242X28/edit
Enjoy!

Sunday, 2 September 2012

Spring Quartz Tutorial

In this post I’ll show you how to use Quartz scheduling with a Spring application. Quartz can be easily integrated with Spring to invoke schedule tasks that call your existing business logic. One of the things I really like about Springs support for Quartz is that you don't have to write any Quartz specific code or amend your existing code to use Quartz scheduling. You simply configure Quartz alongside your existing bean definitions and point it at the logic that you'd like to invoke.

Technologies Used
Our Spring/Quartz example will be built as a stand alone Java application using the following.
  • Spring 3.1
  • Maven
  • Quartz 1.6
It is assumed that readers have a basic knowledge of Spring and Maven. To help you get an example running quickly I’ll add a link to the full source code at the bottom of this post. Once you download the Maven project simply import it into Eclipse and run a Maven build.

Creating a simple Project
We'll start off by creating a very simple Spring project like the one shown in figure 1.0 below.
Figure 1.0 - Project Structure
The main project components are described below.

  • src/main/java folder - this folder structure will contain our Java source. We'll have just 2 classes - TestsService.java and RunTest.java, both of which are defined below.
  • src/main/resources folder - this folder will contain our Spring and log4J configuration files.
Creating a Simple Service
We'll start off by creating a class containing a simple service method that we'll invoke with Quartz. The testServiceMethod method is very simple indeed and just logs out the current time - this will allow us to see exactly when the method is invoked by Quartz. The important thing to note is that this code is not coupled to Quartz in any way - it's a simple POJO. This means that you can integrate Quartz with your application and configure it to invoke existing code without making any changes to the code whatsoever. The service class is defined below.
 package com.blog.samples.quartz;  
   
 import java.util.Date;  
 import org.apache.log4j.Logger;  
 import org.springframework.stereotype.Service;  
   
 @Service  
 public class TestService  
 {  
      private static final Logger logger_c = Logger.getLogger(TestService.class);  
   
      public void testServiceMethod()  
      {  
           Date date = new Date();  
           logger_c.debug("test service method invoked: " + date.toString());  
      }  
 }  
Defining Spring/Quartz Configuration
Next we'll look at the Spring configuration and setting up Quartz in particular. I've explained the various bean configurations with comments below.
 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
           xmlns:context="http://www.springframework.org/schema/context"  
           xsi:schemaLocation="http://www.springframework.org/schema/beans  
                                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                                    http://www.springframework.org/schema/context  
                                    http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
      <!--  
           Our test service bean  
      -->  
      <bean id="testService" class="com.blog.samples.quartz.TestService"/>  
   
      <!--  
            Job Detail bean configuration specifies the target object (our service object defined above)  
            and the method we want to invoke on that object (testServiceMethod). The concurrent property  
            specifies whether or not multiple instances of this job can be invoked concurrently  
      -->  
   <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
     <property name="targetObject" ref="testService" />  
     <property name="targetMethod" value="testServiceMethod" />  
     <property name="concurrent" value="false" />  
   </bean>  
   
      <!--  
           The cron trigger bean allows us to specify the job that we want to invoke (jobDetail above)  
           and a cron expression that defines when the job should be invoked. My configuration below  
           will be invoked every 10 seconds  
      -->  
   <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
     <property name="jobDetail" ref="jobDetail" />  
     <property name="cronExpression" value="0,10,20,30,40,50 * * * * ?" />  
   </bean>  
   
      <!--   
           The SchedulerFactoryBean takes a list of cron triggers - our example has just one   
           cron trigger but larger enterprise applications will typically have a number of different   
           cron trigger for different jobs. The quartz properties property allows you to specify   
           some specific quartz properties. In our simple example we tell Quartz not to check for updates    
      -->  
   <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
     <property name="triggers">  
       <list>  
         <ref bean="cronTrigger" />  
       </list>  
     </property>  
     <property name="quartzProperties">  
                <props>  
            <prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>  
          </props>  
     </property>  
   </bean>  
   
 </beans>  
Creating a Test Class
Next we'll create a test class to run our Quartz application. This is a simple class with a main method that loads the spring application context from the classpath. Once the application context is loaded we sleep the thread for 100 seconds so as to allow the Quartz scheduler to run and invoke our service method. Our Quartz cron definition was set up to run every 10 seconds so we should see our service method called 10 times before our application context is closed and the application shuts down. Our test class is defined below.
 package com.blog.samples.quartz;  
   
 import org.apache.log4j.Logger;  
 import org.springframework.context.support.ClassPathXmlApplicationContext;  
   
 public class RunTest  
 {  
      private static final Logger logger_c = Logger.getLogger(RunTest.class);  
   
      public static void main (String [] args)  
      {  
           logger_c.debug("loading spring application context");  
           ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");  
   
           try  
           {  
                /* sleep thread */  
                Thread.sleep(100000);  
           }  
           catch (InterruptedException ex)  
           {  
                logger_c.debug(ex);  
           }  
   
           /* close down spring application context */  
           applicationContext.stop();  
           logger_c.debug("exiting...");  
      }  
 }  
Running the Application
When we run the main method above we see the following output to the console.You'll see that our service method is invoked by Quartz exactly every 10 seconds as expected.
 DEBUG: [Sep-02 13:27:58,334] samples.quartz.RunTest - loading spring application context  
 INFO : [Sep-02 13:27:58,578] context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@314c194d: startup date [Sun Sep 02 13:27:58 BST 2012]; root of context hierarchy  
 INFO : [Sep-02 13:27:59,552] quartz.core.QuartzScheduler - Quartz Scheduler v.1.6.0 created.  
 INFO : [Sep-02 13:27:59,556] quartz.simpl.RAMJobStore - RAMJobStore initialized.  
 INFO : [Sep-02 13:27:59,556] quartz.impl.StdSchedulerFactory - Quartz scheduler 'org.springframework.scheduling.quartz.SchedulerFactoryBean#0' initialized from an externally provided properties instance.  
 INFO : [Sep-02 13:27:59,556] quartz.impl.StdSchedulerFactory - Quartz scheduler version: 1.6.0  
 INFO : [Sep-02 13:27:59,560] quartz.core.QuartzScheduler - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@5773ec72  
 INFO : [Sep-02 13:27:59,578] context.support.DefaultLifecycleProcessor - Starting beans in phase 2147483647  
 INFO : [Sep-02 13:27:59,579] scheduling.quartz.SchedulerFactoryBean - Starting Quartz Scheduler now  
 INFO : [Sep-02 13:27:59,579] quartz.core.QuartzScheduler - Scheduler org.springframework.scheduling.quartz.SchedulerFactoryBean#0_$_NON_CLUSTERED started.  
 DEBUG: [Sep-02 13:28:00,048] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:00 BST 2012  
 DEBUG: [Sep-02 13:28:10,024] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:10 BST 2012  
 DEBUG: [Sep-02 13:28:20,018] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:20 BST 2012  
 DEBUG: [Sep-02 13:28:30,014] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:30 BST 2012  
 DEBUG: [Sep-02 13:28:40,021] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:40 BST 2012  
 DEBUG: [Sep-02 13:28:50,013] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:50 BST 2012  
 DEBUG: [Sep-02 13:29:00,020] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:00 BST 2012  
 DEBUG: [Sep-02 13:29:10,020] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:10 BST 2012  
 DEBUG: [Sep-02 13:29:20,027] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:20 BST 2012  
 DEBUG: [Sep-02 13:29:30,018] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:30 BST 2012  
 INFO : [Sep-02 13:29:39,583] context.support.DefaultLifecycleProcessor - Stopping beans in phase 2147483647  
 INFO : [Sep-02 13:29:39,587] quartz.core.QuartzScheduler - Scheduler org.springframework.scheduling.quartz.SchedulerFactoryBean#0_$_NON_CLUSTERED paused.  
 DEBUG: [Sep-02 13:29:39,590] samples.quartz.RunTest - exiting...  
Conclusion
The sample code in this tutorial is very simple indeed, but should provide you with the information required to get Quartz scheduling up and running within a Spring application. If you want to run this tutorial locally you can grab the full source code here https://docs.google.com/open?id=0B_SZOyniHfc1M0FDeW4tT0J1SWs.