Using Complex Types in Web Services
by Balaji Loganathan

This article will explain how to use complex types in a web service with Apache Axis. Part I will cover a simple web service that uses complex types. Part II will cover a slightly advanced web service that uses an array of complex types.

The fundamentals

The below questions are just to recap your knowledge; they're not a detailed introduction to Web Services.

What are web services ?
In simple terms a web service is an application or business logic that is accessible using standard Internet protocols
What is Apache Axis ?
The Apache Axis is a webservice implementation tool based on open source and the Apache standards. This article will use Apache Axis web services engine for its demonstration. Visit http://ws.apache.org/axis/java/ for more information about the Apache Axis.
What are primitive types and complex types?
In Java, primitive types includes data types like int, char,short, float, byte, boolean, long and byte array.Similarly complex types are objects created by the developer. For example, an object called Tree with the properties Stem and Leaves is a complex type.

Object: Tree
Attributes:
Stem
Leaves

This article will address publishing web services that employ these kind of complex types.

Getting ready

What do you need to run this demo ?

Apache Axis and the resource files can be plugged-in with most of the servlet containers. Setting up the Apache Axis with a Servlet container is beyond the scope of this article. The process is explained very well here. The reader is encouraged to read the installation instructions of Apache Axis and try to deploy and run a simple web service before going any further. For this demonstration, the resource files will be hosted in Axis + Servlet container and the client will be a JSP page.

Using Complex Types in a Web Service: Part I

What will this web service do ?
This web service will have a single method/operation called getCompanyData. When a SOAP request is made it will return a complex type object "Company". The below diagram shows the structure of the object "Company"
Object: Company
Attributes:
ID
Names
EmployeeNames[ ]

The below diagram shows the raw SOAP response after the SOAP request was made (available here:)
getCompanyData_SOAPResponse.xml
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
		   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    		   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <soapenv:Body>
 <getCompanyDataResponse
            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <getCompanyDataReturn href="#id0" />
 </getCompanyDataResponse>
 <multiRef id="id0" soapenc:root="0"
            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 		    xsi:type="ns1:Company"
 	            xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 	            xmlns:ns1="urn:CompanyService">
  <companyID href="#id1" />
  <companyName xsi:type="soapenc:string">Test</companyName>
  <employeeNames xsi:type="soapenc:string">Balaji</employeeNames>
  <employeeNames xsi:type="soapenc:string">LSMS</employeeNames>
  <employeeNames xsi:type="soapenc:string">LQ</employeeNames>
 </multiRef>
 <multiRef id="id1" soapenc:root="0"
     soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
     xsi:type="xsd:int"
     xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">2311</multiRef>
 </soapenv:Body>
</soapenv:Envelope>


This data might be displayed by a JSP page like this:


OK, now lets see how we can install and run this stuff.br>

Step 1: Create server side classes

We will need two classes:
  1. Company.java => A simple complex type object
  2. CompanyService.java => A simple class with a single method called getCompanyData(). This will return the complex type object Company as response.
Both classes have the package com.server (see the resource files). Compile the Java source files Company.java and CompanyService.java and place the class files under the WEB-INF/classes/com/server directory of your Axis context.
The Axis context is the place where you have installed the Axis web context inside your servlet container.
Company.java
package com.server;
public class Company {
 	public int companyID;
 	public String companyName;
 	public String[ ] employeeNames;
}

CompanyService.java
package com.server;
public class CompanyService{

	/**
	 * @return company object
	 */
	public Company getCompanyData(){
		Company company = new Company();
		String [] employeeNames = new String[] { "Balaji", "LSMS" , "LLQ" };
		company.companyID = 2311;
		company.companyName = "Test";
		company.employeeNames = employeeNames;
		return company;
	}
}

Step 2: Deploy the web service using AdminClient

Apache Axis comes with a great utility called AdminClient, which can be used to deploy the web service and make it available to clients.
To use the AdminClient you should have your classpath set up properly as described in the Apache Axis documentation.
Now type the following command into the command prompt
java org.apache.axis.client.AdminClient deployCompanyService.wsdd 
After executing the above command, the command window should display "Done processing".

deployCompanyService.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
                    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <service name="CompanyRepository" provider="java:RPC">
        <parameter name="className" value="com.server.CompanyService"/>
        <parameter name="allowedMethods" value="getCompanyData"/>
        <parameter name="wsdlTargetNamespace" value="CompanyService"/>
        <beanMapping qname="myNS:Company"
                        xmlns:myNS="urn:CompanyService"
                        languageSpecificType="com.server.Company"/>
     </service>
