CHAPTER 5

image

EJB Message-Driven Beans

This chapter discusses the need for message-oriented architecture. It introduces Java Message Service (JMS), the typical architecture for messaging applications, and details the concepts behind EJB message-driven beans (MDBs). The chapter also covers annotations, dependency injection, and interceptors in relation to MDBs.

Message-Oriented Architecture

Today’s IT organizations have dozens of applications and services that perform such well-defined tasks as inventory, billing, expense reporting, and order entry. With the evolution of the Internet and e-business, enterprises have started to think about how different applications can work independently but still be a part of an information workflow process at the same time.

This new demand brings us to the concept of integrating existing applications, as well as building new applications that work coherently with existing applications. Integrating existing applications with new applications is a very complex task: first due to the large number of applications used by most enterprises, and second because of their complex business workflow.

Messaging is one of the most viable solutions for integrating existing and new applications in an asynchronous communication and loosely coupled transaction model. Asynchronous messaging allows applications to communicate by exchanging messages independently without them having to be hardwired to each other. An application or business process sending a message does not have to wait for the receiver as long as both sender and receiver understand and agree upon a message format and an intermediate destination.

What Is JMS?

JMS is a Java Message-Oriented Middleware (MOM) API that allows applications to send and receive messages asynchronously. JMS is part of the standard Java EE API as defined by JSR 914. JMS is analogous to JDBC (Java Database Connectivity), which provides a standard API to connect to several types of databases (Oracle, DB2, MySQL). Likewise, JMS provides a standard API to connect to several types of messaging systems (IBM MQ, SonicMQ).

JMS architecture consists of the following:

  • JMS provider: A messaging system (as shown in Figure 5-1) that handles the routing and delivery of messages. A JMS provider can be a messaging component of an application server (such as Oracle WebLogic Server, IBM WebSphere, Oracle GlassFish Server). JMS providers are also known as JMS servers.

    9781430246923_Fig05-01.jpg

    Figure 5-1.  A JMS messaging system

  • JMS client: Any Java application or Java EE component that uses the JMS API either to consume or produce a JMS message.
  • JMS consumer: A JMS client application that consumes JMS messages. The inventory, billing, and shipping applications shown in Figure 5-1 are JMS message consumers.
  • JMS producer: A JMS client that generates the message. The order entry application shown in Figure 5-1 is a JMS message producer.
  • JMS message: A message consisting of a header, properties, and a body. The header identifies the message, the properties provide additional attributes that are specific to the application and provider, and the body contains the content of the message. The JMS specification provides support to send and receive different types of messages. Table 5-1 shows the message types and descriptions.

    Table 5-1. JMS Message Types

    Message Type Description
    ByteMessage Consists of a series of bytes
    MapMessage Consists of a set of name/value pairs
    ObjectMessage Consists of a serialized Java object
    StreamMessage Consists of a sequence of primitive data types
    TextMessage Consists of strings

Messaging Application Architecture

Generally, two different classes of messaging applications exist:

  • The point-to-point (P2P) model
  • The publish-subscribe (pub-sub) model

The P2P model is based on message queues, where a queue holds the JMS messages sent by the JMS client application. Message producers and consumers decide upon a common queue to exchange messages.

The P2P model is used if there is one and only one message consumer for each message. For example, the order entry system shown in Figure 5-2 sends a new order into the message queue, which is picked up by the inventory system. Similarly, the message sent by the inventory system is consumed by the shipping system, and the message from the shipping system is consumed by the billing system.

9781430246923_Fig05-02.jpg

Figure 5-2.  A JMS messaging system using queues

The pub-sub model is based on topics, where the topic is the destination address of the message. Multiple recipients or JMS consumers can retrieve each message. In this model, publishers are not always aware of possible subscribers. The pub-sub model is used for broadcast-type applications, as shown in Figure 5-3, in which a message is delivered for more than one JMS client. Topics, each having a unique name, are defined in the messaging server. Each message, with its associated subject, gets published and delivered to all subscribers.

9781430246923_Fig05-03.jpg

