Enterprise Java

Java EE 7: JMS 2.0 With Glassfish v4

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.
 

Reference: Java EE 7: JMS 2.0 With Glassfish v4 from our JCG partner Piotr Nowicki at the Piotr Nowicki’s Blog blog.

Piotr Nowicki

Piotr is a Java fascinate since his computer science studies (2007). He's currently working as a senior Java EE developer in utilities and industry sector. He's mostly interested in designing and development of web applications using Java EE technology stack.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Kavinder Singh
Kavinder Singh
11 years ago

very nicely explained article for people who even don’t have idea about the JMS1.1 . Even I got the idea how this will work.

Al
Al
11 years ago

Useless. J2EE tutorial already shows all of this. How about you show how to connect using a Java client instead? That is almost impossible to find on the web.

Back to top button