Chapter 1. Make a SOAP client using Java

Table of Contents
1.1. WSDL and AXIS presentation
1.1.1. WSDL description for an RPC/encoded style
1.1.2. Use AXIS and JAX-RPC to create a SOAP client with WSDL2Java (for rpc encoded or document literal operation style)
1.2. First Java SOAP client (rpc/encoded or literal and document/literal in section 1.2.5)
1.2.1. The client class
1.2.2. Test the client
1.2.3. View the SOAP messages
1.2.4. Rcx live, reading light sensor (still rpc/encoded)
1.2.5. Simple SEI client for document/literal operation style
1.2.6. SOAP Clients: Axis and JAX-RPC "drink" complex type (document/literal)
1.3. Advanced AXIS and JAX-RPC client: rpc operation style, serializer/deserializer howto
1.3.1. Consume a method returning an object (rpc, encoded and literal)
1.3.2. Consume a Method returning an array of simple type (rpc, encoded and literal): register your serializer/deserializer or not?
1.3.3. Psychiatric treatment: array of object (rpc/encoded operation style)
1.3.4. Registering "ArrayOfArrayOf" serializer/deserializer.
1.3.5. Client using SEI: conclusion
1.4. Dynamic Invocation Interface (DII) with JAX-RPC (RPC/encoded and document/literal in section 1.4.5)
1.4.1. Reading light sensor using Dynamic Invocation Interface (rpc/encoded)
1.4.2. Consume a remote procedure returning an array of simple type (rpc/encoded)
1.4.3. Consume a remote procedure returning an object with DII (rpc/encoded)
1.4.4. DII client: remote procedure returning an array of object (rpc/encoded)
1.4.5. DII client for document/literal operation style
1.5. Java HTTP post for XML SOAP message.
1.5.1. How to write a SOAP message
1.5.2. Java XML HTTP post (document/literal)
1.6. Send or Post a SOAP message using SAAJ (document/literal)
1.6.1. Construct dynamically your SOAP message
1.6.2. Post a prepared SOAP message with SAAJ
1.7. Consume a .NET web service with Java
1.8. JAX-RPC client-side handler
1.8.1. View the SOAP message using a handler.
1.8.2. Modify the SOAP message using DOM from inside a handler.
1.8.3. Register a JAX-RPC client handler.

1.1. WSDL and AXIS presentation

We will begin with a short description of the web-service I will use for these demo. Web services can be difficult to create but cannot (should not) be difficult to consume... Because of the relative complexity we have sometime to face, I decided to add some fun to this demo: I developed a web service using JWSDP (see Java Web Service Developer Pack 1.3) to interface my Lego RCX rover. This was possible by using Lejos API (see this wonderful system: Lejos at source forge). Well, for the time being you can just query the brick to have some info about memory, battery, internal clock, temperature and light sensor value. I will add more functionalities with the time.

With this documentation, I expect you will be able to create your own SOAP client based on Java, Php, Perl and Python, whatever the platform and/or operating system you are working on. You will find here several examples using these languages. My first goal is to share with you my experience using web-services by providing different ways to create a client and I hope you will have enough information to use these samples as templates for your use.

1.1.1. WSDL description for an RPC/encoded style

Here is the WSDL file of my RPC/encoded web service (Now I also have a document/literal version on-line). Check here if on-line. The WSDL file will be used by your client to know what functionalities are exposed, or directly by you to write by hand the SOAP message you will upload manually to the server! ;-) Oh yes, such examples will follow later. But we will try something more simple first. We are now going to focus to consume the method String readLS(String your_message) (simulation of a RCX light sensor reading, so the remote procedure will return a fixed string). Here is the WSDL you will get if you request it to my rpc/encoded web service. This file is an XML representation of the methods exposed by the web service (You can take a look at the items in bold). I will refer to this listing later when constructing our first client:

NoteWSDL and WS-I compliance
 

The listing below is the WSDL from my Java WSDP web service, you will find it at this address if on-line: http://www.pascalbotte.be/rcx-ws-rpc/rcx?WSDL. Now you can also request it to my document/literal web service here: http://www.pascalbotte.be/rcx-ws/rcx?WSDL, this is a more WS-I compliant version.

Example 1-1. Typical JWSDP RCX web service wsdl (RPC-style, encoded)

<?xml version="1.0" encoding="UTF-8"?>

<!-- Here are some namespace definitions we will use later -->

