Friday, May 11, 2012

Java Message Service with JBoss AS 7

PREVIOUS NOTE: Although not deprecated, as this example should work just fine with Java EE 7, I would consider this message to be obsolete. I would advise the reader to consider this message instead.

In this message, I will guide the reader through the process of writing a simple sender-receiver application that uses a Java Message Service queue for the JBoss Application Server 7.1. It may be my fault, but as the time of writing of this message, I failed to find good documentation on how to run the most basic example of JMS in JBoss AS 7. This is what I show here. I will assume that the reader is familiar with JMS. There are plenty of resources out there on the web covering this topic, and it is not the goal of this blog to do the same.

I tried all this on Mac OS X Lion with the Brontes version of JBoss.

The first thing to know is that by default JBoss doesn't start with messaging. Therefore, you must explicitly use a different configuration that enables messaging. In the standalone case, you can do as follows, but you may want to defer starting the server to a later stage, after performing all necessary changes I describe:

./standalone.sh --server-config=standalone-full.xml

In other words, you need to specify the configuration file standalone-full.xml. The bad news is that we will need to change this configuration file, but this is not particularly difficult.

But before going into the server details, let us focus on the client applications. I will create one sender and one receiver application. They are pretty basic, in the sense that they only exchange one message and finish. I present them here. You can create your own standard Java project in Eclipse (or NetBeans) and add these two classes to the default package:


import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Sender {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageProducer mp;

public Sender() throws NamingException, JMSException {
InitialContext init = new InitialContext();
this.cf = (ConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao", "pedro");
this.c.start();
this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
this.mp = this.s.createProducer(this.d);
}

private void send(String string) throws JMSException {
TextMessage tm = this.s.createTextMessage(string);
this.mp.send(tm);
}

private void close() throws JMSException {
this.c.close();
}

/**
* @param args
* @throws JMSException 
* @throws NamingException 
*/
public static void main(String[] args) throws NamingException, JMSException {
Sender s = new Sender();
s.send("Ola ca estou eu!");
s.close();
}


}

The Receiver:


import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Receiver {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageConsumer mc;

public Receiver() throws NamingException, JMSException {
InitialContext init = new InitialContext();
this.cf = (ConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao", "pedro");
this.c.start();
this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
mc = s.createConsumer(d);
}

private String receive() throws JMSException {
TextMessage msg = (TextMessage) mc.receive();
return msg.getText();
}

private void close() throws JMSException {
this.c.close();
}

/**
* @param args
* @throws JMSException 
* @throws NamingException 
*/
public static void main(String[] args) throws NamingException, JMSException {
Receiver r = new Receiver();

String msg = r.receive();
System.out.println("Mensagem: " + msg);
r.close();
}

}


In this source code, what I found really problematic was to initialize the destination (the queue) and the ConnectionFactory. Eventually, I managed to use JNDI, as I should. I summarize the process in following lines:


InitialContext init = new InitialContext();
this.cf = (QueueConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao""pedro");

We need the extra "jms" before the name of the resource. For this to work, we also need an extra file, the jndi.properties. Place this file in the src folder of your project (this will not work for WildFly. Check the new version in this message):


java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=remote://localhost:4447
jboss.naming.client.ejb.context=true
#username
java.naming.security.principal=joao
#password
java.naming.security.credentials=pedro




To use the JMS libraries (and other JBoss client libraries by the way) you need to add the following jar file: (jboss-home)/bin/client/jboss-client.jar.



Note that we are providing the (very weak) user credentials of "joao" with password "pedro" to create the connection. For this to work, we need to add this user in the server running the add-user.sh (or .bat) available in the bin directory. The script will let us know in exactly which files did it do the modifications, but this is for the sake of curiosity only. I show this process in the next figure.


For this example to work we also need to add the queue to the standallone-full.xml file (refer to the command line in the top).



                <jms-destinations>
                    <jms-queue name="testQueue">
                        <entry name="queue/test"/>
                        <entry name="java:jboss/exported/jms/queue/test"/>
                    </jms-queue>
                    <jms-queue name="PlayQueue">
                        <entry name="queue/PlayQueue"/>
                        <entry name="java:jboss/exported/jms/queue/PlayQueue"/>
                    </jms-queue>
                    <jms-topic name="testTopic">
                        <entry name="topic/test"/>
                        <entry name="java:jboss/exported/jms/topic/test"/>
                    </jms-topic>
                </jms-destinations>



Now, you should be able to start the server first and then run the receiver and the sender in any order. Good luck!

8 comments:

  1. Hi and thanks for this example too easy to understand. I follow the steps but i have always the same exception.

    Exception in thread "main" javax.jms.JMSSecurityException: Unable to validate user:dave

    Im working under jboss as 7 server in eclipse. I run the main methon as an aplication.

    Please Help!! Thanks!

    ReplyDelete
  2. ok, I made again a new server instance on eclipse and it's works ok. it was extraterrestrial fail.

    Thanks, very usefull for me.

    ReplyDelete
  3. I never normally make comments I just download the examples and use the scripts I find online.

    But I have to say thank you very much for this easy to use example.

    I just wished I had seen this example earlier; it would have saved me a great deal of time and frustration.

    ReplyDelete
  4. I can't get this to work. Where are you using that properties file?

    I have everything setup but I get
    Exception in thread "main" javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
    at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
    at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
    at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
    at javax.naming.InitialContext.lookup(InitialContext.java:411)
    at JMSTest.JMSTest.Sender.(Sender.java:23)
    at JMSTest.JMSTest.Sender.main(Sender.java:46)

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Very good and simple examples. Thanks

    ReplyDelete
  7. Thanks so much for very useful post which saved me a lot of time and nice demonstration step by step..
    keep going.. you rock...

    ReplyDelete
  8. It is really a great work and the way in which u r sharing the knowledge is excellent.Thanks for helping me to understand Java Message Service with JBoss AS 7 concepts. As a beginner in java programming your post help me a lot.Thanks for your informative article.java training in chennai | chennai's no.1 java training in chennai

    ReplyDelete