Creating SOAP Services with Cocoon
March 18, 2003
The Apache Cocoon framework excels at processing and manipulating XML documents, which makes it an easy and ideal platform for SOAP services. All the necessary components exist in the standard Cocoon release, except for one. This article introduces the XmlHttpTransformer, a component which allows mid-pipeline Cocoon elements to operate as SOAP clients retrieving information from external services. Two simple examples are presented with source code available.
Apache Cocoon, part of the Apache XML Project and soon to become a top-level project, is a highly flexible web publishing framework for creating application from reusable components. Although reusability is an oft-touted quality of software frameworks, Cocoon stands out because of its XML component orientation. As long as a component accepts and emits XML, it can be integrated into the framework. For a more detailed introduction see my previous article (Getting Started With Cocoon 2) or other articles listed in reference section.
Apache Cocoon Notation
This article uses the following Apache Cocoon Graphical Notation:
Cocoon can be used to build web services based on its standard building blocks. However, accessing and utilising such services from Cocoon in a flexible manner required the creation of a new component called XmlHttpTransformer. Given this new component, in conjunction with existing components, it becomes possible to create and use web services fully within Cocoon.
Example Service: SOAP Stock Quote<o:p>
You've all seen this example before: request the price of a current stock given its ticker symbol. Here we implement this service with five simple Cocoon components, all directed by XML files, without writing any procedural code. Specifically, it's written to emulate the SOAP protocol header and body.
Figure 1 shows the design of our simple Apache Cocoon Framework Stock Server. If you are unfamiliar with the basic building blocks of Apache Cocoon see "Getting Started With Apache Cocoon." I assume that the stock price information is located in a relational database. To achieve the goal of creating a SOAP stock quote service, we need only three interesting components.
TOMCAT Installation from cocoon-soap.war (Tested with Tomcat 4.1.12)
- An XSLT SQL Formatter. We use an XSL transformer and associated stylesheet ("sql.xsl") designed to pick out the stock symbol text from the incoming SOAP message. Then we formulate a SQL query statement in the appropriate Cocoon SQL Transformer constructs.
- The second component is the SQL Transformer component itself. It will process the SQL query created by the XSLT SQL Formatter and return the results in XML to the next component.
- Finally, the stock price value is picked out of the SQL Transformer output and then reformatted as a SOAP return message by means of a second XSL transformer and associated stylesheet ("soap.xsl").
The beginning and ending components (Cocoon generator and serializer) handle the protocol translation between the outer environment (XML over HTTP) and the inner environment (Cocoon's XML Sax Event stream). The major functional difference between the two is that bidirectional or "round trip" behavior, whereas the XML SAX Event stream is more a unidirectional event.
Simple and easy? You bet. In this solution, we employ three "source files": two XSL stylesheet "sources" and a Cocoon "site map" file that describes the component topology as shown in figure 1. Download package xmlhttp.zip (25Kbytes) to examine these files in detail.
The XML document emitted from the Cocoon Generator component is a SOAP request:
<SOAP-ENV:Envelope"> <SOAP-ENV:Body> <ns1:getQuote xmlns:ns1="urn:xmethods-delayed-quotes"> <symbol xsi:type="xsd:string">IBM</symbol> </ns1:getQuote> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
After transformation by a simple XSL stylesheet, the XML document sent to the SQL Transformer will look like this:
<sql:execute-query> <sql:query> SELECT price FROM stock_demo WHERE name = 'IBM' </sql:query> </sql:execute-query>
The XML document emitted from the SQL Transformer looks like:
<sql:rowset> <sql:row> <sql:price>86.92</sql:price> </sql:row> </sql:rowset>
And the final SOAP response document will be
<soap:Envelope> <soap:Body> <n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes"> <Result xsi:type="xsd:float">86.92</Result> </n:getQuoteResponse> </soap:Body> </soap:Envelope>
This example demonstrates the "XML Directed Software" idea; that is, realizing solutions by directing generic procedural algorithms with application-specific XML "source code".
The Apache Cocoon XmlHttpTransformer Component
Creating this first web service required the use of standard Cocoon components; however, the client side of the example requires a new component. I created the XmlHttpTransformer component for this purpose. It's in at an alpha development stage, has been tested against release 2.0.3, and is free to download and use. If there is sufficient interest I will integrate it into the Apache Cocoon project probably with release 2.1. Until then, I'll accept enhancement requests with an eye toward satisfying public need.
Example Client: Multi Currency Subsystem
In this example, I demonstrate a web service client subsystem. A Cocoon Subsystem is a portion of a larger Cocoon application solution that accepts and emits an XML document. Formulating exactly which services are to be requested (i.e., the input to this subsystem) is determined by the preceding component stages (see resource articles for examples of simple complete solutions). Processing the results -- for example, translating the results into HTML -- happens in subsequent component stages. This example focuses solely on retrieval of information from remote web services.
I construct a service similar to the SOAP stock service from the earlier example; in this case, though, it will return the price in some specified foreign currency. The core executes two SOAP queries over the Internet: a request for the stock price in US dollars and a request for the desired currency conversion rate.
Figure 2 above shows the simplified interior block diagram of this subsystem. Again, the building blocks and content flow are very simple. The behavior and results of each stage are as follows:
The first XSL Transformer accepts a very simple XML document specifying the stock symbol to be looked-up and the target currency type. A typical input document to this transformer would look like
<demo> <stock>IBM</stock> <exchange>Japan</exchange> </demo>
An XSLT stylesheet source ("client.xsl") contains static information, including the URIs of the various servers to be accessed and the expected request format. Thus, the input file to the XmlHttpTransformer component will look something like:
<demo xmlns:xht="http://cocoon.clsw.com/xmlhttp"> <xht:messages> <xht:message soapAction="urn:xmethods-delayed-quotes" url="http://services.xmethods.net:9090/soap"> <SOAP-ENV:Envelope> <SOAP-ENV:Body> <ns1:getQuote xmlns:ns1="urn:xmethods-delayed-quotes"> <symbol xsi:type="xsd:string">IBM</symbol> </ns1:getQuote> </SOAP-ENV:Body> </SOAP-ENV:Envelope> </xht:message> <xht:message soapAction="urn:xmethods-CurrencyExchange" url="http://services.xmethods.net:9090/soap"> <SOAP-ENV:Envelope> <SOAP-ENV:Body> <ns1:getRate xmlns:ns1="urn:xmethods-CurrencyExchange"> <country1 xsi:type="xsd:string">United States</country1> <country2 xsi:type="xsd:string">Japan</country2> </ns1:getRate> </SOAP-ENV:Body> </SOAP-ENV:Envelope> </xht:message> </xht:messages> </demo>
The XmlHttpTransformer component processes these requests and replaces the request XML elements (all nested by <xht:messages>) with the responses returned from the respective web services. In this case, we use two web services from the xmethods site. The output of this stage will look something like:
<demo xmlns:xht="http://cocoon.clsw.com/xmlhttp"> <xht:messages> <soap:Envelope> <soap:Body> <n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes"> <Result xsi:type="xsd:float">86.92</Result> </n:getQuoteResponse> </soap:Body> </soap:Envelope> <soap:Envelope> <soap:Body> <n:getRateResponse xmlns:n="urn:xmethods-CurrencyExchange"> <Result xsi:type="xsd:float">122.52</Result> </n:getRateResponse> </soap:Body> </soap:Envelope> </xht:messages> </demo>
Finally, the returned response envelope formats are stripped off and the two results combined into a single numeric value of the stock price in the foreign currency; in this case, the value of IBM stock in yen:
<demo> <foreign-currency-price>10649.43</foreign-currency-price> </demo>
The examples in this article demonstrate how SOAP-conforming services can be accessed and emulated by manipulating the SOAP header by way of XSL transformations. These examples show that is it possible to implement such solutions with the entire application domain solution captured in various XML documents: for example, XSL stylesheets with content directing components such as SQLTransformer and XmlHttpTransfomer. The only new procedural software written, the XmlHttpTransfomer component, is generic in nature, reusable, and independent of any particular application domain.
Where to Go and What to Do
The www.xmethods.net site offers a wide range of web services that can be experimented with. The second example in this article demonstrates usage of two of these services.
Source code for the two examples and the XmlHttpTransformer component can be freely downloaded.