Java Message Service 2.0

Introduction: JMS specification version 1.1 was released in 2002.JMS 2.0 its first update was released in April 2013, a decade later, though JMS is one of the most successful APIs around. In JMS 2.0, the objective was to catch 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 much simpler to use now than earlier, JMS had remained unchanged with a successful, but rather verbose, API. The 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 Web or EJB 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.

Simplified API

The new API is known as the simplified API. It is intended to be simpler and easier to use than the existing JMS 1.1 API, which is now referred to as the classic API.

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

  • JMSContext: This combines in a single object the functionality of two separate objects from the JMS 1.1 API: a Connection and a Session. This can be injected by the container for web and EJB apps (container-managed) or created in the SE app by calling one of the createContext methods on a ConnectionFactory(application-managed). 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 name java:comp:DefaultJMSConnectionFactory without the need to previously create the connection factory using administrative tools. Look at the code snippet below:

@Inject
//    @JMSConnectionFactory("java:comp/DefaultJMSConnectionFactory")  ---optional
JMSContext context;

When you ask the container to inject JMSContext you can optionally specify the default connection factory in @JMSConnectionFactory annotation. Even if you don’t do it the default connection factory will be used to inject JMSContext.

  • 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, and MessageConsumer 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.

Sample:
Let us create a New à Java Web Application in NetBeans. Among the frameworks you can choose JSF. After the IDE creates a skeleton project, create the package “ram” and create the java class “Resources.java ”, the code for which is given below. From the earlier article on JMS, you can recall that ConnectionFactory and Destination (Queue or Topic) are called administered objects. Earlier you can only create them as an administrator using Web Console/ application server management tool. Now connection factory is provided by the JMS Provider and, as shown below, in code we can create Destination (Queue for Point- to- Point and Topic for Publish-Subscribe models respectively)

Resources.java

package ram;

import javax.jms.JMSDestinationDefinition;

    @JMSDestinationDefinition(
name = Resources.BOOKING_QUEUE,
interfaceName = "javax.jms.Queue"
)    
public class Resources{
public static final String BOOKING_QUEUE = "java:global/jms/bookingQueue";
}   

MessageSender-CDI bean

We will use a CDI bean to send the message and the bean will act as a backing bean to a JSF page from which it will obtain the message to be sent.

MessageSender.java

package ram;

import javax.inject.Inject;
import javax.jms.JMSContext;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import javax.jms.Queue;

@Named  // ---------1)
@ApplicationScoped
public class MsgSender {
private String msgText;

@Inject
//    @JMSConnectionFactory("java:comp/DefaultJMSConnectionFactory")
JMSContext context;

    @Resource(mappedName = Resources.BOOKING_QUEUE) //----------2)
Queue bookingQueue;

    public String sendMsg() {
sendMessageToQueue( msgText);  //-------4)
return "confirmation";
}
private void sendMessageToQueue(String messageData) {
context.createProducer().send(bookingQueue, messageData);  //-------3)
}
/**
* @return the msgText
*/
public String getMsgText() {  // getter and setter
return msgText;
}

    /**
* @param msgText the msgText to set
*/
public void setMsgText(String msgText) {
this.msgText = msgText;
}
}

 1)  @Named annotation ensures that MsgSender is available as “msgSender” to the Expression language in the JSF page. One of the Scope annotation is essential for a CDI bean as the manages the bean within a specific scope.
2)  See how the Resources we created is mapped to the Queue instance.
3) In JMS2.0, 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.
4) This method is used to obtain the message to be sent from JSF page and invoke the actual method which sends the message and returns control to another JSF page, once the message is successfully sent.

JSF files

The code for index.xhtml which interacts with the user and gets the message to be sent from him is given below:

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Send JMS Message</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="msgText" value="Enter Message Text:"/>
<h:inputText id="msgText" value="#{msgSender.msgText}"/> //------1)
<h:panelGroup/>
<h:commandButton value="Submit" action="#{msgSender.sendMsg()}"/>  //-------2)
</h:panelGrid>
</h:form>
</h:body>
</html>

  • The input is set in the msgText property of msgSender CDI bean.
  • sendMsg() method in msgSender bean is invoked.

