February 2011


In the first post, I wrote JMS basics and how to write a simple JMS client. In this post, I ll delve deeper in to the code and try to reveal the secrets of the magic 🙂

One can write a JMS client following the three steps below…

1. Setting up the JNDI context

2. Validating the JMS destinations

3. Creating and using the connection for the data exchange

Let us see each step in detail…

Setting up the JNDI context

Java Naming and Directory Interface(JNDI) is a standard implementation-independent API that allows applications to discover and look up data and objects using a “Name”. As a JMS client, I need to understand where to look for the ConnectionFactory and Destinations (topic/queue) that are intermediaries between the producer and consumer for the data exchange. JMS API doesn’t support this. Unlike the connections, sessions, producers, consumers and messages, ConnectionFactory and Destination objects cannot be obtained using JMS API. JNDI comes as a savior and provides a dynamic, portable and configurable mechanism to obtain these objects.  

Firstly, we need to create a connection to the JNDI Naming service and obtain the ConnectionFactory and Destination objects from them. JNDI provides a class javax.naming.InitialContext  for this purpose. This is the starting point for any JNDI lookup. The properties we put in the InitialContext depends on the JMS directory service we are using.

In the previous post, the initial context was pretty simple…

jndiContext = new InitialContext();

Ideally, we need to put all the required information to connect to the JNDI service. The  

Hashtable<String,String> environment = new Hashtable<String,String>();

environment.put(“java.naming.provider.url”,”ormi://machine1:12401”);         

environment.put(“java.naming.security.principal”,”UserName”);

environment.put(“java.naming.security.credentials”, “Password”);

environment.put(“java.naming.factory.initial”, “com.evermind.server.ApplicationClientInitialContextFactory”);

InitialContext  jndiContext = new InitialContext(environment);

Most of the JNDI lookups require all the four properties to be defined in the Initial context. The url is the one where we can locate the registry that contains the directory information. And the “java.naming.factory.initial” is the property that is used to select the service provider as the initial context. It specifies the class name of the initial context factory for the provider. The jar file that contains this class must be loaded into the JMS during the execution. In the above example where we are trying to write a client to OC4J JMS server, we are using “com.evermind.server.ApplicationClientInitialContextFactory” which is a part of oc4j-client.jar.  The username and password are the credentials to connect to the JMS. Some of the JMS providers support anonymous security context, while most assume that the credentials can be obtained from the JNDI or current thread.

 Validating the JMS destinations

Once the initial context is set, we need to get the JMS connection factory and the destination objects.

connectionFactory = (ConnectionFactory) jndiContext.lookup(“jms/TopicConnectionFactory”);
dest = (Destination) jndiContext.lookup(destName);

The javax.jms.ConnectionFactory is used to create connection object to a JMS server. A ConnectionFactory is a type of administered object, which means that its attributes and behavior are configured by the system administrator responsible for the messaging server. And the connection can created by the ConnectionFactory in the next step which represents a connection to the message server.

The javax.jms.Destination is an interface that encapsulates a provider-specific address. Queues and Topics are two different destinations and they are the administered objects just like the ConnectionFactory. We can get the destination object by looking up the administered objects in the JNDI namespace/registry. To this specific object the consumer would be created.

Creating and using the connection for the data exchange

connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
consumer = session.createConsumer(dest);
listener = new TextListener();
consumer.setMessageListener(listener);
connection.start();                                                                                                                                                                                             

//delay to read the messages in onMessage()                                                                                                       

connection.stop();

As mentioned before the connection represents a connection to the JMS server. Every connection created from the ConnectionFactory would be unique. The connection can be managed using start(), stop() and close() methods. Once the start() method is invoked the JMS server will start sending the messages. If we haven’t subscribed to any of the topic, all the messages will be discarded. So its better to subscribe to a destination before starting any connection. A stop() method will stop inbound messages on that connection until the start() is called again and close() method will close the connection with the JMS server.

A connection object is used to create a session object.  A Session object is responsible for creating message, consumer and producer objects. To have a good granular control over the consumers, producers and their transactions, we can have multiple sessions created from a connection. We can also create multiple connections to the server to serve the same purpose. But the connections are pretty costly. So it is always better to create multiple sessions than to create multiple connections.  

The first parameter of the createSession() method indicates if the session object will be transacted or not. Confused? Let me explain… In JMS, a transaction groups message or a message set in to one single atomic processing unit. Failure of the delivery of a single message may result in the redelivery of the message set.  This is the reason why we have to include jta.jar while running a JMS client as it would be expecting some JTA objects. In the above code snippet, the parameter is set to false, which means the Session will not be transacted. The second parameter indicates the acknowledgment mode used by the JMS client. An acknowledgment is nothing but a notification to the message server that the JMS client has received the message. In this case we chose AUTO_ACKNOWLEDGE, which means that the message is automatically acknowledged after it is received by the client.

The session object can also be used to create producer.

producer = session.createProducer(dest);

 It is also used to create a message and send it using the producer like below…

TextMessage message = session.createTextMessage();

message.setText(“Mymessage”);

producer.send(message);

“dest” is the Topic object which is a handler to the physical Topic on the messaging server. Topic is nothing but some kind of a news group to which a lot of message consumers can subscribe. When a message is published on to the Topic by a publisher, the message is sent to all the subscribers subscribed to the Topic.

Finally, the setMessageListener() method of the consumer object would register a listener to the object. Setting this would invoke the onMessage() method of the “listener” object whenever a JMS server pushes a message to the subscriber. We also need to note that setting a message listener on a consumer object while the object has already registered a message listener is undefined in the JMS specification. So it is better to avoid that situation.

I guess this post would help you in understanding how a JMS client works and would help you in writing one. Happy coding 🙂

JMS- Java Message Service is a standard that allows applications to send, receive, create and delete the messages.

Let us go through some basics of JMS before delving in to the code…

JMS was designed by Sun and several partner companies to standardize the messaging. It defines a common set of interfaces and associated semantics that allow programs written in the Java programming language to communicate with other messaging implementations.

Why JMS?
JMS makes life easy for a programmer while writing complex messaging components. Any JMS implementation promises maximum portability, asynchronous delivery of messages and reliable communication directly out of the box. JMS supports both point to point and publish/subscribe methods. Those who have written some basic socket programming to achieve the same would be able to appreciate JMS.

A few JMS elements we need to understand…

A JMS provider is the one who provides the implementation of the JMS interface.
A JMS client is an application that produces and/or receives messages
A JMs producer is an application that publishes the messages
A JMS consumer is an application that receives messages
A JMS message is a standard message which contains the data that needs to be transferred.
A JMS queue is where the messages are sent by the producer so that they get consumed by the consumer. Please note that this is a point to point solution. So there can be only one consumer for a queue.
A JMS Topic is a publish/subscribe solution which can have multiple consumers. A JMS client needs to subscribe to a topic to consume the messages
A Durable subscriber/consumer is a consumer when subscribes to a Topic/Queue, the Topic/Queue would save the message until it is consumed by the subscriber even if the subscriber is down.

Done!! Too much of theory… Time to get our hands dirty…

// Creating a JNDI API InitialContext object if none exists yet.
try {
jndiContext = new InitialContext();
} catch (NamingException e) {
System.out.println(“Could not create JNDI API context: ” +
e.toString());
System.exit(1);
}

/*
* Look up connection factory and destination. If either
* does not exist, exit. If you look up a
* TopicConnectionFactory or a QueueConnectionFactory,
* program behavior is the same.
*/
try {
connectionFactory = (ConnectionFactory) jndiContext.lookup(
“jms/TopicConnectionFactory”);
dest = (Destination) jndiContext.lookup(destName);
} catch (Exception e) {
System.out.println(“JNDI API lookup failed: ” + e.toString());
System.exit(1);
}

/*
* Create connection.
* Create session from connection; false means session is
* not transacted.
* Create consumer.
* Register message listener (TextListener).
* Receive text messages from destination.
* When all messages have been received, type Q to quit.
* Close connection.
*/
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
consumer = session.createConsumer(dest);
listener = new TextListener();
consumer.setMessageListener(listener);
connection.start();
System.out.println(“To end program, type Q or q, ” +
“then “);
inputStreamReader = new InputStreamReader(System.in);

while (!((answer == ‘q’) || (answer == ‘Q’))) {
try {
answer = (char) inputStreamReader.read();
} catch (IOException e) {
System.out.println(“I/O exception: ” + e.toString());
}
}
} catch (JMSException e) {
System.out.println(“Exception occurred: ” + e.toString());
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
}
}

