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.
|
|
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:
|
|
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!
|
|
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!
|
JSR-101 |
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.
|
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):
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.
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:
java.rmi.Remote.java.rmi.RemoteException but can throw other business exceptions as well.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:
configuration_options are from Table 2.configurationfile.xml is any configuration file as described below.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 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.
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.
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.