Welcome!

SOA & WOA Authors: Peter Silva, Maureen O'Gara, Tony Bishop, Mark O'Neill, Yeshim Deniz

Related Topics: SOA & WOA

SOA & WOA: Article

Axis-izing Legacy Applications

Axis-izing Legacy Applications

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.

    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
  • More Stories By Adelene Ng

    Adelene Ng is an independent software consultant. She holds a PhD in Computer Science from the University of London, and an M.Sc. from the University of Manchester, United Kingdom.

    Comments (1) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    Nuwan 12/09/04 12:33:14 AM EST

    Your example would be COMPLETE if you could also publish the coding for a custom serializer. This article helped us a lot in many ways. But for some time now I have been trying to implement a custom serializer... but without success. No documentation on this area is available. I sincerely hope you can help me out with this.

    Thanking you in advance

    Nuwan