Java EE 7 has been recently accepted by the Executive Committee. This means that soon we should have Java EE 7 application servers available on the market. One of the specifications that constitutes Java EE 7 is JMS 2.0. Some interesting improvements were introduced since version 1.1.

JMS has a lot of weird stuff like: Connection#createSession(boolean transacted, int acknowledgeMode) method.

First method argument (transacted) defines if the session should be transacted. Java EE applications have JTA that takes care of transactions. We can choose if transactions are supported (default) or not. So why do we need this argument then?

Second method argument (acknowledgeMode) is an integer constant taken from Session object. Seriously, integers constants makes the API seems sooo legacy.

Finally, what is a meaning of those parameters in Java EE environment? JavaDoc says that acknowledgeMode is ignored if the session is transacted.

What does it all means? Nothing less than: those arguments doesn’t have any sense for JMS producer used in Java EE context. That’s why they encourage you to use (true, 0) parameter values to avoid unnecessary confusion.

This smells of legacy.

Ok, but let’s get back to the main subject. I wanted to take a look what’s new in JMS world and how it allows me to code easier and in more maintainable way. I’ve prepared a simple web application that uses JMS 2.0, JAX-RS and EJB (SLSB and MDB) and pushed it to my github repository here.

Prerequisites and Infrastructure

To be able to run this code you should create a sample queue. I’ve configured it under the jms/queue/myqueue JNDI name.

I was using Glassfish v4 build 87. To use Java EE 7 API I needed to add following Maven dependency:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0-b87</version>
    <scope>provided</scope>
</dependency>

which resides in this repository:

<repository>
    <id>Java EE 7</id>
    <url>https://maven.java.net/content/groups/promoted/</url>
</repository>

And that’s all for the configuration.

Payload and REST Configuration

BusinessObject is a simple object that will act like a payload for our JMS messages. It will be sent by producer and received by consumer. Nothing fancy here, so let’s move along.

RESTConfiguration is even simpler – it just defines JAX-RS endpoint prefix for our application. This prefix is: “/rest”.
You can invoke producer EJB’s by accessing your application URL /rest/producer/jms11 or /rest/producer/jms20 (e.g. http://localhost:8080/jms2_0_spike/rest/producer/jms20).

JMS Producer

Now this is where things starts to be interesting. Below you can find JMS 1.1 code for a SLSB JMS message producer:

@Stateless 
public class JMS11Producer {

    @Resource(lookup = "jms/__defaultConnectionFactory")
    private ConnectionFactory connectionFactory;

    @Resource(lookup = "jms/queue/myqueue")
    private Queue queue;

    @Path("/jms11")
    @GET
    public String produce() {
        String status = "OK";

        Connection connection = null;
        try {
            connection = connectionFactory.createConnection();
            Session session = connection.createSession(true, 0);
            MessageProducer producer = session.createProducer(queue);

            BusinessObject payload = new BusinessObject(UUID.randomUUID().toString());

            ObjectMessage message = session.createObjectMessage();
            message.setObject(payload);

            producer.send(message);
        } catch (JMSException e) {
            status = e.getMessage();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException e) {
                    status = e.getMessage();
                }
            }
        }
        return status;
    }
}

Despite the JAX-RS result status fiddling, that’s an obnoxius lot of boilerplate code that fuzzies method main responsibility. It should just send a message to the queue but it does an awful lot of things.

It creates a connection, session (nasty, ignored parameters included), object type message, initializes it and then finally sends the message to the queue… oh yeah – of course don’t forget about checked exceptions and nested try/catch blocks.

We can try to optimize it — e.g. by moving Connection creation to some @PostConstruct method and closing to @PreDestroy — but it’s still a lot of unnecesary noise.

Now let’s take a look at functionally the same code expressed in JMS 2.0:

@Stateless 
public class JMS20Producer {

    @Resource(lookup = "jms/queue/myqueue")
    private Queue queue;

    @Inject
    private JMSContext jmsContext;

    @Path("/jms20")
    @GET
    public String produce() {
        BusinessObject payload = new BusinessObject(UUID.randomUUID().toString());

        jmsContext.createProducer().send(queue, payload);

        return "OK";
    }
}

Pretty neat, huh?
It’s much easier to figure out what this method is doing: it creates a payload and sends it to the queue.

That’s it – that’s all this method is about.

Exception handling, connection and session creation, message type – everything is done for us. If those responsibilities can be moved to the container to ease developer’s life, so why not doing it?

Let’s take a look at few JMS 2.0 features used in this example:

  • no need for ConnectionFactory,
  • no need for Connection or Session,
  • JMSContext is a new object that combines Connection and Session capabilities; it can be injected by the container,
  • no checked exceptions – just JMSRuntimeException,
  • chain-invocation for message producing makes it easier to read.

JMS Consumer

Message Consumer hasn’t been changed so much since JMS 1.1. It’s still a MDB but now it’s easier to get to the expected payload because of message casting with message.getBody(Clazz):

@MessageDriven(mappedName = "jms/queue/myqueue") 
public class Consumer implements MessageListener {

    @Override
    public void onMessage(Message message) {
        try {
            // In JMS 1.1:
            //          ObjectMessage objectMessage = (ObjectMessage)message;
            //          BusinessObject payload = (BusinessObject)objectMessage.getObject();
            BusinessObject payload = message.getBody(BusinessObject.class);
            System.out.println("Message received: " + payload);
        } catch (JMSException e) {
            System.err.println("Error while fetching message payload: " + e.getMessage());
        }
    }
}

Conclusion

This was just a very quick glance at JMS 2.0. However, I found it interesting to see how much cleaner code can be produced in comparison to JMS 1.1. For more detailed information about JMS 2.0 take a look at its official specification.

Did you like this? Share it:

5 thoughts on “Java EE 7: JMS 2.0 With Glassfish v4

  1. Thanks.
    It is close to Spring JmsTemplate features
    – Reduces boilerplate code
    – Manages resources transparently
    – Handles exception properly
    – Converts checked exceptions to runtime equivalents
    – Provides convenience methods such as the convertAndSend() method that take a destination (a queue here) and a payload object as message.

    Reply
    • Hello Gregory,

      Yeah, while I was testing JMS 2.0 I also thought it seemed very close to Sprint Framework’s JMSTemplate. I guess that even before JMS 2.0 people from Java EE world would do the same using @Produces methods that take care of all the underlying mess. Nevertheless, it’s good to have such features in the specification itself.

      Thanks for your comment!

      Piotr

      Reply
  2. Very good article, but I have one question.

    JMS11Producer is wrapped in Stateless bean – so it has transactions provided by default from the container.

    Do we still have to write explicitly

    Session session = connection.createSession(true, 0);

    saying that session is transacted, instead of

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

    Reply
    • Hello Khozzy,

      Take a look at Connection Javadoc. It states that if there is an active JTA transaction than both parameters are ignored, so using (false, Session.AUTO_ACKNOWLEDGE) makes sense only if there is no active JTA transaction.

      Thanks for your comment!

      Reply
  3. Pingback: JMS 2.0 Änderungen - Java Blog | Javainsel-Blog

Leave a reply

required


*

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>