Contract First Development with Axis2 (Part II)

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Amila Suriarachchi
  • 16 Dec, 2007

Introduction

Defining the Web service contract using a WSDL document and implementing it with Axis2 was described in the part 1 of this article[1]. Part I describes only the document style with a simple in-out operation. This article starts by describing the RPC style with a similar in-out operation. Then it describes the fault handling and SOAP headers using appropriate samples. Code for all the samples can be downloaded from samples.tar.

Using RPC Style

The main difference between the document style and the RPC style is that in the former style, the message parts always refer to an element, and in the latter, the message parts always refer to a type.  Sample2.wsdl describes an RPC style document. Let's examine it starting from the portType.

<portType name="Sample2PortType">
    <operation name="echoString" parameterOrder="part1 part2 part3">
        <input message="tns:EchoStringRequest"/>
        <output message="tns:EchoStringResponse"/>
    </operation>
</portType>

As in the document style, the operation element defines an operation called echoString, with an input message EchoStringRequest and an output message EchoStringResponse. In addition to this, it has another attribute called parameterOrder. This attribute is used to define the parts order of the request message. Now let's look at the messages section.

<message name="EchoStringRequest">
    <part name="part1" type="xsd:string"/>
    <part name="part2" type="xsd:int"/>
    <part name="part3" type="xsd:double"/>
</message>
<message name="EchoStringResponse">
    <part name="part4" type="xsd:string"/>
</message>

Unlike in the document style, here message parts refer to a type. Although the XMLSchema Simple type has been given here, these types can be any XMLSchema Complex type given in the Types section.

So how do we send or receive a type as an XML message? What is the actual XML input message required by this operation? To understand the rules regarding creating the actual input Xml message and the output Xml message, the corresponding binding should also be taken into account. Here is the binding.

<binding name="Sample2Soap11Binding" type="tns:Sample2PortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="echoString">
        <soap:operation style="rpc" soapAction="urn:echoString"/>
        <input>
            <soap:body use="literal" namespace="http//tempuri.org/sample2/types"/>
        </input>
        <output>
            <soap:body use="literal" namespace="http//tempuri.org/sample2/types"/>
        </output>
    </operation>
</binding>

The binding style has been defined  as RPC as expected. Also, in addition to the attributes defined in the document style, there is a new attribute called namespce in the soap:body element. This attribute is used to define the top element namespace of the actual input and output messages. Here is the schema for the input message. It is necessary to understand that this schema is not given in the WSDL file itself.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http//tempuri.org/sample2/types"
            targetNamespace="http//tempuri.org/sample2/types"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">
    <xsd:element name="echoString">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element form="unqualified" name="part1" type="xsd:string"/>
                <xsd:element form="unqualified" name="part2" type="xsd:int"/>
                <xsd:element form="unqualified" name="part3" type="xsd:double"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:element name="echoStringResponse">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element form="unqualified" name="part4" type="xsd:string"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

The local part of the input message is always equal to the operation name. In this case, it is echoString. The namespace of this element is given by the namespace attribute of the soap:body element as described earlier. But this namespace attribute is an optional attribute. Therefore, if this attribute is not present, then the target namespace of the WSDL file is taken. Local parts of the inner elements are taken from the message part names, and their order is determined by the parameterOrder attribute. Finally, all the inner elements belong to the default namespace and hence they are not namespace qualified. In contrast to the input element local part, the local part of the output element always has the Response suffix. Other than this it follows the same rules.

Now the service is defined and the skeleton and the stub can be generated using the wsdl2java tool as in the earlier example. Although the client can also be invoked using the previous method, let's try to invoke it asynchronously. Axis2 supports asynchronous service invocations using call back handlers. The following code can be used to invoke this service.

