©1994-2003 Kevin Boone
Home     Section index     K-Zone home Software development

Site search

Glossary
Confused by computer jargon? Look it up!

Shameless plug


Now available!

Articles
- Ten-minute guide to setting up a WAP site

- Talk like your boss: new developments in managerese

More...

Development
File handling in the Linux kernel

Java development for the Sony-Ericsson P800

SunONE Application Server 7 FAQ

More...

Linux
Using Linux with the Treo 600

- Linux on the Tecra M1

- Some notes on openzaurus

More...

Download
Java stuff

Linux stuff

More...

(Please read the download policy)

Home automation
The X10 system

Linux TW723 driver

More...

The K-Zone
K-Zone computing

K-Zone law

K-Zone education and science

K-Zone motorcycles

K-Zone DIY

K-Zone railways

K-Zone martial arts

About the author

K-Zone home page

 
Computing
Java on the P800
J2ME FAQ
Perl for Java programmers
CLASSPATH
EJBs on jBoss
CMP with jBoss
Bean managed persistence
C++ tutorial
Simple Java
JDK setup
Howto-appletviewer
Howto-dosprompt
EJB lifecycles
EJB transactions
Inside the J2EE RI
S1AS7 FAQ
iAS 6.0 FAQ
iAS JMQ
Creating a simple XML-RPC Web Service

What this tutorial is about

This tutorial describes how to create a very simple XML-RPC Web Service, to be hosted using Sun's `Java Web Services Developer Pack' (JWSDP). This will allow one Java class to call methods on another over a network. In effect we will be doing remote method invocation (RMI) or remote procedure calls (RPC) using using HTTP as the carrier and XML as the data format. (Although there are technical differences between RMI and RPC, they aren't important here. I will use the term `RMI' as it is more familiar to Java developers.) Why would you want to do this? One of the substantial problems that exists with RMI in the wide-area (e.g., business-to-business) arena is that it can be difficult to find a platform-independent way to allow distributed method calls to be made without compromising security. As an RMI carrier, Corba's IIOP protocol has broad industry support, but it can be difficult (sometimes impossible) to use it in configurations that require it to pass a firewall. In addition, few vendors support the `common secure interoperability' extensions that allow it to be encrypted and authenticated in a vendor-neutral way.
     Using XML with HTTP as the carrier goes some way to solving the problems inherent in other RMI schemes. XML has almost universal support in the computing industry, and generation and parsing libraries exist for use in most programming languages for most platforms. HTTP is well-understood, tolerated by firewalls, and has a well-established integration with SSL (secure sockets layer) encryption. Thus XML-RPC is a vendor-independent, platform-independent, language-independent way of doing remote method invocation.
     You should appreciate that some of the underlying technology in XML-RPC is exceptionally complex. The benefit of developer tools like Sun's JWSDP is that they conceal this complexity, allowing the developer to use standard programming techniques. We shall see that, using JWSDP, the code in both the calling class and the target class is almost identical to that which would be used if they were two classes interacting locally (although, to be fair, I should point out that I have chosen a very simple example that emphasises this similarity).

Scope of this tutorial

This tutorial demonstrates the following features of XML-RPC Web Service development:
  • coding the calling (client) class and the called (server) class;
  • generation of the `plumbing' that will connect these classes at the network level, using tools provided with the JWSDP;
  • packaging of the server classes and plumbing into a WAR archive for deployment;
  • deployment and testing of the service.
The tutorial does not cover other features of the Web Services model, such as XML registries, UDDI, and XML messaging. Although important, XML-RPC is enough for one article. The example provides a class that can calculate compound interest payments; as such it similar to the simple EJB example also on this Web site. You may like to compare the Web Services way of doing RMI with the EJB way: they are actually quite similar. Because this is a tutorial and not a technical article, we will describe how to do the job first, and then discuss what's going on in the infrastructure. However, it will be necessary to provide a small amount of background first.

Readership and prerequisites

This article is written for reasonably experienced developers, who may already have some experience of distributed programming. In particular, I assume that the reader has a good working knowledge of the `Ant' build tool, the Java language, command-line development tools (javac, etc), and is not entirely baffled by XML.

