cocoon-docs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache Wiki <wikidi...@apache.org>
Subject [Cocoon Wiki] Update of "WebServiceServer" by JanHinzmann
Date Wed, 30 Mar 2005 15:14:33 GMT
Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Cocoon Wiki" for change notification.

The following page has been changed by JanHinzmann:
http://wiki.apache.org/cocoon/WebServiceServer

New page:
= Using Cocoon as WebServiceServer =
an approach with Flowcontrol, Continuations  and JXTemplates based on ideas of

 *http://www.mail-archive.com/users@cocoon.apache.org/msg20832.html
 *joose and ugocei (irc.freenode.net#cocoon) Big Thanks to you both!

'''Status:''' Draft
----
Please add comments and corrections --JanHinzmann

==== Motivation/Idea ====

The approach is to generate a received SOAP-Envelope with a StreamGenerator and extracting
the called method and the arguments using XSLT, Flow and JXTemplates.

The flowscript will compute the request and finally an answer-envelope will be serialized
to the client.

==== Example ====
Here is an example of a possible setup, building a webservice which provides an echo-method:

{{{
public class Webservice(){
    public synchronised String echo(String echo){
        return echo;
    }
}
}}}

Now customers are sending SOAP-envelopes (using a middleware like axis) like the following:
{{{
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
      <ns1:echo soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="CodataWS">
        <ns1:arg0 xsi:type="xsd:string">Hello Echo!?</ns1:arg0>
      </ns1:echo>
    </soapenv:Body>
  </soapenv:Envelope>
}}}

This will be generated with a ''org.apache.cocoon.generation.StreamGenerator'' and
forwarded from the sitemap to the flowscript with (be sure to have an XMLSerializer who will
not include the <?xml ...-declaration):
{{{
...
      <map:serializer name="xmlnope" 
		  logger="sitemap.serializer.xml" mime-type="text/xml" 
		  src="org.apache.cocoon.serialization.XMLSerializer">
          <omit-xml-declaration>yes</omit-xml-declaration>
        </map:serializer>

<!-- == Webservice by Cocoon == -->
      <map:pipeline>
          <map:match pattern="webservice">
              <map:call function="mainWS"/>
          </map:match>
    
       <!-- getting the soap-envelope -->
       <map:match pattern="soapData">
           <map:generate type="stream"/>
           <map:serialize type="xmlnope"/>
       </map:match>
...
}}}

The first matcher calls a flowscript, which will request the envelope and save it in an ''ByteArrayOutputStream''
using the second matcher. Here is the relevant part from the flowscript:

{{{
  function mainWS(){
    var soapData  = new java.io.ByteArrayOutputStream();
    
    //getting the envelope out of the request (can be done only once)
    cocoon.processPipelineTo("soapData", null, soapData);
  ...
}}}

Now that we have saved the soapData in an ''ByteArrayOutputStream'', we are sending it
back to the Sitemap, to get the Method out of the envelope ('echo' in this case). Therefore
we are using an JXTemplate with a macro and a function in the flowscript which converts the
string into SAX (this is from the mailinglist, I have found an error in it at line 6 (...setRootElement(ignore)
NOT ...setRootELement(true) right joose?):
{{{
/**
 * function from joose: http://joose.iki.fi/cocoon/saxInJX.txt
 */
function stringToSAX( str, consumer, ignoreRootElement ) {
        var is = new Packages.org.xml.sax.InputSource( new java.io.StringReader( str ) );
        var ignore = ( ignoreRootElement == "true" );
        var parser = null;
        var includeConsumer = new org.apache.cocoon.xml.IncludeXMLConsumer(consumer, consumer
);
        includeConsumer.setIgnoreRootElement( ignore );
        try {   
                parser = cocoon.getComponent(Packages.org.apache.excalibur.xml.sax.SAXParser.ROLE
);
                parser.parse( is, includeConsumer );    
        } finally {
                if ( parser != null ) cocoon.releaseComponent( parser );
        }
}
}}}

now lets go back in the flowscript right to the point, after we have called the  "soapData"
matcher:
{{{
function mainWS(){
    var soapData  = new java.io.ByteArrayOutputStream();
    
    //getting the envelope out of the request (can be done only once)
    cocoon.processPipelineTo("soapData", null, soapData);
  ...
//here we go:

    //getting the method out of the soap-content
    cocoon.session.setAttribute( "saxer", stringToSAX  );
    var soapMethod   = new java.io.ByteArrayOutputStream();	
    cocoon.processPipelineTo("soapMethod", {"soapData":soapData}, soapMethod);	
    clog("soapMethod:\n " + soapMethod + "\n");
}}}

Back in the sitemap we generate the content with the JXGenerator and a Template as follows
(be aware, that linebreaks and blanks after the </jx:macro> are going right in the xmlcontent):
{{{
<?xml version="1.0"?>
<soap xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
 <jx:macro name="inject">
  <jx:parameter name="value"/>
  <jx:parameter name="ignoreRootElement" default="false"/>
  <jx:set var="ignored" value="${cocoon.session.saxer( value, cocoon.consumer, ignoreRootElement
)}"/>
 </jx:macro><inject value="${soapData}" ignoreRootElement="false"/></soap>
}}}

The matcher in the Sitemap looks like:
{{{
    <!-- which Method is called? -->
    <map:match pattern="soapMethod">
    	<map:generate type="jx" src="xml/dummy.jx">
    		<map:parameter name="soapData" value="{flow-attribute:soapData}"/>
    	</map:generate>
        <!--<map:transform src="xsl/soapMethod.xsl"/>-->
            <map:serialize type="xml"/>
      	</map:match>
}}}

'''Note''': the soapMethod.xsl should now transform the methodname out of the envelope. (work
in progress) it could be something like:
{{{
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    
    <xsl:template match="/soap/Envelope/Body">
        <xsl:apply-templates/>
    </xsl:template>
    
    <xsl:template match="*[1]">
        <xsl value-of select="local-name()"/>
    </xsl:template>

</xsl:stylesheet>
}}}

TODO: dispatch the method and generate eventually passed parameters with matchers like ''soapMethod''


The answer can then be serialized with an other matcher like:
{{{
      	<map:match pattern="answer">
            <map:generate src="xml/dummy.xml" />
            <map:transform src="xsl/soap.xsl">
              <map:parameter name="ret" value="{flow-attribute:ret}"/>
            </map:transform>
            <map:serialize type="xml"/>
         </map:match>         
}}}
and a soap.xsl like:
{{{
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="ret"/>

<xsl:template match="/">

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
	 	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
     <ns1:helloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

    	xmlns:ns1="CodataWS">
    	<ns1:helloReturn xsi:type="xsd:string">
    		<xsl:value-of select="$ret"/>
    	</ns1:helloReturn>
    </ns1:helloResponse>
  </soapenv:Body>
</soapenv:Envelope>
  
</xsl:template>
</xsl:stylesheet>
}}}

The returnparameter would be passed from the flowscript as follows:
{{{
//sending the answer
	cocoon.sendPage("answer", {"ret":ret});
}//end of mainWS
}}}

Where this should be refactored to a JXTemplate maybe
----

:) Ok, thats my first wikipage. If its all crap, just delete it ;)

What do you think about this approach?

Any suggestions are welcome.
Mime
View raw message