Chapter 9: Using WebLogic JMS


The Java Message Service (JMS) specification defines a standard set of interfaces for accessing messaging systems. WebLogic Server provides an enterprise-class messaging system that completely supports the JMS APIs. In addition, WebLogic Server goes the extra mile to make it easy to use other JMS-accessible messaging systems transparently from your J2EE applications. In this chapter, we begin by giving you a brief review of some key JMS concepts. Next , we jump into a detailed discussion of how the WebLogic JMS provider works. Then, we spend some time talking about WebLogic JMS design considerations. We follow that with a brief discussion of WebLogic JMS programming. Finally, we finish up this chapter with a brief discussion on using external JMS providers with WebLogic Server.

Like the rest of this book, this chapter is not intended as an introduction to either JMS or WebLogic Server s JMS implementation. If you are unfamiliar with the basics of JMS, we suggest that you study the book Java Message Service by Richard Monson-Haefel and David Chappell (O Reilly, 2000) for a complete treatment of JMS. For more information on WebLogic JMS, please refer to the WebLogic Server JMS documentation at http://edocs.bea.com/wls/docs81/jms/index.html.

JMS Key Concepts

In this section, we will give you a brief review of key JMS concepts. We begin by discussing the messaging models that JMS supports. Next, we spend the bulk of this section reviewing the JMS API, which will be important for our discussions in the following sections. We end the chapter with a brief discussion of the next version of the JMS specification.

Understanding the Messaging Models

JMS supports two distinct messaging models: point-to-point and publish-and-subscribe . With point-to-point messaging, the message producer , also known as the sender , creates a message and sends it to a destination known as a queue . Messages sent to queues can be persistent or non-persistent . Persistent messages sent to a queue will survive server shutdowns and failures. When a message arrives at a queue, the JMS provider places the message in the queue in the order in which it was received. Each message is held in the queue until one of the following events occurs:

  • A message consumer successfully processes the message.

  • The message time-to-live expires .

  • If the message is non-persistent, the server on which the queue resides shuts down or fails.

  • The queue is deleted.

Message consumers , also known as receivers , process messages placed in a queue. Each message will be processed by only a single receiver. By default, JMS delivers messages in first-in-first-out (FIFO) order. If multiple receivers are concurrently processing messages from a single queue, the JMS provider makes sure that each message goes to only one receiver.

With the publish-and-subscribe model, the message producer creates a message and sends it to a destination known as a topic . Messages sent to topics can be persistent or non-persistent. Messages sent, or published , to a topic are delivered only to the active consumers, also known as subscribers , which have registered their interest, or subscribed , to messages sent to the topic. Subscriptions can be either durable or nondurable . When a consumer subscribes to the topic, that subscriber will receive messages sent only during the lifetime of that subscription. For durable subscriptions, the JMS provider will save a message until one of the following events occurs:

  • All durable subscribers successfully process the message.

  • The message time-to-live expires.

  • If the message is non-persistent, the server on which the topic resides shuts down or fails.

  • The topic is deleted.

For nondurable subscriptions, the JMS provider delivers each message only to the currently connected subscribers.

Reviewing the JMS 1.0.2b API

In this section, we briefly review the primary objects associated with the JMS 1.0.2b APIs. This review is intended to make it easier for you to differentiate between what the JMS specification provides and what WebLogic JMS provides. As we hope will become clear, JMS is just an interface to messaging systems that defines some of the expected behavior of the underlying messaging provider. The JMS specification stops well short of defining everything you need to build enterprise-class messaging applications using J2EE.

Connection Factories

In JMS, you use a connection factory to create connections. Applications look up connection factories in JNDI. You can think of connection factories as a set of templates used by an administrator to define common attributes about connections. Connection factories come in two primary flavors, javax.jms.QueueConnectionFactory and javax.jms.TopicConnectionFactory , which you use to create either point-to-point or publish-and-subscribe connections. JMS also provides XA versions of these connection factories for use in distributed transactions. To get a connection factory, an application uses code similar to the following:

 InitialContext ctx = new InitialContext(connectionProperties); QueueConnectionFactory queueConnectionFactory =      (QueueConnectionFactory)ctx.lookup(MyConnectionFactory); 

Connections

A JMS connection conceptually represents a physical connection to the underlying messaging system. Each JMS application will require a JMS connection in order to communicate with the JMS provider. For multithreaded applications, the specification guarantees JMS connections to be thread-safe and does not provide any specification- related reason to require more than one JMS connection to a particular JMS provider. Of course, you may find reasons for needing multiple JMS connections when working with a specific provider.

