Section 8.3. Working with JMS

team bbl


8.3. Working with JMS

In this example, you'll learn how to communicate asynchronously with JMS. You'll send a new reservation to the billing system, so that the system can bill the customer. We'll focus on billing (the producer side of the equation), but Spring 1.2 intends to support a robust, pooled, and transactional message consumer architecture as well.

Many architects have hailed JMS as one of the best J2EE specifications. It provides a clean separation between the specification interface and pluggable implementations. It provides for a variety of payload structures, and distinct messaging models for peer-to-peer communication and publish-subscribe style communication. In this section, you're going to integrate JMS with Spring.

8.3.1. How do I do that?

The first job is to configure JMS. To do so, you'll need a JMS provider. We chose ActiveMQ (http://activemq.codehaus.org) because it is open source and its authors have provided excellent Spring support, but other implementations should work as well. Download the application, and then launch the JMS provider by navigating to the /bin folder and issuing the activemq command.

In the context, you'll need to configure a connection factory to manage the JMS connection, as in Example 8-9.

Example 8-9. RentABike-servlet.xml
<bean  >    <property name="brokerURL">       <value>vm://localhost</value>    </property> </bean>

Next, you'll need a JMS transaction manager, which plugs into the Spring transaction architecture (Example 8-10).

Example 8-10. RentABike-servlet.xml
<bean  >    <property name="connectionFactory">       <ref local="jmsConnFactory"/>    </property> </bean>

You'll also need to create your consumer method. It's going to use a JMS template. Call it BillingManager, and then front it with a façade. This class will use a JMS template (Example 8-11).

Example 8-11. RentABike-servlet.xml
<bean  >    <property name="connectionFactory">       <ref local="jmsConnFactory"/>    </property> </bean>

You'll need to change the façade to notify the queue manager whenever you create a new reservation (Example 8-12).

Example 8-12. HibRentABike.java
public void addReservation(Reservation reservation, double amount)     throws AddReservationException {    try {       MonetaryTransaction tx = new MonetaryTransaction(amount,           reservation.getReservationId( ));       getHibernateTemplate( ).saveOrUpdate(reservation);       accountsFacade.addTx(tx);       jmsTemplate.send("billingQueue", new MessageCreator( ) {          public Message createMessage(javax.jms.Session session)              throws JMSException {             return session.createTextMessage("New Reservation: " +                    reservation.toString( ));          }        });    } catch (Exception ex) {       throw new AddReservationException( );    } }

Finally, wire it into your façade with a new method and dependency injection. Example 8-13 shows the new property on the façade.

Example 8-13. HibRentABike.java
private JmsTemplate jmsTemplate;     public JmsTemplate getJmsTemplate( ) {    return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) {    this.jmsTemplate = jmsTemplate; }

And Example 8-14 gives the modification to the façade's configuration.

Example 8-14. RentABike-servlet.xml
<bean  >    <property name="storeName"><value>Bruce's Bikes</value></property>    <property name="sessionFactory"><ref local="sessionFactory"/></property>    <property name="transactionManager">       <ref local="transactionManager"/>    </property>    <property name="jmsTemplate">       <ref local="billingJmsTemplate"/>    </property> </bean>

You can build a standalone consumer (Example 8-15) so you can watch objects as the application creates them (heavily influenced by the sample receiver in the ActiveMQ download).

Example 8-15. StandaloneListener.java
import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueReceiver; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; public class StandaloneListener {     public static void main(String[] args) {         String queueName = "billingQueue";         Context jndiContext = null;         QueueConnectionFactory queueConnectionFactory = null;         QueueConnection queueConnection = null;         QueueSession queueSession = null;         Queue queue = null;         QueueReceiver queueReceiver = null;         TextMessage message = null;         try {             jndiContext = new InitialContext( );         }         catch (NamingException e) {             System.out.println("Could not create JNDI API " +                     "context: " + e.toString( ));             System.exit(1);         }                  try {             queueConnectionFactory = (QueueConnectionFactory)                     jndiContext.lookup("QueueConnectionFactory");             queue = (Queue) jndiContext.lookup(queueName);         }         catch (NamingException e) {             System.out.println("JNDI API lookup failed: " +                     e.toString( ));             System.exit(1);         }         try {             queueConnection =                     queueConnectionFactory.createQueueConnection( );             queueSession =                     queueConnection.createQueueSession(false,                             Session.AUTO_ACKNOWLEDGE);             queueReceiver = queueSession.createReceiver(queue);             queueConnection.start( );             while (true) {                 Message m = queueReceiver.receive(1);                 if (m != null) {                     if (m instanceof TextMessage) {                         message = (TextMessage) m;                         System.out.println("Reading message: " +                                 message.getText( ));                     }                     else {                         break;                     }                 }             }         }         catch (JMSException e) {             System.out.println("Exception occurred: " +                     e.toString( ));         }         finally {             if (queueConnection != null) {                 try {                     queueConnection.close( );                 }                 catch (JMSException e) {                 }             }         }     } }

And you're ready to let it rip.

8.3.2. What just happened?

JMS manages queues through templates. Think of a template as a default implementation for the things that you might want to do to a JMS queue, like compose a message. The changes in the context specified your JMS transaction strategy and configured your connection.

You then built the BillingManager class to create a JMS message for each reservation. The application invokes your new method whenever a reservation is created. The JMS template then creates a message. The JMS destinations get handled automatically in the template.

    team bbl



    Spring. A developer's Notebook
    Spring: A Developers Notebook
    ISBN: 0596009100
    EAN: 2147483647
    Year: 2005
    Pages: 90

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net