Figure 5-3.  A JMS messaging system using topics

Using MDBs

An MDB is an asynchronous message consumer that processes messages delivered via JMS. While MDBs do the job of processing the messages, the EJB container in which the MDBs run take care of the services (transactions, security, resources, concurrency, message acknowledgment), letting the bean developer focus on the business logic of processing messages. Traditional JMS applications would have to custom write some of these services. MDBs are stateless in nature, which means that EJB containers can have numerous instances of MDBs execute concurrently to process hundreds of JMS messages coming in from various applications or JMS producers and also provide quality of service (QoS), such as high availability and reliability for enterprise applications.

EJB client applications cannot access MDBs directly as they can with session beans and entities. The only way to communicate with an MDB would be by sending a JMS message to the destination to which the MDB is listening. Any Java application or Java EE component using the JMS API can be the message provider for the MDB by sending messages to queues or topics.

When Do You Use MDBs?

Earlier in the chapter, we discussed the need for asynchrony in enterprises. Asynchronous messaging provides loose coupling between applications, systems, and services, thus providing greater flexibility and change management for applications and systems. MDBs provide a standard messaging component model that achieves the goal of asynchronous and message-oriented architecture in enterprises.

Figure 5-4 shows a message-oriented application that has order entry, inventory, billing, and shipping systems that communicate asynchronously to handle a workflow that starts with a new purchase order and ends when the order gets shipped to the customer. An order entry system captures a new order from a customer, processes the order, and sends it into a designated message queue (in Figure 5-4, this is the New Order queue). The inventory system picks up the message from the queue and checks whether or not the inventory is available. If not, it sends a message to the Suppliers queue; if the order can be shipped, then it puts a message into the Order Ready queue. This new message is picked up by the billing system, which processes the billing for the customer and puts a message back into the Shipping queue. Finally, the shipping application picks up the message, gets the order shipped to the customer, and sends an e-mail to the customer with tracking information.

9781430246923_Fig05-04.jpg

Figure 5-4.  An order-to-shipping JMS messaging system

MDB Classes

Unlike a session bean, an MDB doesn’t have any business interfaces. It has only the following:

  • A message-driven class
  • An optional callback listener class
  • An optional interceptor class

An MDB class is any standard Java class that has a class-level annotation @MessageDriven. If deployment descriptors are used instead of annotations, the bean class should be denoted as an MDB class. In the case of mixed mode, in which you are using annotations and deployment descriptors, the @MessageDriven annotation must be specified if any other class-level or member-level annotations are specified in the bean class. The @MessageDriven annotation parameters can be used to specify the JMS queues or topics to which the bean is listening. Table 5-2 details the parameters.

Table 5-2. Parameter details for the @MessageDriven Annotation

Parameter Description
ActivationConfigProperty The set of properties used to specify the destination name and type
description A description of the bean class
mappedName The physical Java Naming and Directory Interface (JNDI) name of the topic or queue to which the MDB is listening
messageListener The interface name of the interface class that the MDB is extending
name The name of the MDB, if it has to be a different name than the bean class

To illustrate the use of an MDB, we will create the use case shown in Figure 5-5. We will have an application client, which will be a Java command-line program that invokes a business method in the OrderProcessing session bean. The OrderProcessing session bean will create and send a JMS message to a topic registered/configured in the GlassFish application server. An MDB, StatusMailer, will listen to the topic and process the incoming message. The message received will contain details for the customer, and it will be used to send an e-mail notification to the customer regarding his or her order status. This simple use case will allow us to demonstrate how MDBs work and how to inject different types of resources in session beans and MDBs.

9781430246923_Fig05-05.jpg

Figure 5-5.  A sample MDB use case

Listing 5-1 shows the definition of a StatusMailer MDB. We have marked the StatusMailerBean class with the @MessageDriven annotation.

Listing 5-1.  StatusMailerBean.java

package com.apress.ejb.chapter05;
import javax.ejb.MessageDriven;
@MessageDriven
public class StatusMailerBean {
}

