Integrating JMQ with iPlanet Application ServerBy Kevin BooneLast updated September 2001.
DisclaimerThe author is a certified iPlanet instructor, but not a member of the iPlanet organization. I am no more privy to internal information about the iAS product than anyone else. Nothing in this document should be taken to constitute official policy of Sun Microsystems.ScopeThis document describes how to use the Sun/iPlanet messaging product JMQ with the iPlanet Application Server. It assumes that JMQ is already installed. For illustration we will use the JMS servlet examplesimplequeue, which is provided in the iAS code samples package.
This example consists of a single servlet which puts a message in a queue and
retrieves it itself.
This article assumes that the reader is already basically familiar with JMS, messaging and the iPlanet Application Server. BackgroundAt present, a J2EE-compliant application server is required to provide an interface to a messaging service provider that can be used by applications. It is not required to provide the messaging infrastructure itself. Most products have procedures by which a commercial messaging product -- e.g., IBM MQ Series -- can be integrated into the application server.What form does this `integration' take? Well, the developer expects to be able to enter lines of code like this (from the simplequeue example):
QueueConnectionFactory qcf =
(QueueConnectionFactory) initialContext.lookup
("java:comp/env/jms/SampleFactory");
Queue queue =
(Queue) initialContext.lookup
("java:comp/env/jms/SampleQueue");
This leaves the application server with three problems:
In what follows we will look at the specific techniques used by the iPlanet Application Server, version 6.0SP2. We will use the Sun/iPlanet JMQ messaging product as the service provider. Initial setupiAS is not supplied with a messaging provider. In what follows we will describe how to define JMQ queues and connection factories and register them with iAS, but first the application server must be provided with some basic information about the messaging provider. Specifically thekjs CLASSPATH must be
extended so that it can find the classes that will be returned as a result
of JNDI lookup operations. For JMQ, the required classes are in the two
JAR files jmq.jar and jmqadmin.jar. If you did a
default installation of JMQ, these classes will be in /opt/SUNWjmq/lib.
You could make the CLASSPATH changes manually, but iAS is provided
with a script that does it:
[iasroot]/ias/jms/bin/jmssetupThis script prompts the operator for additions to the CLASSPATH. It also asks for additions to the LD_LIBRARY_PATH environment variable, so
that it can find native-code libraries; JMQ does not require any, so this section
can be ignored.
You will need to restart iAS after making these changes. JMQ uses Java security policies files to dictate who can do what on the messaging system; for ordinary operations (posting and retrieving messages) these policies should not need to be changed. However, to add queues and topics using command-line tools, you may need to modify /opt/SUNWjmq/security/jmqadmin.policy.
Adding this line will work:
grant { permission java.security.AllPermission; };
Name mappingThis section describes how iAS maps the `java:comp/env' name onto a real JNDI name. This should be familiar to developers who have used similar techniques for JDBC.As we have seen, the Java code in the servlet contains these instructions:
QueueConnectionFactory qcf =
(QueueConnectionFactory) initialContext.lookup
("java:comp/env/jms/SampleFactory");
Queue queue =
(Queue) initialContext.lookup
("java:comp/env/jms/SampleQueue");
These lookups do not correspond to real JNDI names, but to names local to the
servlet (strictly, to the WAR module). `java:comp/env' is not part of the name,
but an indication to the JNDI subsystem to make a local name lookup. The part
of the string after `java:comp/env' is referred to as the coded name. When
we create the deployment descriptor web.xml for the servlet we
indicate the coded name and the type of resource to which it provides access.
So we have:
<resource-ref>
<res-ref-name>jms/SampleQueue</res-ref-name>
<res-type>javax.jms.Queue</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<res-ref-name>jms/SampleFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
This XML indicates the coded name of the resource, the type of the resource, and
who is to provide authentication information (container). It does not indicate
the real name of the resource, nor the security credentials themselves. So,
where does this information go? This is vendor-specific. With iAS, it goes into the
iAS-specific deployment descriptor which, for a WAR file, is ias-web.xml.
In that file we see the rest of the information:
<resource-ref>
<res-ref-name>jms/SampleFactory</res-ref-name>
<jndi-name>jms/theFactory</jndi-name>
</resource-ref>
<resource-ref>
<res-ref-name>jms/SampleQueue</res-ref-name>
<jndi-name>jms/theQueue</jndi-name>
</resource-ref>
That is, the local name java:comp/env/jms/SampleFactory maps onto the JNDI
name jms/theFactory, while java:comp/env/jms/SampleQueue maps
onto the JNDI name jms/theQueue.
Although it isn't strictly necessary to know this, it can be helpful for debugging to understand that the JMS information from both web.xml and ias-web.xml
end up in the same place in the iAS registry, under the J2EE-Module branch:
J2EE-Module
jms-simplequeue
resource-ref
jms/SampleFactory
jndi-name=jms/theFactory
res-auth=container
res-type=javax.jms.QueueConnectionFactory
jms/SampleQueue
jndi-name=jms/theQueue
res-auth=container
res-type=javax.jms.Queue
This is all very well, but all we have done is translate one unknown name into another
unknown name. What exactly are jms/theFactory and jms/theQueue?
The answer is that theQueue is the JNDI name of a queue that will be registered with iAS,
while theFactory is the JNDI name for a proxy that will stand for the real
queue connection
factory. This proxy will be created using an iAS utility called jmspadm, as
described later; first we must create and register the queue and connection factory.
Creating the queueIn JMS aQueue object is really something that describes the logical
properties of a message queue; it does not represent a connection to anything.
Therefore there is no need to pool Queue objects or to provide
proxies for them. What we must do, however, is create the queue definition within
the messaging service itself, and assign it a JNDI name with iAS. We must do the
same with the connection factory, although its JNDI name won't be used by
applications (they will use its proxy).
Creating the Queue is an operation on the messaging service provider; however, it is the application server that will need access to the created object. With Sun JMQ the approach taken is to ask the service provider to create the queue, and provide a JNDI context to which the service provider can add its own reference. JMQ provides a utility called jmqconfig for this;
if you wanted to run this command at the prompt to create the queue for the
SimpleQueue servlet, you would need to do something like this
(but don't; there is a better way: see below):
/opt/SUNWjmq/bin/jmqconfig \ -a \ -t q \ -n theQueue \ -o name=SampleQueue \ -i com.netscape.server.jms.RefFSContextFactory \ -u /jmsIn this example, `-a' means `add the object'. `-t q' indicates that the object required is a Queue. `-n theQueue' indicates the JNDI name by which the
queue will be referenced. `-o name=SampleQueue' sets the internal name of the queue
(this is not a JNDI name, and won't be used by applications). `-i'
indicates a JNDI naming context class. This is not provided by JMQ, but by iAS. JMQ
will use this class to do a JNDI bind() operation to map the JNDI name
onto the queue reference. This is how JMQ can add queues and other objects to the
namespace of any application server, even one that it knows nothing about; the real
work in this case is done by the RefFSContextFactory class, which
is part of iAS. Finally, `-u /jms' sets the system property
java.naming.provider.url before carrying out the JNDI bind().
This is so that the RefFSContextFactory class, which may not be specific
to JMS lookups, knows where in the directory to insert the new name.
The problem with running the jmqconfig command manually is that it
requires an immense CLASSPATH, which would have to be set beforehand. Happily,
iAS provides a script to run it with the correct classpath. This script is
[iasroot]/ias/ias-samples/jms/docs/jmsjmqadm.sh. You may need to
tweak this script before running it, as its references to the iAS installation
directory won't be correct unless you installed the product with defaults.
If you do this, then the same effect as the jmqconfig command
above can be gained by doing:
jmsjmqadm.sh q theQueue SampleQueuePlease note that SampleQueue is the internal name of the queue, and
need not be the same as the name used in the Java code, or as the JNDI name.
However, theQueue is a name that will be used by the application;
it corresponds to the resource reference that we put in ias-web.xml.
Although not strictly necessary, it may be helpful to see how the queue creation operation is reflected in the iAS registry:
Application Server
6.0
JMSObjects
theQueue
ClassName=com.sun.messaging.Queue
...
RA1=S#destName#SampleQueue
Note the `RA1' entry that provides the link to the internal (JMQ) name of the queue.
Creating the factoryThe application will not use the connection factory directly, but it still has to exist. If you followed the discussion of creating a queue above, then creating the connection factory should present no additional problems:jmsjmqadm.sh qf providerFactory SampleFactory SampleFactory is the internal name of the object, and doesn't have
to correspond to any name used in the application. providerFactory
is a JNDI name, but this won't be used by applications either: they will use
the proxy. After running this command, you will be able to see a new branch
in the iAS registry in the same branch as the queue object created earlier. This
new branch will be called providerFactory.
Proxy generationiAS uses proxies for database connections as well as for messaging services, but the difference for the developer and administrator is that, for JMS, the proxy generation process is exposed. For JDBC, it is handled internally. In the previous step we created the connection factory, with JNDI namejms/providerFactory
and JMQ interal name SampleFactory. Now we must create the iAS proxy for
this object.
To do this, we use the script jmspadm, which is provided in the
directory [iasroot]/ias/jms/bin. The script requires the JNDI name of the
factory, and the (new) JNDI name of the proxy. The name of the proxy must match
the name of the reference in ias-web.xml, as this is what will get
looked up when the servlet runs. So we need:
jmspadm theFactory providerFactoryThis creates the following entries in the registry:
Application Server
6.0
JMSObjects
theFactory
ClassName=com.netscape.server.jms.QueueConnectionFactoryProxy
...
RA0=S#connFactoryName#jms/providerFactory
...
Note that the class name is com.netscape..., not com.sun.messaging...;
that is, the servlet will get a reference to a proxy provided by the application
server, not a class that is part of Sun JMQ. Note also the reference to the
real JNDI name of the JMQ object jms/providerFactory.
SummaryA large number of name mapping steps are involved between the servlet's JNDI lookup and the JMQ object itself. The diagram below summarizes these steps for the connection factory.Servlet java:comp/env/jms/SampleFactory (servlet's name for factory) iAS jms/theFactory (JNDI name of proxy for factory) iAS jms/providerFactory (JNDI name of factory) JMQ SampleFactory (internal name of factory in JMQ)The steps to instantiating, registering and creating proxies for a publish-subscribe messaging system ( Topic and TopicConnectionFactory) are
almost exactly the same as for queues.
|
©1994-2003 Kevin Boone, all rights reserved