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
orSession
, JMSContext
is a new object that combinesConnection
andSession
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 objectMsg = (ObjectMessage)message;
// BusinessObject payload = (BusinessObject)objectMsg.getObject();
BusinessObject payload = message.getBody(BusinessObject.class);
System.out.println("Message received: " + payload);
} catch (JMSException e) {
System.err.println("Error fetching msg 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.