Publishing complex types web services Part-I & II

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.

A little fundamentals:
The below questions are just to recap your knowledge and will not address a detailed introduction.
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, 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 with the system:
What 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 which is explained very well in here.
The reader is highly recommended to read the installation instructions of Apache Axis and try to deploy and run a simple web service before running this demonstration.
Note: For this demonstration, the resource files will be hosted in Axis + Servlet container and the client will be a JSP page.

Publishing Complex Types Web Services 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
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>


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 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:
  The service name,( CompanyRepository )
  The method/operation to be published ( getCompanyData ) and
  A special beanmapping tag to specify the serializer/deserializer class to be used to interpret Company object.
As opposed 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".
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, also some servlet containers may expect you to restart the engine to recognize the new server classes

Additionally by invoking the web service in a 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".
The WSDL2Java will generate all the necessary Java stub files like SOAPbinding file, Locator file, serializers and deserializers files.
Now type the following command into the command prompt
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 this 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 java.net.URL endpoint = new java.net.URL("http://<servername:port>/<webcontext name>/services/CompanyRepository"); //Replace the servername and context according to your server settings CompanyRepositorySoapBindingStub stub = new CompanyRepositorySoapBindingStub(endpoint,null); //Initialize the stub class with the endpoint variable. %> <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 // A more elegant coding practice shd be used for proper error handling, //this is only a demo Company company = stub.getCompanyData(); //Display datas 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 Webservices 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
Industry.java
  <?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 two 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;
}

Product.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;
	}
}

IndustryService.java
package com.server;
public class IndustryService {
 //will copy the complete source code soon..wait
 public Industry getIndustryData(int industryID){
 ..
 ..
 ..
 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 service name,( IndustryRepository )
  The method/operation to be published ( getIndustryData ) and
  A special typeMapping tag to specify the serializer/deserializer class to be used for Industry, Product and Product Array objects.
As opposed to simple complex type bean mapping, the complex type objects with arrays requires an extra mapping details to properly encode the SOAP response.
In part-I 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 them,
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, also 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.
Now type the following command into the command prompt
java org.apache.axis.wsdl.WSDL2Java  -p com.client http://<servername:port>/<webcontext name>/services/IndustryRepository?wsdl
Compile the Java based client stubs.
Move this compiled client stubs to the lt;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 lt;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
	 java.net.URL endpoint = new java.net.URL("http://<servername:port>/<web context name>/services/IndustryRepository");
	 //Replace the servername and context according to your server settings
	 IndustryRepositorySoapBindingStub stub = new IndustryRepositorySoapBindingStub(endpoint,null);
	 //Initialize the stub class with the endpoint variable.
	%>

<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 the method and assign the returned result to the company object
		// A more elegant coding practice shd be used for proper error handling,
		//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>//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://<servername:port>/<webcontext name>/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, Product
In the onClick event of GetData, paste the below code
Form1.vb (GetData onClick event)
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 & "Products 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