Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page



Simple Java Web Service Example
Using Axis

Peter Rose - 09/2009


ABSTRACT

SUMMARY

DOWNLOAD AND INSTALL NECESSARY FILES AND RESOURCES

CREATE NEW WEBSERVICES DIRECTORY STRUCTURE

GETTING INITIAL FILES CREATED

GENERATING WEB SERVICE WSDL FILE WITH AXIS

GENERATE SERVER SIDE AND CLIENT SIDE CLASSES

BUNDLE REQUIRED CLASSES

REGISTER WEB SERVICE WITH TOMCAT

BUILDING THE WEB SERVICE CLIENT

GETTING A CLIENT WEB SERVICE CALL THROUGH A PROXY SERVER





Abstract
Create local web services environment using Axis for the SOAP functionality wrapper for both server and client side parts.

Axis is an Apache Open Source project http://ws.apache.org/axis/java/user-guide.html. "Axis is essentially a SOAP engine -- a framework for constructing SOAP processors such as clients, servers, gateways, etc." All that means is that instead of writing the complex and very messy class and api method structures, you use a few simple Axis command line calls, and Axis builds all of these classes for you.

There are other products that do what Axis does, and you can hard code everything yourself. Writing this example using Axis eliminates the clutter that might otherwise obscure the whole point of creating a web service and how that web service can be used.


Summary

General Process
After doing a little housekeeping to get the environment setup, we'll create a pojo that we will later decide to make a web service out of, i.e. TellMe.java, and then we'll show how to write a client object that access TellMe.java.

TellMe.java will not have to change. All you will have to do is create an interface class that your web service will use to reference TellMe.java. We'll be calling this ITellMe.java but TellMe.java will not implement it. Rather, this interface is simply going to be used to identify the methods on TellMe.java that you will want to expose to a web service call.

First, I'll show how to make TellMe.java available to be called, and then I'll show how to write a client object that can call it. What all this means is TellMe.java is going to be exposed as a web service which someone else, i.e. the client, on another server on another planet, will write a client object to access it.

Phases of Work

  • Download and install necessary files and resources
  • Create new WebServices directory structure
  • Getting initial files created
  • Generating web service wsdl file with Axis
  • Generate server side and client side classes for web service



Download and install necessary files and resources
Download Axis-1_4 and spin it off (unjar/zip it) under some [axishome] directory. Put the following 4 jars from axis that are found in [axishome]\lib into both [tomcathome]\lib and [yourserver]\[commonjars]

  • axis. jar
  • commons-discovery-0.2.jar
  • *** only if you don't have it: commons-logging-1.0.4.jar
  • jaxrpc.jar
  • *** only if you don't have it: log4j-1.2.8.jar
  • saaj.jar
  • wsdl4j-1.5.1.jar

Be careful to note if you have other saaj.jar type files from earlier releases, like saaj-api.jar or saaj-ri.jar, delete them or you'll get name space clashes. And also note that I am writing this in July of 2009 and so anything can change here - even Axis. Reader beware.... On the other side of the coin, regardless of Axis, the process you will use is what I'm driving at in this article.

Now, temporarily, copy the Axis-1_4\webapps\axis directory into [tommycat]\webapps. This will enable us to produce a .wsdl file by using Axis engine rather than trying to figure out how to hard code the thing. Stay tuned here with me; just get the axis directory copied into tommycat for now.


Create new WebServices directory structure
There are two directory structures we're going to be talking about: that on the server side and that on the client side.

Client Side Directory Structure

Business scenario: Project is seek at the following directory level

C:\myprojects\seek\src\com\me\seek

Under seek are the usual suspects: action, beans, bo, etc. I will add a new directory called web services under this to hold all web service client files that will hookup to some external site, let’s call it Bob, and access their web service TellMe.tellMeTheDates().

Thus, my final client side directory path looks like this:

C:\myprojects\seek\src\com\me\seek\web services

Server Side Directory Structure
At Bob’s site, they have a directory structure where they put their web service files:

C:\projects\widgets\src\com\bob\widgets

You'll note that I'm using my own machine/structure to put these files, but just assume this bob directory is off in cyberspace somewhere else.


Getting initial files created
Summary of web services files
Web services are the objects that are to be called by a client. Web services are built around a simple pojo.