public void testEchoStringAsynchronous() {
        try {
            Sample2ServiceStub sample2ServiceStub = new Sample2ServiceStub();
            sample2ServiceStub._getServiceClient().getOptions().setProperty(
                    Constants.Configuration.TRANSPORT_URL,
                    "http://localhost:8088/axis2/services/Sample2Service");
            sample2ServiceStub._getServiceClient().getOptions().setProperty(
                    HTTPConstants.CHUNKED, Constants.VALUE_FALSE);
            EchoString echoString = new EchoString();
            echoString.setPart1("test string");
            echoString.setPart2(5);
            echoString.setPart3(34.5);
            Sample2ServiceCallbackHandler callBackHandler = new Sample2ServiceCallbackHandler() {
                public void receiveResultechoString(EchoStringResponse result) {
                    System.out.println("Response message ==>" + result.getPart4());
                    synchronized (this) {
                        this.notifyAll();
                    }

                }

                public void receiveErrorechoString(Exception e) {
                    System.out.println("Error has occured");
                }
            };

            sample2ServiceStub.startechoString(echoString, callBackHandler);
            synchronized (callBackHandler) {
                try {
                    // wait until the response receives
                    callBackHandler.wait();
                } catch (InterruptedException e) {
                }
            }
        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (java.rmi.RemoteException e) {
            e.printStackTrace();
        }
    }

Here are the request and response messages.

Request message

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <ns1:echoString xmlns:ns1="http//tempuri.org/sample2/types">
            <part1>test string</part1>
            <part2>5</part2>
            <part3>34.5</part3>
        </ns1:echoString>
    </soapenv:Body>
</soapenv:Envelope>

Response message

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <ns1:echoStringResponse xmlns:ns1="http//tempuri.org/sample2/types">
            <part4>test string</part4>
        </ns1:echoStringResponse>
    </soapenv:Body>
</soapenv:Envelope>

Handling Exceptions Using the Fault Element

Exceptions are a very common term in any computer programing language since anything can go wrong at anytime. This applies to Web services as well. Therefore, there must be a mechanism to handle exceptions in WSDL as well. Sample3.wsdl illustrates, how these exceptions are declared in a WSDL document. Let's start to examine this starting from the portType as usual.

<portType name="Sample3PortType">
    <operation name="echoString">
        <input message="tns:EchoStringRequest"/>
        <output message="tns:EchoStringResponse"/>
        <fault name="fault" message="tns:FaultMessage"/>
    </operation>
</portType>

As you can see, there is a fault message declared for the operation element in addition to the input and output messages. In WSDL terms, a fault is also a message. Unlike in other messages, a fault message always has a name attribute. In this case, it is a fault. The reason for this is that there can be many faults for one operation. Therefore, there must be a mechanism to uniquely identify the fault messages at the binding. Here is the fault message.

<message name="FaultMessage">
    <part name="part1" element="ns1:faultMessage"/>
</message>

As shown in the previous examples, input and output messages can either refer to a type or an element depending on the binding. However, fault messages should always refer to an element, irrespective of the binding style. Let's move on to the binding.

<binding name="Sample3Soap11Binding" type="tns:Sample3PortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="echoString">
        <soap:operation style="document" soapAction="urn:echoString"/>
        <input>
            <soap:body use="literal"/>
        </input>
        <output>
            <soap:body use="literal"/>
        </output>
        <fault name="fault">
            <soap:fault name="fault" use="literal"/>
        </fault>
    </operation>
</binding>

It is worth to note that always a fault message is bound to a soap:fault. This means that if a fault occurs at the server side, then a soap:fault message is received at the client side. Inside a soap:fault there is an element called detail which contains the fault message element as declared in the message section.

Now let's generate the service using the wsdl2java tool as done previously. This time, the generated service method has an exception at the method signature in addition to input and output parameters.

public types.sample3.org.tempuri.http.EchoStringResponse echoString(
                    types.sample3.org.tempuri.http.EchoString echoString) throws FaultMessage {
}

The generated code is very similar to a normal Java code where there is an exception of the given type thrown if something goes wrong. To examine the fault message, implement the following method.

public types.sample3.org.tempuri.http.EchoStringResponse echoString(
                    types.sample3.org.tempuri.http.EchoString echoString) throws FaultMessage {
        FaultMessage faultMessage = new FaultMessage();
        types.sample3.org.tempuri.http.FaultMessage message =
            new types.sample3.org.tempuri.http.FaultMessage();
        message.setParam("test param");
        faultMessage.setFaultMessage(message);
        throw faultMessage;
    }

When implementing the service, it is the responsibility of the service writer to set the fault detail element. Otherwise nothing would go within the SOAP fault details element, and will be wrong according to the WSDL. The client can be written as follows to catch the exception thrown at the server side.

public void testEchoString(){
        try {
            Sample3ServiceStub sample3ServiceStub = new Sample3ServiceStub();
            sample3ServiceStub._getServiceClient().getOptions().setProperty(
                    Constants.Configuration.TRANSPORT_URL,
                    "http://localhost:8088/axis2/services/Sample3Service");
            sample3ServiceStub._getServiceClient().getOptions().setProperty(
                    HTTPConstants.CHUNKED,Constants.VALUE_FALSE);
            EchoString echoString = new EchoString();
            echoString.setParam("test param");
            EchoStringResponse response = sample3ServiceStub.echoString(echoString);
            System.out.println("Got param ==> " + response.getParam());
        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (java.rmi.RemoteException e) {
            e.printStackTrace();
        } catch (FaultMessage faultMessage) {
            System.out.println("Got a fault message ==> " + faultMessage.getFaultMessage().getParam());
        }
    }

The request message is the same as earlier. Here is the output fault message.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <soapenv:Fault>
            <faultcode>soapenv:Server</faultcode>
            <faultstring>FaultMessage</faultstring>
            <detail>
                <ns1:faultMessage xmlns:ns1="http//tempuri.org/sample3/types">
                    <ns1:param>test param</ns1:param>
                </ns1:faultMessage>
            </detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

The main difference is that there is a Fault element inside the SOAP body and the message element can be seen within the detail element.

Using SOAP Headers and In-Only Operations

Sample4.wsdl shows a sample WSDL file with two operations. One is an in-only operation and the other has a SOAP header message. As in the previous example, let's start to read the WSDL from the portType.

<portType name="Sample4PortType">
    <operation name="sendString">
        <input message="tns:SendStringRequest"/>
    </operation>
    <operation name="echoString">
        <input message="tns:EchoStringRequest"/>
        <output message="tns:EchoStringResponse"/>
    </operation>
</portType>

There are two operations in this portType and the sendString operation has only an input message. But there is no indication about a SOAP header message. The reason is that the SOAP header is only a binding specific detail. Therefore it cannot be found at the interface level. Here are the messages for these operations.

<message name="SendStringRequest">
    <part name="part1" element="ns1:sendString"/>
</message>
<message name="EchoStringRequest">
    <part name="part1" element="ns1:echoString"/>
    <part name="part2" element="ns1:headerString"/>
</message>
<message name="EchoStringResponse">
    <part name="part1" element="ns1:echoStringResponse"/>
</message>

Unlike in other messages, the EchoStringRequest message has two parts. Here the idea is to use part1 as the input message and part2 as the header message. According to the basic profile[3], there can be only one part for a document style message unless the part being used at the soap:body element in the corresponding binding is specifically specified. Here is the binding element.

<binding name="Sample4Soap11Binding" type="tns:Sample4PortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="sendString">
        <soap:operation style="document" soapAction="urn:sendString"/>
        <input>
            <soap:body use="literal"/>
        </input>
    </operation>
    <operation name="echoString">
        <soap:operation style="document" soapAction="urn:echoString"/>
        <input>
            <soap:body parts="part1" use="literal"/>
            <soap:header part="part2" message="tns:EchoStringRequest" use="literal"/>
        </input>
        <output>
            <soap:body use="literal"/>
        </output>
    </operation>
</binding>

Here the distinguish element is the input element of the echoString operation. First the normal input element is bound to the SOAP body and given the message part to be used with the parts attribute. This is a must for document style if there is more than one part for a WSDL message. Then part2 of that message is bound to the soap:header. In this case it is necessary to give the message as well, since the message is not declared in the portType. It is also important to remember that in the soap:body message, the part is given using the parts attribute, and at the soap:header it is part not parts.

Again, service and client codes can be generated using the wsdl2java tool. In the generated skeleton code there is no parameter to access the header element. Whereas in the stub, there is a method to pass the header element. Always the skeleton is generated according to the portType so that it can be invoked using any binding; but the stub is generated in a binding dependant manner. This means a separate stub should be generated for each port to invoke the service using that port address. Let's implement the service method as follows to access the header details as well.

public types.sample4.org.tempuri.http.EchoStringResponse echoString(
                    types.sample4.org.tempuri.http.EchoString echoString) {

        // getting the message context from using the current message context method
        MessageContext messageContext = MessageContext.getCurrentMessageContext();
        // getting soap envelop
        SOAPEnvelope soapEnvelope = messageContext.getEnvelope();
        // getting soap header. there is only one header element per soap message
        // so we have to find our header using the qname.
        SOAPHeader soapHeader = soapEnvelope.getHeader();
        OMElement headerElement = soapHeader.getFirstChildWithName(
                new QName("http//tempuri.org/sample4/types","headerString"));
        System.out.println("Header element ==> " + headerElement.toString());

        // sending a response message
        EchoStringResponse echoStringResponse = new EchoStringResponse();
        echoStringResponse.setParam(echoString.getParam());
        return echoStringResponse;
    }

Axis2 uses AXIOM as its XML object model. Therefore as given in the above method, the header om element can be found using the message context. Here is the client code to invoke this operation. It can be seen that the stub provides a method to pass the header parameters.

public void testEchoString(){
        try {
            Sample4ServiceStub sample4ServiceStub = new Sample4ServiceStub();
            sample4ServiceStub._getServiceClient().getOptions().setProperty(
                    Constants.Configuration.TRANSPORT_URL,
                    "http://localhost:8088/axis2/services/Sample4Service");
            sample4ServiceStub._getServiceClient().getOptions().setProperty(
                    HTTPConstants.CHUNKED, Constants.VALUE_FALSE);
            EchoString echoString = new EchoString();
            echoString.setParam("body param");
            HeaderString headerString = new HeaderString();
            headerString.setParam("header param");
            sample4ServiceStub.echoString(echoString,headerString);

        } catch (AxisFault axisFault) {
            fail();
        } catch (java.rmi.RemoteException e) {
            fail();
        }
    }

Here is the request message with a SOAP header element. There it can be seen that the XML element given in message part2 is passed as a SOAP header.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
        <ns1:headerString xmlns:ns1="http//tempuri.org/sample4/types" soapenv:mustUnderstand="0">
            <ns1:param>header param</ns1:param>
        </ns1:headerString>
    </soapenv:Header>
    <soapenv:Body>
        <ns1:echoString xmlns:ns1="http//tempuri.org/sample4/types">
            <ns1:param>body param</ns1:param>
        </ns1:echoString>
    </soapenv:Body>
</soapenv:Envelope>

Other request and response messages are similar to the above example. The only difference is that there is no response SOAP message for the sendString operation which is an in-only one. The only thing received is the HTTP response with the success code.

Summary

Web service description language is the language used to describe a Web service. This description includes the formats of the input, output and fault messages and the underlying encoding and protocol details to invoke those services. These two articles describe all the above details together with the two styles used in WSDL documents. It also shows how to implement these services using Axis2, and access them using the clients generated with Axis2.

References

[1]Contract First Development with Axis2 (Part I)

[2]Web Services Description Language (WSDL) 1.1

[3]Basic Profile Version 1.1

[4]Apache TCPMon

[5]Axis2 User Guide

Author

Amila Suriarachchi, Senior Software Engineer, WSO2 Inc., amila at wso2 dot com

About Author

  • Amila Suriarachchi
  • Architect, Member, Management Committee - Data Technologies
  • WSO2 Inc.