Contacting the author

I am happy to receive comments on, and questions about, the contents of the article. Please address these to kb@kevinboone.com. However, please don't contact me about:
  • using command-line tools on your particular OS (I have tested this stuff on Solaris and Linux; it should work on Windows and Mac systems, but that's down to you to sort out);
  • oddities of the Java class search path and any exception that relates to a class not being found;
  • problems installing and running the JWSDP itself or the Tomcat Web server.
I honestly don't know any more about these issues than will be found on my Web site; you have absolutely nothing to gain by contacting me directly! Sorry, but I have a day job; I don't even have time to reply to such queries.

Abbreviations and terminology

In what follows, I will use the following abbreviations; please replace them where necessary with whatever is appropriate on your system.
JWSDP_HOME Installation directory for JWSDP, chosen when running the installer
SOURCE_ROOT root of the source code directory; wherever you unpacked the source code package
The class that will be initiating the method calls will be refered to as the `client' class, while the class that receives the method calls will be the `target' class. I will use the term `server' for the system/software that hosts the target class; of course, in a practical system each physical host will be both client and server for different objects at some time.

Background

We will be writing two classes that communicate using XML documents carried over HTTP. Of course, we don't want to manipulate the HTTP protocol or the XML in our code, which means we need these three sets of classes as well as the ones we will write ourselves:
  • a Java-enabled Web server (or something else that can handle HTTP requests);
  • client-side helper classes that encapsulate the XML and HTTP operations on the calling class;
  • server-side helper classes that encapsulate the XML operations on the target class;
We will also need various configuration files, in XML and other formats.
     The Web server supplied with the JWSDP is Tomcat 4; all the other supporting classes will be generated using a command-line tool called xrpcc supplied with the JWSDP.

Step 1: install the JWSDP

The Sun JWSDP contains the Tomcat Web server, a pile of supporting classes to do the XML operations, and tools to generate the network-level plumbing. It also contains the Ant build tool. In principle, JWSDP can be added to an existing Tomcat installation, or to the J2EE Reference Implementation. However, such topics are beyond the scope of this article. I will assume that you are working with a new and complete installation of JWSDP. It can be downloaded from the JavaSoft Web site. You may need to upgrade your basic Java installation; JWSDP works only with JVMs compatible with Sun JDK 1.3 or later. You should be able to get a recent JDK at the same place as you got the JWSDP itself. The download is a self-extracting installer; it should only be necessary to run it. On Solaris you will almost certainly need some OS patches as well; the installer will indicate which ones are necessary. On my Solaris 8 (October 2000 release) system I needed a patch for the Motif GUI.
     The installer will prompt for a username and password for the `manager' application; this will be required later to deploy the Web Service. I have assumed that the username and password are both `manager' for the sake of simplicity.
     By default the Tomcat Web server that gets installed with JWSDP listens for connections on port 8080; if you have other services on this port (e.g., another installation of Tomcat), change the port number in JWSDP_DIR/conf/server.xml, but remember to change it in the build configuration file as described below.
     The `bin' directory created by the installer contains a script or batch file to run Ant. This script sets the class search path to be appropriate for the build files that are likely to be required for Web Services. In particular, it enables the Tomcat hot deployment tasks. If you already have Ant, you may need to modify its classpath to get the build files in this example to work; alternatively, use the version of Ant that comes with the JWSDP by running it using its full path on the command line.

Step 2: get the source code for this tutorial

Space precludes a full listing of all the source files and configuration files in this article; if you want to try it for yourself you will need to get the source code package and unpack it into any handy directory.
     The files and directories included in the source package are as follows:
build.xml generic Ant build file; this contains no definitions that are particular to this example, and should work with other Web Services builds. All the project-specific definitions are in...
build.properties definitions of class and service names, classpath, etc., appropriate to this project and site
config.xml Web Service definition, used by the xrpcc tool to generate the stubs and skeletons (plumbing), and the Web Service configuration file that will be read by the server-side supporting classes.
web.xml Web application deployment descriptor; required by the Web server to configure the application. This maps URLs onto servlet classes, and indicates the location of the Web Service configuration file.
src/server source code for the implementation of the server-side target class
src/shared source code for the interface that describes the Web Service. This is required both by the client (which uses it), and the target class (which implements it). This is the only class that is required on both sides of the link, and forms the syntactic bridge between caller and target
src/testclient a simple, stand-alone test client for the service
build generated classes, WAR files, config files, etc., will go here
dist this directory will be used to store the JAR file that contains client-side supporting plumbing (stubs). In a practical implementation you may want to distribute this to systems that will be making calls on your service (hence the name `dist'). We will have more to say about this issue later.

Step 3: set up the build environment

Edit build.properties to reflect your working environment.
script-suffix this is used by the Ant build script to run the right version of xrpcc. Change it to `bat' on Windows systems.
username, password manager user name and password for the Tomcat Web server, assigned by the JWSDP installer. Set these to match whatever you told the installer. If these are wrong, or you did not select manager credentials, you won't be able to use the hot deployment feature of Tomcat
If you are working with a new, clean installation of JWSDP you should not need to change any other settings in this file. However, if you changed the Tomcat port number from the default (8080) you will need to reflect this change in build.properties.

Step 4: create and compile the interface

The interface forms the bridge between the Web Service and its clients. Creating a Web Service interface is very similar to creating a standard Java RMI interface: it must extend java.rmi.Remote, and all methods must be declared to throw RemoteException.
     In this simple example, our service is defined by the interface Interest, in package com.web_tomorrow.xmlrpctut. It exposes one method, getCompoundInterest. The full source is listed below; this needs to be in the directory SOURCE_ROOT/src/shared/com/web_tomorrow/xmlrpctut.
package com.web_tomorrow.xmlrpctut;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
Service interface for the `Interest' Web Service
(c)2002 Kevin Boone/Web-Tomorrow
*/
public interface Interest extends Remote 
{
public double getCompoundInterest
   (double principal, double interest, int terms) 
      throws RemoteException;
}
The interface is straightforward to compile, as it is plain, standard Java; there is no XML or RPC stuff in it. So, using command-line tools, and assuming that the current directory is SOURCE_ROOT:
javac -d build/shared/ src/shared/com/web_tomorrow/xmlrpctut/Interest.java
This creates the compiled class in build/shared/.... Alternatively, using Ant, do:
ant compile-shared
which should have the same effect.

Step 5: create and compile the implementation

The implementation class provides the `flesh' for the `bones' of the interface created in step 4. Again, it is standard Java; there is no XML or RPC stuff in it. The source code should be in SOURCE_ROOT/src/server/com/web_tomorrow/xmlrpctut/InterestImpl.java. Note that although this class is in a different directory from the interface generated in step 4, it is in the same Java package. Some developers like to specify different packages for the interfaces and implementations, but I don't like this. In any event, if it is in the same package, you can't call this class `Interest' as well, even though its source code is in a different directory to the interface.
package com.web_tomorrow.xmlrpctut;

/**
Implementation class for the Interest Web Service; must implement
the service interface

(c)2002 Kevin Boone/Web-Tomorrow
*/

public class InterestImpl implements Interest  
{
public double getCompoundInterest(double principal, 
    double interest, int terms) 
  {
  double total = principal;
  for (int i = 0; i < terms; i++)
    total = total * (1 + interest/100.0);
  return total - principal; 
  }
}
To compile this class, be aware that it refers to the interface Interest, which we compiled in step 4. The classpath seen by the Java compiler must allow this interface to be found. So:
javac -d build/server/ -classpath build/shared/ 
  src/server/com/web_tomorrow/xmlrpctut/InterestImpl.java 
(Please note that I have written this command on two separate lines so as not to upset your Web browser; in practise, of course, it is one long command.) This command creates the compiled class in build/shared/.... Alternatively, do
ant compile-server
which also compiles the interface if necessary.
     So we now have the interface and the implementation compiled; what we need now is to create the server-side plumbing for XML-RPC.

Step 5: create server-side plumbing

To create the plumbing (technically, the skeletons and Web Services configuration file), we use the xrpcc that comes with the JWSDP. This takes as its input a configuration file in XML. A suitable file is listed below:
<?xml version="1.0" encoding="UTF-8"?>
<configuration
   xmlns="http://java.sun.com/jax-rpc-ri/xrpcc-config">
   <rmi name="InterestService"
      targetNamespace="http://hello.org/wsdl"
      typeNamespace="http://hello.org/types">
      <service name="InterestService" 
          packageName="com.web_tomorrow.xmlrpctut">
      <interface name="com.web_tomorrow.xmlrpctut.Interest"
         servantName="com.web_tomorrow.xmlrpctut.InterestImpl"/>
      </service>
   </rmi>
</configuration>

This file is specific to the Sun JWSDP, and this job may be done differently in different products; what is important is that the interface definition is product-independent. In the XML file we specify the name of the service, and the Java package and classes that will form the interface and implementation.
     In this example, the configuration file is just called config.xml, so run xrpcc like this:
xrpcc.sh -server -d build/server/ -classpath build/shared/ config.xml 
On Windows systems, you need xrpcc.bat rather than xrpcc.sh. In any case, you can use Ant instead:
ant xrpcc-server
The `-d' switch to xrpcc tells the tool where to put its output files. These consist of compiled classes, and configuration files. In this case we have specified build/server, to align with the compiled implementation classes. In the end, we will be packaging all these classes into a WAR file for deployment, so it's convenient if they're in the same directory. xrpcc also generates two configuration files. InterestService.wsdl is a `Web Services description language' (WSDL) file. This is a standard format for describing Web Services in some sort of Services registry. We won't be using this in the current example. In any event, you would need to modify it to indicate the true URL of your Service (as it would be seen from the Internet). The other file, InterestService_Config.properties, is going to be used; the supporting classes on the Tomcat Web server will read this to determine which classes provide the implementation of the Service. Later this is going to have to be moved to a different directory, but we'll deal with this when the time comes.

Step 6: package the server side classes, plumbing and configs

The Tomcat Web server expects to receive applications in `WAR' format, as defined in the J2EE specifications. A WAR file is nothing more than a standard Java JAR archive with a name that ends in `.war', but it has a particular directory structure. For our Web Service we will need the WAR to look like this:
WEB-INF (directory)
  web.xml (file)
  InterestService_Config.properties (file)
  classes (directory)
    ... implementation, interface, and plumbing classes ...
That is, all the compiled classes must go in subdirectories of WEB-INF/classes. In principle, the .properties file does not need to go in WEB-INF, as its location is specified in web.xml (see below), but it's as good a place as any. Note that this file structure is a J2EE matter, not a Web Services matter. If your hosting server is not a J2EE-compliant Web server, the deployment process may be different; it may not even be in the form of WAR files. In addition, if you were creating your services using, say, a graphical development tool, the entire packaging and deployment process may be invisible.
     Anyway, working with command-line tools we have three jobs to do: create the web.xml file, copy the classes and config files to the right directories, package into a WAR file.

Step 6a: create web.xml

The format of this file is a Tomcat/J2EE matter, and a complete description is beyond the scope of this article. The important sections are as follows.
   <servlet> 
      <servlet-name>JAXRPCEndpoint</servlet-name> 
      <servlet-class>com.sun.xml.rpc.server.http.JAXRPCServlet</servlet-class> 
      <init-param> 
         <param-name>configuration.file</param-name> 
         <param-value>/WEB-INF/InterestService_Config.properties</param-value> 
      </init-param> 
   </servlet> 
   <servlet-mapping> 
      <servlet-name>JAXRPCEndpoint</servlet-name> 
      <url-pattern>/jaxrpc/*</url-pattern> 
   </servlet-mapping> 
The servlet section defines the name of a servlet (JAXRPCEndpoint) and a class that implements it. The name is arbitrary, and the class is provided with the JWSDP, so we don't need to know much about it. However, the configuration.file parameter is important: this references the configuration file that was created by xrpcc earlier, and this in turn tells the servlet which of your classes to invoke when a request is received from a client. The servlet-mapping section tells Tomcat which URL will be specified by clients for this service. Again, this is arbitrary, but must agree with the URL that the client will specify (we'll see how to tell the client about this later).

Step 6b: copy files and directories

We need to create the directory build/WEB-INF and copy in the config files and the classes in directories build/server and build/shared that were created in previous steps. This is an operating-system issue, and I will assume that you know how to copy files on your system. Alternatively, use Ant:
ant setup-web-inf

Step 6c: package the WAR file

Finally, we need to get all the server-side stuff into a WAR file. At the command-line, use the jar utility:
jar cf build/InterestService.war -C build WEB-INF
Alternatively, use Ant:
ant package
which does all the preceding steps as well if necessary.

Step 7: deploy the service

This is where Ant comes in really useful; we can define Ant tasks that reference the Tomcat hot deployment facilities. The alternative way to deploy is to copy the WAR file from step 6c into JWSDP_DIR/webapps and re-start the Tomcat server. I will assume that you will want to use the Ant method, which is much faster. First, make sure the server is running. Change to the directory JWSDP_DIR, and execute
./bin/startup.sh
(startup.bat for Windows systems). To test whether Tomcat is running, point your Web browser at
http://localhost:8080/
and you should get an welcome page. If not, you need to fix the problem before continuing; everything else is predicated on a working Tomcat.
     If Tomcat is running, you should be able to deploy by doing:
ant install
The `install' task carries out all the preceding steps as well, if necessary. Note that if you have already deployed, you will need to do
ant remove install
to undeploy the old version first.

Step 8: quick test of the deployment

Although we want to test the deployment properly using a test client, it is helpful at this stage to do a quick test, and prove that the packaging and deployment were successful. If this test fails, there is no point using a test client: it is bound to fail as well. Point your Web browser at:
http://localhost:8080/InterestService/jaxrpc
You should get an message of the form `a Web Service is installed at this URL...'. Any kind of exception represents a failure, and must be resolved. Common problems include badly-formed web.xml, and missing or badly-formed Web Service configuration file.

Step 9: generate client-side plumbing

We should now be in a position where we can peek at the Web Service using a Web browser, as described in step 8. However, what we really need is a way for other Java classes to call methods on it. Of course, we could code the Java to form the appropriate XML, and then issue it on the HTTP protocol, but this is a drag. What we really want is to hide all this stuff in helper classes (`stubs'), so we get a nice clean Java implementation of the client. Better still, we want the helper classes to be generated automatically. This is where xrpcc comes in (again):
xrpcc.sh -classpath build/shared/ -client -d build/client config.xml 
Note that we need the `classpath' option so that xrpcc can find the interface Interest; this is what will be used to build the client-side stub with the right method calls available. Using Ant, we can instead do:
ant xrpcc-client
This operation puts the generated classes in subdirectories of build/client; if you look at the generated files you will see classes with `SOAP' in the names; the significance of this will be described later.

Technical note: you will also see that one of the generated classes is called `InterestService.class'. This is a bit unfortunate in a way: generating a class with the same name as the service prevents us making the interface name the same as the service name, because we would get two different Interest classes: one from the interface and one from xrpcc. As we want to package up the interface with the generated classes later, this is a slight irritation. This is why I suggested earlier that the interface name should be different from the service name. In the JWSDP examples supplied by Sun, they get around this problem by giving the interface class an odd name (e.g., Service name `Hello', interface name `HelloIF'). I don't approve of this, as it isn't customary in Java to name classes according to the infrastructure roles they play. My preference is to use a standard class name for the interface (Interest, in this case), and suffix it with Service to get the service name.

To make life easy for the client developer, we want to produce a single JAR file that contains everything that the client needs to make method calls on the remote service. So this JAR should contain the contents of build/shared, and the contents of build/client. Note that the JAR will NOT contain the actual implementation of the service (from build/server); the implementation lives on the server and is invisible to clients, which only see the interface. Using Ant, we can build the client JAR like this:

ant jar-client
which also does the xrpcc step if necessary. This gives us the file InterestService.jar in the directory dist. This is the only thing a client needs to make method calls on the remote service, apart from the implementation of XML-RPC itself, which will be product-specific.

Step 10: test the service with a test client

There are three parts to this: writing the test client, compiling it, and running it.

Step 10a: writing the test client

As far as possible, the test client should contain only standard Java. Some XML-RPC stuff needs to be included, as the client needs to tell the RPC infrastructure where to find the remote service. I like to separate this part of the job from the `standard Java' stuff. For this example, here is a suitable test client:
import com.web_tomorrow.xmlrpctut.*;

public class InterestClient
{
/**
Invoke this test client with the service URL as an
argument
*/
public static void main(String[] args) throws Exception
  {
  /* 
  get the stub for the Web Service and set its endpoint URL 
  */

  Interest interest =
     (Interest)(new InterestService_Impl().getInterest());
   ((Interest_Stub)interest)._setProperty
     (javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, args[0]);

  /* 
  standard Java from this point on... 
  */
   System.out.println
    ("Interest on $100 at 10% per term for 10 terms is $" 
      + interest.getCompoundInterest(100.0, 10.0, 10));
  }
}
It is intended to be invoked with the URL of the service as a command-line argument. If you are familiar with EJB or RMI/IIOP development, you will notice that this code has similarities and differences with what you are used to. In these other technologies, the manipulation and configuration of the stub, although it has to be done, is usually wrapped up inside JNDI API calls, and is less ugly. At present, as far as I know, there is no JNDI implementation for Web Services stubs.
     It's also worth remembering that the configuration of the XML-RPC stub is defined by the JAX/RPC API, and should not be product specific; this client code should work on any XML-RPC implementation that follows the JAX specifications.