As far as web services go, these are some of the basic files needed:

  • Pojo: TellMe.java, the business object you want to expose to the web as a service.
  • Web Service Interface: ITellMe.java is an interface of that pojo specifically used as the gateway to that object from outside.
  • A wsdl: tellme.wsdl is an xml definition file of the web service, similar to an xml DTD. It is used by a client programmer to identify which objects and methods are available for web service.
  • A wsdd xml file provides the glue to identify where to find the web service. Each web service has one, and each definition is combined into a servlet-config.xml file.
  • Locator: A java file that is a generated java file that enables a client to find the web service
  • Bindings: A java file that is a generated java file that enables a client to access the web service through the pojo’s web service interface.

Other than the pojo and its interface, all of these files are tedious at best to create and so we let Axis do the heavy lifting for us.

Server Side Files
Bob has a service class on his site called TellMe.java that he wants to expose as a web service. TellMe.java is just a business object on Bob’s site; nothing special.

TellMe.java


package com.bob.widgits.services;

import java.util.Date;
import com.bob.widgits.beans.ReportBean;

public class TellMe {
  public ReportBean[] tellMeTheDates() {
    ReportBean[] beans =
        new ReportBean [6];
    for(int i=0; i<6; i++) {
      beans[i] = new ReportBean();
      beans[i].setName("Name of object");
      beans[i].setCode("CODE01");
      beans[i].setReportDate(new Date().toString());
    }
    return beans;
  }

  public String whoAmI(String thisIsMe) {
    return thisIsMe;
  }
}

The example is simple, and may in fact, contain dozens of methods. However, Bob wants to expose just the tellMeTheDates() method as a web service. You will note that the method returns a simple array of what are very simple DTO type beans. You have to be careful what you try to return in a web service, i.e. watch out for complex structures or very language specific returns like nested objects or a java.util.List which is specific to java. These things are listed out, so, check on what constitutes good returnable data types.

Because Bob only wants to expose the tellMeTheDates() method as a web service, he would create an interface stating this contract.

ITellMe.java


package com.bob.widgits.services;

import com.bob.widgits.beans.ReportBean;

public interface ITellMe {
  public ReportBean[] tellMeTheDates();
}

Note that TellMe.java was compiled without implementing ITellMe.java. Also note that Bob would probably put the ITellMe.java file somewhere other than right in with the service object in his own web services directory. It’s all up to what you want to do.


Generating web service wsdl file with Axis

Add jars to classpath
First, in order to use the Axis tool we'll be discussing, Bob has to have the following in his java classpath:

set CLASSPATH=%CLASSPATH%;
C:\bobtools\axis-1_4\lib\axis. jar;
C:\bobtools\axis-1_4\lib\commons-discovery-0.2.jar;
C:\bobtools\axis-1_4\lib\jaxrpc.jar;
C:\bobtools\axis-1_4\lib\saaj.jar;
C:\bobtools\axis-1_4\lib\wsdl4j-1.5.1.jar;
C:\bobtools\axis-1_4\lib\commons-logging-1.0.4.jar;
C:\bobtools\axis-1_4\lib\log4j-1.2.8.jar

If you don't have this, then run it now so you have the Axis references. In addition, be sure you run a build against your code base so that class files are generated where you will be creating your .wsdl file from or they will not be picked up correctly.

Generate the wsdl file
Next, cd into your (looking at things from Bob’s perspective here) project’s classes directory. Note that it is Bob that is doing this: it is he that needs to provide a .wsdl to a client. That .wsdl file will be used by the client to generate all of the files necessary for the client to find this ITellMe.java file.

Make sure that you are going into the directory that will be deployed to your tomcat server, and not into your IDE internal build directory (unless they are the same).

The wsdl creation command would then become:

\bobtools\Java\jdk1.6.0_10\bin\java org.apache.axis.wsdl.Java2WSDL
-o ..\tellme.wsdl
-n urn: com.bob.widgits.services
-l http://localhost:8080/axis/services/tellme
com.bob.widgits.services.ITellMe

What this does is link the interface with the file that it is describing without that file needing to implement it.

Note that this will put the wsdl file at the widgets level (it will need to reference src from there). Note that the .wsdl file has picked up the instance of the ReportBean so that it can be duplicated on the client side and that only the method exposed by the interface is applied.


