JMS For non-AsynFW Users

 

 

JMS for AsyncFW Users.

 

The Java Messaging System presents a very interesting model for Asynchronous messaging. In essence, it provides the plumbing for sending a messages (or serializable object) from disparate systems to a listener which monitors the queue. Once a message is received by the listener, it can then take some action on it. You can think of it as e-mail or Instant Messaging for applications. The listener will receive the messages in the order they were received by the queue.

 

The AsyncFW framework allows you to configure your messaging process via the FWDefault.properties file. This allows you to easily configure multiple queues and to make those queues available to your ServiceLet’s and your client side Java Script (via FWDirect). This means you can post messages from a Service call, an HTTP Client request, or a JavaScript call.

 

But first there is a bit of setup involved with using JMS.  You will need to define your Connection Factories, and Destination Factories. The Setup of the Factory and Queues in Glassfish is done exactly as it would be for any JMS system.

Configure Glassfish:

 

Start Glassfish and open the Admin Console: http://localhost:4848/

 

 

 

 

To use JMS, you first need to configure a ‘Connection Factory’ and a ‘Destination Resource’.

 

Select the ‘Connection Factories’ menu tree option, and click ‘New’.

 

Next, fill out the connection factory information as shown.

 

 

…and click the ‘Save’ button.

 

NOTE: It is good practice to actually name your JNDI resources something like the following “jms/AsyncFWFactory”, but for now, the above is fine.

 

Select the ‘Destination Resources’ menu tree item, and click the ‘New’ button.

 

 

Enter the information below;

 

 

…and click the Save button.

 

NOTE: It is good practice to actually name your JNDI resources something like the following “jms/AsyncFWQ”, but for now, the above is fine.

 

That’s it! You have just completed the configuration of a JMS Queue in Glassfish.

 

Configuring the new resources in your project

 

Before your application can use JNDI to locate the queue, you will need to configure it in your web.xml. Please add the following;

 

<resource-ref>

     <description>Default QueueFactory</description>

     <res-ref-name>AsyncFWQFactory</res-ref-name>

     <res-type>javax.jms.QueueConnectionFactory</res-type>

     <res-auth>Container</res-auth>

</resource-ref>

<resource-ref>

     <description>Default Queue</description>

     <res-ref-name>AsyncFWQ</res-ref-name>

     <res-type>javax.jms.Queue</res-type>

     <res-auth>Container</res-auth>

</resource-ref>

 

Let’s write some code.

 

Coding For The AsyncFWJMS Queue

There is nothing about the AsyncFW JMS implementation that precludes you from just hand-coding the entire implementation, just as you would in any other application. The AsyncFW implementation simply allows you to integrate JMS with the other AsyncFW tools.

 

“Talking to yourself is stupid!” – Setting up the listener:

 

An AsyncFW JMS listener is simply a standard JMS listener.

 

import javax.jms.JMSException;

import javax.jms.Message;

import javax.jms.MessageListener;

import javax.jms.ObjectMessage;

import javax.jms.TextMessage;

import com.FW.test.jms.SampleMsg;

public class JMSListener implements MessageListener{

     

      @Override

      public void onMessage(Message message) {

        try {

           SampleMsg sm = (SampleMsg) ((ObjectMessage) message).getObject();

            System.out.println("\nObject Messgage From:" +

                         sm.getFrom()+"\nMSG:"+sm.getMsg());               

 

            } catch (Exception e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            }

           

      }

 

}

 

To create a listener, all you really need is to implement the MessageListener interface, and implement it’s onMessage method. Notice in the above code that we are using a Message Object rather than MessageText. The MessageText object is a built in messaging object that can be used to exchange simple text messages. I find this very limiting, and of little benefit, since you can simply create your own class to accomplish the same thing – while still supporting potential future enhancements. However, by using an object, you are assuming that your clients can instantiate the object, and post it to the queue.

 

Create a Message Object

 

import java.io.Serializable;

 

public class SampleMsg implements Serializable{

      private String Msg;

      private String From;

      public SampleMsg(){

           

      }

      public String getMsg() {

            return Msg;

      }

      public void setMsg(String msg) {

            Msg = msg;

      }

      public String getFrom() {

            return From;

      }