An MDB class has one method, onMessage(), that gets invoked by the EJB container on the arrival of a message in the queue/topic to which the MDB is listening. The onMessage() method contains the business logic on how to process the incoming message. The onMessage() method contains one parameter, which is the JMS message. In the case of the StatusMailer bean, the onMessage() method checks whether the message is of MapMessage type, and then it gets the customer information from the message, creates an e-mail message about the order status, and then sends an e-mail to the customer. Listing 5-2 shows the onMessage() method code. In a try block, we start by checking whether the message received is of type MapMessage, as we are expecting. If it is, then we use getStringProperty() to retrieve the values of the from, to, subject, and content attributes in the message.

Listing 5-2.  The onMessage Method Code

package com.apress.ejb.chapter05;
import javax.ejb.MessageDriven;
@MessageDriven
public class StatusMailerBean {
    public void onMessage(Message message){
        try  {
            if (message instanceof MapMessage) {
                MapMessage orderMessage = (MapMessage)message;
                String from = orderMessage.getStringProperty("from");
                String to = orderMessage.getStringProperty("to");
                String subject = orderMessage.getStringProperty("subject");
                String content = orderMessage.getStringProperty("content");
             }
            else {
                System.out.println("Invalid message ");
                }
        } catch (Exception ex)  {
            ex.printStackTrace();
        }
     }
}

In addition to marking the standard Java class with the @MessageDriven annotation, the following requirements apply to an MDB class:

  • The MDB class must implement the message listener interface. In the case of JMS, this will be javax.jms.MessageListener.
  • The class cannot be final or abstract.
  • The class should have a no-argument public constructor that is used by the EJB container to create instances of the bean class.

If both annotations and deployment descriptors are used, the settings or values in the deployment descriptor will override the annotations in the classes during the deployment process.

Configuration Properties

Bean developers can provide configuration properties along with MDB classes, which get used at deployment time. The EJB container uses these properties to configure the bean and link it to the appropriate JMS provider. These configuration properties can be set using the @ActivationConfigProperty annotation. This annotation can be provided as one of the parameters for the @MessageDriven annotation. Listing 5-3 shows the @MessageDriven annotation with properties for the StatusMailer MDB. We have defined two ActivationConfigProperty annotations that specify the logical destination name and the destination type.

Listing 5-3.  The @MessageDriven Annotation with Properties for the StatusMailer MDB

@MessageDriven(activationConfig= {
@ActivationConfigProperty(propertyName="destinationName",
propertyValue="StatusMessageTopic"),
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Topic")
}, mappedName="StatusMessageTopic")

public class StatusMailerBean implements javax.jms.MessageListener {

}

Standard configuration properties available for MDBs in EJB version 3.0 and 3.1 correspond to the configuration properties in JMS version 1.1. Standard configuration properties for MDBs in EJB version 3.2 have been extended to correspond to the configuration properties in JMS version 2.0. Table 5-3 shows the correspondence between the EJB version and JMS version.

Table 5-3. Mapping of EJB MDB version to JMS version

EJB Version JMS Version
EJB 3.0 JMS 1.1
EJB 3.1 JMS 1.1
EJB 3.2 JMS 2.0

In the following sections, we will show what configuration properties can be set for MDBs.

Message Acknowledgment

The EJB container provides a message acknowledgment service. There are two message acknowledgment modes:

  • Auto-acknowledge
  • Dups-ok-acknowledge

In the case of Auto-acknowledge, the message delivery acknowledgment happens after the onMessage() method. This property is useful for applications that require no duplicate messages. For example, a new order should be received by the inventory system once and only once. In the case of Dups-ok-acknowledge, the acknowledgment is done lazily, which means that there might be duplicate delivery of messages, but it reduces the overhead for the session in terms of immediate acknowledgement. For example, an e-mail message that gets sent out during the order process can possibly allow duplicate messages. We can use the @ActivationConfigProperty annotation to specify the message acknowledgment property. Listing 5-4 shows the property set to allow duplicates.

Listing 5-4.  The @ActivationConfigProperty Annotation