</deployment>
The above shown deployCompanyService.wsdd is web service deployment descriptor file that tells Axis web services engine:

As compared to primitive datatypes, the complex type objects require an extra class specifier to properly encode the SOAP response when it sends complex type objects as response.

Step 3: Check the web service WSDL

Once Step 2 is completed successfully, your service should be available for clients to consume them. Lets see whether our web service is available by it name "CompanyRepository". First. launch your browser and type http://<servername:port>/<webcontext name>/services/CompanyRepository?wsdl. The browser should display you a WSDL file similar to CompanyRepository.wsdl. Note that the service endpoint may vary according to your servername and webcontext; some servlet containers may also expect you to restart the engine to recognize the new server classes.

Invoking the web service in this crude way will help you to tell whether the web service method can perform the desired business logic and send response back or not. To test this, launch the broswer and type

http://<servername:port>/<webcontext name>/services/CompanyRepository?method=getCompanyData

and check the SOAP response generated. A successful SOAP response will look like this getCompanyData_SOAPResponse.xml.

Step 4: Create and run the client to access the web service

We're using a JSP page as the web service client just for an example. It is quite possible to create clients as Java Servlets, standalone Java applications, or using non-Java plaforms like Microsoft.NET. The simplest way to create a client stubs files is by running a simple but great utility called "WSDL2Java". WSDL2Java will generate all the necessary Java stub files like the SOAPbinding file, Locator file, serializers and the deserializers files.

Type the following command into the command prompt (all on one line):

java org.apache.axis.wsdl.WSDL2Java  \
          -p com.client http://<servername:port>/<webcontext \
          name>/services/CompanyRepository?wsdl

The option -p com.client in the above command will tell the WSDL2Java to create all the stub files with the package name and directory com.client respectively. Now compile the WSDL2Java generated client stubs. Move the compiled client stubs to the lt;web context>/web-inf/classes/com/client directory: we will use them in our JSP page.

Prepare the JSP file and save it under the folder lt;web context>/ directory. As shown in the below diagram the JSP file will use the WSDL2Java generated classes instances to access the web service, which makes coding part very easier instead of writing your own client stubs and accessor methods.

AccessCompanyService.jsp
<!-- The package import com.client.* will import all the --
          -- necessary class files to be used later -->	
<%@ page import ="com.client.*, javax.xml.rpc.Service, java.net.URL" %> <%
//Initialize the endpoint URL endpoint = new URL("http://<servername:port>/<webcontext>/services/CompanyRepository"); //Replace the servername and context according to your server settings CompanyRepositorySoapBindingStub stub = new CompanyRepositorySoapBindingStub(endpoint,null); %> <html> <head> <title>Access Company Service</title> </head> <body text="#0077FF"> <p style="margin-top='30px'"> <h3>Company Repository Access WebService ::: JSP Client</h3> <% try{ // Call the method and assign the returned result to // the Company object. Proper error handling would be // appropriate, but this is only a demo Company company = stub.getCompanyData(); //Display data out.println("<b>Company name: </b>" + company.getCompanyName()); out.println("<br><b>Company ID: </b>" + company.getCompanyID()); out.println("<br><b>Employee Names:: </b>"); String[] employeeNames = company.getEmployeeNames(); for(int i=0; i< employeeNames.length; i++) out.println("<br>   -" + employeeNames[i]); } catch(Exception e) { out.println("Error in get and display data: <br>" + e); } %> </p> </body> </html>

Launch the browser and type http://<servername:port>/<webcontext>/AccessCompanyService.jsp. The browser should display the data as we predicted in the beginning.

Publishing An Array of Complex Types: Part-II

What will this web service do ?

This web service will have a single method/operation called "getIndustryData".When a SOAP request is made it will return a complex type object "Industry" which in turn has a complex type array attribute "Product". The below diagram shows the structure of the objects "Industry and Product"

Object:Industry
Attributes:
industryID
industryName
Product[ ]
Object:Product
Attributes:
productID
productName

The below diagram shows the raw SOAP response after the SOAP request was made

  <?xml version="1.0" encoding="UTF-8" ?>
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
 		   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 		   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <soapenv:Body>
 <getIndustryDataResponse
                   soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <getIndustryDataReturn href="#id0" />
  </getIndustryDataResponse>
 <multiRef id="id0" soapenc:root="0"
            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
  		    xsi:type="ns1:Industry"
            xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  	        xmlns:ns1="urn:IndustryService">
  <industryID href="#id1" />
  <industryName xsi:type="soapenc:string">Test</industryName>
  <products href="#id2" />
  <products href="#id3" />
  </multiRef>
 <multiRef id="id3" soapenc:root="0"
            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 		    xsi:type="ns2:Product" xmlns:ns2="urn:IndustryService"
 		    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
  <productID href="#id4" />
  <productName xsi:type="soapenc:string">Light Beamer</productName>
  </multiRef>
  <multiRef id="id1" soapenc:root="0"
          soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
  	  xsi:type="xsd:int"
          xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">2311</multiRef>
 <multiRef id="id2" soapenc:root="0"
                    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                    xsi:type="ns3:Product" xmlns:ns3="urn:IndustryService"
                    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
  <productID href="#id5" />
  <productName xsi:type="soapenc:string">Sensor Light</productName>
  </multiRef>
  <multiRef id="id5" soapenc:root="0"
      soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      xsi:type="xsd:int"
      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">712</multiRef>
  <multiRef id="id4" soapenc:root="0"
          soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          xsi:type="xsd:int"
          xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">1774</multiRef>
  </soapenv:Body>
  </soapenv:Envelope>

If you want to see how the output will look like if a request is made from a JSP page, see the below picture.




OK, now lets see how we can install and run this stuff:

Step 1: Create server side classes

We will need three classes:
  1. Industry.java => A complex type object with an array object Product
  2. Product.java => A simple complex type object
  3. IndustryService.java => A simple class with a single method called getIndustryData(int industryID), this will return the complex type object Industry as response which has an array of complex type object Product as an attribute in it.
Both classes have the package com.server. Compile the Java source files Industry.java, Product.java and IndustryService.java and place the class files under the WEB-INF/classes/com/server directory of your Axis context.
Industry.java
package com.server;
public class Industry {
 	public int industryID;
 	public String industryName;
 	public Product[ ] products;
}

IndustryService.java
package com.server;
public class IndustryService{

	/**
	 * @param industryID
	 * @return industry object
	 */
	public Industry getIndustryData(int industryID){

		Product product1 = new Product();
		product1.productID = 712;
		product1.productName = "Sensor Light";

		Product product2 = new Product();
		product2.productID = 1774;
		product2.productName = "Light Beamer";

		Product [] products = new Product[] { product1, product2 };

		Industry industry = new Industry();
		industry.industryID = 2311;
		industry.industryName = "Test";
		industry.products = products;

		return industry;
	}
}

Step 2: Deploy the web service using AdminClient

Now type the following command into the command prompt
java org.apache.axis.client.AdminClient deployIndustryService.wsdd 
The command window should display "Done processing" after executing the above command.

The deployIndustryService.wsdd is Web service deployment descriptor file that tells Axis web services engine

The complex type objects with arrays requires extra mapping details to properly encode the SOAP response. In Part I, the beanMapping tag was used in WSDD to specify the data mapping details of the object "Company". Here typeMapping tag is used just to show the option that it can also be used instead of beanMapping. The WSDD has three typeMapping tags. Note the new typeMapping 'ProductArray' whose serializer and deserializer were mapped to org.apache.axis.encoding.ser.ArraySerializerFactory and org.apache.axis.encoding.ser.ArrayDeserializerFactory.

deployIndustryService.wsdd
<deployment
    xmlns="http://xml.apache.org/axis/wsdd/"
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

  <service name="IndustryRepository" provider="java:RPC" style="rpc" use="encoded">
      <parameter name="wsdlTargetNamespace" value="urn:IndustryService"/>
      <parameter name="className" value="com.server.IndustryService"/>
      <parameter name="allowedMethods" value="getIndustryData"/>

      <typeMapping
        xmlns:ns="urn:IndustryService"
        qname="ns:Industry"
        type="java:com.server.Industry"
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
        deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      />
      <typeMapping
        xmlns:ns="urn:IndustryService"
        qname="ns:Product"
        type="java:com.server.Product"
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
        deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      />
      <typeMapping
        xmlns:ns="urn:IndustryService"
        qname="ns:ProductArray"
        type="java:com.server.Product[]"
        serializer="org.apache.axis.encoding.ser.ArraySerializerFactory"
        deserializer="org.apache.axis.encoding.ser.ArrayDeserializerFactory"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      />
  </service>
