Hi all,
 
As part of a project for my employer, I've developed a "SOAPGenerator" and "SOAPSerializer" that support creating pipelines that accept and process SOAP document style requests.  My employer has given me the okay to contribute these to the Cocoon project.  This e-mail is intended to describe the components I've created, explain why they're different from the existing Axis block, and solicit responses from the community as to whether this is generally useful functionality suitable for inclusion into Cocoon.
 
First off, let me describe the use case this set of components is meant to solve.  My employer currently serves some reports out of Cocoon generated by standard pipelines that access a database and perform various transformations.  The URL of the report contains a pair of keys ( e.g. http://server/cocoon/report/KEY1/KEY2) and returns HTML content.  In the near future, we'd like to return the report's contents in XML, and furthermore we'd like to obtain the report contents via a SOAP request.  Not all Cocoon pipeline developers in our organization are up to speed on SOAP, so we desire a way to let them write pipelines that serve SOAP requests without having to deal with the SOAP envelope and its details.
 
At first glance, we  could create pipelines like this:
 
<map:pipeline>
    <map:match pattern="soap-based-report">
        <map:generate type="stream"/>
        <map:transform src="">
 
        <!-- generate the report XML -->
        <map:transform .../>
 
        <map:transform src="">
        <map:serialize type="xml"/>
    </map:match>
</map:pipeline>
 
This has some limitations.  First, the StreamGenerator and "remove-soap-envelope.xsl" don't insure that the request is actually a properly formed SOAP request, nor do they verify the type of request (RPC, document).  The SOAP client can send any method name and if it sets 'mustUnderstand="true"' in any SOAP headers, it will be silently ignored.  Hence our SOAP implementation isn't compliant with the SOAP standard.
 
Instead, we provide a SOAPGenerator and SOAPSerializer to handle these details:
 
<map:pipeline>
    <map:match pattern="soap-based-report">
        <map:generate type="soap">
            <parameter
                name="method-names"
                value="\{http://report.soap.employer.com\}process"/>
        </map:generate>
 
        <!-- generate the report XML -->
        <map:transform .../>
 
       <map:serialize type="soap"/>
    </map:match>
</map:pipeline>
 
This limits the pipeline author's knowledge of SOAP to the name of the SOAP method.  The SOAPGenerator throws ProcessingException if the request is not made to the correct operation (the namespace-qualified element process in the example; this can be a list of names) or is not a SOAP document request.  It also throws if there are any "mustUnderstand" SOAP headers.  It's my understanding of the SOAP standard that headers not marked as "mustUnderstand" can be ignored.  If anyone has any ideas on how to do more sophisticated SOAP header handling, I'm open to changing how this works.
 
The SOAPSerializer simply wraps the response document in a SOAP envelope and body.  I've also written some XSL templates that convert Cocoon exceptions into SOAP client or server faults.  These are used in <map:handle-errors/> to generate SOAP error messages.
 
Finally, I've written a RequestParameterRegexSelector that extends RequestParameterSelector to allow the test expressions to be regular expressions.  We use this to return WSDL, when appropriate.  The RequestParameterSelector can be used for this, but if ever someone requested http://server/cocoon/soap-service?wsdl=yes, it would attempt to execute the service rather than returning the WSDL. 
 
<map:pipeline>
    <map:match pattern="soap-service">
        <map:select type="request-parameter-regex">
            <map:parameter name="parameter-name" value="wsdl"/>
 
            <!-- if the URL parameter "wsdl" exists, return the .wsdl file;
                 regular request-parameter selector cannot discern between
                 missing param and param with no value -->
            <map:when test=".*">
                <map:generate src="">
                <map:serialize type="xml"/>
            </map:when>
 
            <map:otherwise>
                <!-- SOAP process as above -->
            </map:otherwise>
        </map:select>
    </map:match>
</map:pipeline>
 
The proposed generator and serializer are different from the existing Axis block because they allow the web service itself to be implemented as a Cocoon pipeline.  It seems to me that the Axis block allows an existing SOAP service to be executed via a reader and allows calls to external SOAP services to be made.  Our reports lend themselves to being written as Cocoon pipelines, so it seems wasteful to recreate them as SOAP services and just pass their output through Cocoon.
 
Thanks for making it this far.  I tried to be as brief as I could.  :-)  Please let me know if you'd like to see this submitted as a contribution to Cocoon.  If so, I'll do the necessary clean up (making sure it matches Cocoon's coding standards, unit tests and such) and submit it via bugzilla.
 
Thanks for your consideration.
 
Stephan Zuercher
 

Reply via email to