Generating a Web Service Client using Axis2 and JiBX

This document explains how to generate a Web service client using Axis2 and JiBX data binding. The service has the following WSDL:

Code Listing 1: The WSDL file

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
   xmlns:apachesoap="http://xml.apache.org/xml-soap"
   xmlns:impl="http://apache.org/axis2/Axis2UserGuide"
   xmlns:intf="http://apache.org/axis2/Axis2UserGuide"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   targetNamespace="http://apache.org/axis2/Axis2UserGuide">

  <wsdl:types>
    <schema
       elementFormDefault="qualified"
       targetNamespace="http://apache.org/axis2/Axis2UserGuide"
       xmlns="http://www.w3.org/2001/XMLSchema">
      
      <!-- ELEMENTS -->
      
      <element name="DoInOnlyRequest">
        <complexType>
          <sequence>
            <element name="messageString" type="xsd:string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="TwoWayOneParameterEchoRequest">
        <complexType>
          <sequence>
            <element name="echoString" type="xsd:string"/>
          </sequence>
        </complexType>
      </element>
      <element name="TwoWayOneParameterEchoResponse">
        <complexType>
          <sequence>
            <element name="echoString" type="xsd:string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="NoParametersRequest">
        <complexType/>
      </element>
      <element name="NoParametersResponse">
        <complexType/>
      </element>
      
      <element name="MultipleParametersAddItemRequest">
        <complexType>
          <sequence>
            <element name="itemId" type="xsd:int"/>
            <element name="itemName" type="xsd:string"/>
            <element name="price" type="xsd:float"/>
            <element name="description" type="xsd:string"/>
          </sequence>
        </complexType>
      </element>

      <element name="MultipleParametersAddItemResponse">
        <complexType>
          <sequence>
          <element name="itemId" type="xsd:int"/>
          <element name="successfulAdd" type="xsd:boolean"/>
          </sequence>
        </complexType>
      </element>

    </schema>
  </wsdl:types>

  
  <!-- MESSAGES -->

  <wsdl:message name="DoInOnlyRequestMessage">
    <wsdl:part name="input" element="impl:DoInOnlyRequest"/>
  </wsdl:message>

  <wsdl:message name="TwoWayOneParameterEchoRequestMessage">
    <wsdl:part name="input" element="impl:TwoWayOneParameterEchoRequest"/>
  </wsdl:message>
  <wsdl:message name="TwoWayOneParameterEchoResponseMessage">
    <wsdl:part name="output" element="impl:TwoWayOneParameterEchoResponse"/>
  </wsdl:message>

  <wsdl:message name="NoParametersRequestMessage">
    <wsdl:part name="input" element="impl:NoParametersRequest"/>
  </wsdl:message>
  <wsdl:message name="NoParametersResponseMessage">
    <wsdl:part name="output" element="impl:NoParametersResponse"/>
  </wsdl:message>

  <wsdl:message name="MultipleParametersAddItemRequestMessage">
    <wsdl:part name="input" element="impl:MultipleParametersAddItemRequest"/>
  </wsdl:message>
  <wsdl:message name="MultipleParametersAddItemResponseMessage">
    <wsdl:part name="output" element="impl:MultipleParametersAddItemResponse"/>
  </wsdl:message>


  <!-- Port type (operations) -->

  <wsdl:portType name="Axis2UserGuidePortType">

    <wsdl:operation name="DoInOnly" parameterOrder="input">
      <wsdl:input name="DoInOnlyRequestMessage"
                  message="impl:DoInOnlyRequestMessage"/>
    </wsdl:operation>

    <wsdl:operation name="TwoWayOneParameterEcho" parameterOrder="input">
      <wsdl:input name="TwoWayOneParameterEchoRequestMessage"
                  message="impl:TwoWayOneParameterEchoRequestMessage"/>
      <wsdl:output name="TwoWayOneParameterEchoResponseMessage"
                  message="impl:TwoWayOneParameterEchoResponseMessage"/>
    </wsdl:operation>

    <wsdl:operation name="NoParameters" parameterOrder="input">
      <wsdl:input name="NoParametersRequestMessage"
                  message="impl:NoParametersRequestMessage"/>
      <wsdl:output name="NoParametersResponseMessage"
                   message="impl:NoParametersResponseMessage"/>
    </wsdl:operation>

    <wsdl:operation name="MultipleParametersAddItem" parameterOrder="input">
      <wsdl:input name="MultipleParametersAddItemRequestMessage"
                  message="impl:MultipleParametersAddItemRequestMessage"/>
      <wsdl:output name="MultipleParametersAddItemResponseMessage"
                  message="impl:MultipleParametersAddItemResponseMessage"/>
    </wsdl:operation>

  </wsdl:portType>


  <!-- BINDING (bind operations) -->
  <wsdl:binding
     name="Axis2UserGuideSoapBinding"
     type="impl:Axis2UserGuidePortType">
    <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operation name="DoInOnly">
      <wsdlsoap:operation soapAction="DoInOnly"/>
      <wsdl:input>
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
    </wsdl:operation>

    <wsdl:operation name="TwoWayOneParameterEcho">
      <wsdlsoap:operation soapAction="TwoWayOneParameterEcho"/>
      <wsdl:input>
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>

    <wsdl:operation name="NoParameters">
      <wsdlsoap:operation soapAction="NoParameters"/>
      <wsdl:input>
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>

    <wsdl:operation name="MultipleParametersAddItem">
      <wsdlsoap:operation soapAction="MultipleParametersAddItem"/>
      <wsdl:input>
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>


  <!-- SERVICE -->

  <wsdl:service name="Axis2UserGuideService">
    <wsdl:port binding="impl:Axis2UserGuideSoapBinding"
               name="Axis2UserGuide">
      <wsdlsoap:address location="http://localhost:8080/axis2/services/Axis2UserGuide"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Notice that the document defines four operations, DoInOnly, NoParameters, TwoWayOneParameterEcho, and MultipleParametersAddItem. Each of the clients will include methods for calling each of these operations.