Step 10b: compiling the test client

To compile the test client we need the client JAR file created in step 9, as this provides the definitions of Interest, Interest_Stub, etc. However, the compiler also need access to the supporting classes that are provided with the JWSDP; the client JAR does not contain the complete XML-RPC implementation itself (this would make for a very large file to distribute). So:
javac -d build/testclient/ 
  -classpath dist/InterestService.jar:[jwsdp-classes] 
  src/testclient/InterestClient.java 
[jwsdp-classes] should reference the essential JAR files that come with JWSDP. In the present version of the product, the two important JARs are JWSDP_DIR/common/lib/jaxrpc-ri.jar and JWSDP_DIR/common/lib/jaxrpc-api.jar. In later versions of the product this may will change, and you may need to try adding JARs in the `lib' directory until all the `class not found' messages go away.
     To compile with Ant, just do:
ant compile-test-client

Step 10c: running the test client

To run the client, the class search path needs to include almost all the JAR files supplied with JWSDP, so I won't list them here (refer to build.properties). It will also be necessary to specify the Service URL on the command line. For this example, the Service URL is
http://localhost:8080/InterestService/jaxrpc/Interest
Don't forget to change the port number in build.properties if you did not use the default when you installed JWSDP.
     The simple way to run the test client is with Ant:
