ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Hangin with the JAX Pack, Part 4: JAX-RPC

by Al Saganich
04/24/2002

In my prior articles, I've introduced and discussed various aspects of the JAX Pack, a set of APIs for working with Web services and the underlying supporting protocols. In this article, we'll look at JAX-RPC, a specification for making remote procedure calls via XML and SOAP over HTTP. Specifically we'll look at the client side of JAX-RPC, as it shows the most promise.

Everyone has heard the old adage "the more things change, the more they stay the same." Well, with JAX-RPC (the Java API for XML-based RPC), the adage comes true once more. JAX-RPC, while a cool way to expose Web services' functionality to Java as if they were local calls, is really just another instance of Remote Method Invocation (RMI).

For those unfamilar with RMI, a quick review is in order. Figure 1 shows a traditional development model, where client applications access an API to perform some function. Nothing unusual happens; the application is compiled against the API and at runtme, the OS directs the client call to the appropriate library and it's handled normally.


Diagram
Figure 1. Traditional Local Model.

Figure 2 shows the RMI model, where the client still makes what appears to be a local call. Actually, the call is handled by a stub that looks like the API call, but in fact the stub redirects the call to a remote server that provides the service. The call is then made to a remote skeleton that makes the call on the remote object. The result of the call is then returned in a similar fashion.

Specifically, an RMI call works as follows:

  1. The client calls the stub, which was provided at compile time as if it were a normal library.
  2. The stub redirects the call to the appropriate server.
  3. The server, which was waiting for such events, catches the call and redirects it to a framework.
  4. The framework, which wraps the actual impementation of the service, then calls the service on behalf of the client.
  5. The framework returns the call to the server.
  6. The service, in turn, returns the information to the originating client stub.
  7. Finally, the client stub returns the information to the client application as if it were an everyday method call.

Diagram
Figure 2. RMI Model.

JAX-RPC, for all practical purposes, extends this model to Web services and, as we will see shortly, allows Java applications to call Web services as if they were local calls. In fact, JAX-RPC calls look very much like their RMI cousins. As shown in Figure 3, the JAX-RPC model is identical to the RPC model shown in Figure 2, except for the introduction of Web services. Of course, there is a single, incredibly important difference between JAX-RPC and historic RMI -- JAX-RPC can be used to access non-Java Web services!

Diagram.
Figure 3- JAX-RPC Model.

JAX-RPC provides support for three different services:

From a client perspective, JAX-RPC provides two specific mechanisms for invoking Web services:

From a building blocks perspective, JAX-RPC provides two specific mechanisms for binding functionality to Web services:

In a nutshell, JAX-RPC provides the ability to generate WSDL from a Java interface and a Java interface from WSDL -- and, of course, the ability to generate the client-side stubs!

JAX-RPC

JSR-101
Current specification status: Current Version v0.8 (proposed final draft)
Expected release: March 5, 2002. (Although date has passed, currently not released)

The spring release of the Java XML Pack contains Early Access Release 2 of JAX-RPC. While the JAX-RPC specification has not completed its review, you can download an early access edition. Additionally, BEA's WebLogic Server 7.0 beta includes support for JAX-RPC.

Working with Clients

Let's look at a concrete example of how we might go about coding a JAX-RPC client. There are three steps in creating a JAX-RPC client (assuming we already have the client jar containing the stubs):

  1. Obtain an instance of the interface stub.
  2. Set the endpoint property of the stub to point to the service endpoint of the Web service.
  3. Call the method.

Let's look at an actual example, modified from the Web services tutorial provided by Sun.


Listing 1: HelloClient.java

00 public class HelloClient {
01    public static void main(String[] args) {
02        try {
03            HelloIF_stub stub =
04                (HelloIF_stub)(new HelloWorld_Impl().getHelloIF() );
05            stub._setProperty( javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,args[0] );
06            System.out.println(stub.sayHello("Duke!"));
07        } catch (Exception ex) {
08            ex.printStackTrace();
09        }
10    }    
11 }

The structure is actually quite simple, but bears some explanation. Every Web service exposed for us with JAX-RPC contains both an interface definition and an implementation. Lines 3 and 4 represent obtaining an instance of a stub and casting that stub to the correct format. Since the implementation of the service might contain many different interfaces, we need to specify which interface to use (HelloIF_stub, in this case).

