This is a simple example that will show you how to develop a Web Service client and service starting from a Java remote interface (RMI). The development process can roughly be divided into the following steps:
Write the remote Java interface for the service.
Compile the interface.
Create stubs and skeletons using the rmi2soapcompiler.
Implement the client.
Implement the server.
Deploy the server in a servlet container.
Run the client against the server.
The build process is conveniently represented in the ANT build.xml
files, which are included with each example. We describe the commands required for
each step below and if you are not using the ANT file you need to setup your
environment as stated in the README.
You can use the env.bat
, env.sh
, or env.csh
file to easily set up your build environment.
Below is the RMI for the Hello World application. The Hello interface has
one method, sayHello
that takes no parameters and returns a string.
The RMI serves as a contract between the object implementation and its clients.
package hello; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; }
We'll use the target directory WEB-INF/classes
, since this directory
structure is necessary when creating the servlet WAR file later. If the directory
doesn't already exist, use the following commands to create it:
mkdir WEB-INF
cd WEB-INF
mkdir classes
Then compile the interface using the familiar javac compiler. On UNIX systems:
javac -d WEB-INF/classes Hello.java
On Windows systems:
javac -d WEB-INF\classes Hello.java
Before generating the SOAP bindings, you must have the classes directory
available in the CLASSPATH
. Set your CLASSPATH
as
follows (depending on operating system and shell):
set CLASSPATH=WEB-INF/classes:$CLASSPATH
Also, ensure that the Novell exteNd WSSDK binaries are in your PATH
.
Adapt the below examples to fit the location where you installed Novell exteNd WSSDK
on your system. On Unix (bash):
export PATH=/usr/local/wssdk/bin/solaris;$PATH
On Windows:
set PATH=C:\wssdk\bin\win32;%PATH%
Finally, you must ensure that the Servlet library is in your CLASSPATH
.
If you're using Tomcat you might do:
set CLASSPATH=%CLASSPATH%;C:\jakarta-tomcat-4.0.1\lib\servlet.jar
Run the rmi2soap
compiler on the Hello.class
file to generate the SOAP bindings from
the above Java remote interface.
rmi2soap -d WEB-INF/classes hello.Hello
The rmi2soap compiler generates the Java code to the
WEB-INF/classes
directory,
and compiles the generated Java code to the same directory. The following
classes are created in the hello Java package:
Hello_Stub.class | // stub |
_Hello_ServiceSkeleton.class | // inheritance based skeleton |
_Hello_ServiceTieSkeleton.class | // delegation based skeleton |
HelloService.class | // service interface |
HelloServiceImpl.class | // service implementation class |
You can run rmi2soap with the -keep flag if you wish to keep the source files for the stubs and skeletons. Please consult the rmi2soap man page for a detailed description of the command line flags.
The client implementation uses JNDI to lookup the service object. The service can in turn be used to create a stub for the remote service. The source for the client is shown below:
package hello; import javax.xml.rpc.Stub; import javax.naming.InitialContext; public class Client { public static void main(String[] args) throws Exception { | // get the initial context | InitialContext ctx = new InitialContext(); | | // lookup the service for Hello | HelloService svc = (HelloService) | ctx.lookup("xmlrpc:soap:hello.HelloService"); | | // get the hello stub | Hello hello = (Hello) svc.getHelloPort(); | | // set the end point address | ((Stub)hello)._setProperty("javax.xml.rpc.service.endpoint.address", | args.length > 0 ? args[0] :"http://localhost:9090/hello"); | | // invoke the service | System.out.println(hello.sayHello()); } }
The preferred way to set an end point address on the stub is to use the
javax.xml.rpc.service.endpoint.address
property.
Once you're done you can compile the client part of your Web Service:
javac -d WEB-INF/classes Client.java
The server implementation is much as if you were implementing any other
Java interface. The server implementation class must extend the
_Hello_Skeleton
class. In this case the server is very simple
as shown below:
package hello; import java.rmi.Remote; import java.rmi.RemoteException; public class HelloImpl extends Hello_ServiceSkeleton { public String sayHello() throws RemoteException { | return "Hello World!"; } }
Once you're done you can compile the server part of your Web Service:
javac -d WEB-INF/classes HelloImpl.java
You can now create a WAR file and deploy the server
in your favorite servlet container. Before creating the WAR you need to
supply a standard web.xml
file, which must be located in the
WEB-INF
directory.
Please consult the one in the hello directory for an example. Then create
the WAR as follows:
jar -cfM services.war WEB-INF
If you're using Tomcat, you can copy the services.war
file into
the Tomcat webapps
directory and restart Tomcat. Then you're ready
to run the client and test the Hello World application.
As an alternative to using a servlet container, you can use the simple Web Server that ships with the Novell exteNd WSSDK. You can start jwebserv like this:
jwebserv -verbose services.war
For a full description of how to use the Novell exteNd WSSDK Server, please look here.
Running the client is now straightforward. If you're using jwebserv you can run the client without any arguments, as http://localhost:9090/hello is the default binding. Below is the command line to run against Tomcat:
java hello.Client http://localhost:8080/services/hello
The client program will print "Hello World!".
You can now use the tunnel tool to inspect what goes over the wire. Run the tunnel on port 9999 like this:
tcptunnel 9999 localhost 9090
In the above command line 9999 is the tunnel port and 9090 is the port where your Web Service is running (note the default is 8080 for Tomcat). After running this command you should see a window with two panels.
You now need to run the client again through the tunnel:
java hello.Client http://localhost:9999/hello
What goes over the HTTP connection to the servlet container is the following SOAP message:
<Envelope
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/1999/XMLSchema' xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'> <Body> <ns1:sayHello SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:ns1='http://www.hello'> </ns1:sayHello> </Body> </Envelope> |
Figure 1: The SOAP message going from client to server in Hello World.
And below is the SOAP response message coming back from the server:
<Envelope
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/1999/XMLSchema' xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'> <Body> <ns1:sayHelloResponse SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:ns1='http://www.hello'> <result xsi:type='xsd:string'>Hello World!</result> </ns1:sayHelloResponse> </Body> </Envelope> |
Figure 2: The SOAP message going from server back to client in Hello World.
Copyright © 2003, 2004 Novell, Inc. All rights reserved. Copyright © 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.