 | Level: Intermediate David Currie (david_currie@uk.ibm.com), Staff Software Engineer, IBM UK Ltd
09 Apr 2004 As more and more application servers that comply with the new J2EE 1.4 spec become available, Java developers will have to consider how -- and if -- they should migrate their J2EE 1.3-compliant code. In this article, David Currie takes a look at the changes to one particular area of the J2EE specification: messaging. You'll see what you'll need to change, what you can leave as is, and what new opportunities await you.
In this article, I'll provide an overview of the changes to the support for messaging introduced by the 1.4 release of the J2EE specification. In particular, I'll look at the requirement for JMS 1.1 and the new restrictions imposed on its use, at the new concepts relating to message destinations, and at the significant changes that version 2.1 of the EJB spec makes to message-driven beans. This material will be of particular interest to developers and administrators with a knowledge of messaging in J2EE 1.3 who are looking to write new applications or migrate existing messaging applications to a J2EE 1.4-compliant application server.
JMS 1.1
Perhaps the most obvious change in the latest version of the J2EE specification is that J2EE-compliant application servers are now required to support version 1.1 of the Java Message Service (JMS) specification. JMS 1.1 is entirely backwards compatible with the 1.0 level required by the J2EE 1.3 specification, so there should be no need to change existing applications. JMS 1.1 introduces a unified message domain and is covered in detail in this article by Bobby Woolf. It is worth stressing that, unless you require backwards compatibility, there is no reason to write new JMS applications using the old queue and topic interfaces. New applications should just use the new unified interfaces, as shown in the example in Listing 1.
Listing 1. Example illustrating unified JMS interfaces
InitialContext context = new InitialContext();
ConnectionFactory factory =
(ConnectionFactory) context.lookup("java:comp/env/jms/cf");
Destination source =
(Destination) context.lookup("java:comp/env/jms/source");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(source);
Message message = consumer.receive();
connection.close();
|
As Bobby states in his article, the unified interfaces don't just simplify the messaging programming model; they also allow an application to receive a message from a queue and send a message to a topic (or vice-versa) using a single transacted Session. This means that you can perform the send and receive as part of the same transactional unit of work without resorting to a bean- or container-managed transaction.
J2EE restrictions on JMS usage
As with previous versions of the J2EE specification, version 1.4 places some restrictions on JMS use. I'll look at these restrictions in this section.
Restricted interfaces
Although many developers probably don't know it, the J2EE specification has always contained a list of restrictions on how the JMS API may be used by a J2EE application. For example, the following interfaces are intended for integration between the JMS provider and the application server (they are part of the Application Server Facilities described in the JMS specification) and therefore may not be used by an application:
javax.jms.ServerSession
javax.jms.ServerSessionPool
javax.jms.ConnectionConsumer
- All
javax.jms.XA interfaces
Restricted methods
The J2EE 1.3 specification has a confusing statement about a set of methods that "must not be used by application components executing in containers that prevent them from creating threads." The EJB container does not permit applications to create threads, but a Web container may allow applications to do so. Consequently, under J2EE 1.3 you may or may not be able to call these methods, depending on the Web container you are running. Thankfully, the J2EE 1.4 specification has removed this confusing statement and simply states that the following methods may only be used by an application running in a client container -- in other words, they may never be called in the Web or EJB container:
javax.jms.ServerSession method setMessageListener()
javax.jms.ServerSession 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 method setExceptionListener()
javax.jms.Connection method stop()
javax.jms.Connection method setClientID()
The first six of these methods are also part of the Application Server Facilities, so it seems reasonable to exclude them; but why the others? The decision to forbid setMessageListener() and getMessageListener() will probably cause the biggest problems for most application developers. These are the methods that are used to register a MessageListener so that its onMessage() method is invoked when a message arrives at a destination. The reason that this is not permitted in the Web and EJB container is because of context. When EJB and servlet methods are invoked, the container keeps context, such as the current transaction and security principal, associated with the thread. When a MessageListener is invoked by a JMS provider, there is no way for the application server to intercept the call and add the appropriate context. Instead of using these methods, you should either poll for messages using the synchronous receive() methods or consider using a message-driven bean.
Although the last three methods in the list above were also forbidden in the previous version of the J2EE specification, you may still be wondering why they aren't permitted. This comes down to the way in which the application server manages connections. It may wish to share the same connection among different applications, and it would not be able to do so if applications could call methods that change the state of the connection. The setExceptionListener() and stop() methods are only useful when using a MessageListener; thus, not being able to call them is not a problem, and you should be able to set a client identifier when you administratively define a connection factory.
One Session per Connection
There is one other new restriction that has the potential to break existing applications. The J2EE specification now states that an application can only have a single active (not closed) Session per Connection. In other words, once you have called createSession(), if you call it again before you have closed the original Session, then an exception may be thrown.
The specification provides no explanation as to why this restriction has been added, but I'll give you my theory. The latest version of the Java Connector Architecture (JCA) specification suggests that a JMS provider may be implemented as a JCA resource adapter. In the JCA programming model, like JDBC, there are only two objects (ConnectionFactory and Connection), compared with the three in JMS (ConnectionFactory, Connection, and Session). More importantly, in JCA it is a Connection that is associated with a transaction, whereas in JMS it is the Session. Consequently, if you allowed more than one Session per Connection in JMS, then the Connection would in effect be associated with more than one transaction. In summary, this restriction makes it easier to implement a JMS provider as a JCA resource adapter.
So where does all this leave the application developer? In the past, you may have been tempted to take advantage of the multithreaded nature of JMS Connection objects and cache them in a static variable of an EJB component or servlet. Instead, you should now either cache the Connection only as an instance variable of the EJB component and ensure that you only ever have a single session at any one time, or just create a Connection each time you need a Session. Most application servers will perform some sort of pooling of Connection objects, so this may not be as expensive as you might fear (providing that you remember to close the Connections again when you have finished with them).
Message destinations
J2EE applications that use JMS typically break down into two camps:
- Applications that use JMS to communicate with back-end systems
- Applications that use JMS to provide asynchronous communication between different parts of the application
In this section, I'll describe a change in the J2EE spec that will make the second type of application easier to deploy. I'll show you as an example two EJB components that communicate by sending a message from one to the other. In J2EE 1.3, each of those beans would define a resource-env-ref in its deployment descriptor, which it would then look up from its local namespace. In the example shown in Listing 2, SenderEJB would look up its destination from java:/comp/env/jms/target, and ReceiverEJB would look up its destination from java:/comp/env/jms/source. As the references are in separate local namespaces, there is no mechanism for the application assembler to indicate to the deployer that these resource-env-refs should actually be bound to the same destination.
Listing 2. Deployment descriptor snippet showing resource-env-ref
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>SenderEJB</display-name>
...
<resource-env-ref>
<resource-env-ref-name>jms/target</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>
...
</session>
<session>
<ejb-name>ReceiverEJB</display-name>
...
<resource-env-ref>
<resource-env-ref-name>jms/source</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>
...
</session>
</enterprise-beans>
</ejb-jar>
|
In J2EE 1.4, you can still use resource-env-ref to define your destinations; however, there is also a new message-destination-ref element that is very similar to resource-env-ref but has two additional sub-elements. The first of these is message-destination-usage, which, as its name suggests, is used to indicate to the deployer how the application intends to use the destination. It can take one of the following values:
Produces
Consumes
ProducesConsumes
A deployer might therefore typically expect to bind a reference that Produces and another that Consumes to the same destination.
The second additional element is a message-destination-link. This may be used to tie together two or more message-destination-refs. The name contained within the link must match the name given in another new element -- message-destination. When the application is deployed, the deployer now binds the single message-destination to a JMS destination, and all of the message-destination-refs pick up this same binding.
Thus, under J2EE 1.4, the example from Listing 2 can now be rewritten as shown in Listing 3.
Listing 3. Deployment descriptor snippet with message-destination-ref and message-destination elements
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>SenderEJB</display-name>
...
<message-destination-ref>
<message-destination-ref-name>jms/target</message-destination-ref-name>
<message-destination-type>javax.jms.Queue</message-destination-type>
<message-destination-usage>Produces</message-destination-usage>
<message-destination-link>destination</message-destination-link>
</message-destination-ref>
...
</session>
<session>
<ejb-name>ReceiverEJB</display-name>
...
<message-destination-ref>
<message-destination-ref-name>jms/source</message-destination-ref-name>
<message-destination-type>javax.jms.Queue</message-destination-type>
<message-destination-usage>Consumes</message-destination-usage>
<message-destination-link>destination</message-destination-link>
</message-destination-ref>
...
</session>
</enterprise-beans>
<assembly-descriptor>
...
<message-destination>
<message-destination-name>destination</message-destination-name>
</message-destination>
...
</assembly-descriptor>
</ejb-jar>
|
To emphasize the difference these new elements make, consider the following two figures. In each figure, the EAR file shaded blue indicates the sphere of influence of the application assembler. It is the application deployer who adds the red arrows to bind the resources defined in the application to the actual JMS queue. In Figure 1, you can see that the application assembler is left with two resource-env-refs, one for each EJB component, and has to ensure that the deployer knows that each one should be bound to the same JMS destination.
Figure 1. Example application using resource-env-refs
In contrast, in Figure 2 the application assembler can make his or her meaning entirely clear by linking the two message-destination-refs to the same message-destination. The deployer then just adds a single binding.
Figure 2. Example application using message-destination
You can also link to a message-destination defined in a different JAR file within the same application using the same syntax as you'd use with an ejb-link. For example, <message-destination-link> ../other/other.jar#destination </message-destination-link> would link to a message-destination with the name destination in the JAR file at the relative path ../other/other.jar. It is also possible to set a message-destination-link to indicate the destination from which a message-driven bean should consume messages; I'll discuss this in more detail in the next section.
Message-driven beans
The final change to J2EE's messaging system that you shall look at in this article is to message-driven beans (MDBs). In the EJB 2.0 specification, an MDB had to implement the interface javax.jms.MessageListener. In the EJB 2.1 specification, this has been relaxed so that an MDB can implement any interface, as long as you can find someone prepared to deliver messages to it! For a JMS provider, you will typically still implement javax.jms.MessageListener, and indeed, you don't need to change your MDB code to accommodate the new specification. However, in order to support other interfaces, the deployment descriptor has changed radically.
Listing 4 shows a typical example of an MDB deployment descriptor for an EJB 2.0 application. Listing 5 shows how the deployment descriptor might look when the same MDB is used as part of an EJB 2.1 application.
Listing 4. EJB 2.0 deployment descriptor
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>DurablePubSubMDB</ejb-name>
<ejb-class>example.ExampleMDB</ejb-class>
<transaction-type>Bean</transaction-type>
<acknowledge-mode>Auto-acknowledge</acknowledge-mode>
<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
<subscription-durability>Durable</subscription-durability>
</message-driven-destination>
<message-selector>
JMSType = 'person' AND gender = 'male'
</message-selector>
</message-driven>
</enterprise-beans>
</ejb-jar>
|
Listing 5. EJB 2.1 deployment descriptor
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>DurablePubSubMDB</ejb-name>
<ejb-class>example.ExampleMDB</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Bean</transaction-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>
acknowledgeMode
</activation-config-property-name>
<activation-config-property-value>
Auto-acknowledge
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
destinationType
</activation-config-property-name>
<activation-config-property-value>
javax.jms.Topic
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
subscriptionDurability
</activation-config-property-name>
<activation-config-property-value>
Durable
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
messageSelector
</activation-config-property-name>
<activation-config-property-value>
JMSType = 'person' AND gender = 'male'
</activation-config-property-value>
</activation-config-property>
</activation-config>
</message-driven>
</enterprise-beans>
</ejb-jar>
|
The first thing to note is the new messaging-type element, which gives the name of the interface class that the MDB implements. Secondly, the acknowledge-mode, destination-type, subscription-durability, and message-selector have been removed. Instead, there is a generic set of name-value pairs. For a JMS provider, the JCA specification recommends that the names and values given in Table 1 be supported.
Table 1. Recommended JMS activation-config-property values
activation-config-property-name | activation-config-property-value | destinationType | javax.jms.Queue or javax.jms.Topic | acknowledgeMode | Auto-acknowledge (default) or Dups-ok-acknowledge | subscriptionDurability | NonDurable (default) or Durable | messageSelector | String selector |
Unfortunately, these properties are only recommended, and not every JMS provider will necessarily support them. How do you know what properties are supported by your provider? The contract between a JMS provider and an application server for the delivery of messages to an MDB is now defined in the "Message Inflow" chapter of the JCA specification. As part of this contract, the JMS provider implements a JavaBeans component called an ActivationSpec whose properties are those required by the provider in order to deliver messages. The administrator may define default values for these properties, but when the application containing the MDB is deployed, these will be overridden by any activation-config-property elements defined in the MDB's deployment descriptor. A JMS provider that is following the recommendation will therefore have properties called destinationType, acknowledgeMode, subscriptionDurability, and messageSelector on its ActivationSpec.
Note that under this new model, you can leave a property out of the MDB's deployment descriptor and define it administratively instead. Conversely, there may be other, provider-specific properties that you previously had to define administratively that you can now include in the MDB's deployment descriptor (although this will obviously reduce your application's portability).
One of the other recommended properties for a JMS ActivationSpec is destination. Unfortunately, the JCA specification does not define the type of this property or what it means. For example, it could be a string requiring the deployer to specify some provider-specific name for the destination. One special case is when the type of the property is javax.jms.Destination. In this scenario, the application assembler can specify a message-destination-link in the MDB's deployment descriptor to indicate the message-destination from which the MDB should receive messages. Rather than being part of a message-destination-ref, this link, along with its message-destination-type, is defined as a direct sub-element of the message-driven element, as shown in the example deployment descriptor in Listing 6. At runtime, the application server can then resolve this link into a JMS destination object and set it into the destination property on the ActivationSpec.
Listing 6. EJB 2.1 deployment descriptor showing an MDB message-destination-link
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>DurablePubSubMDB</ejb-name>
<ejb-class>example.ExampleMDB</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
...
<message-destination-type>
javax.jms.Queue
</message-destination-type>
<message-destination-link>
destination
</message-destination-link>
...
</message-driven>
...
</enterprise-beans>
...
</ejb-jar>
|
Conclusion
There are many changes and new functionalities for JMS applications in the J2EE 1.4 specification. Some of the changes, such as the tightening of the restriction on the use of setMessageListener() to include the Web container, the limit of one Session per Connection, and the overhauled MDB deployment descriptor, will require existing applications to be modified. Other changes, such as the unified JMS 1.1 API, will simplify the programming of new applications and extend existing functionality. Whether you are migrating J2EE 1.3 applications or have the luxury of writing new applications, I hope that, as more J2EE 1.4 application servers become available, you will find this article of use.
Resources
About the author  | 
|  | David Currie is a software engineer at the IBM development lab in Hursley, UK. He has worked on WebSphere Application Server since version 3.5, initially in the area of transactions and more recently on messaging support. You can reach David at david_currie@uk.ibm.com. |
Rate this page
|  |