ant run
Note that this does not compile first; do the compile stage separately. All being well, the output should be:
Interest on $100 at 10% per term for 10 terms is $159.37

Development cycle

Assuming this all worked according to plan, what do you need to do if you change the implementation of the service and want to redeploy and test again? This is inevitable unless you are one of those remarkable people who can get your code to work first time. If you have changed the implementation, but not the interface, you can re-deploy and re-test very simply:
ant remove install
ant run
If you have changed the interface, then you need to recompile the client support classes as well (and you will probably have to modify the test client and recompile it):
ant remove install
ant jar-client
ant compile-test-client
ant run
It's as simple as that. This demonstrates the power of Ant: once the source tree and build files are set up, the edit-compile-deploy-test cycle become trivial.

So what have we achieved?

The operations described above may seem quite complicated but, as I hope to have demonstrated, the Java coding itself is perfectly straightforward -- it's just ordinary Java. In addition, with tools like Ant the code maintenance is straightforward as well. By doing this we have implemented a Web Service that can, in principle, be called by any client written in any programming language, using a protocol (HTTP) that is easy to secure and with a data format (XML) that everyone understands. For example, an open-source implementation of an XML-RPC implementation for C/C++ clients is available at SourceForge. This allows C and C++ clients to make calls on Web Services written in Java, for example, and vice-versa. There is even provisional support for implementing XML-RPC services in Microsoft's .NET environment, which suggests that it should be possible to write .NET clients in Java, or using non-Microsoft tools. This is a big step forward for interoperability.