(You can get more information on WSDL at http://www.w3.org/2002/ws/desc/ .)

JIBX

The Axis2 WSDL2Java tool's JiBX extension supports unwrapping of suitable WSDLs in order to provide a very clean and intuitive API (see the JiBX unwrapped example). In cases where the unwrapped parameters are all of simple types with Java equivalents you can just use the unwrapped support directly. For cases where the unwrapped parameters are more complex types, a JiBX binding definition can be used to define how the Java structures are to be converted to and from XML.

The WSDL used for this example is almost wrapped doc/lit suitable for unwrapping with JiBX data binding. The only problem is the MultipleParametersAddItemResponseMessage, where the response element has two child elements. Only one child element can be present in an wrapped response, so this WSDL does not qualify. If you can structure your WSDL to use a wrapped structure you'll generally find this to be a cleaner and easier solution, especially when working with JiBX.

Since the example used in this guide is not wrapped doc/lit, this section provides the current instructions for generating code and JiBX bindings from the schema. Doing so currently involves using an outdated tool from the JiBX project, the Xsd2Jibx tool. Xsd2Jibx is being replaced by a new tool which should make the process of generating a JiBX client much easier in the future. For updated information about the techniques used to work with JiBX in Axis2, see the JiBX Axis2 Wiki page.

JiBX is not part of the Apache project, so in order to use it to generate your clients, you will need to do some setup work to start with. To generate your client, execute the following steps:

The short story:

  1. Download the latest JiBX package (release jibx-1.1 at the time of this writing) from http://sourceforge.net/projects/jibx/ . Extract the zip file, and copy the jars in the lib directory to the AXIS2_HOME/lib directory. (Delete the stax-api.jar file; it's superseded by the version that comes with Axis2.)
  2. Download xsd2jibx version beta2a from sourceforge. Create a directory called xsd2jibx in your working directory and extract the files into it. This utility does not work with the latest release of JiBX, so download jibx-1.0RC1 from Sourceforge. Extract the files from this archive and copy the *.jar files in the lib directory into the xsd2jibx/lib directory.
  3. Create a schema based on the data structures of your WSDL file and save it in your working directory.
  4. Make sure that only the xsd2jibx jar files are on the classpath and execute the following command to create the basic binding file: java -jar xsd2jibx\lib\xsd2jibx.jar Axis2UserGuide.xsd
  5. Copy the org directory to the src directory to place the generated classes into the project so that the compiler will see them.
  6. Remove the xsd2jibx-related *.jar files from your CLASSPATH and add the Axis2 jar files back onto it. Execute the following command to generate the stubs:
    %AXIS2_HOME%\bin\wsdl2java -uri Axis2UserGuide.wsdl -p org.apache.axis2.axis2userguide -d jibx -Ebindingfile org\apache\axis2\axis2userguide\binding.xml -s
  7. Create the client file in the org/apache/axis2/axis2userguide directory.
  8. Copy the org directory and all of its contents to the src directory.
  9. Compile the first set of classes by typing: ant jar.client
  10. Change to the build/classes directory and run the JiBX compiler:
    java -jar C:\apps\axis2\lib\jibx-bind.jar 
    ..\..\org\apache\axis2\axis2userguide\binding.xml
  11. Run ant again to package the new auto-generated JiBX classes into the client jar by typing: ant jar.client
  12. Add the build/lib/Axis2UserGuideService-test-client.jar file to the CLASSPATH and run the client by typing:
    java org.apache.axis2.axis2userguide.Client

The long story:

In order to use JiBX to generate your client, you'll need to use it for two different functions. Of course, you use it to generate the stubs your client will use, but before you get to that point, you'll need to generate a binding file that maps objects to XML elements. To do that, you'll need the xsd2jibx utility, which creates a binding file from an XML Schema document. Once you have the binding file, you can run JiBX to create the actual object. In order to do all that you'll need to have the appropriate versions of the JiBX software.

Download the latest jibx package (release jibx-1.1 at the time of this writing) from http://sourceforge.net/projects/jibx/ . Extract the zip file, and copy the jars in the lib directory to the AXIS2_HOME/lib directory. (Delete the stax-api.jar file; it's superseded by the version that comes with Axis2.) These are the files that pertain to the main JiBX application.

Download xsd2jibx version beta2a from Sourceforge. Create a directory called xsd2jibx in your working directory and extract the files into it. Unfortunately, this utility does not work with the latest release of JiBX, so you will need to download jibx-1.0RC1 from Sourceforge. Extract the files from this archive and place the *.jar files in the lib directory into the xsd2jibx/lib directory. This way, you can use them exclusively with the xsd2jibx utility.

You'll need an XML schema from which to generate the binding file, which links XML elements in to the Java classes. As defined in the sample WSDL file, its content should be as follows in Code Listing 2.

Code Listing 2: XML Schema

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
   elementFormDefault="qualified"
   targetNamespace="http://apache.org/axis2/Axis2UserGuide"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  
  <!-- ELEMENTS -->
  <xsd:element name="DoInOnlyRequest">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="messageString" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  
  <xsd:element name="TwoWayOneParameterEchoRequest">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="echoString" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="TwoWayOneParameterEchoResponse">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="echoString" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  
  <xsd:element name="NoParametersRequest">
    <xsd:complexType/>
  </xsd:element>
  <xsd:element name="NoParametersResponse">
    <xsd:complexType/>
  </xsd:element>
  
  <xsd:element name="MultipleParametersAddItemRequest">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="itemId" type="xsd:int"/>
        <xsd:element name="itemName" type="xsd:string"/>
        <xsd:element name="price" type="xsd:float"/>
        <xsd:element name="description" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="MultipleParametersAddItemResponse">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="itemId" type="xsd:int"/>
        <xsd:element name="successfulAdd" type="xsd:boolean"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>

</xsd:schema>

Save the above XML schema file as Axis2UserGuide.xsd.

In order to map this schema into a JiBX binding file, you'll need to use the xsd2jibx utility. Clear your CLASSPATH and add only the jar files in thexsd2jibx/lib directory. Execute the following command to create the basic binding file:

java -jar xsd2jibx\lib\xsd2jibx.jar Axis2UserGuide.xsd

This operation creates the basic class files, as well as the mapping file, called binding.xml. You'll use this file to do the actual WSDL-to-Java conversion.

Remove the xsd2jibx jar files from your CLASSPATH and add the Axis2 jar files back onto it. Execute command in Code Listing 3 to generate the stubs.

Code Listing 3: Generating the stubs

%AXIS2_HOME%\bin\wsdl2java -uri Axis2UserGuide.wsdl -p org.apache.axis2.axis2userguide -d jibx -Ebindingfile org\apache\axis2\axis2userguide\binding.xml -s

Create the client file, Client.java, in the org/apache/axis2/axis2userguide directory. Add the following code in Code Listing 4.

Code Listing 4: Creating Client.java

package org.apache.axis2.axis2userguide;

public class Client{
    public static void main(java.lang.String args[]){
        try{
            Axis2UserGuideServiceStub stub =
                new Axis2UserGuideServiceStub
              ("http://localhost:8080/axis2/services/Axis2UserGuideService");

            doInOnly(stub);
            twoWayOneParameterEcho(stub);
            noParameters(stub);
            multipleParameters(stub);
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }

    public static void doInOnly(Axis2UserGuideServiceStub stub){
        try{
            DoInOnlyRequest req = 
                new DoInOnlyRequest();

            req.setMessageString("fire and forget it!");

            stub.DoInOnly(req);
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }

    public static void twoWayOneParameterEcho(Axis2UserGuideServiceStub stub){
        try{
            TwoWayOneParameterEchoRequest req = 
                new TwoWayOneParameterEchoRequest();

            req.setEchoString("echo! ... echo!");
        System.out.println(stub.TwoWayOneParameterEcho(req).getEchoString());
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }

    public static void noParameters(Axis2UserGuideServiceStub stub){
        try{
            NoParametersRequest req =
                new NoParametersRequest();

            System.out.println(stub.NoParameters(req));
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }

    public static void multipleParameters(Axis2UserGuideServiceStub stub){
        try{
            MultipleParametersAddItemRequest req =
                new MultipleParametersAddItemRequest();

            req.setPrice((float)1.99);
            req.setItemId((int)23872983);
            req.setDescription("Must have for cooking");
            req.setItemName("flour");

            MultipleParametersAddItemResponse res =
                stub.MultipleParametersAddItem(req);

            System.out.println(res.getItemId());
            System.out.println(res.getSuccessfulAdd());
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }
}

Now it's time to compile the client. In order for the generated files to be found, they need to be in the source directory, so copy the org file to the src directory.

Compile the first set of classes by typing: ant jar.client

This action compiles most of the available classes, but not everything. Fortunately, it does compile the classes needed by the JiBX compiler, so you can now generate the actual JiBX resources. Change to the build/classes directory and run the JiBX compiler:

java -jar C:\apps\axis2\lib\jibx-bind.jar ..\..\org\apache\axis2\axis2userguide\binding.xml

Now that you have the new files in place, rerun the ant task to generate the client: ant jar.client

This action adds all of the appropriate files to the build/lib/Axis2UserGuideService-test-client.jar file, so add that jar file to your CLASSPATH and run the client by typing: java org.apache.axis2.axis2userguide.Client