[ Team LiB ] Previous Section Next Section

JMS Unified Messaging Domain Model

Throughout this chapter, we have mentioned the fact that the core JMS API abstractions can now be used to support both the point-to-point queuing and publish-subscribe messaging models. Indeed, this unified messaging domain model is a feature new to JMS v1.1 and part of J2EE v1.4. In fact, the JMS v1.1 specification indicates that you should generally code JMS applications using this new unified model. The specification even hints that the JMS APIs that are specific to point-to-point queuing and publish-subscribe messaging might be deprecated in a future JMS specification version. Regardless, the domain-specific JMS APIs will still be used for some time because many legacy JMS-based applications already exist and because many developers still prefer the domain-specific model to the unified model. Furthermore, BEA WebLogic Server 8.1 is only geared for J2EE v1.3 compliance and thus can claim only JMS v1.0 compliance. However, as we'll see, it is a rather simple task to extend your applications to use the new JMS v1.1–compliant APIs.

We discuss the unified model in this section. In reality, you have already been introduced to this model by virtue of the fact that we've already examined the core JMS architecture earlier in this chapter and we've also discussed the types of domain-specific destinations and two models that the unified messaging model can support. Figure 12.29, in fact, depicts the main abstractions involved with the unified domain model. As you can see, the APIs involved in this diagram have already been introduced. Now let's discuss how they are used for unified domain messaging with a BEA WebLogic Server.

Figure 12.29. JMS unified domain messaging.


As depicted in Figure 12.29, a generic ConnectionFactory is looked up using JNDI and then the ConnectionFactory is used to create a Connection, which in turn is used to create a Session handle as before. The JMS v1.1 Session interface now offers a host of methods that enable us to create QueueBrowser, TopicSubscriber, Queue, TemporaryQueue, Topic, and TemporaryTopic objects. You've already seen all of these method signatures on the domain-specific QueueSession or TopicSession interfaces described earlier. The JMS v1.1 Session interface also now offers methods that allow us to create generic MessageConsumer and MessageProducer object handles that actually may implement domain-specific message consumer and message producer functionality, respectively. These methods are simply more generic forms of methods previously defined for the QueueSession and TopicSession interfaces.

In fact, using the generic methods on the core JMS APIs, you don't even have to code your JMS application to have any specific knowledge of queues or topics. This is because after you have a handle to a generic Session object, you can use JNDI to look up a handle to a Destination object when given a name for the destination. You can then use the generic createProducer() and createConsumer() methods on the Session interface to create handles to generic MessageProducer and MessageConsumer objects, respectively. Such object handles can then also be used to generically produce and consume JMS messages, respectively.

Unified Domain Messaging Sample Source Code

We present an example here to illustrate the use of JMS domain-independent messaging. Our example manifests itself in three core classes. A UnifyManager class implements generic initialization of JMS destination resources. A UnifySupplier class implements a generic message producer that sends a message to a destination. The message sent to the destination may then be read by the UnifyConsumer class. A UnifyConsumer class implements a generic message consumer that reads a message from a destination that is sent using the UnifySupplier class.


The sample code strewn throughout this section leaves out some exception handling and other nonessential features in the interest of simplifying the description. The UnifyManager, UnifySupplier, UnifyConsumer, and MessageHandler classes implement the core of this example, and Props, OrderItem, and OrderManager classes are also used.

You must first download the JMS v1.1 API JAR file from http://java.sun.com/products/jms/docs.html and install it in a directory for your sample applications to use. You then need to set the location of this JAR file for the jar.jms1_1 property in the chapter's build.properties file. As with the previous examples, Ant can be used to execute the build.xml compilation script associated with this example. However, you must execute the jms11 target to build these JMS v1.1 examples along with the previous examples. That is, simply type ant jms11 at the command line in the directory containing this chapter's sample source code. After running the Ant script, the generated rununify-supplier and rununify-consumer script files can be used to execute the example.