How does it work?

Most of the `magic' takes place in the stubs and skeletons. When the client makes a call on the Interest interface, it is really making a call on the stub that implements that interface. Inside the stub is code that serializes the method call information into a SOAP document. SOAP is a widely-supported method for representing the structure of an object in XML. In this example, the SOAP that is generated looks like this:
<env:Envelope ...>
  <env:Body>
    <ns0:getCompoundInterest>
      <double_1 xsi:type="xsd:double">100.0</double_1>
      <double_2 xsi:type="xsd:double">10.0< /double_2>
      <int_3 xsi:type="xsd:int">10</int_3>
    </ns0:getCompoundInterest>
  </env:Body>
</env:Envelope>
This document specifies that the method call is to be made on a method called getCompoundInterest, and that there are three parameters: two doubles and an integer. The values of these parameters are specified in text format in the XML. Notice that we aren't specifying the object or service that the method call is directed to; this was specified by the URL that the client used to reach the service in the first place.
     The stub and supporting code then establish an HTTP connection to the URL, and issue the SOAP document as a request. The servlet on the server reads the XML, parses it, then uses the data extracted to make the method call specified. It knows which Java object to call, because we specified this in the .properties file that got deployed in the WAR. This method (we hope) completes successfully and returns a value. The servlet then serializes this value into another SOAP document, which looks (in this case) like this:
<env:Envelope ...>
  <env:Body>
    <ns0:getCompoundInterestResponse>
      <result xsi:type="xsd:double">159.37424601000026</result>
    </ns0:getCompoundInterestResponse>
  </env:Body>
</env:Envelope>
This says that there is one return element of type double, and gives its value. The client then receives this XML in the response it obtains from the servlet. The stub and its supporting classes parse the XML, extract the value, and pass it back to the client, completing the method call.

Going forward

If you have followed and understood the preceeding material, you may care to give some thought to the following questions:
  • How could we pass objects as parameters rather than primitives?
  • Could we pass objects by reference? This is effectively what happens in standard Java programming. The problem here is that if the target receives a reference, it should be able to make calls on it, and if these calls change the state of the object, the state change should occur on the referenced object, not on a copy of it. When we use serializable (in SOAP or anything else), we tend to pass copies of the objects around; how will this fit with passing references?
  • How could we restrict which clients are allowed access to which Services?
  • How do we encrypt the communication itself? And perhaps the biggest issue of all...
  • Is the Web Service stateful or stateless? What happens if the client makes a method call that changes its state? To enable this feature requires that clients are able to create private instances of the Web service, otherwise all clients would share the same state. How can the client create new private instances of the service?
There are solutions to all these problems but that's a job for another day...