8.5. ShipOrder Message-Driven Bean

Asynchronous messages are useful for instigating tasks that take a long time to complete. The sender doesn't have to wait for a message to be delivered or for a receiver to carry out the subsequent request. Furthermore, loose coupling between senders and receivers makes systems more flexible as requirements change.

In this section we'll show you a JMS client producer that uses PTP messaging to interact with our online Music Collection Order processing system. This client, OrderApp, sends a message to a ShipOrder message-driven bean (ShipOrderBean). Upon receipt, the message-driven bean invokes a business method (shipOrdersByDate()) in our CustomerSession EJB. Recall that this method ships all orders with an order date equal to or earlier than its Calendar argument. The message bean invokes shipOrdersByDate() with an order date received from the client in the message.

Figure 8-10 shows the component diagram. The client and ShipOrder message bean use a queue for the PTP messaging. In this diagram we don't show any web components or any of our other EJBs (MusicCart, MusicPage, MusicIterator), since they do not interact with the message bean.

Figure 8-10. Architectural Overview of the Music Shopping Cart Enterprise Application with Added Components ShipOrder MDB and OrderApp JMS Client (Producer)


Note that the client is completely decoupled from these EJB components. The client also has no knowledge of the CustomerSession EJB, no knowledge of any entity beans (Customer EJB, Order EJB, or LineItem EJB), and doesn't interact with any underlying database structures. This decoupling is a direct result of having the client interact with the JMS server asynchronously. The message bean, on the other hand, invokes the EJB business method shipOrdersByDate() synchronously. Although our message bean doesn't do anything that takes a long time, it could easily be extended to perform more lengthy tasks, like sending e-mails to customers or writing data to log files.

OrderApp Client

The OrderApp producer is a stand-alone Java application client. In its constructor, OrderApp sets up a queue as the message destination and connects to the JMS server with a QueueConnection. Its sendMessage() method creates an ObjectMessage containing a Calendar object argument before sending it to the queue destination. Inside main(), OrderApp creates a Calendar object with today's date and calls sendMessage() with the date as an argument. Listing 8.8 contains the source code for OrderApp.java.

Listing 8.8. OrderApp.java
// OrderApp.java - Client for ShipOrder Bean
import java.io.*;
import java.util.*;
import javax.jms.*;
import javax.naming.*;

public class OrderApp {
  private QueueConnection connect = null;
  private QueueSession sender;
  private QueueSender qsender;

  public OrderApp() {
    try {
      InitialContext jndi = new InitialContext();
      QueueConnectionFactory factory =
        (QueueConnectionFactory)jndi.lookup
           ("java:comp/env/jms/MyQueueConnectionFactory");
      connect = factory.createQueueConnection();
      Queue queue = (Queue)jndi.lookup
        ("java:comp/env/jms/MyQueue");
      sender = connect.createQueueSession(false,
          Session.AUTO_ACKNOWLEDGE);
      qsender = sender.createSender(queue);
    } catch (Exception ex) {
      ex.printStackTrace();
      System.exit(1);
    }
  }

