Java EE 7 specification is going to be fully implemented in the new release of the application server named WildFly. One of the additions included is the new release (2.0) of the Java Messaging Service which includes some enhancements oriented toward the ease of development. Let’s see in detail.
In order to run these samples you need to download a fresh copy of WildFly 8 application server – in my case I’ve tested the samples against the release Beta1 of the application server.
Before the advent of JMS 2.0 a JMS publisher had to create lots of Objects (Factory, Connection, Session) in order to be able to send a JMS message from a Producer to a Consumer:
import javax.annotation.Resource; import javax.enterprise.context.RequestScoped; import javax.jms.*; @RequestScoped public class JMSService { @Resource(mappedName = "java:jboss/jms/queue/exampleQueue") private Queue queueExample; @Resource(mappedName = "java:/JmsXA") private ConnectionFactory cf; private Connection connection; public void sendMessage(String txt) { try { connection = cf.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer publisher = null; publisher = session.createProducer(queueExample); connection.start(); TextMessage message = session.createTextMessage(txt); publisher.send(message); } catch (Exception exc) { exc.printStackTrace(); } finally { if (connection != null) { try { connection.close(); } catch (JMSException e) { e.printStackTrace(); } } } } }
Although the above code can be optimized by using CDI Producer that’s a whole lot of code to send a JMS Message!
Now here’s the same code using JMS 2.0:
@RequestScoped public class JMSService { @Resource(mappedName = "java:jboss/jms/queue/exampleQueue") private Queue queueExample; @Inject JMSContext context; public void sendMessage(String txt) { try { context.createProducer().send(queueExample, txt); } catch (Exception exc) { exc.printStackTrace(); } } }
The most interesting part is the introduction of the JMSContext:
@Inject JMSContext context;
A JMS Context is a new interface which can be used to combine in a single object the functionality of two separate objects from the JMS 1.1 API: a Connection and a Session. In the above code, the JMS Context uses the default JMSConnectionFactory which is bound at “java:/ConnectionFactory”. You can however specify to use a non-default connection factory by including the @JMSConnectionFactory annotation which needs as argument its JNDI binding:
@Inject @JMSConnectionFactory("java:/AnotherConnectionFactory") JMSContext context;
The JMSContext is not just for producing messages but it can be used as well for creating consumers in a similar way. For example, if you were to create an Async consumer of JMS messages for the same queue, the you could use the following template:
@Stateless public class SyncReceiver { @Inject private JMSContext context; @Resource(mappedName = "java:jboss/jms/queue/exampleQueue") Queue myQueue; public String startReceiver() { String message = context.createConsumer(myQueue).receiveBody(String.class); return message; } }
Another cool addition of JMS 2.0 is the Automatic resource definition which can be useful if you are setting up some prototype projects or if you need to create a quick-test environment without the burden to define Destination from the management instruments.
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:
@JMSDestinationDefinition(name = "java:jboss/jms/queue/newQueue", destinationName="newQueue", description="newQueue", interfaceName = "javax.jms.Queue") @JMSConnectionFactoryDefinition(name = "java:/AnotherConnectionFactory") public class JMSService { . . . }
Compiling the JMS 2.0 classes
In order to be able to compile the above classes you need the jboss-jms-api_2.0_spec dependency, which can be solved with:
<dependency> <groupId>org.jboss.spec.javax.jms</groupId> <artifactId>jboss-jms-api_2.0_spec</artifactId> <version>1.0.0.Final</version> </dependency>
As an alternative, you can use the following POM to import automatically your dependency version:
<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-7.0</artifactId> <version>1.0.3.Final</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec.javax.jms</groupId> <artifactId>jboss-jms-api_2.0_spec</artifactId> <scope>provided</scope> </dependency> </dependencies>