<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://phonedirlux.homeip.net/wsdl" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="http://phonedirlux.homeip.net/types" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MyRcxService" 
  targetNamespace="http://phonedirlux.homeip.net/wsdl">

  <!-- we are not using complex types for this first example, so go directly to the "message" tag -->

  <types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://phonedirlux.homeip.net/types" 
      xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
      targetNamespace="http://phonedirlux.homeip.net/types">
      <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
      <complexType name="ArrayOfint">
        <complexContent>
          <restriction base="soap11-enc:Array">
            <attribute ref="soap11-enc:arrayType" wsdl:arrayType="int[]"/>
          </restriction>
        </complexContent>
      </complexType>
      <complexType name="ArrayOfPosCol">
        <complexContent>
          <restriction base="soap11-enc:Array">
            <attribute ref="soap11-enc:arrayType" wsdl:arrayType="tns:PosCol[]"/>
          </restriction>
        </complexContent>
      </complexType>
      <complexType name="PosCol">
        <sequence>
          <element name="XPos" type="int"/>
          <element name="YPos" type="int"/>
        </sequence>
      </complexType>
      <complexType name="RcxQuery">
        <sequence>
          <element name="x" type="int"/>
          <element name="y" type="int"/>
        </sequence>
      </complexType>
      <complexType name="rcxResponse">
        <sequence>
          <element name="battery" type="soap11-enc:float"/>
          <element name="currentTime" type="int"/>
          <element name="memory" type="int"/>
          <element name="memoryTot" type="int"/>
          <element name="status" type="string"/>
        </sequence>
      </complexType>
    </schema>
  </types>

  <message name="RcxReadLS_collInt"/>
  <message name="RcxReadLS_collIntResponse">
    <part name="result" type="ns2:ArrayOfint"/></message>
  <message name="RcxReadLS_collPos"/>
  <message name="RcxReadLS_collPosResponse">
    <part name="result" type="ns2:ArrayOfPosCol"/></message>
  <message name="RcxReadLS_commandLS">
    <part name="int_1" type="xsd:int"/></message>
  <message name="RcxReadLS_commandLSResponse">
    <part name="result" type="xsd:string"/></message>
  <message name="RcxReadLS_queryRcx">
    <part name="RcxQuery_1" type="ns2:RcxQuery"/></message>
  <message name="RcxReadLS_queryRcxResponse">
    <part name="result" type="ns2:RcxQuery"/></message>

  <!-- Here are the messages definition for our method "readLS": 
  reading the light sensor (send and receive a string).
  Typically with JWSDP, the message name is composed by the portType 
  (here RcxReadLS which represent the SEI name class) 
  concatenated, using underscore, with the method name (readLS).
  The "part" tag define the params (here one String param). 
  The namespace xsd is defined as an attribute of the main tag "definitions",
  at the beginning of the wsdl.-->

  <message name="RcxReadLS_readLS">
    <part name="String_1" type="xsd:string"/>
  </message>

  <!-- The next message tag define the return type for the response (also a String) -->

  <message name="RcxReadLS_readLSResponse">
    <part name="result" type="xsd:string"/>
  </message>

  <message name="RcxReadLS_readLSpercent">
    <part name="int_1" type="xsd:int"/></message>
  <message name="RcxReadLS_readLSpercentResponse">
    <part name="result" type="xsd:int"/></message>
  <message name="RcxReadLS_status"/>
  <message name="RcxReadLS_statusResponse">
    <part name="result" type="ns2:rcxResponse"/>
  </message>

  <!-- the portType define the name of the SEI -->

  <portType name="RcxReadLS">

    <operation name="collInt" parameterOrder="">
      <input message="tns:RcxReadLS_collInt"/>
      <output message="tns:RcxReadLS_collIntResponse"/></operation>
    <operation name="collPos" parameterOrder="">
      <input message="tns:RcxReadLS_collPos"/>
      <output message="tns:RcxReadLS_collPosResponse"/></operation>
    <operation name="commandLS" parameterOrder="int_1">
      <input message="tns:RcxReadLS_commandLS"/>
      <output message="tns:RcxReadLS_commandLSResponse"/></operation>
    <operation name="queryRcx" parameterOrder="RcxQuery_1">
      <input message="tns:RcxReadLS_queryRcx"/>
      <output message="tns:RcxReadLS_queryRcxResponse"/></operation>

    <!-- Tag operation under portType define the input/output messages for the method 
    and the tns namespace is also defined at the beginning of the wsdl.-->

    <operation name="readLS" parameterOrder="String_1">
      <input message="tns:RcxReadLS_readLS"/>
      <output message="tns:RcxReadLS_readLSResponse"/>
    </operation>

    <operation name="readLSpercent" parameterOrder="int_1">
      <input message="tns:RcxReadLS_readLSpercent"/>
      <output message="tns:RcxReadLS_readLSpercentResponse"/>
    </operation>
    <operation name="status" parameterOrder="">
      <input message="tns:RcxReadLS_status"/>
      <output message="tns:RcxReadLS_statusResponse"/>
    </operation>
  </portType>

  <binding name="RcxReadLSBinding" type="tns:RcxReadLS">

    <operation name="collInt">
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
            use="encoded" namespace="http://phonedirlux.homeip.net/wsdl"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
            use="encoded" namespace="http://phonedirlux.homeip.net/wsdl"/>
      </output>
      <soap:operation soapAction=""/>
    </operation>
    <operation name="collPos">
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></output>
      <soap:operation soapAction=""/></operation>
    <operation name="commandLS">
      <input>http://www.cnn.com/
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></input>
      <output>

        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></output>
      <soap:operation soapAction=""/></operation>
    <operation name="queryRcx">
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/></output>
      <soap:operation soapAction=""/></operation>

    <!-- Tag operation (under binding) link the different input/output namespace and encoding style  -->

    <operation name="readLS">

      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </output>

      <soap:operation soapAction=""/>

    </operation>
    
    <operation name="readLSpercent">
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </output>
      <soap:operation soapAction=""/>
    </operation>
    <operation name="status">
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded"
            namespace="http://phonedirlux.homeip.net/wsdl"/>
      </output>
      <soap:operation soapAction=""/>
    </operation>

    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>

  </binding>

  <service name="MyRcxService">
    <port name="RcxReadLSPort" binding="tns:RcxReadLSBinding">
      <soap:address xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
            location="http://www.pascalbotte.be:80/rcx-ws/rcx"/>
    </port>
  </service>
