Tip: Send and receive SOAP messages with JAX-RPC

Java APIs for XML-based RPC offers a friendlier approach to messaging

In this tip, IBM developer Russell Butek takes a look at JAX-RPC, a Java API that allows an application to communicate with a Web service without knowing details about the SOAP messaging protocol.

Russell Butek (butek@us.ibm.com), Developer, EMC

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.



02 September 2003

Also available in Japanese

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:

  1. Instantiate a Service class, which is the client-side representation of a Web service
  2. Instantiate a proxy to the Web service's application (and possibly set up that proxy)
  3. 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:

  1. Instantiate a DII Service class with no WSDL
  2. Instantiate a DII Call object proxy and set it up
  3. Call the Call object's invoke method

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:

  1. Instantiate a Service class with the WSDL
  2. Instantiate a proxy
  3. 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();
        }
    }
}

Instantiate a proxy

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();
        }
    }
}

Call the proxy's operation

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();
        }
    }
}

Summary

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.


Download

DescriptionNameSize
Source code for this articlex-tipjaxrpc.zip2KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, SOA and web services
ArticleID=11835
ArticleTitle=Tip: Send and receive SOAP messages with JAX-RPC
publish-date=09022003