  public void sendMessage(Calendar date) {
    try {
      ObjectMessage msg = sender.createObjectMessage();
      msg.setObject(date);
      qsender.send(msg);
      System.out.println("Sent Ship date: " +
               date.getTime());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public void exit() {
    try {
      if (connect != null)
      connect.close();
    } catch (Exception ex) {
      ex.printStackTrace();
      System.exit(1);
    }
    System.exit(0);
  }

  public static void main(String args[]) {
    OrderApp order = new OrderApp();
    try {
      Calendar date = new GregorianCalendar();
      order.sendMessage(date);
      order.exit();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

ShipOrder Message Bean

The ShipOrder message bean (ShipOrderBean.java) is shown in Listing 8.9. ShipOrderBean has two tasks. First, it must implement the onMessage() method to receive messages. Second, the bean must create a CustomerSession object to call the shipOrdersByDate() method in the Customer EJB.

The ejbCreate() method invokes the necessary lookup() and PortableRemoteObject.narrow() methods to obtain a CustomerSessionHome object. It also creates a CustomerSession remote interface object in instance variable customer. Since CustomerSession EJB is a stateless session bean, we don't have to worry about tying up client-specific resources. Note that ejbCreate() does not perform a JNDI lookup for the queue used by the bean. The container performs this task by reading the message bean's deployment descriptor, as you will see shortly.

Inside onMessage(), ShipOrderBean extracts the Calendar object from the received message and calls shipOrdersByDate() with the date as an argument. (See page 344 for the source for CustomerSession EJB's shipOrdersByDate() method.)

Recall that message beans cannot throw application exceptions, since there are no clients that use a message bean directly. If errors occur while processing the message, a message bean could use the JMSReplyTo destination (queue or topic) to report errors. This type of error handling is left as an exercise for the reader.

Listing 8.9. ShipOrderBean.java
// ShipOrderBean.java - Ship Order Message-Driven Bean
import java.io.*;
import java.util.*;
import java.rmi.*;
import javax.ejb.*;
import javax.naming.*;
import javax.jms.*;
import javax.rmi.PortableRemoteObject;
public class ShipOrderBean implements
    MessageDrivenBean, MessageListener {

  private CustomerSession customer;
  private MessageDrivenContext messageContext;

  public void
  setMessageDrivenContext(MessageDrivenContext mdc) {
    messageContext = mdc;
  }

  public void ejbCreate() {
    try {
      InitialContext jndi = new InitialContext();
      Object objref = jndi.lookup(
              "java:comp/env/ejb/CustomerSession");

      // Get Customer EJB home interface object
      CustomerSessionHome home = (CustomerSessionHome)
                PortableRemoteObject.narrow(objref,
               CustomerSessionHome.class);
      customer = home.create();     // create Customer EJB
    } catch (Exception ex) {
      throw new EJBException(ex);
    }
  }

  public void ejbRemove() { }

  public void onMessage(Message message) {
    try {
      ObjectMessage msg = (ObjectMessage)message;
      Calendar shipDate = (Calendar)msg.getObject();

      System.out.println("MDB received message");
      System.out.println("	" + shipDate.getTime());
      // call shipOrdersByDate() in Customer EJB
      customer.shipOrdersByDate(shipDate);
      System.out.println("Shipped Orders.");

    } catch (Exception ex) {
      throw new EJBException(ex);
    }
  }
}

Deployment Descriptors

Let's examine the deployment descriptor for the application client (OrderApp) first. Listing 8.10 contains the XML file. The deployment descriptor contains the necessary resource references and resource environment references for the message queue used by the OrderApp client and the ShipOrderBean.

Listing 8.10. Deployment Descriptor for OrderApp Client
<application-client>
 <display-name>OrderApp</display-name>
 <resource-ref>
    <res-ref-name>jms/MyQueueConnectionFactory
    </res-ref-name>
    <res-type>javax.jms.QueueConnectionFactory</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
 </resource-ref>
 <resource-env-ref>
    <resource-env-ref-name>jms/MyQueue
    </resource-env-ref-name>
    <resource-env-ref-type>javax.jms.Queue
    </resource-env-ref-type>
 </resource-env-ref>
</application-client>

Listing 8.11 shows the deployment descriptor for ShipOrderBean, whose display name is ShipOrderEJB. We use container-managed transactions and assign transaction attribute Required for method onMessage(). This assures that all messages are received and handled within a transaction. The container uses the queue destination type (javax.jms.Queue) to create a queue receiver for the message. The ShipOrderBean also references the CustomerSession EJB using the remote and remote home interfaces.

Listing 8.11. Deployment Descriptor for ShipOrderBean
<ejb-jar>
 <display-name>ShipOrderJAR</display-name>
 <enterprise-beans>
    <message-driven>
      <display-name>ShipOrderEJB</display-name>
      <ejb-name>ShipOrderEJB</ejb-name>
      <ejb-class>ShipOrderBean</ejb-class>
      <transaction-type>Container</transaction-type>
      <message-driven-destination>
        <destination-type>javax.jms.Queue
        </destination-type>
      </message-driven-destination>

       <ejb-ref>
        <ejb-ref-name>ejb/CustomerSession</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <home>CustomerSessionHome</home>
        <remote>CustomerSession</remote>
        <ejb-link>CustomerSessionBean</ejb-link>
      </ejb-ref>

     <security-identity>
        <description></description>
        <run-as>
          <description></description>
          <role-name></role-name>
        </run-as>
      </security-identity>
    </message-driven>
  </enterprise-beans>

 <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>ShipOrderEJB</ejb-name>
        <method-intf>Bean</method-intf>
        <method-name>onMessage</method-name>
        <method-params>
<method-param>javax.jms.Message</method-param>
        </method-params>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
 </assembly-descriptor>
</ejb-jar>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.25.112