Working with message-driven beans Part 2 (EJB 3)

SubscriptionDurability

If our MDB is listening on a topic, we can specify whether the topic subscription is durable or nondurable.

Recall that in the pub-sub domain, a message is distributed to all currently subscribed consumers. In general, this is much like a broadcast message in that anyone who is not connected to the topic at the time does not receive a copy of the message. The exception to this rule is what is known as a durable subscription.

Once a consumer obtains a durable subscription on a topic, all messages sent to the topic are guaranteed to be delivered to that consumer. If the durable subscriber is not connected to a topic when a message is received, MOM retains a copy of the message until the subscriber connects and delivers the message. The following shows how to create a durable subscriber:

tmp24123_thumb_thumb

Here, we are creating a durable subscription message consumer to the javax. jms.Topic playBoyTopic with a subscription ID ofJoeOgler. From now on, all messages to the topic will be held until a consumer with the subscription ID JoeOgler receives them. You can remove this subscription with the following code:


tmp24124_thumb_thumb

For nondurable subscriptions, explicitly set the value of the subscriptionDura-bility property to NonDurable, which is also the default.

messageSelector

tmp24125_thumb[2]

If you want the MDB to be a durable subscriber, then ActivationConfigProperty would look like this:

The messageSelector property is the MDB parallel to applying a selector for a JMS consumer. Our current code consumes all messages at the destination. If we prefer, we could filter the messages we retrieve by using a message selector— a criterion applied to the headers and properties of messages specifying which messages the consumer wants to receive. For example, if we want to receive all shipping requests whose Fragile property is set to true, we use the following code:

tmp24126_thumb_thumb

As you might have noticed, the selector syntax is almost identical to the where clause in SQL 92, but the selector syntax uses message header and property names instead of column names. Selector expressions can be as complex and expressive as you need them to be. They can include literals, identifiers, whitespace, expressions, standard brackets, logical and comparison operators, arithmetic operators, and null comparisons.

Using our JMS message selector example, we could specify in our MDB that we want to handle only fragile shipping requests as follows:

tmp24127_thumb_thumb

Table 4.2 summarizes some common message selector types.

Table 4.2 Commonly used message selector types. The selector syntax is almost identical to the SQL WHERE clause.

Type

Description

Example

Literals

Can be strings, exact or approximate numeric values, or booleans.

tmp24-128

Identifiers

Can be a message property or header name; case sensitive.

tmp24-129

Whitespace

Same as defined in the Java language specification: space, tab, form feed, and line terminator.

tmp24-130

Comparison operators

tmp24-131 tmp24-132

Logical operators

tmp24-133 tmp24-134

Null comparison

tmp24-135 tmp24-136

True/false comparison

tmp24-137 tmp24-138

We’re now ready to examine lifecycle callbacks in MDBs.

Using bean lifecycle callbacks

As you recall from topic 3, similar to stateless session beans, MDBs have a simple lifecycle (see figure 4.8 for a refresher). The container is responsible for the following:

■ Creates MDB instances and sets them up

■ Injects resources, including the message-driven context (discussed in the next topic in detail)

■ Places instances in a managed pool

■ Pulls an idle bean out of the pool when a message arrives (the container may have to increase the pool size at this point)

■ Executes the message listener method; e.g., the onMessage method

■ When the onMessage method finishes executing, pushes the idle bean back into the "method-ready" pool

■ As needed, retires (or destroys) beans out of the pool

The MDB lifecycle has three states: does not exist, idle, and busy. There are only two lifecycle callbacks corresponding to bean creation and destruction; you can use PostConstruct and PreDestroy methods to receive these callbacks.

Figure 4.8 The MDB lifecycle has three states: does not exist, idle, and busy. There are only two lifecycle callbacks corresponding to bean creation and destruction; you can use PostConstruct and PreDestroy methods to receive these callbacks.

The MDB’s two lifecycle callbacks are (1) PostConstruct, which is called immediately after an MDB is created and set up and all the resources are injected, and (2) PreDestroy, which is called right before the bean instance is retired and removed from the pool. These callbacks are typically used for allocating and releasing injected resources that are used by the onMessage method, which is exactly what we do in our example.