There are two types of JMS connections: javax.jms.QueueConnection and javax.jms.TopicConnection . Applications use the connection object to authenticate themselves to the provider, to create sessions, to obtain meta data about the provider, and to register for callbacks when JMS detects there is a problem with the connection. To create a connection, just invoke the appropriate method on the connection factory:

 QueueConnection queueConnection =     queueConnectionFactory.createQueueConnection(); 

Destinations

Destinations represent the intermediate location that producers and consumers use to exchange messages. JMS applications typically look up a destination from JNDI and use it to create a producer or consumer tied to that destination. JMS also provides methods on the session objects for obtaining references to existing destinations using a provider-specific naming syntax. Like JMS connections, destinations are thread-safe. Destinations come in two primary flavors: javax.jms.Queue and javax.jms_.Topic . To get a destination, an application uses code similar to this:

 InitialContext ctx = new InitialContext(connectionProperties); Queue queue = (Queue)ctx.lookup(MyQueue); 

JMS also provides a mechanism for creating temporary destinations that are specific to the JMS connection on which they are created. These temporary destinations are often used to specify a response destination in the JMSReplyTo message header to tell the receiver where to send the response to that particular message.

Sessions

Sessions represent a single-threaded context for producing and consuming messages and are not thread-safe. If an application wants to share a session across multiple threads, it is the application s responsibility to synchronize access to the session. Applications use sessions as factories for creating different types of objects: message producers and consumers, temporary destinations, references to existing destinations, and messages. In addition, sessions provide a mechanism for defining transaction boundaries, serializing the consumption of messages, and limiting the scope of message acknowledgment. Again, sessions come in two primary flavors: javax.jms.QueueSession and javax.jms.TopicSession . XA versions of these sessions also exist.

When a JMS message consumer finishes processing a message or set of messages, it needs to notify the JMS provider that it may delete the message(s). JMS provides two mutually exclusive ways to do this: message acknowledgment and transacted sessions . Message acknowledgment simply involves sending the JMS provider a message that tells it the messages are no longer needed. JMS sessions offer three distinct acknowledgment modes that can be used with nontransacted sessions. The message acknowledgment modes are as follows :

AUTO_ACKNOWLEDGE.     Messages are automatically acknowledged by the underlying provider after the consumption of each message ”for example, when the onMessage() method of the javax.jms.MessageListener returns successfully. While this is the easiest form of message acknowledgment, it is generally the most inefficient because it acknowledges only a single message at a time. With AUTO_ACKNOWLEDGE mode, there is a small window during which it is possible for the last message received, but not yet acknowledged, to be redelivered in the event of a failure.

DUPS_OK_ACKNOWLEDGE.     This is similar to AUTO_ACKNOWLEDGE mode except that the underlying provider can acknowledge the messages in a lazy fashion, making it more efficient. As the name indicates, this lazy acknowledgment can result in the application receiving duplicate messages. This is generally the most efficient form of acknowledgment because it minimizes the work done by the session to eliminate duplicate messages and allows the provider to optimize acknowledgments. This mode exposes the application to the possibility of receiving sets of duplicate messages.

CLIENT_ACKNOWLEDGE.     Messages are acknowledged only when the client explicitly calls the acknowledge() method on a message. Calling acknowledge() acknowledges all consumed messages on the current session, not just the message on which it is invoked. The efficiency of this mode and the application s exposure to duplicate messages depend on the application s acknowledgment strategy. We will talk more about acknowledgment strategies later.

Transacted sessions allow you to define units of work that apply only to messages sent or received in the scope of the JMS session. In contrast to nontransacted sessions that use JTA transactions, transacted sessions do not include any other external resources such as EJBs, databases accessed via JDBC, or enterprise information systems accessed through J2EE Connector Architecture (J2EE CA) adapters. Message acknowledgment is handled automatically when transacted sessions either commit or roll back their units of work.

When creating sessions, applications must specify the transaction and message acknowledgment modes. The transaction mode defines whether you want to use a transacted session; message acknowledgment mode defines which of the acknowledgment modes you want to use for nontransacted sessions. To create a nontransacted session that uses AUTO_ACKNOWLEDGE mode, an application would use code similar to this:

 QueueSession queueSession =  queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); 