Generate server side and client side classes
If you have already generated these files before, you'll have to delete the referenced generated directory as the tool will not overwrite it - and the reason to create this new ‘generated’ directory by specifying it in the command as it makes maintenance simple. Note that we're going to specify the generated directory to fall under the services directory as the files generated into generated relate to files in the services directory.

Change directory so you are at the directory where the wsdl file is. Remember the directory structure is: cd C:\projects\widgets\src\com\bob\widgets so you want to do the following:

cd C:\projects\widgets

Next, execute the create command:

\bobtools\Java\jdk1.6.0_10\bin\java org.apache.axis.wsdl.WSDL2Java
-o src
-p com.bob.widgets.services.generated
-s tellme.wsdl

and you find that Axis has created all of the client side and server side files that will be needed:

  • deploy.wsdd
  • ReportBean.java
  • ITellMe.java
  • ITellMeService.java
  • ITellMeServiceLocator.java
  • TellmeSoapBindingImpl.java
  • TellmeSoapBindingStub.java
  • undeploy.wsdd

And the IDE should compile them with some nudging and you need to do a build....

Of interest is to note the newly created ITellMe.java file in the generated directory that is now set up to communicate:


package com.bob.widgets.services.generated;

public interface ITellMe extends java.rmi.Remote {
  public java.lang.Object[] tellMeTheDates() throws java.rmi.RemoteException;
}




Bundle required classes
This section deals with creating a client and a server jar file. The intent is that these web services and client code can be put into jar into a lib directory rather than mixing the code with the project.

There are pros and cons to each, but the approach I'll take here will be that the server side code will not be in a jar; it will exist as code in the main project hierarchy, i.e. as we have seen in the creation of the web services directory and the use of the .wsdl file to create all of the necessary classes.

However, if I am the client and you provide the .wsdl file to me to generate the files on my server, it’s going to mean that the directory structure that you built these from will be duplicated in my directory structure. This is not good for many reasons: cluttering up my file system, exposing those classes to some fool mucking with them, and other irritating things.

It would be better for the server producing the web service to create the client side jar for me and send this to me rather than the .wsdl file. This is more like me downloading the apache commons logging jar and putting it into my common lib.

Then, if the web service is terminated, for example, all I have to do is delete the jar file from my directory structure. I don't have to take the risk of deleting an entire directory hierarchy (if I even remember where I put that) and then creating a new one. Life is much simpler this way.

So, though I will demonstrate how Axis is used to create both of these jars, in practice the recommendation would be to only produce the client side jar and send that to the client.

Create server side jar
Remember, it is Bob that is creating the client side jar. Now we will create a jar file with all these classes so that we can use it for deploying our web service. First, go to the directory where we can grab the class files (our directory structure is C:\projects\widgets\src\com\bob\widgets):

cd C:\projects\widgets\build

Use the jar command as follows.

\bobtools\Java\jdk1.6.0_10\bin\jar cvf ..\tellmeServerSide.jar
com\bob\widgets\services\*.class
com\bob\widgets\services\generated\*.class

Note that if your java class and interface class were in different locations, you'd have an additional command so that you get them both. And it is important to note that the use of the services directory here implies that only files for this web service are located in here. Otherwise, you need to only jar the TellMe.java and ITellMe.java files

Now copy/move this jar file into tommycat’s webapps lib folder.

copy ..\tellmeServerSide.jar "%CATALINA_HOME%\webapps\axis\WEB-INF\lib

Create client side jar
We will create another jar file to use on the client side. For the client side we only need the classes that were generated by the WSDL2java tool (which are located inside generated package that was created in our file system), except the TellmeSoapBindingImpl class.

Be sure you're in the directory that you can hit the classes:

cd C:\projects\widgets\build

Then execute the jar command

\bobtools\Java\jdk1.6.0_10\bin\jar cvf ..\tellmeClientSide.jar
com\bob\widgets\services\generated\ReportBean.class
com\bob\widgets\services\generated\TellmeSoapBindingStub.class
com\bob\widgets\services\generated\ITellMeServiceLocator.class
com\bob\widgets\services\generated\ITellMeService.class
com\bob\widgets\services\generated\ITellMe.class

Note that I need to include the ReportBean.class (as well as any other beans that show up in there based on what the client will need if you are sending a file that contains many method calls with different bean type content returns).

Now copy/move this jar file into tommycat’s webapps lib folder.

copy ..\tellmeClientSide.jar "%CATALINA_HOME%\webapps\axis\WEB-INF\lib


Register web service with Tomcat
Again, this step is not necessary if you have your server side web service files intermixed with your project source code because the compiled files that will be referenced by the client call will be sitting in the server generated directory and thus when you build, tomcat will be all set.

Axis comes with a tool for registering web services with tomcat; it is called AdminClient. Look into generated folder and you will find two WSDD (web service deployment descriptor) files; deploy.wsdd and undeploy.wsdd. These files were generated by WSDL2Java tool and as used in deploying/undeploying a web service.

Note: (Tomcat) Server must be started before executing the register command.

Next, change directory into your source directory:

cd C:\projects\widgets\src

This command will deploy the web service into axis.

\bobtools\Java\jdk1.6.0_10\bin\java org.apache.axis.client.AdminClient com\bob\widgets\services\generated\deploy.wsdd

Now restart (Tomcat) server.

To verify our web service is deployed correctly; try following url from your browser.

http://localhost:8080/axis/services/tellme?wsdl

(if necessary, change the port 8080 in url to match the port on your machine)

This will show up a complete wsdl file, and it is the complete definition of the web service that we have deployed. Basically, if someone now wants to write a client to access the TellMe web service, they will use this url to build their client .java class.

Now everything on web service (server side) is completed and our web service is successfully deployed.


Building the web service client
Again, if the web service has sent you the client a .jar file then just take the jar and dump it into a common lib seen by tommycat and you're done (well, you'll probably also have to manually stick it in your IDE lib and bring its reference in as well).

Just remember that if you have developed the server side web service code on your local machine/project that your import statements MUST point to the .jar file you put into tomcat - and be sure before you start writing this code that your local lib also contains this .jar or you won't see the references.

In addition, if you have used a common bean in your directory structure to pass around, you must ensure that the reference is only from the generated directory, and not your local version.

Based on the information that we can get from the tellme.wsdl, we can construct the following simple web service client (assuming that we have a copy of the tellmeClientSide.jar stuffed somewhere in a local lib folder). Recall that our directory structure for this client code is:

C:\myprojects\seek\src\com\me\seek\web services

And so our class would be:


package com.me.see.webservices;

import com.bob.widgets.services.generated.ReportBean;
import com.bob.widgets.services.generated.ITellMe;
import com.bob.widgets.services.generated.ITellMeService;
import com.bob.widgets.services.generated.ITellMeServiceLocator;

public class WSClientForBob {
  private static String classname = "WSClientForBob";

  public void wsCallFindAllDateRecords(String[] args) throws Exception {
    ITellMeService service = new ITellMeServiceLocator();
    ITellMe tellMe = service.gettellme();
    Object objbeans = (Object)tellMe.tellMeTheDates();
    ReportBean[] beans = (ReportBean[])objbeans;
    for(int i = 0; i < beans.length; i++) {
      System.out.println("  -Name/Duns/Date: "
        + beans[i].getCtrName() + "/"
        + beans[i].getDuns() + "/"
        + beans[i].getReportDate());
    }
  }
}

Note that though ITellMeServiceLocator ‘looks like’ an interface because of the ‘I’ preface, that was just generated by the Axis process; it’s actually a class.


Getting a Client Web Service Call Through a Proxy Server
As developers, we are usually constrained behind fire walls and proxy servers that limit our ability to test the code that we write.

To test your web service client against a remote web service from your local machine, all you have to do is register your proxy server's properties in your System Properties object.

In current IE browser, for example, to find these settings, open browser Tools; Internet Options; Connections; Lan Settings; Advanced and copy the values. If the host names don't work, ping them and then substitute the ip address.

Generally, you'll want to set these properties in your application's startup servlet. But just remember not to check this code into your code repository or your security folks will get in a twist; it's just so you can run some local testing. Here's a method that shows how to register these properties:


    private void registerLocalProxySettingsForWebServiceTesting() {
        System.setProperty("http.proxyHost", "MyServersProxy");
        System.setProperty("http.proxyPort", "8080");
        System.setProperty("https.proxyHost", "MyServersSecureProxy");
        System.setProperty("https.proxyPort", "8443");
    }


Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page