This topic describes things to consider when designing an enterprise application to use the JMS API directly for asynchronous messaging.
Why and when to perform this task
This topic describes things to consider when designing an enterprise application to use the JMS API directly for asynchronous messaging.
Details of JMS resources that are used by enterprise applications are defined to WebSphere Application Server and bound into the JNDI namespace by the WebSphere administrative support. An enterprise application can retrieve these objects from the JNDI namespace and use them without needing to know anything about their implementation. This enables the underlying messaging architecture defined by the JMS resources to be changed without requiring changes to the enterprise application. When designing an enterprise application, you need to identify the details of the following types of JMS resources:
Point-to-Point | Publish/Subscribe |
---|---|
QueueConnectionFactory Queue |
TopicConnectionFactory Topic |
A connection factory is used to create connections with the JMS provider for a specific JMS queue or topic destination. Each connection factory encapsulates the configuration parameters needed to create a connection to a JMS destination.
For more information about the properties of these JMS resources, see Configuring JMS provider resources.
You must not cache session handles in stateless session beans that operate in transactions started by a client of the bean. Caching handles in this way causes the bean to be returned to the pool while the session is still involved in the transaction. Also, you should not cache non-durable subscribers due to the restriction mentioned above.
JMS defines a generic view of a messaging that maps onto the underlying transport. An enterprise application that uses JMS, makes use of the following interfaces that are defined in Sun's javax.jms package:
The generic JMS interfaces are subclassed into the following more specific versions for Point-to-Point and Publish/Subscribe behavior:
Point-to-Point | Publish/Subscribe |
---|---|
QueueConnection QueueSession, QueueSender QueueReceiver |
TopicConnection TopicSession, TopicSender TopicReceiver |
The
section "J2EE.6.7 Java Message Service (JMS) 1.0 Requirements" of the
J2EE specification
gives a list of methods that must
not be called in Web and EJB containers:
javax.jms.Session method setMessageListener javax.jms.Session method getMessageListener javax.jms.Session method run javax.jms.QueueConnection method createConnectionConsumer javax.jms.TopicConnection method createConnectionConsumer javax.jms.TopicConnection method createDurableConnectionConsumer javax.jms.MessageConsumer method getMessageListener javax.jms.MessageConsumer method setMessageListener javax.jms.Connection setExceptionListener javax.jms.Connection stop javax.jms.Connection setClientID
This method restriction is enforced in IBM WebSphere Application Server by throwing a javax.jms.IllegalStateException.
You can use the JMS message selector mechanism to select a subset of the messages on a queue so that this subset is returned by a receive call. The selector can refer to fields in the JMS message header and fields in the message properties.
When a message is received, you can act on it as needed by the business logic of the application. Some general JMS actions are to check that the message is of the correct type and extract the content of the message. To extract the content from the body of the message, you need to cast from the generic Message class (which is the declared return type of the receive methods) to the more specific subclass, such as TextMessage. It is good practice always to test the message class before casting, so that unexpected errors can be handled gracefully.
In this example, the instanceof operator is used to check that the message received is of the TextMessage type. The message content is then extracted by casting to the TextMessage subclass.
if ( inMessage instanceof TextMessage ) ... String replyString = ((TextMessage) inMessage).getText();
An alternative to making calls to QueueReceiver.receive() is to register a method that is called automatically when a suitable message is available; for example:
... MyClass listener =new MyClass(); queueReceiver.setMessageListener(listener); //application continues with other application-specific behavior. ...
When a message is available, it is retrieved by the onMessage() method on the listener object.
import javax.jms.*; public class MyClass implements MessageListener { public void onMessage(Message message) { System.out.println("message is "+message); //application specific processing here ... } }
Note: A MessageListener can only be used in the client container. (The J2EE specification forbids the use of the JMS MessageListener mechanism for the asynchronous receipt of messages in the EJB and Web containers.)
For asynchronous message delivery, the application code cannot catch exceptions raised by failures to receive messages. This is because the application code does not make explicit calls to receive() methods. To cope with this situation, you can register an ExceptionListener, which is an instance of a class that implements the onException()method. When an error occurs, this method is called with the JMSException passed as its only parameter.
For more details
about using listeners to receive messages asynchronously, see the
Java
Message Service Documentation
.
Note: An alternative to developing your own JMS listener class, you can use a message-driven bean, as described in Using message-driven beans in applications.
Take care when performing a JMS receive() from a server-side application component if that receive() invocation is waiting on a message produced by another application component that is deployed in the same server. Such a JMS receive() is synchronous, so blocks until the response message is received.
This type of application design can lead to the consumer/producer problem where the entire set of work threads can be exhausted by the receiving component, which has been blocked waiting for responses, leaving no available worker thread for which to dispatch the application component that would generate the response JMS message.
To illustrate this problem, picture a servlet and a message-driven bean deployed in the same server. When this servlet dispatches a request it sends a message to a queue which is serviced by the message-driven bean (that is, messages produced by the servlet are consumed by the message-driven bean's onMessage() method). The servlet subsequently issues a receive(), waiting for a reply on a temporary ReplyTo queue. The message-driven bean's onMessage() method performs a database query and sends back a reply to the servlet on the temporary queue. If a large number of servlet requests occur at once (relative to the number of server worker threads), then it is likely that all available server worker threads will be used to dispatch a servlet request, send a message, and wait for a reply. The application server enters a deadly-embrace condition whereby no threads remain to process any of the message-driven beans that are now pending. Since the servlets are waiting in blocking recieves, the server hangs, likely leading to application failure.
Possible solutions are: