In this article we will see how JMS can be used in ADF enterprise applications. We’re going to use Point-to-point model where a sender posts messages to a particular queue and a receiver reads messages from the queue.
This article is split in four sections. First section briefly explains the architectural concept of JMS. Second section will illustrate how to create the JMS System resources administratively from the WebLogic console. In the third section the JMSClient Class is shown which is used by the ADF client to input a message and send it to the JMS queue. The fourth section shows how the Message-Drive-Bean listens to the JMS Queue and consumes the message asynchronously.
You can download the sample ADF application for this article here. In order to run the sample app successfully it is required to complete second section.
I. High-level JMS architecture overview
JMS allows the communication between different components of an application to be loosely coupled, reliable, and asynchronous.
The JMS API supports two models: Point-to-point (Queues) and Publish-and-subscribe (Topics).
Before starting to implement I recommend you first understand the concepts of JMS which can be found here.
The mechanisms for achieving message delivery are as follows:
- A client uses the Connection Factory object to create a Connection with the provider.
- The Connection object creates a Session object.
- Then the Session object creates the Message and the Message Producer objects.
- The Message Producer sends the message to the Destination i.e. a Queue.
II. Creating and maintaining objects administratively from WebLogic Console
JMS System resources such as destinations and connection factories are best maintained administratively rather than programmatically from the WebLogic console. Such resources are configured and stored as JMS Modules.
The JMS Server is a container for the targeted destinations. It hosts the JMS modules and associated persistent stores that reside on a WebLogic Server instance. A WebLogic instance can host one or more JMS Server.
WebLogic supports two types of persistent stores for saving JMS messages: JDBC-accessible and disk-based file.
1. Creating the Persistent Store
Login to WebLogic console then navigate under Services, then select Persistent Stores. Click New and select Create FileStore and then proceed with the wizard to complete the required fields.
2. Creating the JMS Server
Navigate under Services – > Messaging -> Select JMS Servers .
Click New to create a new JMS Server. Select the Persistent store. Specify unique name for the JMS Server then click next and select the targeted WebLogic instance from the dropdown.
3. Creating JMS Modules
Navigate under Services – > Messaging -> Select JMS Modules . Click New to create a new JMS Module. Specify a name for the JMS Module then click next and select the targeted WebLogic instance.
4. Creating Subdeployment
Navigate under Services -> Messaging – > JMS Modules -> Select the Subdeployments tab. Click New button and then specify the Subdeployment Name and next select the targets for subdeployment, i.e. the Weblogic server instance and the JMS Server.
5. Creating the Connection Factory
Navigate under Services -> Messaging – > JMS Modules -> Select the JMS Module that you created above at step 3. Click New button and select Connection Factory. Next specify the Name of the Connection Factory and the JNDI Name which will be used for lookup by the client.
6. Creating the Queue
Navigate under Services -> Messaging – > JMS Modules -> Select the JMS Module that you created above at step 3. Click New button and select Queue. Next specify the Name of the Queue and the JNDI Name which will be used for lookup by the client. Next select the subdeployment you want to use ( i.e. one created on step 4 above).
III. Sending a message from ADF Client to JMS Queue
The client demonstrates the tasks that a JMS application must perform:
- Creating a connection and a session
- Creating message producers
- Sending messages into the destination queue
The following class shows the above tasks:
package model;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.JMSSecurityException;
import javax.jms.MessageFormatException;
import javax.jms.MessageNotWriteableException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JMSClient
{
public String putShortMessage(String queueJNDI, String connFactoryJNDI, String msgText) throws Exception {
ConnectionFactory connectionFactory = null;
javax.jms.Connection connection = null;
javax.jms.Session session = null;
Queue queue = null;
Context ctx = new InitialContext();
try{
connectionFactory = (ConnectionFactory)ctx.lookup(connFactoryJNDI); //Connection Factory jndi api lookup
queue = (Queue)ctx.lookup(queueJNDI); //Queue jndi api lookup
connection = connectionFactory.createConnection(); //Connection Factory creates the Connection
session = connection.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); //Connection creates the Session
MessageProducer messageProducer = session.createProducer(queue); //Session creates the Message Producer
TextMessage message = session.createTextMessage(); //Session creates the Message
message.setText(msgText);
messageProducer.send(message); //Message Producer sends the message to Destination Queue
} catch (NamingException e) {
throw new RuntimeException(e);
}
catch(JMSSecurityException e){
throw new RuntimeException(e);
}
catch(MessageNotWriteableException e){
throw new RuntimeException(e);
}
catch(MessageFormatException e){
throw new RuntimeException(e);
}
catch(JMSException e){
throw new RuntimeException(e);
}
finally {
connection.close();
}
return "Message sent successfully.";
}
}
IV. Processing JMS messages asynchronously with the Message Drive Bean (MDB)
To receive JMS Queue messages asynchronously, we use a message-driven bean which acts as a JMS message listener.
To implement the MDB follow these steps as illustrated:
Once the wizard is complete the Message-Driven Bean with its annotation and onMessage() method are automatically generated. The onMessage() method needs to be updated accordingly to process the received message.
Below code shows the updated onMessage method.
package model;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(mappedName = "JmsQueue_1")
public class MessageDrivenEJBBean implements MessageListener {
/**
* Defines the actions when the message arrives
* This method should handle all exceptions. It must not throw checked exceptions,
* and throwing a RuntimeException, though possible, is considered a programming error.
* @param inMessage
*/
public void onMessage(Message inMessage)
{
TextMessage txtmsg = null;
try
{
//inMessage.acknowledge(); not required since the client creates the session in Session.AUTO_ACKNOWLEDGE mode.
if (inMessage instanceof TextMessage)
{
txtmsg = (TextMessage)inMessage;
String queueMessage = txtmsg.getText(); //get the queue message content
System.out.println(queueMessage);
}
}
catch (JMSException e)
{
System.err.println("Error MessageBean.onMessage 1 : " +"JMSException: " + e.toString());
}
catch (Exception te)
{
System.err.println("Error MessageBean.onMessage 1 : " +"JMSException: " + te.toString());
}
}
}