Standalone JMS client

Configuring a standalone JMS client could be a little bit confusing. Over the Internet you’ll probably find many code snippet to show you how to send or receive a message in a queue but few sites in my experience talk about how you create & configure the connection.

I will use in this example Glassfish 3.0.1 as it’s a full certified Java EE6 Server, in addition it contain OpenMQ the reference implementation for Java Messaging Service (message-oriented middleware platform)

If you’re not familiar with the different concepts of JMS, I suggest that you take a look at this article: OpenMQ, JMS under GlassFish

Administered Objects

These are the objects that the client need to know about and use.

Destination:

This is where the client send or receive messages

ConnectionFactories:

Allow the client to create the connection to a destination

These objects are exposed from the broker(OpenMQ) using JNDI, thus in order to obtain them you have to do a JNDI lookup.

For our examples we will create a ConnectionFactory and a Topic, using GlassFish’s asadmin utility

asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/producer/ConnectionFactory
asadmin create-jms-resource --restype javax.jms.Topic jms/producer/Topic

You can check that the JMS resources are created using asadmin

asadmin list-jms-resources

jms/producer/Topic
jms/producer/ConnectionFactory

or the Web Console

Client run within a Container

If you run your client using the ACC (Application-client-container) for example, the container can Inject you a reference of those JNDI resources


@Resource(lookup = "jms/producer/ConnectionFactory")
 private static ConnectionFactory connectionFactory;
@Resource(lookup = "jms/producer/Topic")
 private static Topic topic;

They are also available in the IntialContext


Context jndiContext = new InitialContext ();

// get Connection Factory

ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("jms/producer/ConnectionFactory");

Topic topic = (Topic) jndiContext.lookup("jms/producer/Topic");

Client run without a Container

So here comes the tricky part, what if my client run as a simple Java application (Ex: java -jar my_jms_client.jar) ?

Step1

Go back to the machine where you installed GlassFish (i.e Broker)

Open the Open Message Queue Administration Console

$GLASSFISH_HOME/imq/bin/imqadmin

Step2

Connect to your broker (or create a new connection) as shown

We can see the previously created destinations

Step3

Create a new object store (right click at the Object Store, then click add Object Store)
Now you have to specify the following JNDI properties: java.naming.factory.initial and java.naming.provider.url

java.naming.factory.initial = com.sun.jndi.fscontext.RefFSContextFactory
java.naming.provider.url = file:///opt/java/my_broker

Step4

Now you have an Object store, we need to added, the destination that the clients will use along with the connection factory.
right click on destination to add a new one


jms/consumer/Topic (is the JNDI name that we will use it in our client code)
jms_producer_Topic is the current destination name the one we created previously with Glassfish asadmin

Step5:

We need to do the same with the connection factory

Right click under Connection Factories (under Our Object Store)


that’s, it we are done with the Queue Administration Console

Step6:

Remember the directory from the step 3, the clients will use the configuration file within this directory in order to lookup the Administred Object.

Here is a simple client that will subscribe to JMS topic and listen for new messages

    // ======================================
    // =           CLIENT                   =
    // ======================================

    public static void main(String[] args) {

         try {
            InitialContext jndiContext = new InitialContext();

            ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("ConsumerConnectionFactory");
            Topic topic = (Topic) jndiContext.lookup("jms/consumer/Topic");

            Connection connection = connectionFactory.createConnection();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageConsumer consumer = session.createConsumer(topic);

            consumer.setMessageListener(new Main());

            connection.start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void onMessage(Message message) {
        try {
            System.out.println("Message received: " + ((TextMessage) message).getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
 
You need to put this properties files in your classpath

jndi.properties

 java.naming.factory.initial = com.sun.jndi.fscontext.RefFSContextFactory
 java.naming.provider.url = file:///opt/java/my_broker
 

Note: If you don’t want to put jndi.properties file into your classpath you have to intialize the JNDI context by specifying the Naming properties

 Properties env = new Properties();
 env.put("java.naming.factory.initial","com.sun.jndi.fscontext.RefFSContextFactory");
 env.put("java.naming.provider.url", "file:///opt/java/my_broker");
 InitialContext jndiContext = new InitialContext(env);
 

But wait there’s more 🙂

If you are curious you can go see what’s under the /opt/java/my_broker directory, well, there is a hidden file called .bindings
you can edit this file to correct/modify the hostname/ip addresse of the broker ex: in my case I nedded to replace localhost with the correct IP addresse of my server, so the client will be able to reach the JNDI namespace.

This file is to be given to all clients, this way the clients don’t worry about the broker configuration (loosely-coupled). So if I want to install my client under Windows, in another machine, I will just modify the JNDI properties java.naming.provider.url that will point to the .bindings directory location.

This is my maven configuration, in case you’re interested to see the dependencies I’m using

<dependencies>  
	 	<dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.jms</artifactId>
            <version>${glassfish-version}</version>
        </dependency>
        
       <dependency>
			<groupId>com.sun.messaging.mq</groupId>
			<artifactId>imq</artifactId>
			<version>4.4.2</version>
			<scope>runtime</scope>
	   </dependency> 
	   
	   <dependency>
			<groupId>com.sun.messaging.mq</groupId>
			<artifactId>fscontext</artifactId>
			<version>4.4.2</version>
			<scope>runtime</scope>
		</dependency> 
            
            
  </dependencies>

Server side code


public class Main {
    @Resource(lookup = "jms/producer/ConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(lookup = "jms/producer/Topic")
    private static Topic topic;

    // ======================================
    // =           PRODUCER                 =
    // ======================================

    public static void main (String [] args){
        try {
            Connection connection = connectionFactory.createConnection();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer producer = session.createProducer(topic);

            // Sends a text message to the topic
            TextMessage message = session.createTextMessage();
            message.setText("Hello, Clients !! ");
            producer.send(message);

            connection.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
Advertisements

About nhachicha

Android Developer, share experience about Java, System and Android
This entry was posted in Java and tagged , , , , , , . Bookmark the permalink.

4 Responses to Standalone JMS client

  1. JMS is very powerful. Do you have your app hosted online or a screen cast to look at?
    Did you know that JMS can be extended all the way to the browser as well? Basically you can run your JMS app in the browser using JavaScript and use JMS as your messaging layer on top of WebSockets. Here is a demo that was built with JMS on the client-side (JavaScript) only – pretty amazing…
    http://blog.kaazing.com/2012/01/26/the-simplest-way-to-use-your-smartphone-as-a-game-controller-a-websocket-race-car-demo/

  2. Alex says:

    Nabil, thank you very much for your blog.
    With this example I could establish a working connection. Before I found your blog, I tried a lot of examples, none of them really helped me.

  3. Stav says:

    Good tutorial! I’ve looked a lot, but nowhere else but here is a clear list of steps for a Java SE app to communicate over JMS. Consider changing the code examples to the new JMS 2.0 simplified API so that it is even more concise and up to date.

  4. Alex says:

    Totally agree,this is good tutorial! But my client is still not working. Help me please. The problem with:
    NoInitialContextException: Cannot instantiate class: com.sun.jndi.fscontext.RefFSContextFactory
    Caused by: java.lang.ClassNotFoundException: com.sun.jndi.fscontext.RefFSContextFactory
    Why this class not found?(sorry for my english, I from Ukraine)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s