Home

Who are we

Articles

SOAP - A Quick Intro to Webservices

XML-RPC - A Five-Minute Tutorial

Installing Apache2 and mod_perl (Windows XP)

Links

Untitled Document

A SOAP RPC Server and Client in Java

After all the preparation work is done, let's get to work and implement the java version of dateInfoServer.

The dateInfoServer using Java/Axis

As mentioned, the Axis service will be running on Tomcat server. Setting up a basic service is really easy with Axis. We just need the class that provides the service and that's it:

 
dateInfoServer.java
 
import java.util.Date;
import java.text.MessageFormat;
 
public class dateInfoServer {
   
   public String[] dateInfo(String option) {
      Object[] dateArgs = {new Date(System.currentTimeMillis())};
      String[] reply;
      if (option.equals("date")) {
         reply = new String[1];
         reply[0] = MessageFormat.format("{0,date,MM/dd/yyyy}",
                                         dateArgs);
         
      } else if (option.equals("time")) {
         reply = new String[1];
         reply[0] = MessageFormat.format("{0,date,HH:mm:ss}",
                                         dateArgs);
      } else if (option.equals("all")) {
         reply = new String[2];
         reply[0] = MessageFormat.format("{0,date,MM/dd/yyyy}",
                                         dateArgs);
         reply[1] = MessageFormat.format("{0,date,HH:mm:ss}",
                                         dateArgs);
      } else {
         reply = new String[1];
         reply[0] = "Error - unrecognized option.";
      }
      return(reply);
   }
}

The surprising thing is - we don't see any specific SOAP code at all. The return value of the service is an array of strings. In the XML-RPC example we used a vector as a return type. We could do that here too, but it turned out that there is a problem with perl's SOAP::Lite package resolving the XML document returned by the Axis service. Perl uses a hash to store the data in the body of the message. However if the XML document is such that it has two different elements with the same name like

 
<vector>
   <item>item1</item>
   <item>item2</item>   
</vector>

then perl can't store the data this way since the hash storing the data structure can't have two elements with the same name. To resolve these things in perl we could look at the deserialized data directly and iterate over the entries as described e.g. here. However in the particular case here axis doesn't include the data directly in the xml document, but rather uses a reference to a data structure (<multiRef>) found later in the xml document. For some reason I can not manage to access this data structure using the perl SOM. Of course there are still ways to work around this limitation but not really in the context that I would like to present things here. For completeness one way to access the data is described here.

Back to our main topic, implementing and deploying a java web service. We already have the service code. To deploy the service we can simple copy the java class over to the webservice\axis directory and rename it from *.java to *.jws. In our case that would be copy dateInfoServer.java to <tomcat>\webservices\axis and rename it to dateInfoServer.jws. Provided the tomcat server is running we should now be able to test the service, e.g. with http://localhost:8080/axis/dateInfoServer.jws?wsdl. We should get something like this:

 
<wsdl:definitions targetNamespace=
      "http://localhost:8080/axis/dateInfoServer.jws">
	<!--
WSDL created by Apache Axis version: 1.3
Built on Aug 31, 2005 (10:05:42 GMT+00:00)
-->
  <wsdl:types>
    <schema targetNamespace=
            "http://localhost:8080/axis/dateInfoServer.jws">
      <import namespace=
	         "http://schemas.xmlsoap.org/soap/encoding/"/>
	    <complexType name="ArrayOf_xsd_string">
	      <complexContent>
	        <restriction base="soapenc:Array">
              <attribute ref="soapenc:arrayType" 
                         wsdl:arrayType="xsd:string[]"/>
            </restriction>
         </complexContent>
       </complexType>
     </schema>
  </wsdl:types>
   ...

We can now try to access the java service with the perl or python client. To do so we will have to change the scripts a little since the simple service is deployed at a different location than the standalone perl and python services. Besides that, the ports for the standalone services and the tomcat server are typically going to be different as well. Here are the changes to be applied to the perl client:

 
my $service = "http://localhost:8080/axis/dateInfoServer.jws";
my $dateProxy = SOAP::Lite->uri('urn:/dateInfoServer')
	->proxy($service);
 
dateInfoClientAxis.pl (full perl code)

And here for the python client:

 
serverUrl='http://localhost:8080/axis/dateInfoServer.jws'
namespace='urn:/dateInfoServer'
  dateInfoClientAxis.py (full python code)

If you run the client with e.g. dateInfoClientAxis.pl all you should get the familiar response.

The client using Java/Axis

Implementing a java client using Axis is only a little more difficult:

  dateInfoClientJ.java
 
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;

public class dateInfoClientJ {
   
   public dateInfoClientJ(String option) {
      java.lang.Object response = null;
      try {
         String endpoint = "http://localhost:9000/";
         Service service = new Service();
         Call call = (Call) service.createCall();
         
         call.setTargetEndpointAddress(new java.net.URL(endpoint));
         call.setOperationName(new QName
                               ("urn:/Date", "dateInfo"));
         call.addParameter("option", 
                           org.apache.axis.Constants.XSD_STRING,
                           javax.xml.rpc.ParameterMode.IN);
         call.setReturnType(org.apache.axis.Constants.SOAP_ARRAY);
         response = call.invoke( new Object[] { option } );
      } catch (Exception e) {
         System.err.println(e.toString());
      }
      
      if (option.equals("date")) {
         String[] vResponse = (String[])response;
         System.out.println("Current server date is " + 
                            vResponse[0]);
      } else if (option.equals("time")) {
         String[] vResponse = (String[])response;
         System.out.println("Current server time is " + 
                            vResponse[0]);
      } else if (option.equals("all")) {
         String[] vResponse = (String[])response;
         System.out.println("Current server date is " + 
                            (String)vResponse[0]);
         System.out.println("Current server time is " + 
                            (String)vResponse[1]);
      } else {
         String[] vResponse = (String[])response;
         System.out.println("Response: " + vResponse[0]);
      }
   }
   
   public static void main(String[] args) {
      String option = args[0];
      dateInfoClientJ dateInfoClient = new dateInfoClientJ(option);
   }
}

So what is going on here? The relevant code sits in the first part of dateInfoClientJ. We start off getting a Call object by using a default Service. We then go on configuring the call by first setting the web address that should be called and then defining the name space and the name of the service to be called. The next two lines set the types of the parameters sent in the request, followed by the return type. Finally we call the service with call.invoke which takes an array of java objects as parameter. Because we defined what the parameters are, they are going to be correctly translated into a xml data structure. Now we can compile and run it. To make compilation a bit simpler I added an ant build file, in case you have ant installed, or alternatively a windows batch script called jbuild.cmd to SOAPQuick.zip to compile the java programs in the package. To use these utilities, as well as the batch scripts to wrap the java client, you will need to change the paths to the packages to match your system configuration.

If you set your default classpath to include all the necessary libraries then you can just run the client with java dateInfoClientJ all. If not, and you don't like typing endless paths in your cmd window, you can use the above mentioned batch scripts e.g. using dateInfoClient.bat you can simply call the service with dateInfoClient all.

And finally - to access the Axis/tomcat service we just have to change the web address and the name space. I leave that up to you (or check out dateInfoClientAxis.java)

Previous: Getting Started with Java/Axis