Contract First Development with Axis2 (Part I)

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
  • 26 Nov, 2007

Introduction

There are two widely used techniques in used in Web service development, namely, Contract-First development and Code-First development. Apache Axis2, the widely used Web services framework supports both of these techniques.

With the contract-first approach, the contract for the Web service is determined first and then the supporting tools are used to generate the implementation code. In contrast, in the code-first approach, first the implementation code is created and then the Web service development framework is used to produce the contract for the Web service.

Out of these two techniques, contract-first is the recommended way to create a Web service as in this approach the main focus is given to the messages that come in and goes out from the Web service. This is also the correct approach to use in the SOA.

What is the Contract

The first step in the contract-first approach is to create the contract. The Web service contract basically consists of two parts.

  1. Input, output and fault message format details.

    This section describes intended input (request) message format, output (response) message format and the possible fault message formats for any thing that could go wrong.

  2. Binding and transport details.

    This section describes how this service can be accessed. In other words, this describes the message encoding protocol (SOAP11, SOAP12, HTTP), the actual transport used to send the encoded message (HTTP, HTTPS, SMTP, JMS) and the actual physical address from which this service can be accessed with the given transport.

How to Specify the Contract

Web services description language (WSDL) is used to describe the contract for a Web service. Currently, there are two versions of WSDL, 1.1 and 2.0. This article only focuses on the WSDL 1.1 to describe the above concepts. WSDL 1.1 document mainly consists of five parts called Types, Messages, PortTypes, Bindings and Services. Out of these five parts, the first three are used to describe the message format and the other two are used to describe the transport and encoding protocols.

There are two types of WSDL 1.1 documents, named Document and RPC (Remote Procedure Call). Out of these two types, the Document style is easier to understand than the RPC style. So, the rest of this article considers a sample WSDL file written in Document style, while the second part of this article will discuss a WSDL 1.1 document written in RPC style.

Let's consider the sample1.wsdl file which describes a Web service with one in-out operation.

Starting with the portType is the easiest way to understand a WSDL file. PortType is used to describe the service interface and it consists of all the operations of the service being described. An operation describes the input, output and possible fault messages of that operation. Following is the portType of this WSDL.

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

This shows that the given service has an operation called echoString and that it has an input message called EchoStringRequest and an output message called EchoStringResponse. Note that the WSDL definition file has a targetNamespace (in this sample http://tempuri.org/sample1) and all the messages, portTypes bindings and services belong to that namespace. Now let's move on to the messages section and see how it has defined the messages.

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

Each message consists of a part and each part points to an XmlSchema element. Actually this XmlSchema element is used to define the expected XML message. In other words, it says this operation expects an input XML message as given in echoString element and it generates a response as given in echoStringResponse element. Here is how it is described in the types section.

<types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                    xmlns:tns="http//tempuri.org/sample1/types"
                    targetNamespace="http//tempuri.org/sample1/types"
                    elementFormDefault="qualified"
                    attributeFormDefault="unqualified">
            <xsd:element name="echoString">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="param" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="echoStringResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="param" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </types>

The Types section consists of a set of schemas that have elements pointed out by the messages. According to this schema the echoString operation expects an input XML message to look like this:

<ns2:echoString xmlns:ns2="http//tempuri.org/sample1/types">
            <ns2:param>test</ns2:param>
        </ns2:echoString>

As described so far, the service interface can be determined by using the details given in the portType, message and types. Then comes the question, 'how can this service actually be invoked?'. Binding and service elements are used to provide those details. Here is the SOAP 1.1 binding for this service;

<binding name="Sample1Soap11Binding" type="tns:Sample1PortType">
        <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>
        </operation>
    </binding>

A binding always refers to a portType which it describes. Therefore, in a binding there must be a corresponding binding operation for each operation in the portType. Each binding operation describes how the input and output messages are being sent in the SOAP message. In this case they are sent in the body part of the SOAP envelope. Optionally, the header part can also be used to send the message and it is explained in the second part of this article. A binding operation can also have a soapAction which is used to identify the operation. Finally, the service is used to describe the actual physical address of this service.

<service name="Sample1Service">
        <port name="Soap11Port" binding="tns:Sample1Soap11Binding">
            <soap:address location="http://localhost:8080/axis2/services/Sample1Service"/>
        </port>
        <port name="Soap12Port" binding="tns:Sample1Soap12Binding">
            <soap:address location="http://localhost:8080/axis2/services/Sample1Service"/>
        </port>
    </service>

Each service can have many ports corresponding to each binding. Therefore a port always refers to a binding and it gives the actual location of the service according to the transport used. In this case it uses the http transport and it is available at http://localhost:8080/axis2/services/Sample1Service.

Now it should be quite clear to you how a WSDL document describes a Web service. Let's see how to implement and publish this service using Axis2.

To support the contract-first approach, Axis2 comes with a tool named wsdl2java. Please refer to the Axis2 user guide[4] to learn how to install Axis2 and use the wsdl2java tool. Assuming you've taken care of Axis2 installation, you can generate the code with the following command:

sh $AXIS2_HOME/bin/wsdl2java.sh -uri wsdl/sample1.wsdl -o sample1_server -ss -sd

This generates the java code into the "sample_server" folder and the generated code will contain a java class called Sample1ServiceSkeleton. This is the service implementation class and there is a method called echoString in this class corresponding to the WSDL operation echoString. Here is the generated method.

public types.sample1.org.tempuri.http.EchoStringResponse echoString(
            types.sample1.org.tempuri.http.EchoString echoString) {
        //TODO : fill this with the necessary business logic
        throw new java.lang.UnsupportedOperationException("Please implement " + this.getClass().getName() + "#echoString");
    }

This method has a parameter of the type types.sample1.org.tempuri.http.EchoString corresponding input element and it's return type is types.sample1.org.tempuri.http.EchoStringResponse corresponding to the output element. Actually, these classes are generated data binding classes for XmlSchema elements. Now let's implement this method as follows to add the echo behavior:

public types.sample1.org.tempuri.http.EchoStringResponse echoString(
            types.sample1.org.tempuri.http.EchoString echoString) {
        EchoStringResponse response = new EchoStringResponse();
        response.setParam(echoString.getParam());
        return response;
    }

Now the service has been implemented. Let's see how to deploy it using the simple HTTP server that comes with the Axis2 standard distribution. The first step to create a service with Axis2 is to create the .aar file for that service. This can easily be done by executing the ant script found with the generated code. Invoking the "ant" command in the sample1_server folder would create the Sample1Service.aar inside the build/lib folder.

Then, the service can be deployed by copying this Sample1Service.aar file into the $AXIS2_HOME/repository/services folder. When you run the Axis2 server, the sample1 service will be up and running.

Now let's see how to access this service using the generated stub code. The following command can be used to generate the client stub:

sh $AXIS2_HOME/bin/wsdl2java.sh -uri http://localhost:8080/axis2/services/Sample1Service?wsdl -o sample1_client -u

The generated code includes a class called Sample1ServiceStub which has the following method signature to invoke the service:

public types.sample1.org.tempuri.http.EchoStringResponse echoString(
            types.sample1.org.tempuri.http.EchoString echoString2)
            throws java.rmi.RemoteException

Let's invoke this method using the following test method

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

When generating code with the wsdl2java tool, it hard-codes the endpoint reference (EPR) address given in the WSDL file. This can be overridden by passing the correct EPR as a parameter to the stub constructor. In the given sample the transport URL has been changed to port 8088 to send this message through TCPmon. HTTP chunking has also been switched off. Here are the request and response SOAP messages that pass through the tcpmon[3];

Request Message

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Body>
         <ns1:echoString xmlns:ns1="http//tempuri.org/sample1/types">
            <ns1:param>test param</ns1:param>
         </ns1:echoString>
      </soapenv:Body>
   </soapenv:Envelope>

Response Message

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Body>
         <ns1:echoStringResponse xmlns:ns1="http//tempuri.org/sample1/types">
            <ns1:param>test param</ns1:param>
         </ns1:echoStringResponse>
      </soapenv:Body>
   </soapenv:Envelope>

It can be seen that the request and response SOAP messages are clearly matched with the input and output messages defined in the WSDL document.

More About Generated Code...

  1. server side code

    Server side code consists of MessageReceivers, ServiceImplementation, Data binding classes and the service descriptor (service.xml). The wsdl2java tool always generates a custom message receiver which extends from an Abstract message receiver. These custom message receivers implement the corresponding invokeBussiness method which gets the message context object from the Axis2 Engine. Within this method it invokes the service implementation class with the data binding classes generated with the data binding framework. The service descriptor file is used to specify the service implementation and message receiver classes for the service.

  2. client side code

    client side code consists of Stubs and data binding code. A separate method is created for each operation given in the portType. These methods can be invoked with the databinding classes and within the method it creates the SOAP envelope and invokes the operation client to send the message.

Summary

This article describes how to define a Web service using a WSDL 1.1 document and how to implement it using the wsdl2java tool which comes with the Axis2. Look forward to part II that will explain the rpc style, fault handling and sending messages using the SOAP header.

References

[1]Web Services Description Language (WSDL) 1.1

[2]Basic Profile Version 1.1

[3]Apache TCPMon

[4]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.