[ Team LiB ] |
JMS Unified Messaging Domain ModelThroughout 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 CodeWe 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. NOTE 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. UnifyManagerThe 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 = (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..."); 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 jms.factory.for.unify=UnleashedJMSFactory 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 unify.name=UnleashedQueue UnifySupplierThere 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 super(context); ... // 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..."); connection.start(); // 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..."); producer.send(sendingMessage); } 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. UnifyConsumerThere 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 { super(context); // Create Receiver System.out.println("Creating generic JMS consumer..."); messageConsumer = session.createConsumer(destination); // Register QueueConsumer as Message Listener System.out.println("Setting message listener..."); messageConsumer.setMessageListener(this); // Start Receiving Message System.out.println("Starting generic connection..."); connection.start(); } 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 ] |