The processShippingRequest method saves shipping requests that the onMes-sage method extracts from the incoming JMS message:

tmp24140_thumb[2]

The method creates a statement from an open JDBC connection and uses it to save a record into the shipping_request table containing all the fields from the ShippingRequest object. The JDBC connection object used to create the statement is a classic heavy-duty resource. It is expensive to open and should be shared whenever possible. On the other hand, it can hold a number of native resources, so it is important to close the connection when it is no longer needed. We accomplish both these goals using callback methods as well as resource injection.

First, the JDBC data source that the connection is created from is injected using the @Resource annotation:

tmp24141_thumb[2]

The @Resource annotation tells the EJB container that it should look up a java.sql.DataSource named jdbc/TurtleDS from JNDI and pass it to the set-DataSource method after creating a new instance of the bean. The setDataSource method, in turn, saves the data source in an instance variable. After injecting resources, the container checks whether there are any designated PostConstruct methods that need to be invoked before the MDB is put into the pool. In our case, we mark the initialize method with the @PostConstruct annotation:

tmp24142_thumb[2]tmp24143_thumb[2]

In the initialize method, we are obtaining a java.sql.Connection from the injected data source and saving it into the connection instance variable used in processShippingRequest. At some point, the container decides that our bean should be removed from the pool and destroyed (perhaps at server shutdown). The PreDestroy callback gives us a chance to cleanly tear down bean resources before this is done. In the cleanup method marked with the @PreDestroy annotation, we tear down the database connection resource before the container retires our bean:

tmp24144_thumb[2]

Although database resources and their management are the primary uses of resource injection and lifecycle methods in MDBs, another important resource being used in the JMS sections are also important for MDB. We’re referring to the JMS destination and connection factory administrative objects, as well as the JMS connections. Let’s explore how these are utilized in MDBs.

Sending JMS messages from MDBs

Somewhat ironically, a task you’ll find yourself performing time and again in an MDB is sending JMS messages. As a simple example, suppose that we have an incomplete shipping request and we need to communicate that to ActionBazaar from ShippingRequestProcessorMDB. The easiest way to handle this notification is via JMS messages sent to an error queue that ActionBazaar listens to. Fortunately, you’ve already seen how to send a JMS message in listing 4.1. This task is even simpler and more robust in MDBs. We can inject the queue named jms/ShippingErrorQueue and the connection factory named jms/QueueConnec-tionFactory by using the @Resource annotation:

tmp24145_thumb[2]

Although we didn’t explicitly show it in our example, there is one more MDB feature you should know about: MDB transaction management. We’ll discuss EJB transactions in general in much more detail in the next topic, so here we’ll give you the "bargain basement" version.

Managing MDB transactions

In our plain JMS examples, we specified whether the JMS session would be trans-actional when we created it. On the other hand, if you look closely at the MDB example it doesn’t indicate anything about transactions. Instead, we’re letting the container use the default transactional behavior for MDBs. By default, the container will start a transaction before the onMessage method is invoked and will commit the transaction when the method returns, unless the transaction was marked as rollback through the message-driven context. You’ll learn more about transactions in topic 6.

This brief discussion of transaction management concludes our analysis of the basic features that MDBs offer. We’ve discussed how you can use MDBs to leverage the power of messaging without dealing with the low-level details of the messaging API. As you’ve seen, MDBs provide a host of EJB features for free, such as multithreading, resource injection, lifecycle management, and container-managed transactions. We’ve formulated our code samples so that you can use them as templates for solving real business problems. At this point, we’ll give you tips for dealing with the nuances of MDBs.

We can then create and destroy a shared javax.jms.Connection instance using life-cycle callbacks, just as we managed the JDBC connection in the previous section:

tmp24146_thumb[2]

Finally, the business method that sends the error message looks much like the rest of the JMS session code in listing 4.1:

tmp24147_thumb[2]

Next post:

Previous post: