Axis-izing Legacy Applications
Two approaches that work
Source Code for this article
This article is a follow-up to an earlier article "Exposing Legacy Applications" (WSJ, Vol. 3, issue 5). It demonstrates how to integrate legacy applications with Web services using Apache Axis. Axis, which is a complete rewrite of Apache SOAP, promises to be faster and more flexible than Apache SOAP.
Two approaches will be illustrated
here. The first uses the
Axis API. In the second
approach, the Axis tools
(Java2WSDL, WSDL2Java) will be
used to generate stubs. We then
write a client that uses the generated
stubs to access the Web service.
The legacy application used in
this article captures and outputs
system accounting information in an
ASCII format.
Application Overview
The details of the architecture and description
of the various components can be found
in the first article. I had a legacy application in
which I wanted to expose its commonly called
operations as Web services. One of the uses of
the application was to capture and track print
job information for customer accounting purposes.
It was based on a two-tier client/server
model and the underlying communication
protocol used was ONC–RPC. No changes
were made to the existing legacy application.
Converting the legacy application into a Web
services application would allow a user to
query these operations residing on a Web
Server via a Web browser and have the results
displayed to the user through the Web page
To achieve this, a three-tier architecture
was adopted. The legacy server resides on
the third tier. The second tier contains
both the Web server and the legacy client.
A wrapper class was created to isolate the
legacy client code, making it easier to
maintain. The SOAP client lives on
the first tier. It accesses the legacy
client on the Web server through the
wrapper class. SOAP is used to communicate
between the SOAP client
and the services exposed on the Web
server. The services communicate
with the legacy client through the
wrapper class. ONC–RPC is the protocol
used between the legacy client
and server. Figure 1 shows the overall system
architecture.
Figures 2 and 3 illustrate the call sequence
between the SOAP client, Web server (containing
the wrapper class and legacy client),
and legacy server. The sequence diagram
summarizes the interactions between the different
tiers and clearly shows which platform
each of the classes reside on.
Tools Used
The entire system was written in Java running
on Windows 2000 Professional. The tools
used to build this system are:
Java 2 Platform, Standard Edition (J2SE):
http://java.sun.com/j2se/
ONC-RPC for Java, a commercial ONC–RPC package:
www.distinct.com/
Apache Axis Release 1.1:
http://ws.apache.org/axis/
Apache Tomcat 4.1.24 (Servlet Engine):
http://jakarta.apache.org/tomcat/tomcat-4.1-doc/index.html
What Is Apache Axis?
Axis is an implementation of an XMLSOAP–
based protocol used for exchanging
data in a distributed environment.
Several implementations of SOAP are available,
for example, Apache SOAP
(http://ws.apache.org/soap/), JWSDP
(http://java.sun.com/webservices/webservicespack.html),
GLUE (www.themindelectric.com), WASP (www.systinet.com) – the best
known of which is Apache SOAP. Think of Axis
as Apache SOAP 3.0. It is a complete rewrite of Apache SOAP 2.2.
Differences Between Axis and Apache SOAP
Apache Axis supports SOAP 1.1 and 1.2,
Web Services Description Language 1.1,
XML Schema 1.0, and JAX-RPC whereas
Apache SOAP supports only SOAP 1.1, with
limited support for XML Schemas. It does
not support WSDL and uses a proprietary
API. For those of you who are unfamiliar
with WSDL, it is an XML document that
describes Web services. It specifies the
location of the service and the operations
the service exposes.
In addition, Axis supports both
RPC/encoded and Doc/literal Message formats
whereas Apache SOAP supports only
the former. RPC/encoded messages follow
both SOAP RPC and encoding rules. This
means that the SOAP body contains an
element with the name of the remote procedure
to be invoked. This element in turn
contains an element for each parameter for
that remote procedure. Both the parameters
and return results are encoded in the
SOAP body. The "encoding" portion refers
to a set of serialization rules defined in the
SOAP 1.2 Specification. These specify how
objects, structures, and arrays should be
serialized.
Doc/literal does not use the SOAP
encoding rules for data. Instead, data is
serialized according to some schema usually
expressed using the W3C XML Schema.
Table 1 summarizes the differences
between Axis and Apache SOAP.
Advantages over Apache SOAP
Axis offers the following advantages over Apache SOAP. These are:
Speed: Axis is faster compared to Apache
SOAP. This is achieved by using SAX internally rather than DOM.
Flexibility: Developers can easily add
extensions to the SOAP engine to allow custom header processing or system management.
Instant deployment through the Java Web
Service: Note that this is intended for simple Web services only.
Points to Note When Migrating from Apache SOAP to Axis
Following are some of the items you
need to be aware of when you attempt to
migrate from Apache SOAP to Axis.
1. Axis does not return a generic Parameter
type. The return type has to be explicitly
set via the setReturnType() call.
2. The SOAPException class in Apache
SOAP maps to AxisFault in Axis.
3. Although not used here, the
MessageContext class is the Axis implementation
of the Apache SOAP SOAPMessageContext class, and is core
to message processing in handlers.
Building Web Services with Axis
Two approaches to building Web services
with Axis are illustrated here. The first
approach shows us how we can use the Axis
API to build Web services. The second utilizes
the tools that come with Axis to generate
stubs. The client code is then written to
utilize these stubs to access the Web
service.
Using the Axis API
Create and Implement the "MyService" class
These are the Web service methods that
will be called from a client. For our purposes,
two methods will be exposed as Web services
and are defined in the class MyService. The
methods are:
public int GetNumberOfRecords_W(String
filename): Returns the number of records in
the file specified as the argument
public String[] GetFileData_W(String filename):
Returns all the records in the file
specified in the argument as an array of strings
Create the Web Service Deployment Descriptor File
The WSDD is an XML file containing the
deployment descriptor for statically configuring
the Axis engine. This is proprietary to Axis.
Axis uses the information contained in this
deployment descriptor to track operations
and type mappings. Note, this is different
from WSDL, which defines the custom XML
types and methods exposed as Web services,
the request and response pairs associated
with each method, and the URL bound to this
service. To create the file deploy.wsdd:
1. Specify the WSDD deployment and define the "java" namespace
<deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
2. Define the service and provider name. The
latter indicates it is a Java RPC service that
is built into Axis.
<service name="MyService" provider="java:RPC">
3. Specify the name of the Java class
<parameter name="className" value="Accounting.MyService"/>
4. List all the methods callable from the client
<parameter name="allowedMethods" value="*"/>
5. The "*" allows all methods under the
MyService class to be called. This can be
further constrained by replacing the "*"
with a space or comma-separated list of
available method names:
<parameter name="allowedMethods"
value="GetNumberOfRecords_WGetFileData_W"/>
Axis can only send user-defined Java
objects over the wire if there is a registered
Axis serializer for it. The arbitrary Java
classes have to be defined following the
standard JavaBean convention. The Bean-
Serializer class is then used to serialize this
class. The registration calls are made in
your client code. Next, we need to tell Axis
which Java classes map to which XML
Schema type. This is done in the deployment
descriptor file by adding the following
line:
<beanMapping qName="ns:MyJavaClass" xmlns:ns="urn:SomeService"
languageSpecificType="java:my.java.MyJavaClass" />
If the default bean serialization model is
insufficient to meet your application needs, Axis
provides the ability to write your own custom
serializers/deserializers. Once these are built,
Axis needs to be told which types they should be
mapped to. This is done by adding the
typeMapping tag into the deployment descriptor
file as follows:
<typeMapping qname="ns:MyJavaClass" xmlns:ns="urn:SomeService"
languageSpecificType="java:my.java.MyJavaClass"
serializer="my.java.Serializer"
deserializer="my.java.DeserializerFactory"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
Writing the Client Code to Test the Web Service
The following steps are required to
create the client code. The client accesses
the Web services defined in the MyService
class.
1. JAX-RPC Setup
- Create a Service object
Service service = new Service();
- Create a Call object
Call call = (Call)service.createCall()
2. Set the URL for the Web service
String endpoint = "http://localhost:8080/axis/services/MyService";
call.setTargetEndpointAddress(new java.net.URL(endpoint));
3. Define the serializers/deserializers (if
required) for the classes. Since the application
does not need to transmit any
application-defined data classes as part
of the SOAP Requests, no serializers and
deserializers are needed.
4. Set the name of the method that you will
be calling. To associate the GetNumberOf
Records_W() method with the "call"
instance, we invoke
call.setOperationName(new QName("MyService",
"GetNumberOfRecords_W"));
Likewise, to associate the GetFileData_W()
method with the "call" instance, we invoke
call.setOperationName
(new QName("MyService", "GetFileData_W"));
5. Set the method arguments
call.addParameter("arg1", XMLType.XSD_STRING, ParameterMode.IN);
6. Set the return type of the method. To set the
return type associated with the method
GetNumberOfRecords_W(), we make the following call
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_INT);
Likewise, to set the return type associated with the method,
GetFileData_W(), we make the following call,
call.setReturnType(new QName
("ArrayOf_xsd_string"),java.lang.String[].class);
7. If the method name has been set to "GetNumberOfRecords_W",
then it is invoked via
Integer ret = (Integer) call.invoke( new Object[] { s } );
"s" here refers to the argument that is being passed to the method
that is being called. In this case, "s" represents the input filename.
8. Handle the errors
Deploying the Application
1. Compile all the classes
2. JAR the resulting class files and move the JAR file to the
C:\jakartatomcat-4.1.24\webapps\ axis\WEB-INF\lib directory.
3. Make sure that the distinct ONC-RPC JAR files are placed in the
C:\jakarta-tomcat-4.1.24\webapps\axis\WEB-INF\lib directory.
4. Before deploying the service, make sure that your Web server is up
and running. Here we are using the Apache Tomcat Server. It has
several scripts under the C:\jakarta-tomcat-4.1.24\bin directory. To
start the Apache Tomcat Server, go to the directory,
C:\jakarta-tomcat-4.1.24\bin and type startup.
5. Deployment is done via the AdminClient tool,
java org.apache.axis.client.AdminClient deploy.wsdd
6. Next, run the legacy server application (if not already running).
7. Finally, run the SOAPClient.
To make things simple, I have created batch files for steps 1, 2, 4, 5,
6, and 7 to facilitate the execution of these commands (the source
code for this article is below).
Testing Whether the Web Service Is Correctly Deployed
There are two ways that you can check if your Web service is properly
deployed. The first is to go directly to the Axis home page, http://localhost:
8080/axis/ (see Figure 4) and click on "View the list of deployed Web
services". The page will show all the methods associated with MyService
(see Figure 5). If the page only shows the heading, "And now...Some
Services", then there is most likely an error in the deploy.wsdd file. To get
a good idea of the cause of the error, look at the server log files (Apache
Tomcat) located under C:\jakarta-tomcat-4.1.24\logs\localhost_log.<date>.txt
The second way is to directly invoke the service and the name of the
method to test from the browser, for example,
http://localhost:8080/axis/services/MyService?method=GetNumberOf
Records_W&s=C:\\Experimental\\Axis\\src\\ACCOUNT.LOG
Figure 6 shows the result of invoking the service through the browser.
Using the Axis Tools
The second approach utilizes the tools that come bundled with Axis to
generate the stubs.
Define the MyService and MyServiceImpl Classes
MyService is an interface class containing two methods. These are:
public int GetNumberOfRecords_W(String filename):Returns the number of records in the file specified as the argument
public String[] GetFileData_W(String filename): Returns all the records in the file specified in the argument as an array of strings
The MyServiceImpl class implements the methods defined in MyService.
Java2WSDL
The first step is to generate the WSDL file for the given MyService
Interface. Use the Java2WSDL command to generate the WSDL file
java org.apache.axis.wsdl.Java2WSDL
-o Accounting.wsdl
-l"http://localhost:8080/axis/services/Accounting"
-n urn:Accounting
-p"Accounting" urn:Accounting
Accounting.MyService
Where
-o: Name of the output WSDL file, Accounting.wsdl
-l: URL of the Web Service, http://localhost:8080/axis/services/ Accounting
-n: Target Namespace for the WSDL, urn:Accounting
-p: Package to NameSpace name,value pairs (Accounting = urn:
Accounting)
class-of-portType : Fully qualified class (Accounting.MyService)
WSDL2Java
Next, generate the server-side wrapper code and stubs. The Axis
tool, WSDL2Java, does all of this automatically when we run the
following command.
java org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -p
Accounting.ws Accounting.wsdl
Where
-o: Output directory for generated files
-d: Deployment Scope. This can be one of: Application, Request
or Session
-s: Generate the server side bindings for Web Service
-p: Package – override all namespace to package mappings and
use this package name instead
Name of the WSDL file: Accounting.wsdl (this was generated
in the previous step).
The files shown in Table 2 are generated as a result of running
the WSDL2Java command.
Modifying the AccountingSoapBindingImpl Class
Modify the AccountingSoapBindingImpl class as follows:
1. Add the following import statement after the package Accounting.ws statement:
import Accounting.MyServiceImpl;
2. Add the MyServiceImpl attribute and create an instance of the class:
MyServiceImpl ms = new MyServiceImpl();
3. Replace the generated return statement, "return –3;" in the method
getNumberOfRecords_W() by:
return ms.GetNumberOfRecords_W(in0);
The argument, in0, is of type String and
is the name of the file to be processed.
Note that the return value of -3 is a value
arbitrarily generated by the WSDL2Java
tool.
4. Replace the generated return statement,
"return null;" in the method getFileData_W() by:
return ms.GetNumberOfRecords_W(in0);
The argument, in0, is of type String and
is the name of the file to be processed.
Note that the return value of null is a value
arbitrarily generated by the WSDL2Java
tool.
Writing the Client Code to Test the Web Service
1. Create an MyServiceService object via the MyServiceServicetServiceLocator:
Accounting.ws.MyServiceService service = new Accounting.ws.MyService
ServiceLocator();
2. We use the MyServiceService object to get a remote handle to the service,
Accounting.ws.MyService acct = service.getAccounting();
3. Finally, we make a call to the method
acct.getNumberOfRecords_W("C:\\Experimental\\Axis\\src2\\ACCOUNT.LOG");
Deploying the Service
1. Compile all the classes
2. JAR the resulting class files and move the
JAR file to the C:\jakarta-tomcat-4.1.24\webapps\axis\WEB-INF\libdirectory.
3. Make sure that the distinct ONC-RPC JAR
files are placed in the C:\jakarta-tomcat-4.1.24\webapps\axis\WEB-INF\libdirectory.
4. Before deploying the service, make sure that
your Web server is up and running. Here we are
using the Apache Tomcat Server. It has several
scripts under the C:\jakarta-tomcat-4.1.24\bin
directory. To start the Apache Tomcat Server, go
to the directory, C:\jakarta-tomcat-4.1.24\bin and type startup.
5. Deployment is done via the AdminClient tool
java org.apache.axis.client.AdminClientdeploy.wsdd
6. Run the legacy server application (if not already running).
7. Run the client.
Conclusion
I have presented two approaches that show
how we can integrate legacy applications with
Axis. The first utilizes the Axis API. The second
uses the tools that come bundled with Apache
Axis. I have also summarized the differences
between Axis and Apache SOAP, listed the advantages
of using Axis, and brought up some salient
points to note when migrating from Apache SOAP to Axis.
Acknowledgments
I would like to thank Tzu-Khiau Koh and
Chui-Ching Low for their comments on the initial
drafts of this paper.
References
Ng, Adelene. "Exposing Legacy Applications." Web Services Journal, Vol. 3, Issue 5.
www.sys-con.com/webservices
RPC: Remote Procedure Call Protocol
Specification Version 2, RFC 1831, R. Srinivasan, August 1995:
www.ietf.org/rfc/rfc1831.txt
XDR: External Data Representation Standard, RFC 1832, R. Srinivasan, August 1995:
www.ietf.org/rfc/rfc1832.txt
SOAP 1.2 Specification:
www.w3.org/TR/SOAP
Web Services Description Language (WSDL)
1.1 Specification: http://ws.apache.org/axis
Java API for XML-Based RPC (JAX-RPC):
http://java.sun.com/xml/jaxrpc
XML Schema: www.w3.org/XML/Schema
About the Author
Adelene Ng is a software architect with Xerox Corporation
based in Rochester, NY. She holds a Ph.D. in computer science
from the University of London, and an M.Sc. from the
University of Manchester.
adelene.ng@xssc.sgp.xerox.com
Axis-izing Legacy Applications by Adelene Ng
WSJ Vol 03 Issue 11 - pg.22