@MessageDriven(
activationConfig= {
@ActivationConfigProperty(propertyName="acknowledgeMode",
propertyValue="Dups-ok-acknowledge")}
)

The Message Selector

The message selector allows filtering of incoming messages based on the selection criteria provided by the bean developer using the @ActivationConfigProperty annotation. This property is useful for restricting the messages that the bean receives. For example, the MDB that processes the incoming orders might only process orders pertaining to red and white wines. The property name used to specify is messageSelector.

Message Destination

The message destination describes whether the MDB listens on a queue or topic. Bean developers can provide the description in the bean using the @ActivationConfigProperty annotation. The value of the property must be either javax.jms.Queue or javax.jms.Topic. For example, a new order may need to be processed by an inventory system as a next step in the workflow; in this case, the order entry system doesn’t have to broadcast the new order message. Both the order entry and inventory system can agree on a particular destination. Listing 5-3 shows the code to specify destination name and type.

Subscription Durability

If the bean is designed to listen to a topic, then the bean developer can further specify the durability of the message. The topic can be either Durable or Non-Durable. Usage of Durable topics ensures reliability for the applications. They ensure that messages are not missed, even if the EJB container is temporarily offline. For example, we may need to make sure that the new purchase orders received from client applications are not lost if the EJB container goes down. All purchase orders have to be reliably processed by the MDBs. We can use the @ActivationConfigProperty annotation to specify the durability using the subscriptionDurability property. Listing 5-5 shows the code to set the property to Durable. If this property is not set, the container will assume the default of Non-Durable.

Listing 5-5.  The Code to Set the Property to Durable

@MessageDriven(
activationConfig= {
@ActivationConfigProperty(propertyName="subscriptionDurability", propertyValue="Durable")}
)

In the StatusMailer MDB, we will create properties using the @ActivationConfigProperty annotation. The message’s destinationName is set to StatusMessageTopic, and destinationType is set to javax.jms.Topic. We will use the mappedName parameter to specify the physical destination name of the topic. In our case, it is the same as destinationName. Listing 5-6 shows the StatusMailer MDB in its current state of completion.

Listing 5-6.  StatusMailerBean.java

package com.apress.ejb.chapter05;
import javax.ejb.ActivationConfigProperty;
import javax.jms.Message;
import javax.ejb.MessageDriven;
import javax.jms.MapMessage;

@MessageDriven(activationConfig= {
@ActivationConfigProperty(propertyName="destinationName", propertyValue="StatusMessageTopic"),
@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Topic")
}, mappedName="StatusMessageTopic")
public class StatusMailerBean implements javax.jms.MessageListener{
    public void onMessage(Message message){
        try  {
            if (message instanceof MapMessage) {
                MapMessage orderMessage = (MapMessage)message;
                String from = orderMessage.getStringProperty("from");
                String to = orderMessage.getStringProperty("to");
                String subject = orderMessage.getStringProperty("subject");
                String content = orderMessage.getStringProperty("content");
             }
            else {
                System.out.println("Invalid message ");
                }
        } catch (Exception ex)  {
            ex.printStackTrace();
        }
    }
}

Dependency Injection in MDBs

MDBs can use dependency injection to acquire references to resources such as JavaMail, EJBs, or other objects. The resources that an MDB tries to acquire and use must be available in the container context or environment context.

In the sample use case from Figure 5-5, we talked about creating an e-mail after the message is processed and sending the order status to the customer via e-mail. In order to do this in the StatusMailer message bean, we need to acquire a JavaMail session so that we can create an e-mail and send it. JavaMail is an API that provides a platform-independent framework for building mail applications. The JavaMail API is available with the Java EE platform.

We can acquire a JavaMail session in an MDB using dependency injection. Listing 5-7 shows the completed StatusMailer MDB using dependency injection and the JavaMail API. The @Resource annotation is used to inject a JavaMail session with the name mail/wineappMail, which has been registered as a mail resource in the GlassFish application server. The injected mail session is used to create javax.mail.Message, and the setter methods are used to create the headers and content of the mail message. Finally, the send() method in the javax.mail.Transport class is used to send the created message.

