The article titled Programming Web Services using Apache Axis shows how Axis Framework has simplified the creation of Web Services. It will start with the definition of Web Services and its related terminologies like SOAP and WSDL. Following that the ease with which Web Services are published are explained. The later section explores about the various tools available in the Axis distribution like the Tcp Monitor, Soap Monitor, Mapping between Java and WSDL. Finally the article ends up by giving a Sample Web Service Application deployed in Axis.
also read:
1) Web Services
1.1) Introduction
Web Services enables two applications running in two different machines to communicate. Data exchange between two heterogenous applications can be possible with Web Services. Technically, Web Services uses XML to exchange data between applications. Web Service is not a single technology, but instead it is a mix of the following technologies.
- SOAP
- WSDL
Let us look into these two terminologies briefly in the following sections.
1.2) SOAP
SOAP, which stands for Simple Object Access Protocol, defines the Request and the Response message format in a Web Service Application. Simply put, it is nothing but a XML structure adhering to a well defined Schema. A Web Service client sends Request to a Web Service in the form of SOAP message and gets the response back in SOAP message only. Let us look into the structure of the SOAP message in the following section.
A SOAP message consists of a SOAP Envelope which forms the outer most layer and it merely serves as a container for the SOAP Header and SOAP Body Elements. The SOAP Header will carry information that doesn’t constitute to the original part of the message. For example, a SOAP Header may carry some Application Specific Information language such the Locale information. This SOAP Header is optional only. They SOAP Body consist of the original data that needs to be processed by the Web Service Application. Consider the following xml snippet which illustrates the SOAP message.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soapenv:Header> </soapenv:Header> <soapenv:Body> </soapenv:Body> </soapenv:Envelope>
1.3) WSDL
Web Services Description Language (WSDL) is again a XML Document that describes the definition of Web Services so that they can be accessible by the Client. Technically, WSDL Document contains the name of the Web Services, the arguments it is going to accept, the URL through which it can be accessed. At a higher level, WSDL contains two definition set, one is the Abstract Definition set which contains very generic information about the various supported data-types along with the information pertaining to messages, port-types and operations. The second is the information very specific to the type of protocol that can be used to access the services, the supported protocol list, the URL information etc. Following is the structure of the WSDL Document.
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ttdev.com/ss" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SimpleService" targetNamespace="http://ttdev.com/ss"> <wsdl:types> <!-- Definition of types here --> </wsdl:types> <wsdl:message name="requestName"> <wsdl:part name="requestName"/> </wsdl:message> <wsdl:message name="responseName"> <wsdl:part name="responseName"/> </wsdl:message> <wsdl:portType name="serviceName"> <wsdl:operation name="operationName"> <wsdl:input message="requestName" /> <wsdl:output message="responseName" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="bindingName"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="operationName"> <soap:operation soapAction="someUrl" /> <wsdl:input> <soap:body parts="requestName" use="literal" /> </wsdl:input> <wsdl:output> <soap:body parts="responseName" use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="serviceName"> <wsdl:port binding="bindingName" name="bindingName"> <soap:address location="ClientAccessibleUrl"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
One of the good things about the Axis Engine is that Developers doesn’t really need to know the low-level issues like the SOAP message, WSDL because they are given higher level of abstraction.
2) Utilities
Let us look into the following Utilities that comes along with Axis Distribution.
- Tcp monitor
- Soap Monitor
- Mapping between Java and WSDL File
2.1) Tcp monitor
Tcp Monitor can be used to monitor the Http Request and Http Response made to Axis Application. To start the Tcp Monitor, have the following Utility file that starts the Gui for the Tcp Monitor Application,
RunTcpMonitor.java
package net.javabeat.apache.axis.webservices.playerservices; import org.apache.axis.utils.tcpmon; public class RunTcpMonitor { public static void main(String[] args) { tcpmon.main(args); } }
Enter some port number (other than 8080), say 9090 in the Listen Port Text Field and click the Add button. You can see a new second tab opening in the same dialog. Now, launch the browser with the following URL, 'http://localhost:9090/axis'
. Now go back to the Tab window to find the Request and the Response in the form of Html format. By listening to the port 9090, whatever Request comes to port 8080, the Tcp Monitor simply forwards the Request and Response to the listening port.
Http Request and Response monitoring using Tcp Monitor
2.2) Soap Monitor
SOAP Monitor functions very similarly like a Tcp Monitor. Whereas Tcp Monitor is used to intercept Http Request and Http Response messages, SOAP Monitor is used to track the SOAP Request and SOAP Response Messages. SOAP Monitor is implemented as a Handler and by default it is not enabled for all the Web Services. If we want to enable SOAP Monitor Handler for both Request and Response SOAP messages, then add the following entry in the deployment descriptor file for the Web Service.
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="SomeService" provider="java:RPC"> <requestFlow> <handler type="soapmonitor"/> </requestFlow> <responseFlow> <handler type="soapmonitor"/> </responseFlow> <parameter name="className" value="SomeService"/> <parameter name="allowedMethods" value="*"/> </service> </deployment>
Now, whenever, you request for a Web Service, the Soap Monitor will track the Request and Response from the Web Service and will display the Xml Request and Xml Response content in the UI as shown below.
Soap Monitor monitoring the Soap Request and Soap Response.
3.3) Mapping between Java and WSDL File
Axis comes with Utilities that maps between Java Class and WSDL file Objects. For example, let us assume that we have a WSDL file and we want to generate the various Java Classes that can be used to generate the Client and the Server code describing the Web Service. For example, consider the following WSDL file that represents the Add service with a simple add() operation taking two parameters. Following is the example WSDL File,
AddServer.wsdl
<wsdl:definitions targetNamespace="http://localhost:8080/axis/services/AddService"> <wsdl:message name="addRequest"> <wsdl:part name="in0" type="xsd:int"/> <wsdl:part name="in1" type="xsd:int"/> </wsdl:message> <wsdl:message name="addResponse"> <wsdl:part name="addReturn" type="xsd:int"/> </wsdl:message> <wsdl:portType name="AddService"> <wsdl:operation name="add" parameterOrder="in0 in1"> <wsdl:input message="impl:addRequest" name="addRequest"/> <wsdl:output message="impl:addResponse" name="addResponse"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="AddServiceSoapBinding" type="impl:AddService"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="add"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="addRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://addservice" use="encoded"/> </wsdl:input> <wsdl:output name="addResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/services/AddService" use="encoded"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="AddServiceService"> <wsdl:port binding="impl:AddServiceSoapBinding" name="AddService"> <wsdlsoap:address location="http://localhost:8080/axis/services/AddService"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
To generate the various client and the server related Java files from the WSDL file, Axis provides a Utility called WSDL 2 Java. For example, consider the following Java program makes use of the Utility.
AddWSDL2Java.java
import org.apache.axis.wsdl.WSDL2Java; public class AddWSDL2Java { public static void main(String[] args) { WSDL2Java.main(new String[]{ "http://localhost:8080/axis/services/AddService?wsdl"}); } }
It is important to understand that the default mappings for WSDL to Java. For example, the elements 'portType'
, 'binding'
and 'service'
in the WSDL file will be converted into its corresponding Java class type. In the WSDL, the name of the portType is 'AddService'
which has a single operation by name 'add'
with two parameters called 'in0'
and 'in1'
. For this, the corresponding class which is generated by the Utility looks like,
AddService.java
public class AddWSDL2Java { public static void main(String[] args) { WSDL2Java.main(new String[]{ "http://localhost:8080/axis/services/AddService?wsdl"}); } }
The 'binding'
element in the WSDL file specifies the type of binding (Rpc or Document) and its specifies the protocol used to access the Web Service. It also defines the Encoding Mechanism used for the two input parameters. For this, the corresponding class generated class s given below,
AddServiceSoapBindingStub.java
/** * AddServiceSoapBindingStub.java * * This file was auto-generated from WSDL * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter. */ package localhost.axis.services.AddService; public class AddServiceSoapBindingStub extends org.apache.axis.client.Stub implements localhost.axis.services.AddService.AddService { private java.util.Vector cachedSerClasses = new java.util.Vector(); private java.util.Vector cachedSerQNames = new java.util.Vector(); private java.util.Vector cachedSerFactories = new java.util.Vector(); private java.util.Vector cachedDeserFactories = new java.util.Vector(); static org.apache.axis.description.OperationDesc [] _operations; static { _operations = new org.apache.axis.description.OperationDesc[1]; _initOperationDesc1(); } private static void _initOperationDesc1(){ org.apache.axis.description.OperationDesc oper; org.apache.axis.description.ParameterDesc param; oper = new org.apache.axis.description.OperationDesc(); oper.setName("add"); param = new org.apache.axis.description.ParameterDesc( new javax.xml.namespace.QName("", "in0"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName( "http://www.w3.org/2001/XMLSchema", "int"), int.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc( new javax.xml.namespace.QName("", "in1"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName( "http://www.w3.org/2001/XMLSchema", "int"), int.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName( "http://www.w3.org/2001/XMLSchema", "int")); oper.setReturnClass(int.class); oper.setReturnQName(new javax.xml.namespace.QName("", "addReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[0] = oper; } public AddServiceSoapBindingStub() throws org.apache.axis.AxisFault { this(null); } public AddServiceSoapBindingStub(java.net.URL endpointURL, javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { this(service); super.cachedEndpoint = endpointURL; } public AddServiceSoapBindingStub(javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { if (service == null) { super.service = new org.apache.axis.client.Service(); } else { super.service = service; } ((org.apache.axis.client.Service)super.service).setTypeMappingVersion("1.2"); } protected org.apache.axis.client.Call createCall() throws java.rmi.RemoteException { try { org.apache.axis.client.Call _call = super._createCall(); if (super.maintainSessionSet) { _call.setMaintainSession(super.maintainSession); } if (super.cachedUsername != null) { _call.setUsername(super.cachedUsername); } if (super.cachedPassword != null) { _call.setPassword(super.cachedPassword); } if (super.cachedEndpoint != null) { _call.setTargetEndpointAddress(super.cachedEndpoint); } if (super.cachedTimeout != null) { _call.setTimeout(super.cachedTimeout); } if (super.cachedPortName != null) { _call.setPortName(super.cachedPortName); } java.util.Enumeration keys = super.cachedProperties.keys(); while (keys.hasMoreElements()) { java.lang.String key = (java.lang.String) keys.nextElement(); _call.setProperty(key, super.cachedProperties.get(key)); } return _call; } catch (java.lang.Throwable _t) { throw new org.apache.axis.AxisFault( "Failure trying to get the Call object", _t); } } public int add(int in0, int in1) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[0]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOAP11_CONSTANTS); _call.setOperationName( new javax.xml.namespace.QName("http://addservice", "add")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] { new java.lang.Integer(in0), new java.lang.Integer(in1)}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Integer) _resp).intValue(); }catch (java.lang.Exception _exception) { return ((java.lang.Integer) org.apache.axis.utils.JavaUtils.convert( _resp, int.class)).intValue(); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } }
Similarly, there is one more file generated for the 'service'
element which contains the URL to access the Web Service. Axis also provides Java2WSDL, which takes the Java interface having the various Web Service operations as input and will generate the corresponding WSDL file that can be accessible by the Clients.
3) Sample Application
Let us finish off the article by giving a Sample Application. This Sample Application is all about querying information from a Web Application. For this, let us assume that we have a mini Cricket Database maintained in the backend and we publish the various services to the Client in the WSDL file. Both types of clients, Browser Client and Console Client are included in the sample Application.
3.1) Pre-requisites
For the sample application to work, we need a Web Container (like Tomcat) along with the Axis Distribution.
- Axix – http://www.apache.axis.org
- Tomcat – http://www.apache.tomcat.org
After downloading these bundles make Tomcat point to Axis as a Web Application. To make this one happen, follow the steps,
- Go to AXIS_ROOT directory where you can find a directory called
'webapps'
. - Copy the axis directory within the
'webaps'
directory and paste it intoTOMCAT_ROOT\webapps
directory. - Start the Tomcat Server by running the
startup.bat
in theTOMCAT_ROOT\bin
directory. - Point to the Browser with the URL
'http://localhost:8080/axis/'
(assuming that you are running the server in your local machine with the default port number 8080) to see the following page.
Home page for Axis
3.2) Player.java
Let us model the properties of the player like the player name, the country to which he belongs to, age and the number of runs scored in the Player
class. Following is the implementation of the Player class which merely contains the getters and the setters for the above mentioned properties.
Player.java
package net.javabeat.apache.axis.webservices.playerservices; public class Player { private String name; private String country; private int age; private long runsScored; public Player(String name, String country,int age, long runsScored){ this.name = name; this.country = country; this.age = age; this.runsScored = runsScored; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public long getRunsScored() { return runsScored; } public void setRunsScored(long runsScored) { this.runsScored = runsScored; } }
3.3) Player Database.java
This is the Helper class which creates some Player objects during the start-up. This class will be used by the Player Service class (which will be seen later) for querying the various player information. Following is the implementation of the PlayerDatabase class.
PlayerDatabase.java
package net.javabeat.apache.axis.webservices.playerservices; import java.util.*; public class PlayerDatabase { private static List players; public static List list(){ return players; } public static Player getPlayer(String name){ Iterator iterator = players.iterator(); while (iterator.hasNext()){ Player player = iterator.next(); if (player.getName().equals(name)){ return player; } } return null; } static { initPlayers(); } static void initPlayers(){ players = new ArrayList(); players.add(new Player("Tendulkar", "India", 34, 15000L)); players.add(new Player("Ganguly", "India", 35, 12000L)); players.add(new Player("Dravid", "India", 32, 11000L)); } }
3.4) Player Service.java
We wish to publish the services of this class to the Clients. The Class given below has four services namely getAgeForPlayer
, getCountryForPlayer
, getRunsForPlayer
and listAllPlayers
, the meaning of which is well known from the name of the method names itself.
PlayerService.java
package net.javabeat.apache.axis.webservices.playerservices; import java.util.List; public class PlayerService { public int getAgeForPlayer(String name){ return PlayerDatabase.getPlayer(name).getAge(); } public String getCountryForPlayer(String name){ return PlayerDatabase.getPlayer(name).getCountry(); } public long getRunsForPlayer(String name){ return PlayerDatabase.getPlayer(name).getRunsScored(); } public String listAllPlayers(){ List players = PlayerDatabase.list(); StringBuilder result = new StringBuilder(); for(Player player : players){ result.append(player.getName() + " "); } return result.toString(); } }
3.5) Deploying the Player Service
As we have seen already, the Admin Client
Tility can be used for deploying and un-deploying a Web Service Application. To make things simpler, let us have a simple Java class that does the job of deploying the Player Service to Axis. Following is the code for the same.
DeployPlayerService.java
DeployPlayerService.java package net.javabeat.apache.axis.webservices.playerservices; import org.apache.axis.client.AdminClient; public class DeployPlayerService { public static void main(String[] args) { AdminClient.main(new String[]{"src/deployment/player-service-deploy.wsdd"}); } }
The above code references a Xml file called 'player-service-deploy.wsdd'
which contains the information to deploy the class PlayerService as a Web Service. Following is the code snippet for the Xml file.
player-service-deploy.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="PlayerService" provider="java:RPC"> <parameter name="className" value="net.javabeat.apache.axis.webservices.playerservices.PlayerService"/> <parameter name="allowedMethods" value="*"/> </service> </deployment>
As we can see, the service name is given as 'PlayerService'
which is referring the class 'net.javabeat.apache.axis.webservices.playerservices.PlayerService'
containing all the service methods. Also, we have assigned the value '*'
to the property 'allowedMethods'
meaning that we have granted permission to access all the public methods inside the PlayerService class. Before running the DeployPlayer class, make sure the class-path is made to point the following jars.
- AXIS_ROOT\lib\activation.jar
- AXIS_ROOT\lib\axis.jar
- AXIS_ROOT\lib\axis-ant.jar
- AXIS_ROOT\lib\commons-discovery-0.2.jar
- AXIS_ROOT\lib\commons-logging-1.0.4.jar
- AXIS_ROOT\lib\jaxrpc.jar
- AXIS_ROOT\lib\log4j-1.2.8.jar
- AXIS_ROOT\lib\mail.jar
- AXIS_ROOT\lib\saaj.jar
- AXIS_ROOT\lib\wsdl4j-1.5.1.jar
- TOMCAT_ROOT\lib\xercesImpl.jar
- TOMCAT_ROOT\lib\servlet-api.jar
After successful deployment, you can see the following output made in your console,
Processing file src/deployment/player-service-deploy.wsdd <Admin>Done processing</Admin></code>
To check whether the deployment has actually happened or not, launch your browser (with the Tomcat Server running) to the following URL. http://localhost:8080/axis/servlet/AxisServlet
. There you can see the Player Service among the list of other Web Services, under which you can also find out the list of the operations supported by the Service.
Player Service along the list of Web Services
3.6) Un-deploying the Player Service
Un-deploying the Service follows the same procedure as that of the Deployment , except that the Wsdd Xml file should contain proper instructions for un-deploying a service. Following is the Xml file that un-deploys the Player Web Service.
<undeployment xmlns="http://xml.apache.org/axis/wsdd/"> <service name="PlayerService"/> </undeployment>
The code snippet is very simple as the name of the service is pointing to 'PlayerService'
which we want to un-deploy. Following is the Admin Client utility that should be run to un-deploy the service.
UnDeployPlayerService.java
UnDeployPlayerService.java package net.javabeat.apache.axis.webservices.playerservices; import org.apache.axis.client.AdminClient; public class UnDeployPlayerService { public static void main(String[] args) { AdminClient.main(new String[]{"src/deployment/player-service-undeploy.wsdd"}); } }
Running the above java program and open to the browser to the URL, http://localhost:8080/axis/servlet/AxisServlet
, to find the Web Service not listed in the page.
3.7) Invoking the Web Services
To invoke all the supported operations of the Player Web Service, first make sure that the class files (Player.class, PlayerDatabase.class and PlayerService.class) are copied to TOMCAT_ROOT\webapps\axis\WEB-INF\classes
directory. Restart the Tomcat server and launch the Browser to the following URL, http://localhost:8080/axis/servlet/AxisServlet'
.
Use the following URL’s to access the various web services operations
- http://localhost:8080/axis/services/PlayerService?method=listAllPlayers
- http://localhost:8080/axis/services/PlayerService?method=getRunsForPlayer&name=Tendulkar
- http://localhost:8080/axis/services/PlayerService?method=getCountryForPlayer&name=Dravid
- http://localhost:8080/axis/services/PlayerService?method=getAgeForPlayer&name=Ganguly
3.8) Creating a Console-based Client
Let us see how to access the Web Service using the Axis Client API with a normal Console Client. Following is the code snippet for the same.
PlayerServiceClient.java
package net.javabeat.apache.axis.webservices.playerservices; import java.net.URL; import org.apache.axis.client.Service; import org.apache.axis.client.Call; public class PlayerServiceClient { public static void main(String[] args) { try{ URL url = new URL("http://localhost:8080/axis/services/PlayerService"); Service service = new Service(); Call call = (Call)service.createCall(); call.setTargetEndpointAddress(url); Object result = call.invoke("listAllPlayers", new Object[]{}); System.out.println(result); result = call.invoke("getRunsForPlayer", new Object[]{"Tendulkar"}); System.out.println(result); result = call.invoke("getCountryForPlayer", new Object[]{"Dravid"}); System.out.println(result); result = call.invoke("getAgeForPlayer", new Object[]{"Ganguly"}); System.out.println(result); }catch(Exception exception){ exception.printStackTrace(); } } }
The above Client is making use of Axis specific API to access the Web Service. A Call (org.apache.axis.client.Call
) object encapsulates a Web Service, which should point to a URL as represented by the Call.setTargetEndpointAddress()
. A Call object is usually created using the Service object which is represented by org.apache.axis.client.Service
. Invocation of the Web Service operation is made by specifying the operation name and the list of arguments it is going to accept through Call.invoke()
.
4) Conclusion
This article showed you how to write Web Servcies using the Apache Axis Framework. This Framework has simplified the Developmental Process a lot by hiding many of the Xml Stuffs and the low-level Configuration Details and has matured a lot. To summarize, this article initially explained the concepts like SOAP and WSDL which are the core Components for any Web Service Implmentation. Then it explained about Tcp Monitor and SOAP Monitor which are used to intercept Http/SOAP Request and Response. Then the Mapping Utilities for the conversion between Java and WSDL file is covered in detail. Finally the article ended up by giving a Sample Application along with the Clients.