</definitions>

1.1.2. Use AXIS and JAX-RPC to create a SOAP client with WSDL2Java (for rpc encoded or document literal operation style)

The first client we are going to build will be easy to code, but not necessarily as easy to understand... First bring together all the pieces we need:

If you want to use WSDL2Java here are some tips: this tool comes with AXIS (Eclipse plugging), you will find it at: Ojectlearn. If you are using another IDE than Eclipse, do not worry and just uncompress the package downloaded and copy axis.jar (in /lib directory) in the appropriate location on your system. So your OS will find it at runtime. As soon as axis.jar is in your path, you can invoke the tool by the command line:

[your_prompt]$ java org.apache.axis.wsdl.WSDL2Java

For the Windows users: nothing change except your prompt:

C:\your_path>java org.apache.axis.wsdl.WSDL2Java

If all is working well then you can generate the classes by this command for my on-line document/literal web service (check here if on-line):

[your_prompt]$ java org.apache.axis.wsdl.WSDL2Java http://www.pascalbotte.be/rcx-ws/rcx?WSDL
-Nhttp://phonedirlux.homeip.net/wsdl=rcxwsclient.sei -W

You can also test my rpc/encoded web service using the following command, see I omitted the -W option.

[your_prompt]$ java org.apache.axis.wsdl.WSDL2Java http://www.pascalbotte.be/rcx-ws-rpc/rcx?WSDL
-Nhttp://phonedirlux.homeip.net/wsdl=rcxwsclient.sei

NoteImportant note for web service using operation style: document, use: literal WSDL
 

For a WS-I, document/literal web service you must add the option -W when calling WSDL2Java. So if you want to consume another web service (rpc/encoded) do not use the option -W!

I you don't know the type of the web service you want to consume (rpc/encoded or document/literal), you can use my WSDL parser (check my home page to see if on-line). Type the url pointing to the wsdl you want to parse and see the little report in human-readable format (free service).

Once you have the required files created on your local directory: take a look at the one you will need for this first demo (the service endpoint interface corresponding to the interface RcxReadLS, the portType name in the wsdl):

NoteMapping namespace to package
 

If you didn't use the -N option you will have to manually copy the file RcxReadLS.java in the correct directory of your project (see below).

your_local_directory/net/homeip/phonedirlux/wsdl/RcxReadLS.java (for Linux)

your_local_directory\net\homeip\phonedirlux\wsdl\RcxReadLS.java (for Windows)

The method String readLS(String string_1) (inside this interface) is supposed to return the value of the light sensor on the RCX. For this first test, you will get a fixed string (we will test later another method returning a live value of the light sensor in percent, you will then get an integer).

Here we will just use one method: String readLS(String string_1), it takes a string as param and return a string. To make this example more clear comment the methods we will not use and, if necessary (depending if you used the option -N or not), adapt the package name as shown in bold in Example 1-2. Make sure to copy this file to the appropriate directory of your project: directory_project/rcxwsclient/sei corresponding to the package name.