Listing 5-7.  The Completed StatusMailer MDB Using Dependency Injection and the JavaMail API

package com.apress.ejb.chapter05;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.jms.Message;
import javax.ejb.MessageDriven;
import javax.jms.MapMessage;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

@MessageDriven(activationConfig= {
@ActivationConfigProperty(propertyName="destinationName",
propertyValue="StatusMessageTopic"),
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Topic")
}, mappedName="StatusMessageTopic")

public class StatusMailerBean implements javax.jms.MessageListener{
@Resource(name="mail/wineappMail" )
private javax.mail.Session ms;

    public void onMessage(Message message){
        try  {
            if (message instanceof MapMessage) {
                MapMessage orderMessage = (MapMessage)message;
                String from = orderMessage.getStringProperty("from");
                String to = orderMessage.getStringProperty("to");
                String subject = orderMessage.getStringProperty("subject");
                String content = orderMessage.getStringProperty("content");
                javax.mail.Message msg = new MimeMessage(ms);
                msg.setFrom(new InternetAddress(from));
                InternetAddress[] address = {new InternetAddress(to)};
                msg.setRecipients(javax.mail.Message.RecipientType.TO, address);
                msg.setSubject(subject);
                msg.setSentDate(new java.util.Date());
                msg.setContent(content, "text/html");
                System.out.println("MDB: Sending Message...");
                Transport.send(msg);
                System.out.println("MDB: Message Sent");
             }
            else {
                System.out.println("Invalid message ");
                }

        } catch (Exception ex)  {
            ex.printStackTrace();
        }

    }
}

Lifecycle Callback Methods

There will be certain instances in which an application that uses MDBs requires fine-grained control. Two lifecycle event callbacks are supported for MDBs:

  • PostConstruct
  • PreDestroy

The PostConstruct callback occurs before the first message listener method invocation on the bean and after the container has performed the dependency injection. The PreDestroy callback occurs when the MDB is removed from the pool or destroyed.

For example, a PostConstruct callback can be used to initialize some attributes or resources, and a PreDestroy callback can be used to clean up or release the acquired resources.

Callback methods defined on an MDB class should have the following signature:

public void <METHOD> ()

Callback methods can also be defined on a bean’s listener class, in which case the methods should have the following signature:

public void <METHOD>(Object)

where Object may be declared as the actual bean type, which is the argument passed to the callback method at run time.

Callback methods can be any methods in the MDB that have callback annotations. The following rules apply to these methods:

  • The method should be public.
  • The method cannot be final or static.
  • The return type should be void.

The methods can take either zero or one argument, as shown previously. A callback listener class is denoted by the @CallbackListener annotation on the MDB class with which it is associated.

Interceptors

The EJB specification provides annotations called interceptors, which allow you to intercept a business method invocation. Interceptor methods can be defined for MDBs.

You can add either an @AroundInvoke annotation or an <around-invoke-method> element in the deployment descriptor for a particular method, or you can define an interceptor class whose methods are invoked before the onMessage() method is invoked in the MDB class. An interceptor class is denoted using the @Interceptor annotation on the MDB class with which it is associated. In the case of multiple interceptor classes, the @Interceptors annotation is used. Only one AroundInvoke method may be present on the bean class or on any given interceptor class. An AroundInvoke method cannot be an onMessage() method of the MDB class.

AroundInvoke methods should have the following signature:

public Object <METHOD>(InvocationContext) throws Exception

The definition of InvocationContext is as follows:

  package javax.ejb;
   public interface InvocationContext {
      public Object getBean();
      public java.lang.reflect.Method getMethod();
      public Object[] getParameters();
      public void setParameters(Object[] params);
      public EJBContext getEJBContext();
      public java.util.Map getContextData();
      public Object proceed() throws Exception;
   }