Breaking down lines 3 and 4, we see that first we obtain a local instance of the HelloWorld_Impl class, which has, in addition to its inherited methods, a method for returning the stub class we require. Implementation classes simply extend the javax.xml.rpc.Service and behave in a fashion similar to home interfaces in EJB. We then cast the class to the appropriate interface it represents.

We now have a stub, but it's not linked to the provider service. Line 5 inserts a property that represents the service endpoint that the stub should use to perform the remote call. An example of a service endpoint might be http://somehost.somewhere.com:80/web-service-family/specific-service. Line 6 then shows how we might call the method defined on the interface represented by the stub.

Listing 1 unfortunately generates more questions then it answers. Where did HelloIF_stub come from? What about the implementation? Where did the service endpoint information come from? Examining the anatomy of a JAX-RPC service should clear up many of these questions.

Anatomy of a JAX-RPC Service

In order to answer some of the questions raised by the client code above, we need to understand a little bit more about how a JAX-RPC service is created. A number of files come into play that define, implement, and control the creation of a JAX-RPC-based Web service.

Listing 2 shows an example of the interface that describes the methods that can be called on the Hello class. The interface is relatively straightforward and follows traditional RMI rules, extending java.rmi.Remote, with all methods throwing java.rmi.RemoteException. The interface, however, becomes very interesting when we use it with the XML RPC compiler xrpcc. The XML RPC compiler takes an interface and then can generate the client stubs, the server-side skeletons, or both. Additionally, xrpcc can generate a set of WSDL for the provided interface. Before we move on, let's remember the RMI coding rules.

Specifically, the interface definition must follow RMI rules:

  1. The interface must extend java.rmi.Remote.
  2. All methods must throw java.rmi.RemoteException but can throw other business exceptions as well.
  3. No method can be declared static or private.
  4. Method parameters must be in the defined supported set, shown in Table 1 below.

Table 1: Supported JAX-RPC types

Primitive Types boolean, byte, double, float, int, and long
java.lang.String
java.lang.Boolean
java.lang.Byte
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.BigDecimal
java.lang.BigInteger
java.lang.Calendar
java.lang.Date


Arrays of supported types are allowed, as well as Java beans composed of supported types, and arrays of supported types. Custom types beyond those listed here can also be supported via custom marshal and unmarshal code. Custom-marshalling code is beyond the scope of this article. Developers interested in custom marshalling are referred to the JAX-RPC specification.

Examining Listing 2 against the rules above shows that it is a value RMI interface definition.


Listing 2: HelloIF.java

import java.rmi.Remote; 
import java.rmi.RemoteException; 
public interface HelloIF extends Remote { 
    public String sayHello(String s) throws RemoteException;    
}

Listing 3 shows much of the implementation of the interface shown in Listing 2. Details of how the actual methods are implemented aren't important to this discussion; however, one important point to notice is that the HelloImpl class must implement the HelloIF interface.


Listing 3: HelloImpl.java

package hello;
public class HelloImpl implements HelloIF {
...
    public String sayHello(String s) {
        // whatever is appropriate
    }
}

The syntax of the xrpcc is:

xrpcc[.bat | .sh] configuration_options 
  configurationfile.xml

Where:


Table 2: xrpcc Options

Option Description
-client Generate client artifacts (stubs, etc.)
-server Generate server artifacts (ties, etc.)
-both Generate both client and server artifacts
-d [clientdir] Specify where to place generated client output files.
-server Generate server artifacts (ties, WSDL, etc.)
-s [serverdir] Specify where to place generated server files.
-keepgenerated Retain the generated files.

When using xrpcc, you must specificy -client, -server, or -both. Also note that there are a number of other options; enter the xrpcc command without any parameters for a complete list.

Note that, as of this writing, the configuration of the various early-access software editions for the JAX Pack has changed several times. The download of JAX-RPC Early Access version 2 contains a complete description of how to install and configure xrpcc. See the Early Access documentation, packaged with the download, for installation and configuration information.

xrpcc works by reading a configuration file and then generating the service specified by the configuration. Of course, configurations are defined in XML. Listing 4 shows a sample config.xml file, which defines a service that builds off of an RMI interface and the provided implementation.


