Axis2 RPC Support
This document describes Axis2's Remote Procedure Call support in a set of easy to understand implementation steps.Introduction
Axis2 Remote Procedure Call (RPC) support may seem somewhat tricky and confusing at first glance. However, Axis2 RPC strategy is based on a set of well defined rules. This document aims to drill down to the details of the strategy and resolve most of the unknown bits and pieces. Note that Axis2 currently does not support the rpc/encoded style. But it fully supports the rpc/literal style.We will discuss the Axis2 RPC strategy in the following steps
Step 1 - Converting RPC Style WSDL's into Doc/Lit Style WSDL
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.Take the following piece of WSDL
..... <message name="requestMessage"> <part name="part1" type="xs:string"/> <part name="part2" type="xs:int"/> </message> <message name="responseMessage"> <part name="part1" type="xs:string"/> </message> <portType name="echoPortType"> <operation name="echo"> <input message="y:requestMessage"/> <output message="y:responseMessage"/> </operation> </portType> <binding name="echoBinding" type="y:echoPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="echo"> <soap:operation soapAction="echo"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> .....
- The first element needs to have the operation name as the local name and the operation namespace. (This happens to be the namespace of the porttype - in this case the targetnamespace of the WSDL.)
- The children of this element are non namespace qualified elements with the part names as local names (referred to as part element)
- 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.
<op:echo xmlns:op="porttype namespace"> <part1>Hello World</part1> <part2>123</part2> </op:echo>
<xs:element name="echo"> <xs:complexType> <xs:sequence> <xs:element name="part1" type="xs:string" /> <xs:element name="part2" type="xs:int" /> </xs:sequence> </xs:complexType> </xs:element>
Step 2 - Unwrapping the Schema
If the schema needs to be unwrapped, it brings up a few issues. This is mainly because the only thing that the emitters rely on when generating code is a mapping table.- When the schema is unwrapped, where will the unwrapping information remain? There has to be a store to keep the information seperated. The Axis * hierarchy can be used for this. It has nicely separated information holders and a parameter store that can hold an information bean.
- How do we maintain uniqueness among message part names? Part names are only unique across a message and not globally. However, 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.
The current unwrapper looks for the following patterns and fails if it is not found!
< element > < complexType > < sequence > < element /> < /sequence > < /complexType > < /element >
Step 3 - Populate Type Information
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.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.
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) { populateClassName(eltSchemaType,mapper,opName,xmlSchemaElement.getName()); } else if (xmlSchemaElement.getSchemaTypeName() != null) { eltSchemaType = findSchemaType(schemaMap, xmlSchemaElement.getSchemaTypeName()); if (eltSchemaType!=null){ populateClassName(eltSchemaType,mapper,opName,xmlSchemaElement.getName()); } } } } } } }
private static void populateClassName(XmlSchemaType eltSchemaType, TypeMapper typeMap, String opName, String partName) { Map metaInfoMap = eltSchemaType.getMetaInfoMap(); if (metaInfoMap != null) { String className = (String) metaInfoMap. get(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY); QName partQName = WSDLUtil.getPartQName(opName, WSDLConstants.INPUT_PART_QNAME_SUFFIX, partName); typeMap.addTypeMappingName(partQName,className); 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); } } }
Step 4 - Generate Code with Unwrapped Parameters
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.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.
<input> <param name="param4" type="com.example.www.ServiceNameStub.Echo" shorttype="Echo" value="null" location="body" opname="echo"> <param name="param5" type="java.lang.String" shorttype="String" value="null" location="body" opname="echo" partname="Part1" primitive="yes"/> <param name="param6" type="int" shorttype="int" value="0" location="body" opname="echo" partname="Part2" primitive="yes"/> </param> </input>
Bringing the Parameters Together and Exploding Them
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:private org.apache.axiom.soap.SOAPEnvelope toEnvelope( org.apache.axiom.soap.SOAPFactory factory, java.lang.String param1, int param2, boolean optimizeContent) { com.example.www.ServiceNameStub.Echo wrappedType = new com.example.www.ServiceNameStub.Echo(); wrappedType.setPart1(param1); wrappedType.setPart2(param2); rg.apache.axiom.soap.SOAPEnvelope emptyEnvelope = factory.getDefaultEnvelope(); emptyEnvelope.getBody().addChild(wrappedType.getOMElement( com.example.www.ServiceNameStub.Echo.MY_QNAME, factory)); return emptyEnvelope; }
No comments:
Post a Comment