The following list describes each of the methods:

  • getBean(): Returns the instance of the bean on which the method was called
  • getMethod(): Returns the method on the bean instance that was called
  • getParameters(): Returns the parameters for the method call
  • setParameters(): Allows modification of the parameters for the method call
  • getEJBContext(): Gives the interceptor methods access to the bean’s EJBContext
  • getContextData(): Allows values to be passed between interceptor methods in the same InvocationContext instance using the Map returned
  • proceed(): Invokes the next interceptor if there is one, or invokes the target bean method

Exception Handling

The EJB spec outlines two types of exceptions: application exceptions and system exceptions. For more general information on these exceptions, see the “Exception Handling” section of Chapter 2. In the case of an MDB, the listener method must not throw a java.rmi.RemoteException or, in general, a runtime exception. The client assumes that the message consumer continues to exist even though a runtime exception has occurred. If the client sends a message after a runtime exception is thrown, then the EJB container delegates the messages to a different MDB instance. Also, if you allow an exception to “escape” an MDB, the message isn’t considered to be consumed, and it goes back on the queue/topic. Then the offending message gets redelivered. This is known as the “poison message” problem.

Callback methods can throw runtime exceptions. A runtime exception thrown by a callback method that executes within a transaction causes that transaction to be rolled back. Callback methods must not throw application exceptions.

Client View

To a client application, an MDB is simply a message consumer. A client application can be any Java client of a Java EE component that is using the JMS API to send a message. From the perspective of the client application, the existence of an MDB is completely hidden behind the destination or endpoint for which the MDB is the message listener.

A client’s JNDI namespace may be configured to include the destinations or endpoints of MDBs installed in multiple EJB containers located on multiple machines on a network. The actual locations of an enterprise bean and EJB container are, in general, transparent to the client using the enterprise bean.

References to message destinations can be injected via the @Resource annotation (which is in the javax.annotation package) or via JNDI lookup in cases in which the resource has been defined in the deployment descriptor.

image Note   Starting with EJB 3.2, a JMS resource adapter can construct a subscription name by looking up an MDB using its standard name.

In the use case discussed earlier and shown in Figure 5-5, we have a session bean that is acting as an intermediary between the client application and the message topic. The client application invokes a business method in the session bean, and the session becomes the client or JMS message producer that is creating and sending the message. To illustrate this, we will create a stateless session bean, OrderProcessing, with one business method, SendOrderStatus(). Listing 5-8 shows the code for the OrderProcessing session bean. We are using the @Resource annotation to inject the TopicConnectionFactory and Topic to which the StatusMailer MDB is listening. We will use some hard-coded values in the session bean to simulate the customer e-mail address and the content for the e-mail. In the try block, we create a connection to the statusMessageTopicConnectionFactory and start the connection. Using the created session, we create a topic session and topic producer with the createSession() and createProducer() methods. Finally, we create a MapMessage object, populate the message with the e-mail address, subject, and content, and send the message to the Topic using the send() method.

Listing 5-8.  OrderProcessingBean.java

package com.apress.ejb.chapter05;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnectionFactory;

@Stateless(name = "OrderProcessing")
public class OrderProcessingBean
{
    public OrderProcessingBean() {
    }

    @Resource(mappedName = "StatusMessageTopicConnectionFactory")
    private TopicConnectionFactory statusMessageTopicCF;

    @Resource(mappedName = "StatusMessageTopic")
    private Topic statusTopic;

    public String SendOrderStatus() {
        String from = "[email protected]";
        String to = "[email protected]";
        String content =
        "Your order has been processed " + "If you have questions" +
        " call EJB Application with order id # " + "1234567890";
        
        try {
        System.out.println("Before status TopicCF connection");
        Connection connection = statusMessageTopicCF.createConnection();
        System.out.println("Created connection");
        connection.start();
        System.out.println("statted connection");
        System.out.println("Starting Topic Session");
        Session topicSession =
            connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer publisher = topicSession.createProducer(statusTopic);
        System.out.println("created producer");
        MapMessage message = topicSession.createMapMessage();
        message.setStringProperty("from", from);
        message.setStringProperty("to", to);
        message.setStringProperty("subject", "Status of your wine order");
        message.setStringProperty("content", content);
        System.out.println("before send");
        publisher.send(message);
        System.out.println("after send");
        }
        catch (JMSException e) {
            e.printStackTrace();
        }

        return "Created a MapMessage and sent it to StatusTopic";
    }
}

