Linda's Open Message Queue Blog
JMS101 - part II - the sequel
Squirrels to me are evil creatures. My interactions mostly consist of watching them run along my fence and having them throw acorns down at both my car and me and chatter with malicious glee as I run into the house to avoid the shower. A good friend of mine sees them as poor maligned innocent woodland creatures, I know this because he recently found a hurt squirrel in his back yard, made it a nest on his back porch and is now feeding it dog kibble and nuts as it heals.
You are wondering where I'm going with this right ? Well, mostly I just wanted to share my squirrel pain but its also important to remember that two people look at something in different ways. Thats why administered objects exist in JMS, because two providers look at the system in different ways (see - I managed to go somewhere with it).
So - as you no doubt guessed, my plan for today is to expand the hello world example to use JNDI and administered objects. If you have no idea what I am talking about with the HelloWorld example, start with the initial blog entry on JMS 101.
Again, I'll list all of the caveats. This is an intro to MQ/JMS entry and may not be suitable for more advanced viewers, my background is on the server side not the client side so my terminology is suspect, the names might be changed to protect the innocent.
The prologue
To change a program, such as the HelloWorld example, into one that uses Administered Objects a couple of things are going to happen:
- select whether we use a file or LDAP based repository
- update the code
- create the administered objects
Today, I'm picking a file based repository. Its simpler as an initial example and won't require that I spend time doing screen snapshots of the Sun Directory Server. I promise to do it soon (although not next, since my plan - however tenuous - is to talk about MQ specific features next because I had a request for that).
Quick diversion... while I never specifically mentioned this in the previous JMS 101 discussion, the JMS api is all interfaces. To use it, you have to get a concrete object (usually from JNDI) which is the actual implementation class used by the provider. and then call the interface methods on the class
Before I go any farther, I should take a few seconds to talk about JNDI. If you want the really technical bits, you should take a break for the moment and go off to the Technology overview for JNDI on the java.sun.com page. If you are familiar with it, skip the next few sentences because it may cause my simplistic definition may cause you to cringe. What is does is allow you to create an object, referred to by a name, which contains a set of attributes. In our case we will have a name (HelloConnectionFactory), and we will look it up in the file based repository to retrieve the MQ specific version of that class including any properties we have set on it.
The main Event
Updating the code to use JNDI
In the last example we used the following line to create the connection factory:
ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();
We are now going to replace that line with code to retrieve the object using jndi.
We also used the createQueue api to create our Queue style destination. While its valid to create a destination using that API, we are going to change to retrieve the Queue from JNDI (mostly because we can). That code was :
Destination destination = session.createQueue("HelloWorld");
Since we are using a file based repository, we need to define where its going to live. For the moment it will live in /tmp/jndi.
NOTE: I'm going to pick unix, not windows, paths because that is my primary desktop. If you are running windows, you will have to use something like C:\\tmp\jndi.
Now - I have to determine the names for our connection factory objects. I'm using HelloConnectionFactory and HelloQueue
I'm show the new code, at then end I'll stick in the updated HelloConsumer source so you can see the changes. (I'm not bothering with giving you HelloProducer since the code changes are the same)
First, I'm going to create the initial Context. That is our connection to the file based JNDI store.
- com.sun.jndi.fscontext.RefFSContextFactory Is our INITIAL_CONTEXT_FACTORY (that is the file based implementation provided by java).
- file:///tmp/jndi
The code is:
Hashtable env;
Context ctx = null;
env = new Hashtable();
// Store the environment variables that tell JNDI which initial context
// to use and where to find the provider.
// For use with the File System JNDI Service Provider
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///tmp/jndi");
try {
// Create the initial context.
ctx = new InitialContext(env);
} catch (NamingException ex) {
}
Next we are going to retrieve the HelloConnectionFactory (which we haven't created yet - thats coming soon). To get the object, we use the lookup method on the context object we just created. So:
The more detailed code is:
Finally, we are going to look up HelloQueue. To get the object, we will use the lookup method on the context object we just created yet again. So:
The more detailed code is:
imqobjmgr is the tool which is used to create MQ connection factories (although you can also use the gui based admin tool - but I'm a server side girl so I like command lines). It has five subcommands:
Since this is an extremely basic example, all I'm going to use is add. When you use the add command, at a minimum you must pass in the name and the type as well as object store attributes that allow us to connect to it (you can also pass in configuration properties which I'm also not going to use this time).
In the code above I specified:
So the commands are are going to run are:
The epilogue
Ok - I'm almost done (yea !!! I'm ready to do something else, if you are reading this you are no doubt in the same state). I'll I'm going to show is the final HelloConsumer.java code and the output of the imqobjmgr commands.
The code
creating the Queue
cxt.lookup("HelloConnectionFactory");
ConnectionFactory cf = null;
try {
// Lookup my connection factory from the admin object store.
// The name used here here must match the lookup name
// used when the admin object was stored.
System.out.println("Looking up Connection Factory object with lookup name: HelloConnectionFactory");
cf = (javax.jms.ConnectionFactory) ctx.lookup("HelloConnectionFactory");
System.out.println("Connection Factory object found.");
} catch (NamingException ne) {
System.err.println("Failed to lookup Connection Factory object.");
ne.printStackTrace();
System.exit(-1);
}
cxt.lookup("HelloQueue");
Using imqobjmgr
Queue queue = null;
try {
// Lookup my queue from the admin object store.
// The name I search for here must match the lookup name used when
// the admin object was stored.
System.out.println("Looking up Queue object with lookup name: HelloQueue");
queue = (javax.jms.Queue)ctx.lookup("HelloQueue");
System.out.println("Queue object found.");
} catch (NamingException ne) {
System.err.println("Failed to lookup Queue object.");
ne.printStackTrace();
System.exit(-1);
}
% imqobjmgr add -j java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory -j java.naming.provider.url=file:///tmp/jndi -l HelloConnectionFactory -t cf
% imqobjmgr add -j java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory -j java.naming.provider.url=file:///tmp/jndi -l HelloQueue -t q
creating the Connection factory
import javax.jms.*;
import javax.naming.*;
/**
* simple hello world consumer
*/
public class HelloConsumer
{
/**
* simple consumer
*/
public HelloConsumer() {
try {
Hashtable env;
Context ctx = null;
env = new Hashtable();
// Store the environment variables that tell JNDI which initial context
// to use and where to find the provider.
// For use with the File System JNDI Service Provider
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///tmp/jndi");
try {
// Create the initial context.
ctx = new InitialContext(env);
} catch (NamingException ex) {
ex.printStackTrace();
System.exit(-1);
}
// creating a connection factory
ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();
ConnectionFactory cf = null;
try {
// Lookup my connection factory from the admin object store.
// The name used here here must match the lookup name
// used when the admin object was stored.
System.out.println("Looking up Connection Factory object with lookup name: HelloConnectionFactory");
cf = (javax.jms.ConnectionFactory) ctx.lookup("HelloConnectionFactory");
System.out.println("Connection Factory object found.");
} catch (NamingException ne) {
System.err.println("Failed to lookup Connection Factory object.");
ne.printStackTrace();
System.exit(-1);
}
// create a connection
Connection connection = cf.createConnection();
// create a session
Session session = connection.createSession(
false /* not transacted */, Session.AUTO_ACKNOWLEDGE);
// create destination HelloWorld
Destination destination = session.createQueue("HelloWorld");
Queue queue = null;
try {
// Lookup my queue from the admin object store.
// The name I search for here must match the lookup name used when
// the admin object was stored.
System.out.println("Looking up Queue object with lookup name: HelloQueue");
queue = (javax.jms.Queue)ctx.lookup("HelloQueue");
System.out.println("Queue object found.");
} catch (NamingException ne) {
System.err.println("Failed to lookup Queue object.");
ne.printStackTrace();
System.exit(-1);
}
// create a consumer
MessageConsumer consumer = session.createConsumer(destination);
// now that everything is ready to go, start the connection
connection.start();
// receive our message
TextMessage m = (TextMessage)consumer.receive();
System.out.println(m.getText());
// close everything
consumer.close();
session.close();
connection.close();
} catch (JMSException ex) {
System.out.println("Error running program");
ex.printStackTrace();
}
}
/**
* main method
*/
public static void main(String args[]) {
new HelloConsumer();
}
}
% imqobjmgr add -j java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory -j java.naming.provider.url=file:///tmp/jndi -l HelloConnectionFactory -t cf
Adding a Connection Factory object with the following attributes:
imqAckOnAcknowledge [Message Service Acknowledgement of Client Acknowledgements]
imqAckOnProduce [Message Service Acknowledgement of Produced Messages]
imqAckTimeout [Message Service Acknowledgement Timeout (milliseconds)] 0
imqAddressList [Message Server Address List]
imqAddressListBehavior [Address List Order] PRIORITY
imqAddressListIterations [Number of Address List Iterations] 1
imqBrokerHostName [Broker Host Name] localhost
imqBrokerHostPort [Broker Host Port] 7676
imqBrokerServicePort [Broker Service Port] 0
imqConfiguredClientID [Configure ClientID as]
imqConnectionFlowCount [Connection Flow Count] 100
imqConnectionFlowLimit [Connection Flow Limit (Unconsumed Messages per Connection)] 1000
imqConnectionFlowLimitEnabled [Connection Flow Limit Enabled] false
imqConnectionType [Connection Type] TCP
imqConnectionURL [HTTP URL] http://localhost/imq/tunnel
imqConsumerFlowLimit [Consumer Flow Limit (Unconsumed Messages per Consumer)] 1000
imqConsumerFlowThreshold [Consumer Flow Threshold (Percent)] 50
imqDefaultPassword [Default Password] guest
imqDefaultUsername [Default Username] guest
imqDisableSetClientID [Disable setClientID() JMS API] false
imqJMSDeliveryMode [JMSDeliveryMode value] PERSISTENT
imqJMSExpiration [JMSExpiration value] 0
imqJMSPriority [JMSPriority value] 4
imqLoadMaxToServerSession [Load Upto Maximum Messages to ServerSessions] true
imqOverrideJMSDeliveryMode [Override JMSDeliveryMode] false
imqOverrideJMSExpiration [Override JMSExpiration] false
imqOverrideJMSHeadersToTemporaryDestinations [Override Messages to Temporary Destinations] false
imqOverrideJMSPriority [Override JMSPriority] false
imqPingInterval [Connection Ping Interval (seconds)] 30
imqQueueBrowserMaxMessagesPerRetrieve [Queue Browser Retrieve Size (# of messages)] 1000
imqQueueBrowserRetrieveTimeout [Queue Browser Retrieve Timeout (milliseconds)] 60000
imqReconnectAttempts [Number of Reconnect Attempts per Address] 0
imqReconnectEnabled [Enable Auto-reconnect to Message Server] false
imqReconnectInterval [Reconnect Interval per Address (milliseconds)] 3000
imqSSLIsHostTrusted [SSL Host Trusted] false
imqSetJMSXAppID [Enable JMSXAppID Message Property] false
imqSetJMSXConsumerTXID [Enable JMSXConsumerTXID Message Property] false
imqSetJMSXProducerTXID [Enable JMSXProducerTXID Message Property] false
imqSetJMSXRcvTimestamp [Enable JMSXRcvTimestamp Message Property] false
imqSetJMSXUserID [Enable JMSXUserID Message Property] false
Using the following lookup name:
HelloConnectionFactory
The object's read-only state: false
To the object store specified by:
java.naming.factory.initial com.sun.jndi.fscontext.RefFSContextFactory
java.naming.provider.url file:///tmp/jndi
Object successfully added.
% imqobjmgr add -j java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory -j java.naming.provider.url=file:///tmp/jndi -l HelloQueue -t q
Adding a Queue object with the following attributes:
imqDestinationDescription [Destination Description] A Description for the Destination Object
imqDestinationName [Destination Name] Untitled_Destination_Object
Using the following lookup name:
HelloQueue
The object's read-only state: false
To the object store specified by:
java.naming.factory.initial com.sun.jndi.fscontext.RefFSContextFactory
java.naming.provider.url file:///tmp/jndi
Object successfully added.
Posted at 06:18PM Oct 28, 2007 by lindaschneider in Sun | Comments[5]
Thanks Linda,
The article is perfect for me. I never knew how to set-up a queue from scratch using JNDI. I am going to download MQ and try it out myself.
Posted by Patrick Kimber on November 20, 2007 at 02:01 AM PST #
Code snippet: env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///tmp/jndi");
Linda, one of the benefits I see of external (JNDI) configuration is platform independence, i.e., you can switch to another JMS provider without needing to recompile your Producer/Consumer code.
But using a OpenMQ-specific class (com.sun.jndi.fscontext.RefFSContextFactory) in the producer and consumer code would seem to defeat that. Within JMS, is there a platform-independent way to refer to a file or LDAP directory for JNDI purposes? (If "no", I guess you can otherwise use Dependency Injection for the "...RefFSContextFactory" string and pass in something else if you want to use a different provider.)
Regards,
Glen
Posted by Glen Mazza on November 20, 2007 at 07:10 AM PST #
Hi Linda,
Great introduction to this topic. There is a minor copy/paste bug in this example:
// creating a connection factory
// we are cheating here by not using jdni
ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();
ConnectionFactory cf = null;
You can of course remove the 'we are cheating' comment and the first instantiation of 'cf' since we are not cheating in this version.
Regards,
John
Posted by John Gage on December 18, 2007 at 01:47 AM PST #
Dear John,
You are right about the cut and paste error - fixing it now.
Thank you :-)
Posted by Linda Schneider on December 18, 2007 at 08:39 AM PST #
Hello Linda,
i've created the queue and queueFactory. I use the MQ (SUN MQ4.1) with Peoplesoft Campus Solution with JMS. The export from out PS (JMStarget) works fine, the message arrives nice in the queue(inbound).
Now i want the JMS listening from PS pulls the messages form the outbound queue from the SUN MQ. But I get a error cannot instantiate claas; com.sun.jndi.fscontext.RefFSContextFactory not found. But the bindings file is in ///tmp/mq and the jar files are in /websrv/WEB-INF/lib folder. Pinging give good results..only PS gives in the errorLog this error. Can i use jave -Djava.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory?
Please, do you have experience with this error in combination with SUN MQ and PeopleSoft?
regards,
Rene Visscher
Posted by Rene Visscher on April 17, 2008 at 11:50 PM PDT #