      public void setFrom(String from) {

            From = from;

      }

     

     

}

 

Notice the above class implements Serializable, that is the only requirement.  The above is now our “Message Object”, this is what the clients will need to inflate, and what the listener will receive.

Initialize the Queue:

 

Once you have your listener built, you now need to implement the Queue and its interface. Here is where the framework helps out. First lets create a new class;

 

import javax.jms.JMSException;

import javax.jms.Message;

import javax.jms.MessageListener;

import javax.jms.ObjectMessage;

import javax.naming.NamingException;

import com.FW.JMS.JMSDirectX;

 

 

public class JMSDirectTest extends JMSDirectX {

      String QJNDIDestinationName;

      String QJNDIFactoryName;

      String QListenerClass;

     

      public JMSDirectTest() throws NamingException {

            super();

            // TODO Auto-generated constructor stub

      }

 

      public void setQJNDIDestinationName(String destinationName) {

            QJNDIDestinationName = destinationName;

      }

 

      public void setQJNDIFactoryName(String getQJNDIFactoryName) {

            QJNDIFactoryName = getQJNDIFactoryName;

      }

 

      public void setQListenerClass(String listenerClass) {

            QListenerClass = listenerClass;

      }

 

      @Override

      public String getQJNDIDestinationName() {

            // TODO Auto-generated method stub

            return QJNDIDestinationName;

      }

 

      @Override

      public String getQJNDIFactoryName() {

            // TODO Auto-generated method stub

            return QJNDIFactoryName;

      }

 

      @Override

      public MessageListener getQListenerObject() {

            // TODO Auto-generated method stub

            try {

                  return (MessageListener)

                          Class.forName(QListenerClass).newInstance();

            } catch (InstantiationException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            } catch (IllegalAccessException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            } catch (ClassNotFoundException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            }

            return null;

      }

 

      @Override

      public void classLoaded() {

          try {

                  this.initQueue();

            } catch (NamingException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            } catch (JMSException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            }

           

      }

}

 

Note that we have simply created a POJO that extends the abstract class JMSDirectX. Coding wise, we are done.  Now if we want the queue to be opened and available for all users, we need to modify our FWDefault.properities file.

 

#Message Queues

class.com.FW.test.jms.JMSDirectTest       = JMSDirectTest

JMSDirectTest.setQJNDIFactoryName         = AsyncFWQFactory

JMSDirectTest.setQJNDIDestinationName     = AsyncFWQ

JMSDirectTest.setQListenerClass           = com.FW.test.jms.JMSListener

 

Now it is simply a matter of posting a message (Once we redeploy the updated code). 

 

SampleMsg sm = new SampleMsg();

sm.setFrom("Steve");

sm.setMsg("Hello to all, and to all hello!");

 

((JMSDirectTest)FWOBJProperties.getObjects().

            get("JMSDirectTest")).

            postObjectMessage(sm);

 

First we create an instance of the SampleMsg class, populate it with our message data, then we .postObjectMessage using that newly created SampleMsg instance. By default the JMSDirectTest class, which gets loaded at startup, is shared. It can be accessed from the FWOBJProperties object at anytime from within your application.

 

You are now ready to test!

 

 

Trouble Shooting:


How To Get Rid Of: com.sun.messaging.jmq.io.Packet cannot be cast to com.sun.messaging.jms.ra.DirectPacket in Glassfish V2.X

 

If you are running Glassfish and using JMS (e.g. a Message Driven Bean), you probably saw already the following error in the log files: DirectConsumer:Caught Exception delivering messagecom.sun.messaging.jmq.io.Packet cannot be cast to com.sun.messaging.jms.ra.DirectPacket.

The error is caused by directly embedding the JMS-Host into Glassfish v2 - it can be easily fixed in the admin console:

 Open the admin console: http://localhost:4848

  1. Select: Configuration -> Java Message Service node in the tree on the left side.
  2. Change the type from "Embedded" into "Local". The doc says:
    "
    Choose LOCAL (the default for the server-config configuration) to access the JMS service on the local host. The JMS service is started and managed by the Application Server."
  3. Restart Glassfish

 

 

 

Copyright 2009. All rights reserved by

S. Chappell