Part One

What’s New in JMS 2.0: Ease of Use

Chris Mayer
Java-logo.11

Oracle’s Nigel Deakin shows how to new ease-of-use features enable you to write fewer lines of code

Continuing our Java EE 7 series, Oracle’s Nigel Deakin shows
how to new ease-of-use features enable you to write fewer lines of
code. Reprinted with permission from the Oracle Technology
Network, Oracle Corporation.

This article, which is the first article in a
two-part series, assumes a basic familiarity with Java Message
Service (JMS) 1.1 and introduces some of the new ease-of-use
features in JMS 2.0. In Part Two, we will look at new messaging
features.

JMS 2.0, which was released in April 2013, is the
first update to the JMS specification since version 1.1 was
released in 2002. One might think that an API that has remained
unchanged for so long has grown moribund and unused. However, if
you judge the success of an API standard by the number of different
implementations, JMS is one of the most successful APIs around.

In JMS 2.0, the emphasis has been on catching up with
the ease-of-use improvements that have been made to other
enterprise Java technologies. While technologies such as Enterprise
JavaBeans or Java persistence are now much simpler to use than they
were a decade ago, JMS had remained unchanged with a successful,
but rather verbose, API.

The single biggest change in JMS 2.0 is the
introduction of a new API for sending and receiving messages that
reduces the amount of code a developer must write. For applications
that run in a Java EE application server, the new API also supports
resource injection. This allows the application server to take care
of the creation and management of JMS objects, simplifying the
application even further.

JMS 2.0 is part of the Java EE 7 platform and can be
used in Java EE Web or EJB applications, or it can be used
standalone in a Java SE environment. As I explain below, some of
the features described here are available only in a standalone
environment while others are available only in Java EE Web or EJB
applications.

Simplified API

The new API is known as the simplified API.
As the name suggests, it is intended to be simpler and easier to
use than the existing JMS 1.1 API, which is (rather predictably)
now referred to as the classic API.

The simplified API consists of three new interfaces:
JMSContext, JMSProducer, and JMSConsumer:

  • JMSContext replaces the separate Connection and
    Session objects in the classic API with a single object.
  • JMSProducer is a lightweight replacement for the
    MessageProducer object in the classic API. It allows message
    delivery options, headers, and properties to be configured using
    method chaining (sometimes known as a builder
    pattern
    ).
  • JMSConsumer replaces the MessageConsumer object in
    the classic API and is used in a similar way.

Developers now have a choice as to whether to use the
classic API (the familiar Connection, Session, MessageProducer,
andMessageConsumer objects of JMS 1.1) or the simplified API (the
JMSContext, JMSProducer, and JMSConsumer objects introduced in JMS
2.0).

The simplified API offers all the features of the
classic API plus some additional features. The classic API is not
deprecated and will remain part of JMS indefinitely.

Using the Simplified API to Send a
Message

The JMS 1.1 classic API has been in use for over a
decade and has proven its usefulness. In what ways is the JMS 2.0
simplified API better? The JMS 2.0 simplified API requires less
code.

Listing 1 below shows a simple
example that uses the classic API to send a single text
message.

Listing 1

 

public void sendMessageJMS11(ConnectionFactory connectionFactory, Queue queueString text) {
   try {
      Connection connection = connectionFactory.createConnection();
      try {
         Session session =connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
         MessageProducer messageProducer = session.createProducer(queue);
         TextMessage textMessage = session.createTextMessage(text);
         messageProducer.send(textMessage);
      } finally {
         connection.close();
      }
   } catch (JMSException ex) {
      // handle exception (details omitted)
   }
}

 

Now compare Listing 1 to Listing 2, which shows how
we might do exactly the same thing using the simplified API in JMS
2.0:

Listing 2

 

public void sendMessageJMS20(ConnectionFactory connectionFactory, Queue queue, 
String text) {
   try (JMSContext context = connectionFactory.createContext();){
      context.createProducer().send(queue, text);
   } catch (JMSRuntimeException ex) {
      // handle exception (details omitted)
   }
}

 

 

