JavaTM Message Service Tutorial |
Tutorial Homepage | TOC | Prev | Next | Index |
6 Using the JMS API in a J2EETM Application
This chapter describes the ways in which using the JMS API in a J2EETM application differs from using it in a standalone client application:
- Using enterprise beans to produce and to synchronously receive messages
- Using message-driven beans to receive messages asynchronously
- Managing distributed transactions
- Using application clients and Web components
This chapter assumes that you have some knowledge of the J2EE platform and J2EE components. If you have not already done so, you may wish to read the J2EE Tutorial (
http://java.sun.com/j2ee/tutorial/
) or the JavaTM 2 Platform, Enterprise Edition Specification, v1.3 (available fromhttp://java.sun.com/j2ee/download.html
).6.1 Using Enterprise Beans to Produce and to Synchronously Receive Messages
A J2EE application that produces messages or synchronously receives them may use any kind of enterprise bean to perform these operations. The example in Chapter 8 uses a stateless session bean to publish messages to a topic.
Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a
receive
call in an enterprise bean. Instead, use a timed synchronous receive, or use a message-driven bean to receive messages asynchronously. For details about blocking and timed synchronous receives, see Section 4.2.1, "Writing the PTP Client Programs."Using the JMS API in a J2EE application is in many ways similar to using it in a standalone client. The main differences are in administered objects, resource management, and transactions.
6.1.1 Administered Objects
The Platform Specification recommends that you use
java:comp/env/jms
as the environment subcontext for Java Naming and Directory InterfaceTM (JNDI) API lookups of connection factories and destinations. With the J2EE SDK 1.3.1, you use the Application Deployment Tool, commonly known as the deploytool, to specify JNDI API names that correspond to those in your source code.Instead of looking up a JMS API connection factory or destination each time it is used in a method, it is recommended that you look up these instances once in the enterprise bean's
ejbCreate
method and cache them for the lifetime of the enterprise bean.6.1.2 Resource Management
A JMS API resource is a JMS API connection or a JMS API session. In general, it is important to release JMS resources when they are no longer being used. Here are some useful practices to follow.
- If you wish to maintain a JMS API resource only for the life span of a business method, it is a good idea to close the resource in a
finally
block within the method.- If you would like to maintain a JMS API resource for the life span of an enterprise bean instance, it is a good idea to use the component's
ejbCreate
method to create the resource and to use the component'sejbRemove
method to close the resource. If you use a stateful session bean or an entity bean and you wish to maintain the JMS API resource in a cached state, you must close the resource in theejbPassivate
method and set its value tonull
, and you must create it again in theejbActivate
method.6.1.3 Transactions
Instead of using local transactions, you use the deploytool to specify container-managed transactions for bean methods that perform sends and receives, allowing the EJBTM container to handle transaction demarcation. (You can use bean-managed transactions and the
javax.transaction.UserTransaction
interface's transaction demarcation methods, but you should do so only if your application has special requirements and you are an expert in using transactions. Usually, container-managed transactions produce the most efficient and correct behavior.)6.2 Using Message-Driven Beans
As we noted in Section 1.4, "How Does the JMS API Work with the J2EE Platform?," the J2EE platform supports a new kind of enterprise bean, the message-driven bean, which allows J2EE applications to process JMS messages asynchronously. Session beans and entity beans allow you to send messages and to receive them synchronously but not asynchronously.
A message-driven bean is a message listener that can reliably consume messages from a queue or a durable subscription. The messages may be sent by any J2EE component--from an application client, another enterprise bean, or a Web component--or from an application or a system that does not use J2EE technology.
Like a message listener in a standalone JMS client, a message-driven bean contains an
onMessage
method that is called automatically when a message arrives. Like a message listener, a message-driven bean class may implement helper methods invoked by theonMessage
method to aid in message processing.A message-driven bean differs from a standalone client's message listener in five ways, however.
- The EJB container automatically performs several setup tasks that a standalone client has to do:
- Creating a message consumer (a
QueueReceiver
or aTopicSubscriber
) to receive the messages. You associate the message-driven bean with a destination and a connection factory at deployment time. If you want to specify a durable subscription or use a message selector, you do this at deployment time also.- Registering the message listener. (You must not call
setMessageListener
.)- Specifying a message acknowledgment mode. (For details, see Section 6.3, "Managing Distributed Transactions.")
- Your bean class must implement the
javax.ejb.MessageDrivenBean
and thejavax.jms.MessageListener
interfaces.Your bean class must implement the
ejbCreate
method in addition to theonMessage
method. The method has the following signature:public void ejbCreate() {}If your message-driven bean produces messages or does synchronous receives from another destination, you use its
ejbCreate
method to look up JMS API connection factories and destinations and to create the JMS API connection.- Your bean class must implement an
ejbRemove
method. The method has the following signature:public void ejbRemove() {}If you used the message-driven bean's
ejbCreate
method to create the JMS API connection, you ordinarily use theejbRemove
method to close the connection.- Your bean class must implement the
setMessageDrivenContext
method. AMessageDrivenContext
object provides some additional methods that you can use for transaction management. The method has the following signature:public void setMessageDrivenContext(MessageDrivenContext mdc) {}See
MessageBean.java
for a simple example of a message-driven bean.The main difference between a message-driven bean and other enterprise beans is that a message-driven bean has no home or remote interface. Rather, it has only a bean class.
A message-driven bean is similar in some ways to a stateless session bean: its instances are relatively short-lived and retain no state for a specific client. The instance variables of the message-driven bean instance can contain some state across the handling of client messages: for example, a JMS API connection, an open database connection, or an object reference to an enterprise bean object.
Like a stateless session bean, a message-driven bean can have many interchangeable instances running at the same time. The container can pool these instances to allow streams of messages to be processed concurrently. Concurrency can affect the order in which messages are delivered, so you should write your application to handle messages that arrive out of sequence.
To create a new instance of a message-driven bean, the container instantiates the bean and then
- Calls the
setMessageDrivenContext
method to pass the context object to the instance- Calls the instance's
ejbCreate
methodFigure 6.1 shows the life cycle of a message-driven bean.
Figure 6.1 Life Cycle of a Message-Driven Bean
6.3 Managing Distributed Transactions
JMS client applications use JMS API local transactions, described in Section 5.2.2, "Using JMS API Local Transactions," which allow the grouping of sends and receives within a specific JMS session. J2EE applications commonly use distributed transactions in order to ensure the integrity of accesses to external resources. For example, distributed transactions allow multiple applications to perform atomic updates on the same database, and they allow a single application to perform atomic updates on multiple databases.
In a J2EE application that uses the JMS API, you can use transactions to combine message sends or receives with database updates and other resource manager operations. You can access resources from multiple application components within a single transaction. For example, a servlet may start a transaction, access multiple databases, invoke an enterprise bean that sends a JMS message, invoke another enterprise bean that modifies an EIS system using the Connector architecture, and finally commit the transaction. Your application cannot, however, both send a JMS message and receive a reply to it within the same transaction; the restriction described in Section 5.2.2, "Using JMS API Local Transactions," still applies.
Distributed transactions can be either of two kinds:
- Container-managed transactions. The EJB container controls the integrity of your transactions without your having to call
commit
orrollback
. Container-managed transactions are recommended for J2EE applications that use the JMS API. You can specify appropriate transaction attributes for your enterprise bean methods.Use the
Required
transaction attribute to ensure that a method is always part of a transaction. If a transaction is in progress when the method is called, the method will be part of that transaction; if not, a new transaction will be started before the method is called and will be committed when the method returns.- Bean-managed transactions. You can use these in conjunction with the
javax.transaction.UserTransaction
interface, which provides its owncommit
androllback
methods that you can use to delimit transaction boundaries.You can use either container-managed transactions or bean-managed transactions with message-driven beans.
To ensure that all messages are received and handled within the context of a transaction, use container-managed transactions and specify the
Required
transaction attribute for theonMessage
method. This means that a new transaction will be started before the method is called and will be committed when the method returns. AnonMessage
call is always a separate transaction, because there is never a transaction in progress when the method is called.When you use container-managed transactions, you can call the following
MessageDrivenContext
methods:
setRollbackOnly
. Use this method for error handling. If an exception occurs,setRollbackOnly
marks the current transaction so that the only possible outcome of the transaction is a rollback.getRollbackOnly
. Use this method to test whether the current transaction has been marked for rollback.If you use bean-managed transactions, the delivery of a message to the
onMessage
method takes place outside of the distributed transaction context. The transaction begins when you call theUserTransaction.begin
method within theonMessage
method and ends when you callUserTransaction.commit
. If you callUserTransaction.rollback
, the message is not redelivered, whereas callingsetRollbackOnly
for container-managed transactions does cause a message to be redelivered.Neither the JMS API Specification nor the Enterprise JavaBeansTM Specification (available from
http://java.sun.com/products/ejb/
) specifies how to handle calls to JMS API methods outside transaction boundaries. The Enterprise JavaBeans Specification does state that the EJB container is responsible for acknowledging a message that is successfully processed by theonMessage
method of a message-driven bean that uses bean-managed transactions. Using bean-managed transactions allows you to process the message by using more than one transaction or to have some parts of the message processing take place outside a transaction context. In most cases, however, container-managed transactions provide greater reliability and are therefore preferable.When you create a session in an enterprise bean, the container ignores the arguments you specify, because it manages all transactional properties for enterprise beans. It is still a good idea to specify arguments of
true
and0
to thecreateQueueSession
or thecreateTopicSession
method to make this situation clear:queueSession = queueConnection.createQueueSession(true, 0);When you use container-managed transactions, you usually specify the
Required
transaction attribute for your enterprise bean's business methods.You do not specify a message acknowledgment mode when you create a message-driven bean that uses container-managed transactions. The container acknowledges the message automatically when it commits the transaction.
If a message-driven bean uses bean-managed transactions, the message receipt cannot be part of the bean-managed transaction, so the container acknowledges the message outside of the transaction. When you package a message-driven bean using the deploytool, the Message-Driven Bean Settings dialog box allows you to specify the acknowledgment mode, which can be either
AUTO_ACKNOWLEDGE
(the default) orDUPS_OK_ACKNOWLEDGE
.If the
onMessage
method throws aRuntimeException
, the container does not acknowledge processing the message. In that case, the JMS provider will redeliver the unacknowledged message in the future.6.4 Using the JMS API with Application Clients and Web Components
An application client can use the JMS API in much the same way a standalone client program does. It can produce messages, and it can consume messages by using either synchronous receives or message listeners. See Chapter 7 for an example of an application client that produces messages; see Chapter 9 and Chapter 10 for examples of using application clients to produce and to consume messages.
The J2EE Platform Specification does not define how Web components implement a JMS provider. In the J2EE SDK 1.3.1, a Web component--one that uses either the Java Servlet API or JavaServerPagesTM (JSPTM) technology--may send messages and consume them synchronously but may not consume them asynchronously.
Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a
receive
call in a Web component. Instead, use a timed synchronous receive. For details about blocking and timed synchronous receives, see Section 4.2.1, "Writing the PTP Client Programs."
This Tutorial contains information on the 1.3.1 version of the Java 2 Platform, Enterprise Edition.
Copyright © 2002 Sun Microsystems, Inc. All rights reserved.