blob: e8cb08746804b36249bc0f893e56415b34b5d300 [file] [log] [blame]
<?xml version="1.0" encoding=""?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="" />
<title>Axis2 RPC Support</title>
<link href="../css/axis-docs.css" rel="stylesheet" type="text/css" media="all" />
</head>
<body>
<h1>Axis2 RPC Support</h1>
<p>This documents talks about the Axis2's Remote Procedure Calls support in a
set of easy to understand implementation steps.</p>
<h2>Introduction</h2>
<p>Axis2 Remote Procedure Calls (RPC) support may seem somewhat tricky and
confusing at first glance. However, Axis2 RPC strategy is based on a well
defined set of rules and once the details are in place, it becomes clear as
day. This document aims to drill down to the details of this strategy and
fills up most of the fairly unknown bits and pieces. Note that Axis2
currently does not support the rpc/encoded style fully. It's main support is
for the rpc/lit style.</p>
<p>We will discuss the Axis2 RPC strategy in the following steps</p>
<h2>Step 1 - Converting RPC Style WSDL's into Doc/Lit Style WSDL</h2>
<p>This is probably the most confusing part of the rpc strategy. Since the
Axis2 code generator is based on pure doc/lit style, the first step of the
code generation process is to generate a wrapper schema. This wrapper
generation can be easily explained by using an example.</p>
<p>Take the following piece of WSDL</p>
<pre> .....
&lt; message name="requestMessage"&gt;
&lt;part name="part1" type="xs:string"/&gt;
&lt;part name="part2" type="xs:int"/&gt;
&lt;/message&gt;
&lt;message name="responseMessage"&gt;
&lt;part name="part1" type="xs:string"/&gt;
&lt;/message&gt;
&lt;portType name="echoPortType"&gt;
&lt;operation name="echo"&gt;
&lt;input message="y:requestMessage"/&gt;
&lt;output message="y:responseMessage"/&gt;
&lt;/operation&gt;
&lt;/portType&gt;
&lt;binding name="echoBinding" type="y:echoPortType"&gt;
&lt;soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/&gt;
&lt;operation name="echo"&gt;
&lt;soap:operation soapAction="echo"/&gt;
&lt;input&gt;
&lt;soap:body use="literal"/&gt;
&lt;/input&gt;
&lt;output&gt;
&lt;soap:body use="literal"/&gt;
&lt;/output&gt;
&lt;/operation&gt;
&lt;/binding&gt;
.....</pre>
<p>The binding says its got to be rpc/lit and in this case the message parts
need wrapping in the following order.</p>
<ol>
<li>The first element needs to have the operation name as the local name
and the operation namespace (which happens to be the namespace of the
porttype - in this case the targetnamespace of the WSDL)</li>
<li>The children of this element are non namespace qualified elements with
the part names as local names (referred to as <strong>part
element</strong>)</li>
<li>In case the part refers to a standard type like the example WSDL, the
content of the part element would be of that type. If the part refers to
a complex type defined in the schema, the content of the part element
becomes of that type. Having an element reference in the part when the
binding is rpc is invalid.</li>
</ol>
<p>For example the input wire message for the echo operation mentioned in the
above WSDL fragment would look like this:</p>
<pre> &lt;op:<strong>echo</strong> xmlns:op="porttype namespace"&gt;
&lt;<strong>part1</strong>&gt;Hello World&lt;/part1&gt;
&lt;<strong>part2</strong>&gt;123&lt;/part2&gt;
&lt;/op:echo&gt;</pre>
<p>Note that the element name is in bold. The first one is the operation
name, the second and third are part names. It can be seen that it is quite
possible to generate a schema, representing this structure and then treat the
whole service as pure doc/lit service. In this case, following is the piece
of schema generated to make this rpc to doc conversion. Note that in this
case the wire message stays unchanged. It is only a different WSDL authoring
style</p>
<pre> &lt;xs:element name="echo"&gt;
&lt;xs:complexType&gt;
&lt;xs:sequence&gt;
&lt;xs:element name="part1" type="xs:string" /&gt;
&lt;xs:element name="part2" type="xs:int" /&gt;
&lt;/xs:sequence&gt;
&lt;/xs:complexType&gt;
&lt;/xs:element&gt;</pre>
<p>What the Axis2 code generator does is exactly this. By looking at the
binding style, it generates a wrapper schema in places required before
handing over the Axis* hierarchy to the code generator engine. In every case
(even when the schema needs to be unwrapped) this wrapping part will take
place!</p>
<h2>Step 2 - Unwrapping the Schema</h2>
<p>If the schema needs to be unwrapped then that brings up a few issues
mainly because the only thing that the emitters rely on when generating code
is a mapping table!</p>
<ol>
<li>When the schema is unwrapped where would that unwrapping information
stay?
<p>Good question - It needs to be a store that keeps the information
seperated. Hmm.. What can we use here ? Why of course, the Axis *
hierarchy! It has nicely separated information holders and a parameter
store that can hold a information bean.</p>
</li>
<li>How do we maintain uniqueness among message part names?
<p>Another Good question - part names are only unique across a message
and not globally. But due to the fact that we have a global mapping table
we need a way to differentiate between parts of different messages. The
technique used here is to generate a QName that has the operation name as
a namespace and a suffix (like _input) appended to the local name</p>
</li>
</ol>
<p>Given these solutions the first step in unwrapping is to walk the schema
and figure out the unwrappable items. The key player of the unwrapping
process is the unwrapping extension. What it does is to walk a given schema
and figure out the unwrappable parts if there are any.</p>
<p>The current unwrapper looks for the following patterns and fails if it is
not found!</p>
<pre>&lt; element &gt;
&lt; complexType &gt;
&lt; sequence &gt;
&lt; element /&gt;
&lt; /sequence &gt;
&lt; /complexType &gt;
&lt; /element &gt;
</pre>
<p>Once this pattern is detected the unwrapper details will be added to the
relevant AxisMessage component</p>
<h2>Step 3 - Populate type Information</h2>
<p>The next step is to populate the type information for the parts. This has
to be explicitly done by the data binding extensions, and currently the ADB
and XMLbeans extensions populate the relevant AxisMessage by looking up their
generated type systems. This type information goes into the AxisMessage
inside a MessagePartInformationHolder instance.</p>
<p>The following code fragment from the ADB extension shows how the
AxisMessages get populated with the relevant type information. The code is
almost the same for the XMLBeans extension. Note the items in bold.</p>
<pre> if (message.getParameter(Constants.UNWRAPPED_KEY) != null) {
XmlSchemaType schemaType = message.getSchemaElement().getSchemaType();
if (schemaType instanceof XmlSchemaComplexType) {
XmlSchemaComplexType cmplxType = (XmlSchemaComplexType) schemaType;
XmlSchemaParticle particle = cmplxType.getParticle();
if (particle instanceof XmlSchemaSequence) {
XmlSchemaObjectCollection items =
((XmlSchemaSequence) particle).getItems();
for (Iterator i = items.getIterator(); i.hasNext();) {
Object item = i.next();
if (item instanceof XmlSchemaElement) {
XmlSchemaElement xmlSchemaElement = (XmlSchemaElement) item;
XmlSchemaType eltSchemaType = xmlSchemaElement.getSchemaType();
if (eltSchemaType != null) {
<strong>populateClassName(eltSchemaType,mapper,opName,xmlSchemaElement.getName());</strong>
} else if (xmlSchemaElement.getSchemaTypeName() != null) {
eltSchemaType = findSchemaType(schemaMap,
xmlSchemaElement.getSchemaTypeName());
if (eltSchemaType!=null){
populateClassName(eltSchemaType,mapper,opName,xmlSchemaElement.getName());
}
}
}
}
}
}
}</pre>
<p>The populateClassName looks like this</p>
<pre> private static void populateClassName(XmlSchemaType eltSchemaType,
TypeMapper typeMap,
String opName,
String partName) {
Map metaInfoMap = eltSchemaType.getMetaInfoMap();
if (metaInfoMap != null) {
<strong>String className = (String) metaInfoMap.
get(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY);
QName partQName = WSDLUtil.getPartQName(opName,
WSDLConstants.INPUT_PART_QNAME_SUFFIX,
partName);
typeMap.addTypeMappingName(partQName,className);</strong>
if (Boolean.TRUE.equals(
metaInfoMap.get(SchemaConstants.
SchemaCompilerInfoHolder.CLASSNAME_PRIMITVE_KEY))){
//this type is primitive - add that to the type mapper status
//for now lets add a boolean
typeMap.addTypeMappingStatus(partQName,Boolean.TRUE);
}
}
}</pre>
<h2>Step 4 - Generate Code with Unwrapped Parameters</h2>
<p>The next step is generating the actual code. The
AxisServiceBasedMultiLanguageEmitter has a method that generates the XML
model for the input parameters and that method includes the relevant part
parameters inside the relavant top level input parameter element.</p>
<p>The relevant part of the XML model looks like this. Note that this
intermediate XML model is the one that is parsed against the Stylesheets to
generate the code.</p>
<pre>&lt;input&gt;
&lt;param name="param4" type="com.example.www.ServiceNameStub.Echo" shorttype="Echo" value="null" location="body" opname="echo"&gt;
&lt;param name="param5" type="java.lang.String" shorttype="String" value="null" location="body" opname="echo" partname="Part1"
primitive="yes"/&gt;
&lt;param name="param6" type="int" shorttype="int" value="0" location="body" opname="echo" partname="Part2" primitive="yes"/&gt;
&lt;/param&gt;
&lt;/input&gt;</pre>
<p>The next part is upto the template to handle. Basically, the template
looks after the generation of multiple parameters into the method signatures
and then the generating of the relevant serialization and deserialization
code for the parameters.</p>
<h2>Bringing the Parameters Together and Exploding Them</h2>
<p>This is a somewhat controversial area. The current Axis2 code generator
does the wrapping and unwrapping at the object level and not the XML level. In
short the exploded parameters are only a convenience and the explosion does
not run down to the XML level. The following example of generated source code
makes this clear:</p>
<pre> private org.apache.axiom.soap.SOAPEnvelope toEnvelope(
org.apache.axiom.soap.SOAPFactory factory, java.lang.String param1,
int param2, boolean optimizeContent) {
<strong>com.example.www.ServiceNameStub.Echo wrappedType = new com.example.www.ServiceNameStub.Echo();
wrappedType.setPart1(param1);
wrappedType.setPart2(param2);</strong>
rg.apache.axiom.soap.SOAPEnvelope emptyEnvelope = factory.getDefaultEnvelope();
emptyEnvelope.getBody().addChild(wrappedType.getOMElement(
com.example.www.ServiceNameStub.Echo.MY_QNAME, factory));
return emptyEnvelope;
}</pre>
<p>Note the lines in bold. The wrapped class will anyway be instantiated and
used at the end, but what the user sees is different. Exploding the
parameters happens in a similar way!</p>
<h2>Conclusion</h2>
<p>Axis2 RPC support is a sort of misty area, but it is based on a well
defined set of rules which makes it not <em>that</em> misty after all!</p>
<hr />
</body>
</html>