As you can see, the amount of code we have to write
is significantly reduced. Let’s look at this in more detail.

  • Instead of creating separate Connection and Session
    objects, we create a single JMSContext object.
  • The JMS 1.1 version used a finally block to call
    close on the Connection after use. In JMS 2.0, the JMSContext
    object also has a close method that needs to be called after use.
    However, there’s no need to explicitly call close from your
    code.JMSContext implements the Java SE 7 java.lang.AutoCloseable
    interface. This means that if we create the JMSContext in
    atry-with-resources block (also a new feature of Java SE 7), the
    close method will be called automatically at the end of the block
    without the need to explicitly add it to your code.

    In fact, all JMS interfaces that have a close method have been
    extended to implement the java.lang.AutoCloseable interface, so
    they can all be used in try-with-resources blocks. This includes
    the Connection and Session interfaces as well asJMSContext. So even
    if you’re using the classic API, you can still benefit from this
    feature. Note that because of this change, JMS 2.0 can be used only
    with Java SE 7.

  • When the JMS 1.1 version created the Session
    object, it passed in the parameters (false and
    Session.AUTO_ACKNOWLEDGE) to specify that we want to create a
    non-transacted session in which any received messages will be
    acknowledged automatically. In JMS 2.0, this is the default (for
    Java SE applications), so we don’t need to specify any
    parameters.

    If we wanted to specify one of the other session modes (local
    transaction, CLIENT_ACKNOWLEDGE, or DUPS_OK_ACKNOWLEDGE), we would
    pass in just a single parameter rather than two.

  • There’s no need to create a TextMessage object and
    set its body to be the specified string. Instead, we simply pass
    the string into the send method. The JMS provider will
    automatically create a TextMessage and set its body to the supplied
    string.
  • The JMS 1.1 example provided a catch block for the
    JMSException that almost all methods throw. The JMS 2.0 simplified
    API example has a similar block, but it catches a
    JMSRuntimeException instead.

    One feature of the simplified API is that its methods do not
    declare checked exceptions. If an error condition is encountered,
    aJMSRuntimeException is thrown. This new exception is a subclass of
    RuntimeException, which means it does not need to be explicitly
    caught by the calling method or declared in its throws clause. This
    contrasts with the classic API, in which almost every method is
    declared to throw a JMSException that the calling method must
    either catch or throw itself.

Both Listing 1 and Listing
2
show the ConnectionFactory and Queue objects being
passed in as parameters. The way that these are obtained hasn’t
changed, so we won’t cover this here or in the other listings in
this article. Typically, they would be obtained by a JNDI lookup
from a JNDI repository.

Using the Simplified API to Receive Messages
Synchronously

Listing 3 shows a simple example
that uses JMS 1.1 to receive a single TextMessage synchronously and
extract its text.

Listing 3

 

public String receiveMessageJMS11(ConnectionFactory connectionFactory,Queue queue){
   String body=null;
   try {
      Connection connection = connectionFactory.createConnection();
      try {
         Session session =connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
         MessageConsumer messageConsumer = session.createConsumer(queue);
         connection.start();
         TextMessage textMessage = TextMessage)messageConsumer.receive();
         body = textMessage.getText();
      } finally {
         connection.close();
      }
   } catch (JMSException ex) {
      // handle exception (details omitted)
   }
   return body;

 

 

Listing 4 shows how we might do
exactly the same thing using the simplified API in JMS 2.0:

Listing 4

 

public String receiveMessageJMS20(
ConnectionFactory connectionFactory,Queue queue){
   String body=null;
   try (JMSContext context = connectionFactory.createContext();){
      JMSConsumer consumer = session.createConsumer(queue);
      body = consumer.receiveBody(String.class);
   } catch (JMSRuntimeException ex) {
      // handle exception (details omitted)
   }
   return body;
}

 

 

As with sending a message, the amount of code we have
to write is reduced. Some of the reasons are the same as in the
previous example:

  • Instead of creating separate Connection and Session
    objects, we create a single JMSContext object.
  • We can create the JMSContext in a
    try-with-resources block so that it will be closed automatically at
    the end of the block. This removes the need to call close.
  • We don’t need to specify that we want received
    messages to be acknowledged automatically, because that is the
    default.

