|
Comments
|
Today's Top SOA Links
Feature Java Feature — A Generic JMS Listener for Apache Axis 1.x
A needed transport-level handler
Aug. 24, 2006 10:00 AM
Unlike the HTTP protocol there's no stable default JMS listener for invoking the Web Services exposed in Apache Axis 1.x using JMS (Java Message Service) as the transport protocol - other than the one provided merely for demo purposes.
Apache Axis is a popular Java-based Open Source platform for exposing Web Services. It has native support for handling invocations into Web Services based on the SOAP (Simple Object Access Protocol) application protocol. By default, the Axis server supports HTTP as the protocol for transporting the SOAP payload and provides an HTTP transport listener to do the same on its own. The HTTP transport listener accepts the SOAP requests coming over HTTP and then hands off the SOAP payload to the Axis engine for application-level handling of the request (like SOAP parsing, extracting input parameters, invoking the right service implementation, etc.), and gets the response SOAP message from the Axis engine and sends it back to the caller in the HTTP response. For those enterprise applications where reliable invocation and guaranteed delivery of invocation messages are important, JMS, rather than HTTP, is the preferred protocol for transporting SOAP messages. JMS implementation providers, with built-in reliability features like re-try mechanisms, ensure that messages reach the message consumer application whatever the case. JMS is also the best way to handle asynchronous invocations of Web Services. However, what Axis 1.x provides for JMS transport protocol use is only a basic demo listener that's not really meant for production-level use. This listener is also not easy to use and isn't flexible enough to be able to specify and handle unique endpoint addresses for each individual Web Service exposed. So, for client applications that need to invoke the Axis Web Services over JMS a flexible, stable, and easy-to-use JMS transport listener and handler is required. This article implements a generic JMS listener and describes how it is to be used along with the Axis server. For purposes of this paper, I've considered the Open Source frameworks Apache Axis 1.2 and JMS provider OpenJMS 0.7.6.1. However, this should be largely applicable to the higher versions of Axis in the 1.x series too.
The Addressing Model We can choose to follow a similar model for exposing the same Web Services over the JMS transport protocol. In other words, each service endpoint will be available at a unique JMS destination (aka a queue). So for each Web Service defined in the Axis server, we define a separate queue in the JMS provider - in our case OpenJMS - and update the openjms.xml file in the config folder of the OpenJMS home for defining one queue for each Axis-deployed Web Service that's meant to be accessible over JMS. In this article, the approach taken is to use the service name defined in Axis itself as the queue name (similar to the HTTP concrete binding mentioned above). For example, for the sample Web Service called "MessageService" provided in the Axis distribution, we can define a queue with the same name putting the entry <AdministeredQueue name="MessageService"/> in openjms.xml. This addressing model applies uniformly to both RPC-style and message-style Axis services. It's more straightforward and standards-compatible with the WSDL specs. The JMS destination for each Web Service becomes the address in the concrete port binding for the service and the Web Service clients can directly use this concrete JMS destination mentioned in the WSDL file for invoking the service. This is a better model than the non-standard way of specifying the Axis Web Service name as the prefix in the request SOAP message body's first XML element. For example, for the invocation of the method "getQuote" in the Axis sample Web Service named "urn:xmltoday-delayed-quotes," which is an RPC-style service, the basic JMS listener provided in the Axis distribution expects the client to create the SOAP body element <urn:xmltoday-delayed-quotes:getQuote> containing the service name as the XML prefix. The client does this through code similar to call.setOperationName(new QName("urn:xmltoday-delayed-quotes:getQuote", "getQuote")). This fact doesn't appear in the WSDL definition of the service hence interoperability with different external service clients could become an issue. It's best to stick to the details given in the WSDL file and with the new service endpoint addressing scheme introduced here, all the clients can, in a standard way, just keep the first SOAP body element as <getQuote> following the WSDL details alone, thereby improving interoperability.
The Role of JMS Listener Then, if the client expects a response back from the Web Service (such as in a pseudo-synchronous call), the JMS listener needs to get the SOAP response message from the Web Service and put it as the payload in a new JMS message and send this message to the JMS queue destination the client is waiting on. This JMS listener can be used for receiving requests of both RPC-style and document-style Web Services invocations since it doesn't read and interpret the SOAP message at all; it just sticks to its role as a transport-level handler.
Implementing the Listener First the constructor of this class needs to register with OpenJMS for receiving messages in the queue defined for this Web Service. The constructor takes the Web Service name as an input argument. A static initialization block is used to instantiate the Axis engine and this gets executed when the Java Virtual Machine (JVM) loads the GenericJMSSOAPListenerForAxis. All the instances of this class have to use the same Axis engine instance.
... AxisJMSListenersStarter class's main method starts the listeners by reading the XML file "jmswebsvcs.xml" that contains the list of Axis Web Services, creating and starting one instance of GenericJMSSOAPListenerForAxis for each service in the list by passing the name of the Web Service as an argument to the constructor. In effect this dynamically creates the concrete service endpoint destinations for the JMS protocol, since each Web Service now gets a unique concrete address.
public static void main(String[] args) Now let's look at the message-handling logic in the GenericJMSSOAPListenerForAxis that's instantiated for a particular Web Service, say, "MessageService." Its onMessage() method is called once the message arrives in the queue named "MessageService." After creating the Axis MessageContext for the message the onMessage method sets the serviceHandler field of the MessageContext to tell Axis that the Web Service being invoked is "MessageService" and that for executing service-specific functionality, the service implementation class, as defined in the server-config.wsdd, for service name "MessageService" should be invoked.
public void onMessage(javax.jms.Message inMsg) This method then sends the response SOAP message to the JMS client. However, at this point no correlation id is used. For simplicity's sake it's assumed here that the client, after sending the message, waits on a receive queue expecting to get a response message for the Web Service invocation it just made. This listener class can easily be extended to refer to and use a client-specified JMS correlation id to send a correlated response to the client.
Invoker-side implementation
...String webSvcJMSDestination = "urn:xmltoday-delayed-quotes"; Reader Feedback: Page 1 of 1
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
|||||||||||||||||||||||||||