JMS also supports the use of XA transactions with providers that implement the XA versions of the JMS objects. Many large enterprise applications that use JMS will probably also use XA transactions. As is the case with transacted session, message acknowledgment is handled automatically at transaction commit or rollback. When using XA transactions, make sure to create the session with the transacted argument (the first argument) set to false and any message acknowledgment mode you like because the message acknowledgment mode will be ignored when a transaction context is present.

Message Producers and Consumers

Message producers allow an application to send a message to a destination. Producers also have characteristics that affect messages sent through them. These characteristics include things such as whether to use persistent delivery, the priority of the message, and the message s time-to-live. JMS defines two types of message producers: javax.jms.QueueSender and javax.jms.TopicPublisher . The code to create a message producer looks like this:

 QueueSender queueSender = queueSession.createSender(queue); 

The two types of message consumers are javax.jms.QueueReceiver and javax.jms.TopicSubscriber . Message consumers provide the context by which an application can receive messages from a particular destination. By specifying a message selector, consumers can limit the messages they receive to only the subset of messages in which they have an interest. Message consumers can receive messages synchronously by explicitly calling the consumer s receive() method or asynchronously by registering a callback object that implements the javax.jms.MessageListener interface using the consumer s setMessageListener() method:

 QueueReceiver queueReceiver = queueSession.createReceiver(queue); queueReceiver.setMessageListener(new MyMessageListener()); queueConnection.start(); 

Notice that we are calling the connection s start() method to tell the connection we are ready to start receiving messages. JMS requires that you explicitly start a connection before any messages can be received.

Durable Subscribers

When subscribing to a topic, you can create either a durable or nondurable subscription. Nondurable subscriptions are valid only from the time that you create them until the time you either unsubscribe or otherwise disconnect from the messaging system. This means that any failure that disconnects the application will automatically terminate the subscription. When you resubscribe, any messages sent between the time that you were disconnected and you resubscribed will not be available ”even if the message s delivery mode is persistent. In many situations, this is the desired behavior; if it is not, then you need to use a durable subscription.

With durable subscriptions, the subscriber provides a unique identifier to identify the subscription. Once the subscription is accepted, the messaging system will try to deliver all messages it receives to the durable subscriber. If the subscriber disconnects without unsubscribing, the JMS provider will buffer all of the messages the subscriber has not seen until the subscriber returns. If the delivery mode is non-persistent, the messages are buffered in memory and thus subject to message loss during failures.

Asynchronous Consumers and Transactions

JMS supports two types of transactions: transacted sessions and JTA transactions. With transacted sessions, the transaction includes only the JMS resources associated with the transacted JMS session. JTA transactions are truly global transactions that can include JMS, EJB, JDBC, and J2EE CA resources.

JMS also supports asynchronous message consumers. These asynchronous message consumers can use transacted sessions to define JMS-only units of work. They cannot, however, generally support JTA transactions that include the asynchronous delivery of the message. The reasons for this are somewhat complex. When registering an asynchronous MessageListener , JMS does not provide a mechanism to tell the JMS provider to start a JTA transaction before delivering the message. Because the JMS provider does not start the transaction before it delivers the message, the only other way that we could include the message delivery in the JTA transaction would be if JMS supported a callback mechanism to tell it that a previously delivered, but unacknowledged message should be considered part of an existing JTA transaction (started by the MessageListener after the message was delivered).

The story gets worse when you consider the EJB 2.0 specification s requirements for supporting JTA transactional delivery of messages to message-driven EJBs. There are two primary strategies for addressing this issue. The first strategy is for the JMS provider to provide a nonstandard API that can be used to associate a previously delivered, but unacknowledged message with a JTA transaction. While this works fine for applications that use the same vendor for both the EJB and JMS providers, it clearly does not work when mixing vendors unless the vendors can agree on the nonstandard API and semantics. The other strategy is to simulate asynchronous delivery using synchronous delivery under the covers. As we will see later, WebLogic Server supports both of these strategies to make it possible to integrate J2EE applications with any JMS provider that supports XA transactions.

Message Selectors

Sometimes a consumer is interested in only a subset of the messages delivered to a destination. JMS provides a standard filtering facility for message consumers, known as message selectors . Message selectors use a syntax similar to a SQL WHERE clause to create expressions that JMS will evaluate against message headers and/or properties. You can specify a selector when you create a message consumer:

 QueueReceiver queueReceiver =      queueSession.createReceiver(queue, JMSPriority > 5); queueReceiver.setMessageListener(new MyMessageListener()); queueConnection.start(); 

This selector ensures that messages will be delivered to this consumer only if the value of the JMSPriority header is greater than 5.