The following page gets control only if the message was successfully sent.

confirmation.xtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>JMS message sent</title>
</h:head>
<h:body>
JMS message sent successfully.
</h:body>
</html>


Run the Project

When you Deploy the application, when the application is successfully deployed you can see a message similar to the following:
JmsTry was successfully deployed in 707 milliseconds.

Now go to Services window and expand GlassFish Server -> Applications. Right Click on your project and choose Open in Browser. It will open as shown in Figure -1

Figure 1
Fill up the text box and Click Submit button. You can see “JMS Message was successfully sent” in the Browser and “Message successfully received” in the Server log.

Consume Message
We can create another Java Web application to consume the message sent by the earlier app. In this app, we have a number of options.As JMSConsumer has methods to receive message synchronously or asynchronously. But having an application server, the best option is to code a message driven bean. Message-driven beans (MDBs) are stateless, server-side, and transaction-aware components that process asynchronous JMS messages. One of the most important aspects of message-driven beans is that they can consume and process messages concurrently. This capability provides a significant advantage over traditional JMS clients, which must be custom built to manage resources, transactions, and security in a multithreaded environment. MDB containers manage concurrency automatically so the bean developer can focus on the business logic of processing the messages. An MDB can receive hundreds of JMS messages from various applications and process them all at the same time because numerous instances of it can be executed concurrently in the container. After creating a package in this project you can choose New à Message Driven bean, You will see Figure-2 with an empty Project Destinations. You have to fill up the same with the queue name used in the message sender project and click Next. In the wizard that follows you can accept the default values and click Finish. The IDE will generate MDB code with necessary annotations which contains the configuration information of the MDB. There is no necessity for any xml configuration file.

package ram;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/java:global/jms/bookingQueue"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") 
})      //-------------------1)
public class NewMessageBean implements MessageListener {   //---------2)

public NewMessageBean() {
}

@Override
public void onMessage(Message message) {   //-----------3)
}

}

  • The configuration is done through annotations and if you do not use the IDE, you have to code the same.
  • The MDB must implement the MessageListener interface, which provides only one method, onMessage. This method is called by the container whenever a message is received by the message-driven bean and should contain the application-specific business logic.
  • So fill up the onMessage() with

final String text = message.getBody(String.class); /** In JMS 2.0, the getBody method makes this much simpler as casting is not necessary now */

            System.out.println("Message received (async): " + text);
} catch (JMSException ex) {
System.out.println(ex.toString());
}

Here we have connected the MDB to our bookingQueue destination  bound at java:global/jms/bookingQueue. The purpose of this component will be to trace the message receipt.

You can deploy this project and see the following if the message sent was “Govindha”:

Loading application [ClientTry] at [/ClientTry]
Info:   ClientTry was successfully deployed in 474 milliseconds.
Info:   Message received (async): Govindha

Figure-2

 

Conclusion

Open Message Queue (OpenMQ or Open MQ) is an open source message-oriented middleware project by Oracle (formerly Sun Microsystems) that implements the Java Message Service 2.0 API (JMS). It is the default JMS provider integrated into GlassFish. The same was used when we used to send() JMS message above. Now the administered objects ConnectionFactory and Destination need not be preconfigured and created by the administrator through Web Console/ application server management tool. The default ConnectionFactory provided by JMSProvider can be used and the Destination(Queue or Topic) can be created in code. The injection by the container of  JMSContext (which combines earlier Connection and Session objects) and other JMS resources simplifies our code. To the send() in JMSProducer you can simply pass a String and JMSProvider will construct a MessageText object. Likewise the consumer can use the getBody() in message object and avoid “casting” otherwise /earlier required. Though JMSConsumer has receive() methods to synchronously or asynchronously receive message, a Message Driven Bean(MDB) is preferable for concurrent processing of a number of messages. With the facilities in NetBeans IDE you can easily create a MDB and you can fill up the empty onMessage() generated by the IDE with your application logic. All the configuration of MDB  is done by the IDE through annotations from the information furnished by you when you create a MDB.As mentioned earlier, the simplification in JMS2.0 was made with an eye on “ease of use” and to catch up with the simplification earlier made in other EE components like EJB, JPA etc.









}