Messaging with message-driven beans (EJB 3)

Just as session beans process direct business requests from the client, MDBs process indirect messages. In enterprise systems, messaging has numerous uses, including system integration, asynchronous processing, and distributed system communication. If you’ve been working on enterprise development for some time, you’re probably familiar with at least the idea of messaging. In the most basic terms, messaging involves communicating between two separate processes, usually across different machines. Java EE messaging follows this same idea—-just on steroids. Most significantly, Java EE makes messaging robust by adding a reliable middleman between the message sender and receiver. This idea is illustrated in figure 2.5.

In Java EE terms, the reliable middleman is called a messaging destination, powered by message-oriented middleware (MOM) servers like IBM’s MQSeries or Progress Software’s SonicMQ.Java EE standardizes messaging through a well-known API, Java Messaging Service (JMS), upon which MDBs are heavily dependent.

The Java EE "pony express" messaging model. Java EE adds reliability to messaging by adding a middleman that guarantees the delivery of messages despite network outages, even if the receiver is not present on the other end when the message is sent. In this sense, Java EE messaging has much more in common with the postal service than it does with common RPC protocols like RMI. We'll discuss this model in much greater detail in topic 4.

Figure 2.5 The Java EE "pony express" messaging model. Java EE adds reliability to messaging by adding a middleman that guarantees the delivery of messages despite network outages, even if the receiver is not present on the other end when the message is sent. In this sense, Java EE messaging has much more in common with the postal service than it does with common RPC protocols like RMI. We’ll discuss this model in much greater detail in topic 4.

We’ll discuss messaging, JMS, and MDBs in much greater detail in topic 4. For the moment, this is all you really need to know.

Next, we’ll build a simple example of message producer and an MBD. In our ActionBazaar example, we enable asynchronous order billing through messaging. To see how this is done, let’s revisit the parts of the PlaceOrderBean introduced in listing 2.3 that we deliberately left hidden, namely the implementation of the billOrder method.

Producing a billing message

As we discussed in our high-level solution schematic in section 2.2, the Place-OrderBean accomplishes asynchronous or "out-of-process" order billing by generating a message in the confirmOrder method to request that the order billing be started in parallel. As soon as this billing request message is sent to the messaging middleman, the confirmOrder method returns with the order confirmation to the user. We’ll now take a look at exactly how this piece is implemented. As you can see in listing 2.5, the billing request message is sent to a messaging destination named jms/OrderBillingQueue. Since you have already seen most of the implementation of the PlaceOrder bean, we won’t repeat a lot of the code shown in listing 2.3 here.

Listing 2.5 PlaceOrderBean that produces the JMS message

Listing 2.5 PlaceOrderBean that produces the JMS messageListing 2.5 PlaceOrderBean that produces the JMS message

Not surprisingly, the code to send the message in listing 2.5 is heavily dependent on the JMS API. In fact, that’s all that the code in the billOrder method consists of. If you’re familiar with JDBC, the flavor of the code in the method might seem familiar. The end result of the code is that the newly created Order object is sent as a message to a JMS destination named jms/OrderBillingQueue. We won’t deal with the intricacies of JMS immediately, but we’ll save a detailed discussion of this essential messaging API for topic 4. It is important to note a few things right now, though.

The first thing is that two JMS resources, including the message destination, are injected using the @Resource annotation Q instead of being looked up.

NOTE As we stated earlier, in addition to the @ejb annotation the @Resource annotation provides DI functionality in EJB 3. While the @EJB annotation is limited to injecting EJBs, the @Resource annotation is much more general purpose and can be used to inject anything that the container knows about.

As shown in listing 2.5, the container looks up the JMS resources specified through the name parameter and injects them into the connectionFactory and billingQueue instance variables. The name parameter values specify what resources are bound to the EJB’s environment naming context. Then the PlaceOrderBean establishes a connection to the JMS provider, and creates a session and a message producer C. Secondly, it is important to realize that the MessageProducer.send method Q doesn’t wait for a receiver to receive the message on the other end. Because the messaging server guarantees that the message will be delivered to anyone interested in the message, this is just fine. In fact, this is exactly what enables the billing process to start in parallel to the ordering process, which continues on its merry way as soon as the message is sent. You should also note how loosely coupled the ordering and billing processes are. The ordering bean doesn’t even know who picks up and processes its message; it simply knows the message destination! Finally, PlaceOrderBean cleans up all resources used by it ©.

As we know from our solution schematic in section 2.2, the OrderBillingMDB processes the request to bill the order. It continuously listens for messages sent to the jms/OrderBillingQueue messaging destination, picks up the messages from the queue, inspects the Order object embedded in the message, and attempts to bill the user. We’ll depict this scheme in figure 2.6 to reinforce the concept.

Asynchronously billing orders using MDBs. The stateful session bean processing the order sends a message to the order-billing queue. The billing MDB picks up this message and processes it asynchronously.

Figure 2.6 Asynchronously billing orders using MDBs. The stateful session bean processing the order sends a message to the order-billing queue. The billing MDB picks up this message and processes it asynchronously.

Let’s take a look now at how the OrderBillingMDB is implemented.

Using the order billing message processor MDB

The OrderBillingMDB’s sole purpose is to attempt to bill the bidder for the total cost of an order, including the price of the items in the order, shipping, handling, insurance costs, and the like. Listing 2.6 shows the abbreviated code for the order billing MDB. Recall that the Order object passed inside the message sent by the PlaceOrder EJB contains a BillingInfo object. The BillingInfo object tells OrderBillingMDB how to bill the customer—perhaps by charging a credit card or crediting against an online bank account. However the user is supposed to be charged, after attempting to bill the user the MDB notifies both the bidder and seller of the results of the billing attempt. If billing is successful, the seller ships to the address specified in the order. If the billing attempt fails, the bidder must correct and resubmit the billing information attached to the order.

Last but not least, the MDB must also update the order record to reflect what happened during the billing attempt. Feel free to explore the complete code sample and deployment descriptor entries containing the JMS resource configuration in the zip containing code examples.

Listing 2.6 OrderBillingMDB

Listing 2.6 OrderBillingMDBtmp2449_thumb

As you might have noticed from the code, MDBs are really session beans in JMS disguise. Like stateless beans, MDBs are not guaranteed to maintain state. The @MessageDriven annotation is the MDB counterpart of the @Stateless and @Stateful annotations—it makes the container transparently provide messaging and other EJB services into a POJO. The activation configuration properties nested inside the @MessageDriven annotation tells the container what JMS destination the MDB wants to receive messages from.

NOTE Behind the scenes, the container takes care of several mechanical details to start listening for messages sent to the destination specified by the activation configuration properties. As soon as a message arrives at the destination, the container forwards it to an instance of the MDB.

Instead of implementing a remote or local business interface, MDBs implement the javax.jms.MessageListener interface. The container uses this well-known JMS interface to invoke an MDB. The onMessage method defined by the interface has a single javax.jms.Message parameter that the container uses to pass a received message to the MDB. Believe it or not, this is more or less all you need to know to get by when using MDBs, as long as you have a decent understanding of messaging and JMS.

This wraps up this topic’s discussion of the EJB 3 business-tier components.For now, let’s move on to the other major part of EJB, the Persistence API.

Next post:

Previous post: