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 below questions are just to recap your knowledge; they're not a detailed introduction to Web Services.
What are web services ?Object: Tree |
Attributes: Stem Leaves |
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.
Object: Company |
Attributes: ID Names EmployeeNames[ ] |
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> |
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; } } |
java org.apache.axis.client.AdminClient deployCompanyService.wsddAfter 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> |
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.
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=getCompanyDataand check the SOAP response generated. A successful SOAP response will look like this getCompanyData_SOAPResponse.xml.
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 --> |
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"
|
|
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:
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; } } |
java org.apache.axis.client.AdminClient deployIndustryService.wsddThe command window should display "Done processing" after executing the above command.
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> |
java org.apache.axis.wsdl.WSDL2Java \ -p com.client http://<servername:port>/<webcontext \ name>/services/IndustryRepository?wsdlCompile 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.
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 |
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