One of the foundations of Web services is interoperability. This means that Web services send and receive messages in a standard format. Typically, that format is SOAP. There are many ways to send SOAP messages.
The most basic is to do printlns to a URL stream, but this requires you to know far too much about the SOAP protocol and the SOAP messages that a service expects and sends. There's no easy way to get this information from the service, and this method is not very scalable.
A step up is to use the SAAJ APIs (SOAP with Attachments API for Java), which were described in a previous tip on developerWorks. The SAAJ APIs give you a means to manipulate SOAP constructs at a bit more abstract level, but they still share many of the problems of printlns to a URL stream.
A much friendlier approach is to use a system that pulls an application to a more abstract level above the SOAP messaging protocol. The benefits of such a system include:
- Application programmers can concentrate on their application logic rather than on the rigors of SOAP
- Messaging schemes other than SOAP can be used, and the application code will change very little, if at all
The Java APIs for XML-based RPC (JAX-RPC) provide such an abstract approach.
JAX-RPC and the Barnes & Noble Web service
Instead of relying on SOAP, JAX-RPC relies on the Web Services Description Language (WSDL). WSDL defines the API to a Web service in a declarative, standardized manner. WSDL may define a SOAP binding for a service, but it also defines a more abstract description of the service at a level above the SOAP messaging layer. (See Resources for more details on WSDL.)
The examples in the SAAJ article mentioned above used the Barnes & Noble Web service at www.xmethods.net. The XMethods site also provides the WSDL for this Web service (see Resources). That WSDL is used in these examples -- see Listing 1.
Listing 1. XMethods' Barnes & Noble WSDL
<?xml version="1.0" ?>
<definitions name="BNQuoteService"
targetNamespace="http://www.xmethods.net/sd/BNQuoteService.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://www.xmethods.net/sd/BNQuoteService.wsdl">
<message name="getPriceRequest">
<part name="isbn" type="xsd:string" />
</message>
<message name="getPriceResponse">
<part name="return" type="xsd:float" />
</message>
<portType name="BNQuotePortType">
<operation name="getPrice">
<input message="tns:getPriceRequest" name="getPrice" />
<output message="tns:getPriceResponse" name="getPriceResponse" />
</operation>
</portType>
<binding name="BNQuoteBinding" type="tns:BNQuotePortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="getPrice">
<soap:operation soapAction="" />
<input name="getPrice">
<soap:body
use="encoded"
namespace="urn:xmethods-BNPriceCheck"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</input>
<output name="getPriceResponse">
<soap:body
use="encoded"
namespace="urn:xmethods-BNPriceCheck"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</output>
</operation>
</binding>
<service name="BNQuoteService">
<documentation>Returns price of a book at BN.com given an
ISBN number</documentation>
<port name="BNQuotePort" binding="tns:BNQuoteBinding">
<soap:address location=
"http://services.xmethods.net:80/soap/servlet/rpcrouter" />
</port>
</service>
</definitions> |
In any JAX-RPC application, you must do three things:
- Instantiate a
Serviceclass, which is the client-side representation of a Web service - Instantiate a proxy to the Web service's application (and possibly set up that proxy)
- Call a Web service's application's operation
A JAX-RPC DII client application
The SAAJ APIs do not depend on WSDL -- only SOAP -- but you have to know exactly what the SOAP messages for the service look like. JAX-RPC defines a Dynamic Invocation Interface (DII) API to access a Web service that, strictly speaking, also does not depend on WSDL (although you must be aware of some of the information that is captured in the WSDL). However, you no longer need to know the details of the SOAP message. The three steps specific to a DII JAX-RPC application are:
- Instantiate a DII
Serviceclass with no WSDL - Instantiate a DII
Callobject proxy and set it up - Call the
Callobject'sinvokemethod
Instantiate a DII Service class with no WSDL
This class is not aware of WSDL, but it must have a name. Use the name of the service from the WSDL.
Listing 2. Instantiate a DII Service class
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
Instantiate a DII Call object proxy and set it up
The DII proxy object implements the javax.xml.rpc.Call interface. You get an implementation of the Call interface from the service you just created.
Listing 3. Instantiate a Call object
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
The Call object you now have is not yet usable. It doesn't know anything about the operation. Provide the the following data to the object:
- The operation name:
getPrice - The input parameter: a string
- The return type: a float
- Binding information: rpc style; encoding style
- The endpoint:
http://services.xmethods.net:80/soap/servlet/rpcrouter
Listing 4. Populate the Call object
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
// Next, build up the information about the operation...
// The operation name
QName operationName = new QName(
"urn:xmethods-BNPriceCheck",
"getPrice");
call.setOperationName(operationName);
// The input parameter
call.addParameter(
"isbn", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode
// The return
call.setReturnType(XMLType.XSD_FLOAT);
// The operation is an RPC-style operation.
call.setProperty(
Call.OPERATION_STYLE_PROPERTY,
"rpc");
// The encoding style property value comes from the
// binding's operation's input clauses encodingStyle
// attribute. Note that, in this case, this call is not
// really necessary - the value we're setting this
// property to is the default.
call.setProperty(
Call.ENCODINGSTYLE_URI_PROPERTY,
"http://schemas.xmlsoap.org/soap/encoding/");
// The target endpoint
call.setTargetEndpointAddress(
"http://services.xmethods.net:80/soap/servlet/rpcrouter");
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
Call the Call object's invoke method
Finally, you are now ready to invoke the getPrice operation.
Listing 5. Invoke the getPrice operation
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
// Next, build up the information about the operation...
// The operation name
QName operationName = new QName(
"urn:xmethods-BNPriceCheck",
"getPrice");
call.setOperationName(operationName);
// The input parameter
call.addParameter(
"isbn", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode
// The return
call.setReturnType(XMLType.XSD_FLOAT);
// The operation is an RPC-style operation.
call.setProperty(
Call.OPERATION_STYLE_PROPERTY,
"rpc");
// The encoding style property value comes from the
// binding's operation's input clauses encodingStyle
// attribute. Note that, in this case, this call is not
// really necessary - the value we're setting this
// property to is the default.
call.setProperty(
Call.ENCODINGSTYLE_URI_PROPERTY,
"http://schemas.xmlsoap.org/soap/encoding/");
// The target endpoint
call.setTargetEndpointAddress(
"http://services.xmethods.net:80/soap/servlet/rpcrouter");
// Invoke the operation
Object[] actualArgs = {"0672324229"};
Float price = (Float) call.invoke(actualArgs);
System.out.println("price = " + price);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
When you run java DIITip, you get the price: 44.99.
If you compare this to the code from the SAAJ article, you can see that you have moved a good distance away from knowledge of the SOAP protocol. The only SOAP-related information you needed was the operation style and the encoding style.
The DII programming model, as the name suggests, is designed to work with a dynamic service. If the service changes a bit it doesn't take much to change the client code to match, and if you're really clever, your client code can be so dynamic that it doesn't have to change at all.
This model is still rather complex, particularly where you have to populate the Call object. The more complex the operation, the more complex the information that you need to provide to the Call object. So if you know that the service is static and won't change, then you could climb one more level up the abstraction food chain.
A JAX-RPC SEI client application
This next level of abstraction requires a bit more setup, but the resulting client code is much simpler. You start with the WSDL. JAX-RPC implementations provide a WSDL-to-Java code generator which generates, among other things, a Service Endpoint Interface (SEI). This is the client-side Java interface to the Web service's application. Listing 6 shows an SEI for the XMethods Barnes & Noble application.
Listing 6. Barnes & Noble SEI
package xmethods.bn;
public interface BNQuotePortType extends java.rmi.Remote {
public float getPrice(java.lang.String isbn)
throws java.rmi.RemoteException;
} |
In the WSDL-to-Java tool that I used, I mapped the targetNamespace of http://www.xmethods.net/sd/BNQuoteService.wsdl to the Java package xmethods.bn, so that is the package in which your SEI was generated. The name of the SEI is derived from the WSDL's portType name: BNQuotePortType. The getPrice method is derived from the portType's getPrice operation and the messages and types it refers to. If you run your favorite WSDL-to-Java tool, you'll see that there are other classes generated into the xmethods.bn package. You must compile them all, but your client application need only know about the SEI.
To call the Web service using this SEI, do essentially the same three steps that you did for DII:
- Instantiate a
Serviceclass with the WSDL - Instantiate a proxy
- Call the proxy's operation
Step 1 for SEI and DII are similar. Steps 2 and 3 are simplified, particularly Step 2.
Instantiate a Service class with the WSDL
The only difference between this step in the SEI model and the DII model is that here you provide one extra bit of information: the WSDL.
Listing 7. Instantiate a Service class for the SEI
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
Once you've got the service, you must find an implementation of the SEI. This implementation is a proxy to the real application. All the information about how to access that application is hidden within the implementation -- it's gathered from the WSDL's service port -- so once you've got the proxy, you don't have to do any setup; it's been done for you.
Listing 8. Instantiate an implementation for the SEI
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import xmethods.bn.BNQuotePortType;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
// Get an implementation for the SEI for the given port
QName portName = new QName("", "BNQuotePort");
BNQuotePortType quote = (BNQuotePortType) service.getPort(
portName,
BNQuotePortType.class);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
Finally, calling the operation couldn't be simpler:
Listing 9. Call an operation on the SEI
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import xmethods.bn.BNQuotePortType;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
// Get an implementation for the SEI for the given port
QName portName = new QName("", "BNQuotePort");
BNQuotePortType quote = (BNQuotePortType) service.getPort(
portName,
BNQuotePortType.class);
// Invoke the operation
float price = quote.getPrice("0672324229");
System.out.println("price = " + price);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} |
Of the many ways to call a Web service, the most basic is to manually generate and send SOAP messages. You've got to know far too much about the service and the SOAP protocol -- not very useful. The next step up is to use SAAJ APIs. You still have to know a lot about the service and the SOAP protocol -- still not particularly useful. The next step up from there is to use JAX-RPC DII. This moves you away from SOAP, but the code is still rather complicated. The best approach, typically, is to generate an SEI from the service's WSDL and call the SEI proxy.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for this article | x-tipjaxrpc.zip | 2KB | HTTP |
Information about download methods
- The Web Services Description Language (WSDL) 1.1 is the specification of WSDL.
- Learn more about WSDL in the developerWorks 2-part series Deploying Web services with WSDL (Bilal Siddiqui, November 2001 and April 2002).
- Get another angle on WSDL with this tutorial from W3Schools.
- Here's a little more information on the Barnes & Noble Web service. You can also download the Barnes & Noble WSDL.
-
IBM WebSphere SDK for Web Services (WSDK) is an integrated kit for creating, discovering, invoking, and testing Web services. From this link you can download WSDK 5.0.1 and a number of tutorials.
-
Java API for XML-Based RPC (JAX-RPC) Downloads & Specifications provides links to the JAX-RPC 1.0 specification itself, as well as javadocs, class files, and Sun's JAX-RPC reference implementation.
-
AXIS is the Apache Software Foundation's open source SOAP engine which implements JAX-RPC 1.0.
- Find more XML resources on the developerWorks
XML zone. For a complete list of XML tips to date, check out the tips summary page.
- Find more Web services resources on the developerWorks Web services zone.
Russell Butek is one of the developers of IBM's WebSphere Web services engine. He is also IBM's representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of IBM's CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force.
Comments (Undergoing maintenance)