image Note   In Listing 5-8, update the value of “from” and “to” fields to your e-mail ID.

One last thing we need to do to complete the use case discussed in Figure 5-5 is to come up with the client application that will look up the OrderProcessing session bean and invoke the SendOrderStatus() message. Listing 5-9 shows the code for the client application. In the try block, we are doing a JNDI lookup of the OrderProcessing session bean and calling the SendOrderStatus() business method.

Listing 5-9.  StatusMailerClient.java

package com.apress.ejb.chapter05;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "StatusMailerClient", urlPatterns = {"/StatusMailerClient"})
public class StatusMailerClient extends HttpServlet {
    @EJB
    OrderProcessingBean orderProcessing;
    
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet StatusMailerClient</title>");
            out.println("</head>");
            out.println("<body>");

            out.println("<h1>OrderProcessing session bean lookup to be done</h1>");
            out.println("<h1>Invoking SendOrderStatus() business method now</h1>");
            out.println("<h1>" + orderProcessing.SendOrderStatus() + "</h1>");
            out.println("<h1>Done !!!</h1>");
            out.println("</body>");
            out.println("</html>");
        } finally {
            out.close();
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    public String getServletInfo() {
        return "Short description";
    }
}

In the next section, we will look at compiling, deploying, and running the use case on which we have worked.

Compiling, Deploying, and Testing MDBs

MDBs need to be packaged into EJB JAR (Java Archive) files before they can be deployed into EJB containers. These EJB archives can then be deployed. (For some EJB containers or application servers, they need to be assembled into EAR [Enterprise Archive] files). Most EJB containers or application servers provide deployment utilities or Ant tasks to facilitate deployment of EJBs to their containers. Java-integrated development environments (IDEs) like JDeveloper, NetBeans, and Eclipse also provide deployment features that allow developers to package, assemble, and deploy EJBs to application servers. Packaging, assembly, and deployment aspects are covered in detail in Chapter 11.

In this chapter, we have developed one stateless session bean (OrderProcessing) and one MDB (StatusMailer). JMS providers have to be configured with queues and topics that will be used by the client application and MDB accordingly before the MDBs are deployed.

The following sections describe the steps to compile, deploy, and test these MDBs and session beans.

Prerequisites

Before performing any of the steps detailed in the next sections, complete the “Getting Started” section of Chapter 1, which will walk you through the installation and environment setup required for the samples in this chapter.

Compiling the Session Beans and MDBs

Copy the Chapter05-MDBSamples directory and its contents into a directory of your choice. Run the NetBeans IDE, and open the Chapter05-MDBSamples project using the File -> Open Project menu. Make sure that 'Open Required Projects' checkbox is checked.

9781430246923_Fig05-06.jpg

Figure 5-6.   Opening the Chapter05-MDBSamples project

Expand the Chapter05-MDBSamples-ejb node, and observe that the MDB and the session bean that we created appear in the com.apress.ejb.chapter05 package. Similarly, the two client servlets appear under the Chapter05-MDBSamples-war node.

9781430246923_Fig05-07.jpg

Figure 5-7.   Verifying that the MDB, Session Bean, and their clients are available in the project

Invoke the context menu on Chapter05-MDBSamples node, and build the application by selecting the Clean and Build menu option.

9781430246923_Fig05-08.jpg

Figure 5-8.   Building the application

Creating the JMS and JavaMail Resources

The StatusMailer MDB makes use of JMS and JavaMail resources. Before the Chapter05-MDBSamples application can be deployed to GlassFish, these resources have to be preconfigured. First we will start the GlassFish application server and then configure the JMS and JavaMail resources by using the web-based administrator console. c