</deployment>

Step 3: Check the web service WSDL

Once Step 2 is completed successfully your service should be available for clients to consume. Lets see whether our web service is available by it name "IndustryRepository". Launch the browser and type http://<servername:port>/<webcontext name>/services/IndustryRepository?wsdl. The browser should display you a WSDL file similar to IndustryRepository.wsdl. Note that the service endpoint may vary according to your servername and webcontext, and some servlet containers may expect you to restart the engine to recognize the new server classes. A successful SOAP response will look like this getIndustryData_SOAPResponse.xml

Step 4: Create and run the client to access the web service

We will again use a JSP page as the web service client as an example here. Type the following command into the command prompt (all on one line:)
java org.apache.axis.wsdl.WSDL2Java  \
         -p com.client http://<servername:port>/<webcontext \
         name>/services/IndustryRepository?wsdl
Compile the Java based client stubs and move the compiled client stubs to the <web context>/web-inf/classes/com/client directory; we will use them in our JSP file. Prepare the JSP file and save it under the folder <web context>/ directory.
AccessIndustryService.jsp
<!-- The package import com.client.* will import all the --
       -- necessary class files to be used later -->
<%@ page import ="com.client.*,
		 javax.xml.rpc.Service,
		 java.net.URL" %>
	<%
	//Initialize the endpoint
	URL endpoint =
             new URL("http://<server:port>/<context>/services/IndustryRepository");

	//Replace the servername and context according to your server settings
	IndustryRepositorySoapBindingStub stub =
            new IndustryRepositorySoapBindingStub(endpoint,null);
	%>

<html>
<head>
<title>Access Industry Service</title>
</head>
<body text="#0077FF">
<p style="margin-top='30px'">
<h3>Industry Repository Access WebService ::: JSP Client</h3>
	<%
	try {
		// Call method and assign the result to the company object
		// Proper error handling would be appropriate, but
		// this is only a demo; axis fault or soap fault is not handled.
		Industry industry = stub.getIndustryData(2311);

		//Display datas
		out.println("<b>Industry name: </b>" + industry.getIndustryName());
		out.println("<br><b>Industry ID: </b>" + industry.getIndustryID());
		out.println("<br><b>Product Details:: </b>");

		Product[] products = industry.getProducts();
		for(int i=0; i< products.length; i++)
		    out.println("<br>   -" +
                                products[i].getProductID() + ":" +
                                products[i].getProductName());

	} catch(Exception e) {
            out.println("Error in get and display data: <br>" + e);
        }
	%>
</p>
</body>
</html>

Running the JSP client by typing http://<servername:port>/<webcontext>/AccessIndustryService.jsp in the browser window should display a JSP page as shown below.


Step 5: Creating a VB.Net client (optional only)

Create a new VB.Net project and create a VB form as shown in the picture (VB.NETSCreenShot_Industry.jpg). Name the TextBox as ResultBox with multi line property true. Name the button1 as GetData and caption as Get Data. Name the button2 as CloseME and caption as Exit. Now add a web references pointing to the wsdl url http://<server:port>/<context>/services/IndustryRepository?wsdl with the net.client as the class name. This will create the necessary proxy classes to access the web service, including the vb classes like Industry and Product. In the onClick event of GetData, paste the below code:

Form1.vb (GetData onClick event)
Form1.vb
Private Sub GetData_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles GetData.Click
    Dim IndustryService As New net.client.IndustryServiceService
    Dim industry As New net.client.Industry
    Dim product As New net.client.Product

    industry = IndustryService.getIndustryData(2311)
    ResultBox.Text = "Industry name:" & industry.IndustryName
    ResultBox.Text = ResultBox.Text & ControlChars.NewLine
    ResultBox.Text = ResultBox.Text & "Industry ID:" & industry.IndustryID
    ResultBox.Text = ResultBox.Text & ControlChars.NewLine & "Product Details:"
    ResultBox.Text = ResultBox.Text & ControlChars.NewLine
    product = industry.products
    Dim i As Integer
    For i = 0 To product.Length
        ResultBox.Text = ResultBox.Text & "   -"&
                         product(i).productID & ":" &
                         product(i).productName & ControlChars.NewLine
        Next
End Sub

The output upon SOAP request will look like


Additional tips and notes