Also, there are two additional ways in which JMS 2.0
reduces the amount of code needed to receive a message:

  • Whereas in JMS 1.1 we need to call
    connection.start() to start delivery of messages to the consumer,
    in the JMS 2.0 simplified API we don’t: the connection is
    automatically started. (You can disable this behavior if you need
    to.)
  • There’s no need to receive a Message object, cast
    it to a TextMessage, and then call getText to extract the message
    body. Instead, we call receiveBody, which returns the message body
    directly.

  

    Using the Simplified API to Receive Messages
    Asynchronously

    The previous examples showed how to receive messages
    synchronously by calling a method that blocked until a message was
    received or a timeout occurred.

    If you need to receive messages asynchronously in a
    Java SE application, in JMS 1.1 you need to create a
    MessageConsumer object and then use the method setMessageListener
    to specify an object that implements the MessageListener interface.
    You then need to call connection.start() to start delivery of
    messages:

    MessageConsumer messageConsumer =
    session.createConsumer(queue);

    messageConsumer.setMessageListener(messageListener);

    connection.start();

    The JMS 2.0 simplified API code is similar. You need
    to create a JMSConsumer object and then use the method
    setMessageListenerto specify the object that implements the
    MessageListener interface. Message delivery is started
    automatically; there’s no need to callstart.

    JMSConsumer consumer =
    context.createConsumer(queue);

    consumer.setMessageListener(messageListener);

    Note that if you need to receive messages
    asynchronously in a Java EE 7 Web or EJB application then, as with
    previous versions of Java EE, you need to use a message-driven bean
    rather the setMessageListener method.

    Injecting a
    JMSContext into a Java EE
    Application

    If you’re writing a Java EE Web or EJB application,
    then using the JMS 2.0 simplified API is even easier than in Java
    SE. This is because you can now “inject” the JMSContext into your
    code and leave it to the application server to work out when to
    create it and when to close it.

    The following code is a fragment from a Java EE 7
    session bean or servlet that injects a JMSContext and uses it to
    send a message:

     

    @Inject @JMSConnectionFactory(
    "jms/connectionFactory") private JMSContext context;
    
    @Resource(lookup = "jms/dataQueue") private Queue dataQueue;
    
    public void sendMessageJavaEE7(String body) {
       context.send(dataQueue, body);
    }
    

     

     

    As you can see, there is no code to create the
    JMSContext and no code to close it. Instead, we simply declare a
    field of typeJMSContext and add the annotations @Inject and
    @JMSConnectionFactory.

    The @Inject annotation tells the container to create
    the JMSContext when it is needed. The @JMSConnectionFactory
    annotation tells the container the JNDI name of the
    ConnectionFactory that it should use.

    If the injected JMSContext is used in a JTA
    transaction (whether container-managed or bean-managed), the
    JMSContext is considered to have transaction scope. This
    means that after the JTA transaction is committed, the JMSContext
    will be automatically closed.

    If the injected JMSContext is used when there is no
    JTA transaction, the JMSContext is considered to have request
    scope
    . This means that the JMSContext will be closed when the
    request comes to an end. The length of a request is defined in the
    Contexts and Dependency Injection (CDI) specification, and it
    typically relates to an HTTP request from a client or a message
    received by a message-driven bean.

    An injected JMSContext has some powerful features in
    addition to being created and closed automatically by the
    application server. The most important is that if a servlet calls a
    session bean, or one session bean calls another, and both use an
    injected JMSContext, then as long as the two injected JMSContext
    objects are defined in the same way (for example, they have the
    sameJMSConnectionFactory annotation), they will actually correspond
    to the same JMSContext object. This reduces the number of JMS
    connections used by the application.

    Other API Simplifications in JMS
    2.0

    JMS 2.0 also provides several other
    simplifications.

    New Method to Extract the Body Directly from
    a Message

    A JMS message consists of three parts:

    • The message headers
    • The message properties
    • The message body

    The type of the body varies with the message type:
    the body of a TextMessage is a String. The body of a BytesMessage
    is a byte array, and so on.

    In JMS 1.1, the message body is obtained using
    methods specific to the message type, such as the getText method on
    TextMessage. However, when a message is received by an application,
    the JMS API always provides the message as a javax.jms.Message
    object, which needs to be cast to the appropriate subtype before
    the body can be obtained. This applies both when the message has
    been returned from a call to receive and when the message has been
    delivered asynchronously by a call to the onMessage method of
    aMessageListener.

    For example, if you used the receive method to
    receive a TextMessage, you’d need to cast the returned object from
    a Message to aTextMessage and then call the getText method:

     

    Message message = consumer.receive(1000); // returns a TextMessage
    
    String body = ((TextMessage) message).getText();
    

     

    JMS 2.0 has added a new method that makes it slightly
    simpler to extract the body of a message. This is the getBody
    method onMessage, and it is available to users of both the classic
    and simplified APIs. This method takes the expected body type as a
    parameter and does not require you to perform a cast on either the
    message or the body:

     

    Message message = consumer.receive(1000); // returns a TextMessage
    
    String body = message.getBody(String.class);
    

     

    Let’s look at how getBody simplifies the code needed
    to obtain the body of other message types.

    If the message is a BytesMessage, JMS 1.1 provides
    several ways to extract the byte array from a BytesMessage. The
    simplest is to call the readBytes method on BytesMessage. This
    copies the bytes to the specified byte array.

    Here’s an example of a MessageListener that receives
    a BytesMessage and obtains the body as a byte array:

     

    void onMessage(Message message){ // delivers a BytesMessage
       int bodyLength = ((BytesMessage)message).getBodyLength();
       byte[] bytes = new byte[bodyLength];
       int bytesCopied = ((BytesMessage)message).readBytes(bytes);
       ...
    

     

     

    In JMS 2.0, the getBody method makes this much
    simpler:

     

    void onMessage(Message message){ // delivers a BytesMessage
       byte[] bytes = message.getBody(byte[].class);
       ...
    

     

    If the message is an ObjectMessage, in JMS 1.1 you
    need to call the getObject method on ObjectMessage and then cast
    the returned Serializable to the expected body type:

     

    void onMessage(Message message){ // delivers an ObjectMessage
       MyObject body = (MyObject)((ObjectMessage) message).getObject();
       ...
    

     

     

    Note the need to perform two casts. You need to cast
    the message from Message to ObjectMessage so you can call
    getObject. This returns the body as a Serializable, which you then
    need to cast to the actual type.

    In JMS 2.0, you can do this without any casts:

     

    void onMessage(Message message){ // delivers an ObjectMessage
       MyObject body = message.getBody(MyObject.class);
       ...
    

     

     

    Finally, if the message is a MapMessage, the getBody
    method allows you to return the body as a Map:

     

    Message message = consumer.receive(1000); // returns a MapMessage
    Map body = message.getBody(Map.class);
    

     

     

    The one message type for which getBody cannot be used
    is StreamMessage. This is because the stream typically consists of
    multiple objects that the application should read individually.

    When using the getBody method, if the specified class
    does not match the body type, a MessageFormatException is thrown. A
    companion method, isBodyAssignableTo, has also been added to
    Message, and it can be used to test whether a subsequent call
    togetBody would be able to return the body of a particular Message
    object as a particular type. This is useful if more than one type
    of message is expected.

    Methods to Receive a Message Body
    Directly

    In JMS 1.1, an application consuming messages
    synchronously uses the receive(), receive(timeout), or
    receiveNoWait()methods on MessageConsumer.

    In JMS 2.0, applications using the simplified API can
    do this using the same methods on JMSConsumer.

    These methods return a Message object from which the
    message body can be obtained. The getBody method described
    previously provides an easy way to obtain the body from this
    object.

    Applications using the JMS 2.0 simplified API have an
    additional option. JMSConsumer provides three
    methods—receiveBody(class), receiveBody(class, timeout), and
    receiveBodyNoWait(class)—that will receive the next message
    synchronously and return its body. As with getBody, the expected
    class is passed in as a parameter.

    So instead of using the code in Listing 5 or Listing
    6, the application can use the single line shown in Listing 7.

     

    Listing 5

    JMSConsumer consumer = ...
    Message message = consumer.receive(1000); // returns a TextMessage
    String body = ((TextMessage) message).getText();
    

    Listing 6

     

    JMSConsumer consumer = ...
    Message message = consumer.receive(1000); // returns a TextMessage
    String body = message.getBody(String.class);
    

    Listing 7

     

    JMSConsumer consumer = ...
    String body = consumer.receiveBody(String.class,1000);
    

    The receiveBody methods can be used to receive any
    type of message except for StreamMessage (for the same reason that
    this message type does not support getBody) and Message (since it
    has no body), as long as the class of the expected body is known in
    advance.

    These new methods have been added only to
    JMSConsumer. They have not been added to MessageConsumer. This
    means that this feature is available only to applications using the
    JMS 2.0 simplified API.

    Furthermore, these methods do not provide access to
    the message headers or properties (such as the JMSRedelivered
    message header field or the JMSXDeliveryCount message property), so
    they should be used only if the application has no need to access
    them.

    New Methods to Create a
    Session

    In JMS 1.1, the following method on a
    javax.jms.Connection was used to create a javax.jms.Session, where
    the transacted parameter needed to be set to true or false and the
    acknowledgeMode parameter needed to be set
    toSession.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, or
    Session.DUPS_OK_ACKNOWLEDGE.

     

    Session createSession(
      boolean transacted, int acknowledgeMode) throws JMSException
    

     

    This has always been a rather confusing method for
    two main reasons:

    • It used two arguments to define a single aspect of
      the session.
    • In a Java EE transaction, both arguments were
      ignored anyway.

    Let’s consider these two problems in turn.

    Two Arguments to Define the Same
    Thing

    The first problem with the createSession method in
    JMS 1.1 is that it used two arguments to define what was, in
    practice, a single aspect of the session with four
    possibilities:

    • If the transacted parameter was set to false, the
      session was non-transacted and the acknowledgeMode parameter was
      used to specify which of three kinds of acknowledgment should be
      used when receiving messages.
    • If the transacted parameter was set to true, the
      acknowledgeMode parameter was ignored.

    In addition to being unnecessarily complicated, this
    led to code that was potentially misleading, because if the
    transacted parameter was set to false, the user still had to set
    the acknowledgeMode parameter to some value even if it is ignored.
    For example, the following code was perfectly valid:

     

    amb Session session = 
      connection.createSession(true,Session.AUTO_ACKNOWLEDGE);iguous
    

     

    In a Java EE Transaction, Both Arguments Are
    Ignored Anyway

    The second problem with the createSession method in
    JMS 1.1 is that in a Java EE Web or EJB application, if there is a
    current JTA transaction (as there is by default), both parameters
    to createSession are ignored anyway. However, since the API forced
    developers to specify two parameters, which led to highly
    misleading code, a user writing an EJB bean might write the
    following code in not realizing that the session would actually use
    the EJB’s container-managed JTA transaction.

     

    Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
    

     

    To address these problems, two new methods with the
    name createSession have been added to javax.jms.Connection. One has
    a single parameter and one has no parameters at all. We’ll discuss
    these in turn.

    JMS 2.0:
    createSession with One
    Parameter

    In JMS 2.0, a second createSession method has been added to
    javax.jms.Connection. This has a single parameter,sessionMode:

     

    Session createSession(int sessionMode) throws JMSException
    

     

    In a normal Java SE environment, sessionMode can be
    set to Session.AUTO_ACKNOWLEDGE,
    Session.CLIENT_ACKNOWLEDGE,Session.DUPS_OK_ACKNOWLEDGE, or
    Session.TRANSACTED. In a Java EE transaction, sessionMode is
    ignored.

    JMS 2.0:
    createSession with No
    Parameters

    In a Java EE transaction, even passing a single
    parameter to createSession is misleading because the parameter is
    ignored if there is a Java EE transaction. To allow users to write
    code that is less misleading, a third createSession method has been
    added tojavax.jms.Connection that has no parameters:

     

    Session createSession() throws JMSException
    

     

    This method is particularly intended for use in a
    Java EE transaction, where there is no point in specifying a
    session mode because it is ignored. However, this method can also
    be used in a normal Java SE environment, in which case it is
    equivalent to callingcreateSession(Session.AUTO_ACKNOWLEDGE).

    JMS 2.0:
    createSession with Two
    Parameters

    The existing method createSession(boolean
    transacted,int acknowledgeMode) can still be used and will remain
    part of the API indefinitely. However, developers are encouraged to
    use the single- or no-parameter versions of this method
    instead.

      

    Easier Resource Configuration in JMS
    2.0

    JMS 2.0 enables easier resource configuration in
    several ways.

    Default Connection Factory in Java
    EE

    Java EE 7 introduces a platform default JMS
    connection factory
    . This is a built-in connection factory that
    connects to the application server’s built-in JMS provider.

    Applications can obtain this connection factory by
    performing a JNDI lookup using the
    namejava:comp:DefaultJMSConnectionFactory without the need to
    previously create the connection factory using administrative
    tools:

     

    @Resource(lookup="java:comp/DefaultJMSConnectionFactory") ConnectionFactory cf
    

     

    This connection factory is intended for use by the
    many applications that use the built-in JMS provider and don’t need
    to add any application-specific configuration.

    When injecting a JMSContext into an application, the
    JMSConnectionFactory annotation is used to specify the connection
    factory to be used:

     

    @Inject @JMSConnectionFactory("
      jms/connectionFactory") JMSContext context1;
    

     

    If this annotation is omitted, the default connection
    factory will be used:

     

    @Inject JMSContext context2; // uses the platform default connection factory
    

     

    JMS Resource Definition Annotations in Java
    EE

    Every JMS application starts with a connection
    factory (an object that implements javax.jms.ConnectionFactory) and
    at least one destination (an object that implements either
    javax.jms.Queue or javax.jms.Topic). The ConnectionFactory is the
    object that in JMS is used to create the connection to the JMS
    provider, and the Queue or Topic is the object that identifies the
    physical queue or topic that messages are being sent to or received
    from.

    The way in which these objects are created, and the
    way in which they are configured, varies from one JMS provider to
    another. That’s why JMS recommends that you use a separate,
    provider-specific tool to create, configure, and bind into a JNDI
    store the connection factories and destinations that your
    application needs. Your application can then use JNDI to look up
    these objects without needing to use any nonstandard code. In
    addition to keeping your application code portable, this also means
    you can write your code without needing to know details about how
    it will be deployed.

    When configuring a ConnectionFactory, it is often
    necessary to know things such as the host name and port of the JMS
    server. When configuring a Queue or Topic object, it is usually
    necessary to know the physical name of the queue or topic. Creating
    yourConnectionFactory, Queue, and Topic objects separately from the
    application and storing them in JNDI allows these details to be
    defined by the deployer or administrator, not the developer.

    Although this separation of code from configuration
    is essential in many enterprise environments, it can be an unwanted
    burden in simpler environments. In addition, if the application is
    being deployed into an automated platform as a service (PaaS)
    system, it might be desirable to automate the provisioning of the
    ConnectionFactory, Queue, and Topic objects required by the
    application.

    In many Java EE applications, the default JMS
    connection factory that is now available in any Java EE 7
    application server (which was described in the previous section)
    removes the need for any connection factories to be configured at
    all. However, for those cases when a specially configured
    connection factory is needed—and for queues and topics—Java EE
    7 offers another new feature that allows these objects to be
    created using either annotations in the code, XML elements in the
    deployment descriptor, or a combination of both.

    The main new annotations are
    javax.jms.JMSConnectionFactoryDefinition and
    javax.jms.JMSDestinationDefinition. These can be defined in any
    Java EE component class such as an EJB bean or servlet, as shown in
    Listing 8:

    Listing 8

     

    @JMSConnectionFactoryDefinition(
        name="java:global/jms/MyConnectionFactory",
        maxPoolSize = 30,
        minPoolSize= 20,
        properties = {
            "addressList=mq://localhost:7676",
            "reconnectEnabled=true"
        }
    ) 
    @JMSDestinationDefinition(
        name = "java:global/jms/DemoQueue",
        interfaceName = "javax.jms.Queue",
        destinationName = "demoQueue"
      )
    public class NewServlet extends HttpServlet {
      ...
    

     

    If more than one connection factory or destination
    needs to be defined, these annotations need to be enclosed within
    aJMSConnectionFactoryDefinitions or JMSDestinationDefinitions
    annotation, as shown in Listing 9:

    Listing 9

     

    @JMSConnectionFactoryDefinitions({
        @JMSConnectionFactoryDefinition(
           name="java:global/jms/MyConnectionFactory1",
           maxPoolSize = 30,
           minPoolSize= 20,       
           properties = {
              "addressList=mq://localhost:7676",
              "reconnectEnabled=true"
           }
        ),
        @JMSConnectionFactoryDefinition(
           name="java:global/jms/MyConnectionFactory2",
           maxPoolSize = 30,
           minPoolSize= 20,
           properties = {
              "addressList=mq://localhost:7677",
              "reconnectEnabled=true"
           }
        ) 
    })
    @JMSDestinationDefinitions({
        @JMSDestinationDefinition(
           name="java:global/jms/DemoQueue1",
           interfaceName = "javax.jms.Queue",
           destinationName = "demoQueue1"
        ),
        @JMSDestinationDefinition(
           name="java:global/jms/DemoQueue2",
           interfaceName = "javax.jms.Queue",
           destinationName = "demoQueue2"
        ) 
    })
    public class NewServlet extends HttpServlet {
      ...
    


     

    The JMSConnectionFactoryDefinition annotation defines
    a number of standard attributes that can be specified including
    name(which is the JNDI name), clientId, user, password,
    maxPoolSize, and minPoolSize. In addition, the properties attribute
    can be used to specify additional nonstandard properties that the
    application server might support. In Listing 8 and
    Listing 9, addressList andreconnectEnabled are
    examples of such nonstandard properties.

    The JMSConnectionFactoryDefinition annotation defines
    a smaller number of standard attributes that can be specified
    includingname (which is the JNDI name) and destinationName (which
    is the provider-specific queue or topic name), as well as allowing
    the properties attribute to be used to specify additional
    nonstandard properties.

    Connection factories and destinations defined in this
    way must be in the java:comp, java:module, java:app, or
    java:globalnamespaces, and they typically exist for as long as the
    application that defines them is deployed.

    It is also possible to specify these definitions in
    the deployment descriptor file (for example, web.xml or
    ejb-jar.xml), as shown in Listing 10:

    Listing 10

     

    <jms-connection-factory>
       <name>java:global/jms/MyConnectionFactory</name>
       <max-pool-size>30</max-pool-size>
       <min-pool-size>20</min-pool-size>
       <property>
          <name>addressList</name>
          <value>mq://localhost:7676</value>
       </property>
       <property>
          <name>reconnectEnabled</name>
          <value>true</value>
       </property>    
    </jms-connection-factory>
    
    
    <jms-destination>
       <name>java:global/jms/DemoQueue</name>
       <interfaceName>javax.jms.Queue</interfaceName>
       <destinationName>demoQueue</destinationName> 
    </jms-destination>
    

     

    If desired, the developer can specify some of the
    required attributes in the annotation, with the deployer specifying
    the remaining attributes in the deployment descriptor. This can be
    useful in the case of attributes whose values are not known until
    deployment time.

    In all the examples above, the application server is
    responsible for “provisioning” the JNDI resources that are defined
    in the annotations or deployment descriptor. However, it remains
    the responsibility of the deployer to ensure that the JMS server to
    which the connection factory refers is installed and available, and
    that the physical queues and topics themselves have been
    created.

    Conclusion

    In this article, we covered the ease-of-use features
    added to JMS 2.0, which enable developers to write significantly
    fewer lines of code. In Part Two, we will look at the new messaging
    features in JMS 2.0.

    See Also

    Author Bio: Nigel Deakin, a
    Principal Member of Technical Staff at Oracle, was Specification
    Lead for JSR 343, Java Message Service 2.0. In addition to his
    responsibilities for leading the next versions of the JMS
    specification, he is a member of Oracle’s JMS development team,
    working on Open Message Queue and the GlassFish application server.
    He has spoken recently at JavaOne in San Francisco, US, and at
    Devoxx in Antwerp, Belgium, and he is based in Cambridge,
    UK.

    Reprinted with permission from the Oracle
    Technology Network, Oracle Corporation.

Author
Comments
comments powered by Disqus