Click the 'Services' tab available in the application navigator of NetBeans. 'GlassFish Server 3+' is listed under the 'Servers' node. Invoke the context menu on 'GlassFish Server 3+', and start the server by selecting the Start menu option.

9781430246923_Fig05-09.jpg

Figure 5-9.   Starting the GlassFish application server

After the server has started, open your favorite browser and navigate to http://localhost:4848/. GlassFish server’s administrator console will be loaded as shown in Figure 5-10.

9781430246923_Fig05-10.jpg

Figure 5-10.  Navigating to the administrator console

image Note   If you are running GlassFish on a different machine, substitute that machine name for localhost in the command-line arguments. Similarly, if you are running on a different port, substitute the port number you are running for 4848.

Using the administrator console, as shown in Figure 5-11, create a JMS TopicConnectionFactory named StatusMessageTopicConnectionFactory, which will be used by the OrderProcessing session bean to send a message to the topic that will be consumed by StatusMailer MDB.

9781430246923_Fig05-11.jpg

Figure 5-11.  Creating a TopicConnectionFactory

Then create a JMS topic named StatusMessageTopic as shown in Figure 5-12.

9781430246923_Fig05-12.jpg

Figure 5-12.  Creating the JMS topic

Create a JavaMail resource named mail/wineappMail that will be used by the StatusMailer MDB to send out an e-mail as shown in Figure 5-13.

9781430246923_Fig05-13.jpg

Figure 5-13.  Creating the JavaMail resource

If the SMTP server requires authentication, then set the ‘Additional Properties’ as shown in Figure 5-14.

9781430246923_Fig05-14.jpg

Figure 5-14.  Setting the Additional Properties for Authenticated SMTP Servers

Deploying the Session Beans, MDBs, and their Clients

Once you have configured the JMS and JavaMail resources, you can deploy the application to the GlassFish application server. Invoke the context menu on Chapter05-MDBSamples node, and deploy the application by selecting the Deploy menu option as shown in Figure 5-15.

9781430246923_Fig05-15.jpg

Figure 5-15.  Deploying the MDB, session bean and their clients

Running the Client Programs

Once the MDB, session bean, and their client servlets are successfully deployed, we need to set the run target that we wish to execute. We have a choice of two run targets: StatusMailerClient or StatusMessageClient. To set the run target, invoke the context menu on Chapter05-MDBSamples node, and select the Properties menu option. Select the Run category, enter the run target in Relative URL text field, and click OK. We will set the run target as StatusMailerClient as shown in Figure 5-16.

9781430246923_Fig05-16.jpg

Figure 5-16.  Setting the run target to StatusMailerClient

To run the client servlets, invoke the context menu on Chapter05-MDBSamples node, and select the Run menu option as shown in Figure 5-17.

9781430246923_Fig05-17.jpg

Figure 5-17.  Running the StatusMailerClient application target

Once the StatusMailerClient runs successfully, NetBeans will open your default browser and execute the selected servlet. Here is the output from StatusMailerClient servlet. You should also be able to see an e-mail in the inbox where the message was sent.

9781430246923_Fig05-18.jpg

Figure 5-18.   Output of StatusMailerClient servlet

Conclusion

In this chapter, we introduced you to the concept of message-oriented middleware and why enterprises are looking at loosely coupled applications that can converse in an asynchronous fashion.

We covered message application architecture with the P2P and pub-sub models, and we discussed why messaging is one of the best ways to implement asynchronous applications. We looked at JMS in detail, including different JMS components such as providers, consumers, clients, and different types of messages. We looked at MDBs and the different artifacts that can make them. We covered the different configuration properties of MDBs and how they can be set using annotations. We explained dependency injection in MDBs using the specific example of injecting a JavaMail resource. We discussed what it takes to compile, package, deploy, and test MDBs, along with information on how you can create different types of resources in the GlassFish application server. Finally, we covered running sample client programs using the application client container in GlassFish, and viewing the output and receiving e-mail messages sent by MDBs.

In the next chapter, we will drill down into web services, including how you can publish session beans as web services and how to invoke web services from EJB applications.

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

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