The UnifyManager simply provides a constructor that initializes a set of generic JMS resources given a JNDI Context object. The JNDI context object is first used to look up a ConnectionFactory object in order for a Connection and then a Session object to be created. The JNDI context is then used to look up a handle to a generic Destination object as illustrated here:

package com.wls8unleashed.jms;
public class UnifyManager {
 protected ConnectionFactory connectionFactory;
 protected Connection connection;
 protected Session session;
 protected Destination destination;

 public UnifyManager(Context context){
  // Get JMS factory JNDI name
  String jmsFactoryName = Props.get("jms.factory.for.unify");

  // Create Generic Connection Factory
  System.out.println("Looking up factory name: " + jmsFactoryName);
   = (ConnectionFactory) context.lookup(jmsFactoryName);

  // Create Generic Connection to The Factory
  System.out.println("Creating generic JMS connection...");
  connection = connectionFactory.createConnection();

  // Create Session to the Connection
  System.out.println("Creating generic JMS session...");
   = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

  // Get generic destination name
  String destinationName = Props.get("unify.name");

  // Get generic destination
  System.out.println("Looking up generic JMS destination name: "
            + destinationName);
  destination = (Destination) context.lookup(destinationName);

The ConnectionFactory object looked up via JNDI uses whatever JNDI name is associated with jms.factory.for.unify in the build.properties file. For our example, we can simply use the same JNDI name for the UnleashedJMSFactory that we have already created as illustrated here:

# JMS Connection Factory for Unified JMS Model

The Destination looked up via JNDI uses whatever JNDI name we've associated with unify.name in the build.properties file. For our example, we can use the name of the queue or topic that we've already created earlier in this chapter as illustrated here:

# JMS Unified Destination Name

There is nothing tremendously special about the UnifySupplier class aside from the fact that it uses generic JMS API calls. The UnifySupplier constructor simply delegates much of its work to its UnifyManager superclass constructor and then creates a generic MessageProducer as shown here:

public UnifySupplier(Context context){
 // Call superclass QueueManager constructor
 // Create generic producer
 System.out.println("Creating generic JMS producer...");
 producer = session.createProducer(destination);

When it comes time to send the message, the generic MessageProducer is used to send a JMS message as illustrated here:

public void sendOrder(OrderItem message) throws JMSException {
 System.out.println("Sending order message:" + message);

 // Create empty ObjectMessage on session
 System.out.println("Creating empty object message...");
 ObjectMessage sendingMessage = session.createObjectMessage();

 // Start the generic JMS connection
 System.out.println("Starting generic JMS connection...");

 // Set the order object onto the message carrier
 System.out.println("Setting order item into message...");
 sendingMessage.setObject((Serializable) message);

 // Send the message
 System.out.println("Sending the order message...");

The UnifyConsumer class can be executed to receive the message sent by the UnifySupplier class. You can also use the QueueConsumer to receive the message sent by the UnifySupplier. Alternately, if you set the unify.name property in the build.properties file to UnleashedTopic, the UnifySupplier can act as a topic publisher and hence can be used with the TopicConsumer as well.


There is also nothing tremendously special about the UnifyConsumer class aside from the fact that it also uses generic JMS API calls. The UnifyConsumer constructor simply delegates much of its work to the UnifyManager superclass constructor, creates a generic MessageConsumer, sets itself as a MessageListener on the MessageConsumer, and then starts receiving messages on the connection as shown here:

public UnifyConsumer(Context context)
 throws NamingException, JMSException {


 // Create Receiver
 System.out.println("Creating generic JMS consumer...");
 messageConsumer = session.createConsumer(destination);

 // Register QueueConsumer as Message Listener
 System.out.println("Setting message listener...");

 // Start Receiving Message
 System.out.println("Starting generic connection...");

The UnifyConsumer.onMessage() method then receives and processes JMS messages as they are received from the JMS destination after they are sent to there by the UnifySupplier class or QueueSupplier class. The UnifyConsumer class can also act as a topic consumer if the unify.name in the build.properties file is set to UnleashedTopic and hence can also receive messages published by the TopicSupplier class.

    [ Team LiB ] Previous Section Next Section