Listing 4: config.xml

00 <?xml version="1.0" encoding="UTF-8"?>
01 <configuration
02    xmlns="http://java.sun.com/jax-rpc-ri/xrpcc-config">
03   <rmi name="HelloWorldService"
04      targetNamespace="http://hello.org/wsdl"
05      typeNamespace="http://hello.org/types">
06      <service name="HelloWorld" packageName="hello">
07      <interface name="hello.HelloIF"
08         servantName="hello.HelloImpl"/>
09      </service>
10   </rmi>
11 </configuration>

The configuration file is supplied to xrpcc at compile time and then used to build the stubs and skeletons (called ties in JAX-RPC) based on the name of the service, as well as various interface and implementation stanzas. Line 3 shows the model name and is arbitrary; line 6 defines the service name and its associated package. For an unknown reason, you must place your class files in a package. Line 7 shows the fully qualified path to the implementation of the interface. Likewise, line 8 defines the name of the class that implements the interface.

Generating Clients From Existing Web Services

Up to now, xrpcc has been fairly generic and straightforward. We've been able to develop our own Web services and the call them via JAX-RPC. However, suppose you would rather access a Web service developed by someone else, one where you have access to the WSDL and the service at run time?

xrpcc allows you to do something very useful: develop the client-side stubs, etc., from the WSDL. For example, let's assume that you know a Web service exists and that its WSDL is available from http://localhost:7001/webservices/statelessservices/instantgratification.wsdl.

We could create a configuration file for xrpcc that looks like Listing 5 and use it to generate the actual client stubs required to access the Web service, regardless of how the Web service was implemented.


Listing 5: wsdltoclient.xml

00 <?xml version="1.0" encoding="UTF-8"?>
01 <configuration 
02    xmlns="http://java.sun.com/jax-rpc-ri/xrpcc-config">   
03    <wsdl name="WebServices"     
04          location="<http://localhost:7001/webservices/statelessservices/instantgratification.wsdl"
05          packageName="PackageForGeneratedClasses">                    
06    </wsdl>      
07 </configuration>

Assuming that the actual WSDL described on line 4 existed, you might enter a command such as:

xrpcc -keepgenerated -client   -d=clientclasses wsdltoclient.xm

which would generate the client code for the actual Web service in a package based on the package listed in line 5. You could have done the same thing using -server and generated skeleton classes that could be implemented to provide the service, as well. Note that I showed the use of the -keepgenerated option because I'm curious about what code will actually be produced. A very powerful tool!

A note to the wise: I tried generating code from a number of WSDL samples I had available. Some worked and some resulted in very cryptic error messages, or none at all. Given that it's an early access release, xrpcc works very well; but it still has some ways to go.

Conclusion

In this article, we saw the JAX-RPC specification from the perspective of a client. We discussed the architecture of Remote Procedure Calls and the tools JAX-RPC and the Early Access implementation provide. We looked at a variety of issues and saw the ease with which we could develop JAX-RPC client. It should be noted that I've specifically avoided discussion of the server side of JAX-RPC. This isn't an accident, by any means -- so many different mechanisms exist for developing Web services, of which JAX-RPC is only one, that JAX-RPC by itself pales somewhat when viewed as a way to develop the implementation of a service. JAX-RPC shines, not because it allows us to generate server-side skeletons, but because it allows us to develop client-side code quickly, easily, and with a minumum of fuss and bother.

Al Saganich is BEA Systems' senior developer and engineer for enterprise Java technologies, focused on Java integration and application with XML and Web services.

Previously in this series:

Hangin' with the JAX Pack, Part 3: Registries -- In Part 3 of our JAX Pack series, Al Saganich looks at JAXR, the Java API for XML Registries.

Hangin' with the JAX Pack, Part 1 -- In this three-part series, BEA Systems' Al Saginach takes a look at the JAX Pack, JAVA APIs for providing XML-based Web services handling XML. This week Al looks at JAXP (for XML processing) and JAXB (for XML binding). Next week: XML messaging with JAXM.

Hangin' with the JAX Pack, Part 2: JAXM -- Al Saganich examines JAXM, the Java API for XML Messaging, and shows how it provides support for accessing various messaging formats.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.