|
|
|
|
|
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 projectBefore 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 QueueThere 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 Objectimport 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: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
|
|
|
Copyright 2009. All rights reserved by S. Chappell |