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:
Figure 5-1. A JMS messaging system
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 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.
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.
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.
Figure 5-4. An order-to-shipping JMS messaging system
Unlike a session bean, an MDB doesn’t have any business interfaces. It has only the following:
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.
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:
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.
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:
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:
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 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:
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.
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";
}
}
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.
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.
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.
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.
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.
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.
Figure 5-10. Navigating to the administrator console
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.
Figure 5-11. Creating a TopicConnectionFactory
Then create a JMS topic named StatusMessageTopic as shown in Figure 5-12.
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.
Figure 5-13. Creating the JavaMail resource
If the SMTP server requires authentication, then set the ‘Additional Properties’ as shown in Figure 5-14.
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.
Figure 5-15. Deploying the MDB, session bean and their clients
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.
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.
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.
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.
13.58.123.165