The above piece of code with the comments are self explanatory at a high level. The snippet is useul in writing an message consumer application. The piece of code that is highlighted requires a TextListener class that implements MessageListener. The onMessage method needs to be implemented in TextListener class which will be called when the message is sent to a Topic/Queue. The code look like below…

class TextListener implements MessageListener {
/*
* @param message the incoming message
*/
public void onMessage(Message message) {
TextMessage msg = null;

try {
if (message instanceof TextMessage) {
msg = (TextMessage) message;
System.out.println(“Reading message: ” + msg.getText());
} else {
System.out.println(“Message is not a TextMessage”);
}
} catch (JMSException e) {
System.out.println(“JMSException in onMessage(): ” + e.toString());
} catch (Throwable t) {
System.out.println(“Exception in onMessage():” + t.getMessage());
}
}
}

If you want to write a durable subscriber all we need to do is replace the session.createconsumer call with


private TopicSubscriber subscriber = null;
subscriber = session.createDurableSubscriber((Topic)destTopic, “Identifier”);

and after closing the connection unsubscribe the same.

session.unsubscribe(“Identifier”);

And finally to write a JMS producer application all we have to do is create a producer and send a message

producer = session.createProducer(dest);
producer.send(message);

Since JMS is based on J2EE, it requires application-client.xml file in META-INF directory. The application-client.xml file contains the JNDI information necessary for accessing the server application.

For a simple JMS client application, the content of the file would be something like this…

<application-client>

</application-client>

We also need the ejb.jar during the runtime to interpret these files.

With all the above instructions together you would be able to write a JMS client application that can send and receive messages. Sounds simple!! Yes!! It is…
For all those who believe in “Magic” can stop reading the post here and go ahead with writing your application. For those who don’t… wait for my next blog post to see how the Magic’s biggest secrets finally reveled 😛 …