One of the advantages of using JMS is the ability to write applications which are independent of the JMS implementations, allowing you to plug in a JMS implementation which is appropriate for your environment. However, certain JMS objects must be configured in a way which is specific to the JMS implementation you have chosen. These objects are the connection factories and destinations, queues, and they are often referred to as "administered objects". In order to keep the application programs independent of the JMS implementation, these objects must be configured outside of the application programs. They would typically be configured and stored in a JNDI namespace. The application would lookup the objects in the namespace and would be able to use them straight away, because they have already been configured.
There may be situations, such as on a small device, where it would not be desirable to use JNDI. In these cases the objects could be configured directly in the application. The cost of not using JNDI would be a small degree of implementation-dependence in the application.
Before using JNDI to either store or retrieve objects, an "initial context" must be set up, as shown in this fragment taken from the MQeJMSIVT_JNDI example program:
import javax.jms.*; import javax.naming.*; import javax.naming.directory.*; ... java.util.Hashtable environment =new java.util.Hashtable(); environment.put(Context.INITIAL_CONTEXT_FACTORY, icf); environment.put(Context.PROVIDER_URL, url); Context ctx = new InitialContext(environment );
where:
For more details about JNDI usage, see Sun's JNDI documentation.
environment.put(Context.REFERRAL,"throw");
Once an initial context is obtained, objects can be stored in and retrieved from the namespace. To store an object, use the bind() method:
ctx.bind(entryName, object);
where 'entryName' is the name under which you want the object stored, and 'object' is the object to be stored, for example to store a factory under the name "ivtQCF":
ctx.bind("ivtQCF", factory);
To store an object in a JNDI namespace, the object must satisfy either the javax.naming.Referenceable interface or the java.io.Serializable interface, depending on the JNDI provider you use. The MQeJNDIQueueConnectionFactory and MQeJMSJNDIQueueclasses implement both of these interfaces. To retrieve an object from the namespace, use thelookup() method:
object = ctx.lookup(entryName);
where entryName is the name under which you want the object stored , for example, to retrieve a QueueConnectionFactory stored under the name "ivtQCF":
QueueConnectionFactory factory; factory = (QueueConnectionFactory)ctx.lookup("ivtQCF");
The example program examples.jms.MQeJMSIVT_JNDI can be used to test your installation using JNDI. This is very similar to the examples.jms.MQeJMSIVT program, except that it uses JNDI to retrieve the connection factory and the queue that it uses. Before you can run this program you must store these two administered objects in a JNDI namespace:
Entry name | Java class | Description |
ivtQCF | MQeJNDIQueueConnectionFactory | A QueueConnectionFactory configured to use a WebSphere MQ Everyplace queue manager |
ivtQ | MQeJMSJNDIQueue | A Queue configured to represent a WebSphere MQ Everyplace queue which is local to the queue manager used by the ivtQCF entry |
The program examples.jms.CreateJNDIEntry or the MQeJMSAdmin tool , explained in the following section, can be used to create these entries. Larger installations may have a Lightweight Directory Access Protocol (LDAP) directory available, but for smaller installations a file system namespace may be more appropriate. When you have decided on a namespace you must obtain the corresponding JNDI class files to support the namespace and add these to your classpath. These will vary depending on your choice of namespace and the version of Java you are using.
You must always have the javax.naming.* classes on your classpath. If you are using Java 1 (for example a 1.1.8 JRE) you must obtain a copy of the jndi.jar file and add it to your classpath. If you are using Java 2 (a 1.2 or later JRE) the JRE may contain these classes itself.
If you want to use an LDAP directory, you must obtain JNDI classes that support LDAP, for example Sun's ldap.jar or IBM's ibmjndi.jar, and add these to your classpath. Some Java 2 JREs may already contain Sun's classes for LDAP. See also the section below about LDAP support for Java classes.
If you want to use a file system directory, you must obtain JNDI classes that support the file system, for example Sun's fscontext.jar (which requires providerutil.jar as well) and add these to your classpath. The CreateJNDIEntry example program requires the MQeJMS.jar file on your classpath, in addition to the JNDI jar files. It takes the following command line arguments:
java examples.jms.CreateJNDIEntry -url<providerURL> [-icf<initialContextFactory>][-ldap] [-qcf<entry name><MQe queue manager ini file>] [-q<entry name><MQe queue name>]
An alternative arguement to use is:
java examples.jms.CreateJNDIEntry -h
In the previous two examples:
com.sun.jndi.fscontext.RefFSContextFactory
The url, -url, must be specified and either a QueueConnectionFactory (-qcf) or a Queue (-q), or both, must be specified. The context factory, -icf, is optional and defaults to a file system directory. The LDAP flag, -ldap, should be specified if an LDAP directory is being used, this prefixes the entry name with "cn=", which is required by LDAP.
For example, if a queue manager with the initialisation file d:\MQe\exampleQM\exampleQM.ini exists, and you are using a JNDI directory based in the file system at d:\MQe\data\jndi\, type (all on one line):
java examples.jms.CreateJNDIEntry -url file://d:/MQe/data/jndi -qcf ivtQCF d:\MQe\exampleQM\exampleQM.ini
Note that forward slashes are used in the url, even if the file system itself uses back slashes. The url directory must already exist. To add an entry for the queue you would type (all on one line):
java examples.jms.CreateJNDIEntry -url file:// d:/MQe/data/jndi -q ivtQ SYSTEM.DEFAULT.LOCAL.QUEUE
You could use another local queue instead of the SYSTEM.DEFAULT.LOCAL.QUEUE.
You could also specify the queue name as exampleQM+SYSTEM.DEFAULT.LOCAL.QUEUE, where exampleQM is the name of the queue manager. If the name of the queue manager is not specified, the local queue manager is used.
Both entries could be added at the same time by typing:
java examples.jms.CreateJNDIEntry -url file://d:/MQe/data/jndi -qcf ivtQCF d:\MQe\exampleQM\exampleQM.ini -q ivtQ SYSTEM.DEFAULT.LOCAL.QUEUE
Again, you should type all of this command on one line. A maximum of one connection factory and one queue can be added at a time.
When the JNDI entries have been created, you can run the example .jms.MQeJMSIVT_JNDI program. This requires the same jar files on the classpath as the MQeJMSIVT program, that is:
It also requires the JNDI jar files, as used for the CreateJNDIEntry example program. The example can be run from the command line by typing:
java examples.jms.MQeJMSIVT_JNDI -url<providerURL>
where <providerURL> is the specified URL of the JNDI initial context. By default the program uses the file system context for JNDI:
com.sun.jndi.fscontext.RefFSContextFactory
If necessary you can specify an alternative context:
java examples.jms.MQeJMSIVT_JNDI -url<providerURL> -icf<initialContextFactory>
You can optionally add a -t flag to turn tracing on:
java examples.jms.MQeJMSIVT_JNDI -url<providerURL> -icf<initialContextFactory> -t
To use the entries in the file system directory created in the CreateJNDIEntry example above, type:
java examples.jms.MQeJMSIVT_JNDI -url file://d:/MQe/data/jndi
The example program checks that the required jar files are on the classpath by checking for classes that they contain. It looks up the QueueConnectionFactory and the Queue in the JNDI directory. It starts a connection, which starts the WebSphere MQ Everyplace queue manager, sends a message to the Queue, reads the message back and compares it to the message it sent. The queue should not contain any messages before running the program, otherwise the message read back will not be the one that the program sent. The first lines of output from the program should look like this:
using context factory 'com.sun.jndi.fscontext.RefFSContextFactory' for the directory using directory url 'file://d:/MQe/data/jndi' checking classpath found JMS interface classes found MQe JMS classes found MQe base classes found jndi.jar classes found com.sun.jndi.fscontext.RefFSContextFactory classes Looking up connection factory in jndi Looking up queue in jndi Creating connection
The rest of the output should be similar to that from the example without JNDI. You can also run the two other example programs, examples.jms.PTPSample01 and example .jms.PTPSample02, using JNDI. These programs requires the same JMS and WebSphere MQ Everyplace jar files on the classpath as the MQeJMSIVT_JNDI program, that is:
They also require the jndi.jar file and the jar files for the JNDI provider you are using, for example, file system or LDAP. The examples can be run from the command line by typing:
java examples.jms.PTPSsample01 -url<providerURL>
As in the previous example, providerURL is the URL of the JNDI initial context. By default, the program uses the file system context for JNDI, that is com.sun.jndi.fscontext.RefFSContextFactory. If necessary you can specify an alternative context:
java examples.jms.PTPSsample01 -url<providerURL> -icf<initialContextFactory>
You can optionally add a "-t" flag to turn tracing on: java examples.jms. PTPSsample01 -url <providerURL><-icf initialContextFactory> -t . To use the entries in the file system directory created in the CreateJNDIEntry example above, you would type:
java examples.jms.PTPSample01 -url file://d:/MQe/data/jndi
The program examples.jms.PTPSample02 uses message listeners and filters. It creates a QueueReceiver with a filter "colour='blue'" and creates a message listener for it. It creates a second QueueReceiver with a filter "colour='red'" and also creates a message listener. It sends four messages to a queue, two with the property "colour" set to "red" and two with the property "colour" set to "blue", and checks that the message listeners receive the correct messages. The program has the same command line parameters as the PTPSample01 program and can be run in the same way. Simply substitute PTPSample02 for PTPSample01.