Most JMS destinations (queues and topics) are created administratively and are treated as static resources, even though dynamic destinations (temporary queues and temporary topics) are supported in JMS. Using static queues to communicate between tiers of a client/server application creates barriers to scalability and maintainability. In this article, we will look at the benefits and drawbacks of using temporary destinations in an enterprise healthcare system. We will also look at design perspectives for using temporary queues as an alternative to static queues and explore some design strategies using synchronous requests and replies.
Temporary destinations (temporary queues or temporary topics) are proposed as a lightweight alternative in a scalable system architecture that could be used as unique destinations for replies. Such destinations have a scope limited to the connection that created it, and are removed on the server side as soon as the connection is closed. Only a single well-known static queue is required for producers/senders to connect with consumers using temporary destinations. The JMS header field JMSReplyTo is always used in conjunction with temporary destinations.
Since the identity of the temporary destination is known only by the connection or session that created it, the consumer/receiver cannot know the destination name. The solution is to have the producer/sender send the name of its temporary destination as a header field (
JMSReplyTo), as part of a message, sent to a known static queue listened to by the producer/sender.
There are a few limitations that must be recognized regarding temporary destinations:
Functionally, static and dynamic destinations have contrasting attributes:
Despite the limitations of temporary destinations, there are definite advantages to their use in real-time applications. In this section, we will examine a representative messaging application using JMS.
An enterprise healthcare system accepts updates to client information (such as adding a new member or deleting a member) from multiple interfaces (different providers or health care groups). Responses to these update requests must be sent synchronously to ensure that proper business validation or other transactional security is maintained. This requires the application to support multiple clients who may send thousands of messages, with each client waiting for a reply to each message.
There are two ways you can design the solution:
In the first approach, if you have a single static queue for the requests and a single static queue for responses for multiple clients, you could employ some selection logic using Message Selectors (JMS Specification section 18.104.22.168) to filter the messages from the same static queue, as depicted in Figure 1.
Figure 1. Single static queue for multiple clients model
This approach is time-consuming and may not be acceptable if the response requirement is strict. Having a pair of request and response queues per each client may by a good idea, but involves creating and maintaining multiple static queues, as depicted in Figure 2.
Figure 2. Pair of static queues per client model
In the second approach, there would be a single static queue for all client requests. Each client initially creates a temporary queue for receiving responses (TQa, TQb, and TQc). The client sends the name of its temporary queue as a part of each request to the static queue, as depicted in Figure 3.
Figure 3. Temporary queues for multiple clients model
In this configuration, temporary destinations can be viewed as analogous to callbacks in synchronous processing, i.e., when the callee wants to reach out to the caller in a decoupled way, without the caller and callee having to know about each other in advance.
For this example, we will look at using a message-driven bean (or MDB) as a message listener, and a session facade that coordinates with one or more business objects (stateful or stateless session beans), which generates the response message and sends the outbound message (reply) to the temporary destination as shown in Figure 4.
Figure 4. Suggested design pattern class diagram
The MDB will initially register itself with a static destination through an administrative task. Upon receiving a message, the MDB passes the message to a session facade that picks up the identity of the temporary destination from the value of
JMSReplyTo in the message header. It then interacts with other business object(s) to perform the requested workflow, as shown in Figure 5. MDB will then use
OutboundProcessor (stateful or stateless session bean) to send the message out to the temporary destination.
Figure 5. Suggested design pattern sequence diagram
The session should be closed as soon as processing is completed so that TemporaryQueues will be deleted on the server side. While the JMS session doesn't support concurrency, this can be implemented with connection objects. A session is a single-threaded context to produce and consume the temporary messages. According to the JMS specification:
A session supports transactions and it is difficult to implement transactions that are multithreaded; a session should not be used concurrently by multiple threads.
Even though you are not using a transacted session, you are still required to coordinate individual sessions to implement concurrent processing. The only method in the session object method that can be concurrently called is
close(). Threads are good candidates for blocking calls since they could be executed concurrently.
There is no need to close the consumers (QueueReceiver) of a closed session. As soon as the connection that created the temporary queue is closed, all the constituents of the closed connection and session are closed. Relying on garbage collection to eventually reclaim these resources may not be timely enough, especially in the case of JMS resources that are created outside of JVM.
The following code excerpt shows how to create Temporary Destinations. We use common interfaces like Session as opposed to specific domain constructs like QueueSession or TopicSession. Sun encourages using common interfaces without programming specific to a single message domain so that the JMS application is domain-independent.
<font color= "blue">/** Get the Initial Context Factory */</font> Hashtable env = new Hashtable(); String conFactoryClass = "VendorSpecificCFC"; String url = "MyServer:port"; env.put(Context.INITIAL_CONTEXT_FACTORY, conFactoryClass); env.put(Context.PROVIDER_URL, url); env.put(Context.SECURITY_PRINCIPAL, "userid" ); env.put(Context.SECURITY_CREDENTIALS, "password"); Context = new InitialContext(env); <font color= "blue">/**Access the resources by looking up the JNDI context*/ /**QueueConnectionFactory is a Factory for QueueConnection Object*/</font> javax.jms.ConnectionFactory qcf = (QueueConnectionFactory)context.lookup("queueConFactoryName"); javax.jms.Connection qConnection = (QueueConnection)qcf.createConnection("userid", "password"); javax.jms.Queue myQueue = (javax.jms.Queue)context.lookup("MyQueue"); javax.jms.Topic myTopic = (javax.jms.Topic)context.lookup("MyTopic"); <font color="blue">/**TopicConnectionFactory is a Factory for TopicConnection Object*/</font> javax.jms.ConnectionFactory tcf = (TopicConnectionFactory)context.lookup("topicConFactoryName"); javax.jms.TopicConnection tConnection = (TopicConnection)tcf.createConnection("userid", "password"); <font color="blue">/**Create a Session using Connection Object*/</font> javax.jms.Session qSession = qConnection.createSession(false,Session.AUTO_ACKNOWLEDGE); javax.jms.Session tSession = tConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); <font color= "blue">/**Create the Temporary Destinations using the Session Object*/</font> javax.jms.Queue replyQueue = qSession.createTemporaryQueue(); javax.jms.Topic replyTopic = tSession.createTemporaryTopic();
Temporary destinations are a useful tool in message-driven architectures. They have the distinction of being lightweight, and they allow applications to efficiently scale because you can easily expand the number available at runtime. Static destinations must be created in advance and, although useful in other ways, do not offer you this same flexibility. Because of this, temporary destinations are preferable to static destinations despite the extra work required to set up the communication between the sender and receiver.
Thanks to Ron Gates and the ONJava team for proofreading this article.
Return to ONJava.com.