Message selectors are static. You cannot change them without first closing the current consumer and creating a new one. Changing a durable subscription s selector ends the subscription; all unconsumed messages for that subscription are deleted and the subscription is recreated, as required by the JMS specification.

As you can imagine, the use of selectors adds overhead to message delivery that will affect the performance and scalability of the application. Where possible, it is often better to split a destination into multiple destinations and eliminate the need for message selectors. We will discuss message selector design and performance implications in more detail later.

Messages

Messages form the foundation of any JMS application. Applications use messages to carry data associated with a particular event. As shown in Figure 9.1, JMS divides messages into three logical parts : headers, properties, and the body.

Message headers specify certain characteristics of a message potentially of interest to applications. For most JMS headers, the JMS provider is responsible for setting the values of these characteristics. JMSReplyTo and JMSCorrelationID are two notable exceptions often used by applications to implement a request/reply pattern of message exchange.

Message properties allow applications to define additional characteristics about a message. The JMS specification reserves all property names that begin with JMSX . Typically, message properties are most useful for applications that need to apply message selectors to application-specific data.

The message body contains the payload of the message. What type of information a message contains depends on the type of message the application chooses to use. JMS defines five different types of message objects, all of which derive from javax.jms.Message :

TextMessage.     Applications use this message type to send simple text strings or more complex, text-based data structures like XML messages.

BytesMessage.     Applications use this message type to send a raw array of bytes. Typically, you would use this to retain an application s native data format.

ObjectMessage.     Applications use this message type to send a serialized Java object.

StreamMessage.     Applications use this message type to send an ordered stream of Java primitive types or the object versions of these primitive types, such as java.lang.Integer or java.lang.Double .

MapMessage.     Applications use this message type to send an unordered set of name-value pairs. All names must be unique, and the values must be Java primitive types or the object versions of these primitive types.

To create a message and send it to a destination, use code like that shown here:

 TextMessage message = queueSession.createTextMessage(message body); queueSender.send(message); 

Upcoming JMS 1.1 Changes

J2EE 1.3 currently includes the JMS Specification 1.0.2b as a mandatory component for any J2EE 1.3-compatible application server. With the introduction of J2EE 1.4, JMS 1.1 will replace the older 1.0.2b specification. Before we move on, we want you to understand the changes coming in JMS 1.1.

click to expand
Figure 9.1:  The anatomy of a JMS message.

As we have seen, JMS 1.0.2b forces an application developer to make a strong distinction between the two messaging models during development. Once a developer selects a particular model, it is difficult to switch to a different model because the underlying APIs for each messaging model are very different. Moreover, the two messaging models cannot interoperate within the same JMS transaction context, as defined by transacted sessions. For example, in order to receive messages from a queue and publish messages to a topic, an application must create two sessions, a QueueSession for the receiver and a TopicSession for the publisher. JMS defines the transacted session s transaction scope to be that of the session, and because the receiver and publisher were forced to use two different sessions, the work could not be completed in the context of a single transaction. Of course, this deficiency can be overcome by using JTA transactions if they are available for the chosen JMS provider. We will discuss both transacted sessions and JTA transactions in more detail a little later in this chapter.

JMS 1.1 addresses these problems by defining a unified API for both messaging models. The unified API simplifies the client-programming model, and applications can use the same interfaces regardless of the messaging model used. Additional methods have also been added to support the ability to include point-to-point and publish-and-subscribe messaging in the same transaction. Fortunately, the JMS 1.1 specification is completely backward compatible with JMS 1.0.2b so that existing JMS applications will continue to work without modification.

Although we will not spend additional time discussing JMS 1.1, we wanted to point it out for a specific reason. WebLogic Server provides a unified JMS implementation that can support both the 1.0.2b and 1.1 versions of the JMS specification. Because of J2EE licensing restrictions imposed by Sun Microsystems, the JMS 1.1 specification is not yet officially supported by WebLogic Server. If you download the JMS 1.1 JAR file from Sun s Web site at http://java.sun.com/products/jms/docs.html and put it at the front of your WebLogic Server instance s classpath, you will be able to use the JMS 1.1 APIs. This works for both WebLogic Server 7.0 and 8.1.




Mastering BEA WebLogic Server. Best Practices for Building and Deploying J2EE Applications
Mastering BEA WebLogic Server: Best Practices for Building and Deploying J2EE Applications
ISBN: 047128128X
EAN: 2147483647
Year: 2003
Pages: 125

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net