Chapter 12. JAX-RPC Client APIs

JAX-RPC defines three programming models that Java applications can use to access Web services: generated stubs, dynamic proxies, and DII. Of these, the one you're most likely to use is generated stubs. Dynamic proxies and DII are interesting but less practical for most development efforts. That said, this chapter pays a great deal of attention to all three programming models so you can become familiar with each, and apply them as you see fit.

Generated Stubs

The purpose of the generated stub programming model is to provide object façades for accessing Web service endpoints. In other words, generated stubs make Web service endpoints look like local Java objects. A generated stub is created by a utility called a JAX-RPC compiler. J2EE vendors provide their own JAX-RPC compilers, some as command-line utilities, some built into GUI deployment tools. Regardless of the interface they use, all JAX-RPC compilers do essentially the same thing: They read a WSDL document and generate from it at least two Java class files, an endpoint interface and a generated stub class. The endpoint interface extends java.rmi.Remote and defines the methods that can be invoked on the Web service endpoint. The generated stub class implements the endpoint interface. At runtime an instance of the generated stub class converts each method invocation made on the remote interface into a SOAP 1.1 message, and sends it to the Web service endpoint. The generated stub also converts each SOAP reply message into a method return value, or throws an exception if the reply message is a SOAP fault.

For example, you could use a generated stub to invoke operations on either of the BookQuote endpoints that you developed in Chapters 10 and 11. The endpoint interface would model the BookQuote endpoint by defining a single method, getBookPrice(), which accepts a String parameter containing a book's ISBN, and returns the wholesale price as a float type. The generated stub is responsible for sending SOAP messages to the BookQuote endpoint and processing SOAP reply messages sent back from the endpoint. Figure 12-1 illustrates.

A Generated Stub Communicates with a Web Service

Figure 12-1. A Generated Stub Communicates with a Web Service

The endpoint interface and stub class are generated from the WSDL document that describes the Web service endpoint. A JAX-RPC compiler will read the WSDL document and generate an endpoint interface that represents the abstract portType, and a generated stub class that implements the network protocol and SOAP messaging style described by the binding and port definitions.[1]

The JAX-RPC specification defines a detailed mapping between WSDL and generated stubs. It includes rules for mapping the abstract message, operation, and portType definitions, as well as XML schema types (simple and complex), to JAX-RPC endpoint interfaces. The full complement of rules for these mappings is quite complex. Chapter 15 provides detailed coverage, but in this section I'll keep it simple by using the most basic WSDL definitions and XML schema types.

The Endpoint Interface

The endpoint interface is derived from the WSDL portType definition, and each of the interface's methods represents a WSDL operation definition. To illustrate I'll use the WSDL document defined for the BookQuote Web service, shown in Listing 12-1.

Example 12-1. The WSDL Document for the BookQuote Web Service

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BookQuoteWS"
 targetNamespace="http://www.Monson-Haefel.com/jwsbook/BookQuote"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote"
 xmlns:soapbind="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns="http://schemas.xmlsoap.org/wsdl/">

  <!-- message elements describe the input and output parameters -->
  <message name="GetBookPriceRequest">
    <part name="isbn" type="xsd:string" />
  </message>
  <message name="GetBookPriceResponse">
    <part name="price" type="xsd:float" />
  </message>

  <!-- portType element describes the abstract interface of a Web service -->
  <portType name="BookQuote">
    <operation name="getBookPrice">
      <input name="isbn" message="mh:GetBookPriceRequest"/>
      <output name="price" message="mh:GetBookPriceResponse"/>
    </operation>
  </portType>

  <!-- binding tells us which protocols and encoding styles are used  -->
  <binding name="BookQuote_Binding" type="mh:BookQuote">
    <soapbind:binding style="rpc"
     transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="getBookPrice">
      <soapbind:operation style="rpc"
       soapAction="http://www.Monson-Haefel.com/jwsbook/BookQuote"/>
        <input>
          <soapbind:body use="literal"
           namespace="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
        </input>
        <output>
          <soapbind:body use="literal"
           namespace="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
        </output>
    </operation>
  </binding>

  <!-- service tells us the Internet address of a Web service -->
  <service name="BookQuoteService">
    <port name="BookQuotePort" binding="mh:BookPrice_Binding">
      <soapbind:address
       location="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
    </port>
  </service>

</definitions>

A JAX-RPC compiler will take the portType and operation definitions in this WSDL document and generate the endpoint interface shown in Listing 12-2.

Example 12-2. The Endpoint Interface Generated from the WSDL Document in Listing 12-1

package com.jwsbook.jaxrpc;

public interface BookQuote extends java.rmi.Remote {
  public float getBookPrice(String isbn) throws java.rmi.RemoteException;
}

The name of the endpoint interface comes from the portType name. Similarly, there is a one-to-one mapping between methods defined in the endpoint interface and operation definitions declared by the portType. The parameters and return value of each endpoint method are derived from message definitions that the portType refers to in the input and output elements of an operation element. Each parameter name and type derives from a part of the input message, and the return type derives from the part of the output message. It's important to stress that this is a very simple mapping. More complex mappings may involve complex types, arrays, and multiple OUT and INOUT parameter types, all of which are covered in Chapter 15.

The Generated Stub

The JAX-RPC compiler will also generate the stub class that implements the endpoint interface. An instance of the generated stub class (routinely shortened to “the stub”) is responsible for translating method calls into SOAP communications with the Web service endpoint. It will perform this translation according to the WSDL binding associated with the portType, which describes the style of messaging (rpc or document) and the type of encoding (always Literal in BP-conformant Web services). The stub will use the soap:address element specified by the WSDL port definition as the URL of the Web service.

In addition to implementing the endpoint interface, the stub class will also implement the javax.xml.rpc.Stub interface, which defines methods for reading and setting properties relating to network communication and authentication. Using these, an application can set the endpoint address, user name, password, and so on at runtime. The Stub interface appears in Listing 12-3.

Example 12-3. The javax.xml.rpc.Stub Interface

package javax.xml.rpc;
import java.util.Iterator;

public interface Stub {

    // Standard property: The Web service's Internet address.
    public static String ENDPOINT_ADDRESS_PROPERTY;
    // Standard property: Password for authentication.
    public static String PASSWORD_PROPERTY;
    // Standard property: User name for authentication.
    public static String USERNAME_PROPERTY;
    // Standard property: Boolean flag for maintaining an HTTP session.
    public static String SESSION_MAINTAIN_PROPERTY;

    // Given a property name, get its value.
    public Object _getProperty(java.lang.String name);
    // Get the names of all the properties the stub supports.
    public Iterator _getPropertyNames();
    // Configure a property on the stub.
    public void _setProperty(java.lang.String name, java.lang.Object value);

}

For the most part, you won't use the property methods of the Stub interface. A few of the properties are standard (including all the ones that appear in Listing 12-3), while others are vendor-specific and will be defined in your vendor's documentation.

Service Interfaces

The JAX-RPC compiler can, as an option, generate a service interface representing the service definition from a WSDL document, and a class that implements this interface. A J2EE component can use an instance of the service interface, called a service, to obtain a reference to a specific generated stub. The service interface defines an accessor for every port declared by the service definition. For example, the service interface generated from the BookQuote WSDL document would look like Listing 12-4.

Example 12-4. The Service Interface Generated from the WSDL Document in Listing 12-1

package com.jwsbook.jaxrpc;

public interface BookQuoteService extends javax.xml.rpc.Service{
    public BookQuote getBookQuotePort()
    throws javax.xml.rpc.ServiceException;
}

The name of the service interface, in this case BookQuoteService, comes from the name attribute of the WSDL service definition. The accessor method, getBookQuotePort(), gets its method name from the name attribute of the WSDL port element. The return type is the endpoint interface type that the JAX-RPC compiler generated for the corresponding portType, in this case the BookQuote endpoint interface.

The service interface is implemented by a class generated by the JAX-RPC compiler. An instance of this class instantiates and preconfigures generated stubs requested by the client. In J2EE, the service is bound to the JNDI ENC (Environment Naming Context), and is used to obtain a reference to the generated stub. Think of the service as a factory for generated stubs.

The service interface extends the javax.xml.rpc.Service interface, which defines methods normally used with the dynamic proxy and DII programming models covered later in this chapter. We'll examine javax.xml.rpc.Service in detail at that time.

Using a Generated Stub in J2EE

Once you've generated the endpoint interfaces, the stubs, and the service, a J2EE component can use them to access the Web service endpoint described by the original WSDL document. The generated stub is “hard-wired” to use the protocol and network address described by the WSDL document, so all a client needs to do is obtain a reference to the stub from the service.

A standard J2EE component (J2EE client application, EJB, servlet, JSP, or JSE) can obtain a reference to any service from its JNDI ENC, so from a developer perspective it's easy to access the service and obtain a generated stub. For example, Listing 12-5 is a J2EE servlet that is deployed with a reference to the BookQuoteService registered in its JNDI ENC.

Example 12-5. Using a Generated Stub from a Servlet

package com.jwsbook.jaxrpc;
import javax.servlet.http.*;
import javax.servlet.*;
import javax.naming.InitialContext;

public class BookQuoteServlet_1 extends javax.servlet.http.HttpServlet {
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException,java.io.IOException {
    try{
      String isbn = req.getParameter("isbn");

      InitialContext jndiContext = new InitialContext();

      BookQuoteService service = (BookQuoteService)
      jndiContext.lookup("java:comp/env/service/BookQuoteService");

      BookQuote bookQuote = service.getBookQuotePort();

      float price = bookQuote.getBookPrice( isbn );

      java.io.Writer outStream = resp.getWriter();
      outStream.write("<html><body>The wholesale price for ISBN:"+isbn+
                    " = "+price+"</body></html>");
    }catch(javax.naming.NamingException ne){
      throw new ServletException(ne);
    }catch(javax.xml.rpc.ServiceException se){
      throw new ServletException(se);
    }
  }
}

This servlet first obtains a reference to the default JNDI ENC by instantiating a new javax.naming.InitialContext object. As you recall from Section 10.2.2, when a J2EE component creates a new instance of the InitialContext, the J2EE container intervenes and returns the root context of the JNDI ENC for that component.

At deployment time the implementation class of the BookQuoteService interface is assigned to the JNDI namespace "java:comp/env/service/BookQuoteService" by a service reference element. The service reference element is covered in detail in Section 22.5, but a quick peek now won't hurt. A JAX-RPC Web service is declared in a service-ref element of the deployment descriptor of a J2EE component. Listing 12-6 shows an example of a service-ref element.

Example 12-6. A service-ref Element

<service-ref xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote" >
    <service-ref-name>service/BookQuoteService</service-ref-name>
    <service-interface>com.jwsbook.jaxrpc.BookQuoteService</service-interface>
    <wsdl-file>BookQuote.wsdl</wsdl-file>
    <service-qname>mh:BookQuoteService</service-qname>
</service-ref>

At this point, don't worry about how the service-ref element is declared. The important thing to understand is that the service-ref element declares the JAX-RPC service as a resource available to the J2EE component. At deployment time the deployer will take care of actually binding the service to the JNDI ENC so it can be accessed at runtime.

The service interface will include a method that returns the generated stub. In this case, the BookQuoteService.getBookQuotePort() method returns the BookQuote stub. The stub methods can be invoked as many times as needed. The J2EE component can hold the service reference in an instance variable, so it doesn't need to use the JNDI ENC to get it again. The generated stub reference can also be kept in an instance variable and reused as needed. The BookQuoteServlet_2 shown in Listing 12-7 improves on BookQuoteServlet_1 (Listing 12-5) by caching and reusing the BookQuote generated stub.

Example 12-7. Caching the JAX-RPC Generated Stub

package com.jwsbook.jaxrpc;
import javax.servlet.http.*;
import javax.servlet.*;
import javax.naming.InitialContext;

public class BookQuoteServlet_2 extends javax.servlet.http.HttpServlet {
  // A generated stub reference maintained for the life of the servlet
  BookQuote bookQuote;

  private BookQuote getStub() throws javax.naming.NamingException,
  javax.xml.rpc.ServiceException{
    if(bookQuote == null){
      InitialContext jndiEnc = new InitialContext();
      BookQuoteService service = (BookQuoteService)
          jndiEnc.lookup("java:comp/env/service/BookQuoteService");
      bookQuote = service.getBookQuotePort();
    }
    return bookQuote;
  }
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException,java.io.IOException {
    try{
      String isbn = req.getParameter("isbn");

      float price = getStub().getBookPrice( isbn );

      java.io.Writer outStream = resp.getWriter();
      outStream.write("<html><body>The wholesale price for ISBN:"+isbn+
                    " = "+price+"</body></html>");
    }catch(javax.naming.NamingException ne){
      throw new ServletException(ne);
    }catch(javax.xml.rpc.ServiceException se){
      throw new ServletException(se);
    }
  }
}

While BookQuoteServlet_2 isn't going to win any visual design contests, it does illustrate good program design when using generated stubs from J2EE components: The servlet's private helper method, getStub(), obtains the generated stub only once in the life of the component, and stores it in an instance variable to be reused as needed. The stub reference is lightweight and does not maintain a connection to a Web service—it reconnects briefly every time it's used[2]—so it doesn't consume extra resources while it's inactive. In fact, you should get better performance out of your J2EE component by maintaining a reference to a stub rather than fetching a new one with every request. This approach can be used with any of the J2EE components (J2EE application components, servlets, JSPs, EJBs, and JSEs).

When any of the stub's methods is invoked it will convert the method call into a SOAP message and send that message to the Web service endpoint. The WS-I Basic Profile 1.0 requires the use of an HTTP 1.1 POST to send a SOAP 1.1 message to the Web service.BP When the BookQuote stub's getBookPrice() method is invoked, the SOAP message sent to the Web service will look something like the one in Listing 12-8.

Example 12-8. The SOAP Message Produced by the BookQuote Generated Stub

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
   <soap:Body>
      <mh:getBookPrice>
          <isbn>0321146182</isbn>
      </mh:getBookPrice>
   </soap:Body>
</soap:Envelope>

When the Web service replies, it sends a SOAP message back to the stub using an HTTP reply message. A normal reply from the BookQuote Web service would look like Listing 12-9.

Example 12-9. The SOAP Reply Message from the BookQuote Web Service

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
   <soap:Body>
      <mh:getBookPriceResponse>
          <price>29.99</price>
      </mh:getBookPriceResponse>
   </soap:Body>
</soap:Envelope>

When the stub receives the SOAP reply message, it will extract its return value from the message or, if the message contains a fault, it will throw the appropriate Java exception.

The servlet examples in Listings 12-5 and 12-7 demonstrate how a J2EE servlet accesses a generated stub. The code used to access generated stubs from other J2EE components is exactly the same. They all use the JNDI ENC to get a reference to a service, from which they get a reference to the generated stub.

You can also use JAX-RPC from non-J2EE clients. This flexibility makes learning about JAX-RPC much easier because you don't have to deploy a servlet or EJB to try out some client code. Listing 12-10 shows you how to use JAX-RPC from a plain old J2SE application.

Example 12-10. Accessing a Generated Stub from a Non-J2EE Client

package com.jwsbook.jaxrpc;
import javax.naming.InitialContext;

public class JaxRpcExample_1 {
  public static void main(String [] args) throws Exception{
    String isbn = args[0];

    BookQuoteService service = (BookQuoteService)
    ServiceFactory.loadService(com.jwsbook.jaxrpc.BookQuoteService.class);

    BookQuote bookQuote = service.getBookQuotePort();

    float price = bookQuote.getBookPrice( isbn );

    System.out.println("The price is = "+price);
  }
}

JaxRpcExample_1 uses the static method ServiceFactory.loadService() to obtain a reference to the service object. This method can be used in non-J2EE clients only; J2EE components must use the JNDI ENC instead. Two overloaded versions of the loadService() method provide more specific information, including properties. Properties are vendor-specific, so use this method with care to avoid portability problems.

BookQuote is a Request/Response Web service, but generated stubs can also use One-Way messaging. If they do, their methods all return void. In Listing 12-11, the foo operation is defined with only an input message part, but its binding indicates that it uses RPC/Literal messaging.

Example 12-11. A WSDL Document for a One-Way Web Service

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:tns="http://www.Monson-Haefel.com/jwsbook/Foo"
 targetNamespace="http://www.Monson-Haefel.com/jwsbook/Foo">
  <message name="bar">
    <part name="value" type="xsd:string"/>
  </message>
  <portType name="Foo">
    <operation name="setBar">
      <input message="tns:bar"/>
    </operation>
  </portType>
  <binding name="FooBinding" type="tns:Foo">
    <soap:binding style="rpc"
        transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="setBar">
      <soap:operation soapAction="" style="rpc"/>
      <input>
        <soap:body use="literal"
            namespace="http://www.Monson-Haefel.com/jwsbook/Foo"/>
      </input>
    </operation>
  </binding>
  ...
</definitions>

After compilation, the method in the endpoint interface has a return type of void, as shown in Listing 12-12.

Example 12-12. The Endpoint Interface Generated from the WSDL Document in Listing 12-11

package com.jwsbook.jaxrpc;

public interface Foo extends java.rmi.Remote {
    public void setBar(String value) throws java.rmi.RemoteException;
}

Although the endpoint interface is semantically a one-way operation style, the underlying stub may in fact be using HTTP 1.1, which is a Request/Response protocol. In this case, the method returns after the stub receives the HTTP reply code (for example, 200 for OK) but it doesn't return any application data to the client. We don't have to handle a return value if there isn't a need for one, but we have some assurance that the SOAP message was received.

Dynamic Proxies

A dynamic proxy, as the name suggests, is created dynamically at runtime rather than generated statically at deployment time. Other than the method of accessing them, you use dynamic proxies to invoke Web service operations the same way you use generated stubs. The benefits of using dynamic proxies instead of generated stubs are not clear—it's probably best to stick with generated stubs. That said, it's important to know what your options are when using JAX-RPC.

Using a Dynamic Proxy

JaxRpcExample_2, in Listing 12-13, is a J2EE client application that shows how you can use a dynamic proxy to invoke the BookQuote Web service.

Example 12-13. Using a Dynamic Proxy

package com.jwsbook.jaxrpc;
import javax.naming.InitialContext;

public class JaxRpcExample_2 {
  public static void main(String [] args) throws Exception{
    String isbn = args[0];

    InitialContext jndiContext = new InitialContext();

    javax.xml.rpc.Service service = (javax.xml.rpc.Service)
    jndiContext.lookup("java:comp/env/service/Service");

    BookQuote BookQuote_proxy = (BookQuote)
                                service.getPort(BookQuote.class);

    float price = BookQuote_proxy.getBookPrice( isbn );

    System.out.println("The price is = "+price);
  }
}

There is at least one significant difference between using a dynamic proxy and using a generated stub: You don't need a generated service interface. With dynamic proxies you can use the generic service interface javax.xml.rpc.Service. This interface defines two methods that can be used with dynamic proxies, shown in Listing 12-14.

Example 12-14. The javax.xml.rpc.Service Type's Dynamic Proxy Methods

package javax.xml.rpc;

public interface Service {
    public java.rmi.Remote getPort(java.lang.Class endpointInterface)
    throws javax.xml.rpc.ServiceException;

    public java.rmi.Remote getPort(javax.xml.namespace.QName portName,
                                   java.lang.Class endpointInterface)
    throws javax.xml.rpc.ServiceException;

    ...
}

The whole point of dynamic proxies is that they are generated dynamically the first time you use them. At runtime the JAX-RPC provider (the vendor implementation) will read the WSDL document and generate a proxy to implement the endpoint interface at the moment the getPort() method is invoked. It may do so every time getPort() is invoked, but it will probably cache proxies for subsequent requests. The J2EE container generates the proxy by matching the endpoint interface passed into the getPort() method with a matching portType definition in the WSDL document, or by examining the JAX-RPC mapping file (covered in Chapter 24). If it finds a matching portType, it uses the binding and port definitions associated with that portType to create a class that implements the endpoint interface.

Of course this isn't a guessing game. When you deploy a J2EE component that uses a JAX-RPC dynamic proxy, you include a service-ref element in the deployment descriptor that tells the J2EE container which WSDL document you're using. Listing 12-15 shows a snippet of such a service-ref element.

Example 12-15. A service-ref Element for a Dynamic Proxy

<service-ref xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote" >
    <service-ref-name>service/Service</service-ref-name>
    <service-interface>javax.xml.rpc.Service</service-interface>
    <wsdl-file>BookQuote.wsdl</wsdl-file>
    <service-qname>mh:BookQuoteService</service-qname>
</service-ref>

You probably noticed that the service-interface is the generic javax.xml.rpc.Service interface customarily used with dynamic proxies. The Service interface is associated with a specific service definition in the WSDL document via the service-qname element in the service-ref. That way if the WSDL document defines more than one service, the J2EE container system can tell which service definition this service-ref is referring to. The service-ref element is declared in the deployment descriptor of the J2EE component. All this is covered in more detail in Chapter 23: Web Service Descriptors.

If there is only one port defined for the matching portType, the getPort(Class) method will create a dynamic proxy for that port definition. As you learned in Chapter 5, though, a single portType definition in a WSDL document may be the basis of more than one port definition. One WSDL port may use an RPC/Encoded binding, while another uses RPC/Literal. For example, review Listing 12-16 and assume the BookQuote_EncodedBinding uses RPC/Encoded SOAP 1.1 over HTTP, while the BookQuote_LiteralBinding uses RPC/Literal SOAP 1.1 over HTTP.

The use of RPC/Encoded messaging mode is prohibited by the Basic Profile, so this example should be considered as illustrative only.

Example 12-16. A WSDL service Element That Declares Two port Elements

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote"
 targetNamespace="http://www.Monson-Haefel.com/jwsbook/BookQuote">
 ...
 <service name="BookQuote">
   <port name="BookQuoteEncodedPort" binding="mh:BookQuote_EncodedBinding">
     <soap:address location="http://www.Monson-Haefel/jwsbook/BookQuote/Encoded"/>
   </port>
   <port name="BookQuoteLiteralPort" binding="mh:BookQuote_LiteralBinding">
     <soap:address location="http://www.Monson-Haefel/jwsbook/BookQuote/Literal"/>
   </port>
 </service>
</definitions>

In this case the binding definition used to create the proxy depends on the JAX-RPC mapping file, which pinpoints the exact WSDL binding definition to use in cases where a WSDL service defines multiple ports. Listing 12-17 shows a snippet from a JAX-RPC mapping file that determines which WSDL binding should be used for the service element in Listing 12-16. The JAX-RPC mapping file is covered in detail in Chapter 24: JAX-RPC Mapping Files.

Example 12-17. A Portion of a JAX-RPC Mapping File

<?xml version='1.0' encoding='UTF-8' ?>
<java-wsdl-mapping
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote"...>
  ...
  <service-endpoint-interface-mapping>
    <service-endpoint-interface>com.jwsbook.jaxrpc.BookQuote
    </service-endpoint-interface>
    <wsdl-port-type>mh:BookQuote</wsdl-port-type>
    <wsdl-binding>mh:BookQuote_LiteralBinding</wsdl-binding>
    ...
  </service-endpoint-interface-mapping>
</java-wsdl-mapping>

As an alternative, you can use the Service.getPort(QName, Class) method to create a dynamic proxy for the correct WSDL port and binding. In Listing 12-18 JaxRpcExample_3 uses this method to request a dynamic proxy for the BookQuoteLiteralPort WSDL port definition.

Example 12-18. Another Way of Using a Dynamic Proxy

package com.jwsbook.jaxrpc;
import javax.naming.InitialContext;
import javax.xml.namespace.QName;

public class JaxRpcExample_3 {
  public static void main(String [] args) throws Exception{
    String isbn = args[0];

    InitialContext jndiContext = new InitialContext();

    javax.xml.rpc.Service service = (javax.xml.rpc.Service)
    jndiContext.lookup("java:comp/env/service/Service");

    QName portName =
          new QName("http://www.Monson-Haefel/jwsbook/BookQuote",
          "BookQuoteLiteralPort");

    BookQuote BookQuote_proxy = (BookQuote)
                               service.getPort(portName, BookQuote.class);

    float price = BookQuote_proxy.getBookPrice( isbn );

    System.out.println("The price is = "+price);
  }
}

The QName class is defined in the javax.xml.namespace package (it's currently the only class in that package). An instance of this class represents an XML name. QName declares two constructors and a few accessor methods, as shown in Listing 12-19.

Example 12-19. The javax.xml.namespace.QName Class (Abbreviated)

package javax.xml.namespace;

public class QName implements java.io.Serializable {
    public QName(String localPart) {...}
    public QName(String namespaceURI, String localPart){...}
    public String getNamespaceURI(){...}
    public String getLocalPart(){...}
    public static QName valueOf(String qualfiedName){...}
}

The constructors and accessors are pretty self-explanatory. The localPart is equal to the name assigned to the port, and namespaceURI is the XML namespace of the port. The static QName.valueOf() method allows you to create an instance of the port's QName from a single String value formatted as "{namespaceURI}local Part". The XML namespace of the port is nested in braces to separate it from the local name. The following code snippet creates two identical QNames in different ways, using a constructor, and using the static valueOf() method.

// Use constructor
QName qname1 = new QName("http://www.Monson-Haefel/jwsbook/BookQuote",
                        "BookQuoteLiteralPort");

// Use static valueOf() method
String s = "{http://www.Monson-Haefel/jwsbook/BookQuote}BookQuoteLiteralPort";
QName qname2 = QName.valueOf(s);

Dynamic proxies are usually used with the generic Service interface. You can also obtain them from generated service implementations, because generated service interfaces, like BookQuoteService in Listing 12-4, must extend the javax.xml.rpc.Service interface, and so support all the Service interface methods, including the getPort() methods for obtaining dynamic proxies.

The Service.getPort() methods may return a generated stub instead of a dynamic proxy. If you deploy a J2EE component that uses generated stubs and then make a call to the Service.getPort() method, you'll probably get back a stub rather than a dynamic proxy—it's difficult to tell the difference, because both dynamic proxies and generated stubs implement the javax.xml.rpc.Stub interface.

The endpoint interface used to create a dynamic proxy can be generated (using a JAX-RPC compiler), or it can be hand-coded by the developer. Either way it must conform to the JAX-RPC specification for mappings between WSDL and Java, and between XML and Java. These are covered in more detail in Chapter 15.

Under the Covers

Beneath the surface, the J2EE container system uses the J2SE Reflection API defined by the java.lang.reflect package to generate dynamic proxies. Specifically, it uses the java.lang.reflect.Proxy class, and implementations of the java.lang.reflect.InvocationHandler interface to create JAX-RPC dynamic proxies.

The dynamic proxy class extends the java.lang.reflect.Proxy class, so you have access to the methods defined by Proxy. As an application developer you won't need to use these methods, but knowing that dynamic proxies are a type of java.lang.relect.Proxy can be useful in rare situations.

DII

The Dynamic Invocation Interface (DII) defines an API for invoking operations on Web service endpoints. Unlike generated stubs and dynamic proxies, DII doesn't use an endpoint interface that is different for every portType. Instead, DII uses a fixed API that can be used to invoke operations on just about any endpoint. This makes the DII toolable, meaning it can be used by applications that automatically discover and invoke Web service operations.

Using DII with a WSDL Document

DII can be used with or without a WSDL document. If a WSDL document is available, it's much simpler to use because the details about the Web service operation being invoked can be derived from the WSDL document. To illustrate I'll use the BookQuote.wsdl document in Listing 12-20 as a guide.

Example 12-20. The BookQuote WSDL Document

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BookQuoteWS"
 targetNamespace="http://www.Monson-Haefel.com/jwsbook/BookQuote"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote"
 xmlns:soapbind="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns="http://schemas.xmlsoap.org/wsdl/">

  <!-- message elements describe the input and output parameters -->
  <message name="GetBookPriceRequest">
    <part name="isbn" type="xsd:string" />
  </message>
  <message name="GetBookPriceResponse">
    <part name="price" type="xsd:float" />
  </message>

  <!-- portType element describes the abstract interface of a Web service -->
  <portType name="BookQuote">
    <operation name="getBookPrice">
      <input name="isbn" message="mh:GetBookPriceRequest"/>
      <output name="price" message="mh:GetBookPriceResponse"/>
    </operation>
  </portType>

  <!-- binding tells us which protocols and encoding styles are used  -->
  <binding name="BookQuote_Binding" type="mh:BookQuote">
    <soapbind:binding style="rpc"
     transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="getBookPrice">
      <soapbind:operation style="rpc"
       soapAction="http://www.Monson-Haefel.com/jwsbook/BookQuote"/>
        <input>
          <soapbind:body use="literal"
           namespace="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
        </input>
        <output>
          <soapbind:body use="literal"
           namespace="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
        </output>
    </operation>
  </binding>

  <!-- service tells us the Internet address of a Web service -->
  <service name="BookQuoteService">
    <port name="BookQuotePort" binding="mh:BookQuote_Binding">
      <soapbind:address
       location="http://www.Monson-Haefel.com/jwsbook/BookQuote" />
    </port>
  </service>

</definitions>

Using the WSDL document in Listing 12-20 as a guide, we can access the BookQuote Web service using DII. In Listing 12-21 JaxRpcExample_4 illustrates how to use DII to call the getBookPrice operation of the BookQuote Web service.

Example 12-21. Using the DII with a WSDL Document

package com.jwsbook.jaxrpc;
import javax.naming.InitialContext;
import javax.xml.rpc.Service;
import javax.xml.rpc.Call;
import javax.xml.namespace.QName;

public class JaxRpcExample_4 {
  public static void main(String [] args) throws Exception{
    String isbn = args[0];

    InitialContext jndiContext = new InitialContext();

    javax.xml.rpc.Service service = (javax.xml.rpc.Service)
    jndiContext.lookup("java:comp/env/service/Service");

    QName portName =
     new QName("http://www.Monson-Haefel.com/jwsbook/BookQuote",
               "BookQuotePort");
    QName operationName =
     new QName("http://www.Monson-Haefel.com/jwsbook/BookQuote",
               "getBookPrice");

    Call call = service.createCall(portName,operationName);

    Object [] inputParams = new Object[]{isbn};

    Float price = (Float)call.invoke(inputParams);

    System.out.println("The price is = "+price.floatValue());
 }
}

The javax.xml.rpc.Call interface represents a Web service operation that can be invoked or called using the Call.invoke() method. JaxRpcEample_4 first obtains a javax.xml.rpc.Service object from the JNDI ENC. As with generated stubs and dynamic proxies, the Service type is bound to the JNDI ENC using a service-ref element in a deployment descriptor. The service-ref element associates the Service type with a specific WSDL service element for a specific WSDL document, in this case BookQuote.wsdl. The WSDL document must be stored in the J2EE component's JAR file. wsdl-file specifies a path to the WSDL document, relative to the root of that JAR file.

<service-ref xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote" >
    <service-ref-name>service/Service</service-ref-name>
    <service-interface>javax.xml.rpc.Service</service-interface>
    <wsdl-file>META-INF/BookQuote.wsdl</wsdl-file>
    <service-qname>mh:BookQuoteService</service-qname>
</service-ref>

Using the Service object, you can create a Call object for a specific WSDL port and operation. You use javax.xml.namespace.QName objects to identify the WSDL port and operation (go back to Section 12.2.1 if you need to review what you learned about the QName type). The following snippet from Listing 12-21 illustrates how the QName objects are used to identify the proper WSDL port and operation.

QName portName =
  new QName("http://www.Monson-Haefel.com/jwsbook/BookQuote",
            "BookQuotePort");
QName operationName =
  new QName("http://www.Monson-Haefel.com/jwsbook/BookQuote",
            "getBookPrice");

Call call = service.createCall(portName,operationName);

The Call object is associated with a specific WSDL operation of a specific port, from which it derives the proper operation style (RPC or Document) and encoding (for example, Literal). The Call object uses this information to construct the SOAP message it sends to the Web service. DII, like all JAX-RPC APIs, is required to support RPC/Literal, Document/Literal, and RPC/Encoded SOAP 1.1 messaging over HTTP 1.1. Although JAX-RPC requires vendors to support RPC/Encoded, it is prohibited by the WS-I Basic Profile.

Once the Call object is created, its invoke() method can be executed using the proper arguments as shown in the following snippet from Listing 12-21. In this case the Call object is associated with the getBookPrice operation of the BookQuote Web service, which defines only a single String type input parameter, an ISBN.

Object [] inputParams = new Object[]{isbn};

Float price = (Float)call.invoke(inputParams);

If the input message of the portType defines several part elements (parameters), then a value for each of those parameters is passed into the invoke() method using the Object array. The order that parameters are placed in the Object array depends on the parameterOrder attribute of the WSDL operation element in the portType. For example, in Section 5.2.3.1 the getBulkBookPrice operation was used as an example of an operation that defined a parameterOrder. A fragment of the WSDL document for that operation is shown in Listing 12-22.

Example 12-22. The WSDL Document Definition for the getBulkBookQuote Operation

<message name="GetBulkBookPriceRequest">
  <part name="isbn" type="xsd:string"/>
  <part name="quantity" type="xsd:int"/>
</message>
<message name="GetBulkBookPriceResponse">
  <part name="prices" type="titan:prices" />
</message>
<portType name="GetBulkBookPrice" >
  <operation name="getBulkBookPrice" parameterOrder="isbn quantity">
     <input name="request" message="titan:GetBulkBookPriceRequest"/>
     <output name="prices" message="titan:GetBulkBookPriceResponse"/>
  </operation>
</portType>

In this case the parameters passed into the Call.invoke() method must be in the order defined by the parameterOrder attribute. In addition, the quantity must be a java.lang.Integer value instead of a primitive int—you can't put a primitive into an Object array. The following code snippet illustrates.

public static void main(String [] args) throws Exception{

   String isbn = args[0];
   Integer quantity = new Integer(args[1]);
   ...

   Object [] inputParams = new Object[]{isbn, quantity};

   Float price = (Float) call.invoke(inputParams);
}

The operation return value is always returned by the invoke() method. To retrieve the values of OUT and INOUT parameters, after the invoke() method is called you have to invoke the Call.getOutputValues() method, as here:

java.util.List outputParams = call.getOutputValues();

The returned List object contains all the OUT and INOUT arguments returned in the SOAP reply message. The OUT and INOUT parameters are used by Web service endpoints written in other programming languages—the Java programming language natively supports IN parameters and return values, but requires special facilities for handling OUT and INOUT parameters. The OUT and INOUT parameters are discussed in more detail in Section 15.3: Holders.

Using DII without a WSDL Document

The Basic Profile requires that Web service endpoints provide a WSDL document that describes the Web service. You may need to interact with a non-conformant Web service, however, in which case you can use the DII's facilities for messaging without a WSDL document. This topic is covered in more detail in Appendix H: Using JAX-RPC DII without a WSDL Document.

Using One-Way Messaging with DII

DII can be used to send One-Way messages as well as Request/Response messages. If you are sending an RPC/Literal message that does not require a reply message, you can simply use the invokeOneWay() method instead of invoke(). The only difference is that invokeOneWay() has no return value (or output values) and does not block, waiting for a reply (other than the HTTP reply with code 200). This snippet shows how invokeOneWay() is called—it's very similar to a call to invoke():

Object [] inputParams = new Object[]{value1, value2,...};
call.invokeOneWay(inputParams);

JAX-RPC Standard Properties and Constants

The JAX-RPC API defines a number of properties and constants that are used frequently with DII. You can see examples of these constants in use in Appendix H. This section simply provides a reference for you when using DII.

The javax.xml.rpc.Call class defines seven constants corresponding to standard properties, most of which are optional. Table 12-1lists each constant, its value, whether it's required to be supported by JAX-RPC vendors, and the meaning of the property.

Many of the standard properties need not be supported by JAX-RPC implementations, but most are. If a property is not supported, the Call.setProperty() method will throw a javax.xml.rpc.JAXRPCException. Just because a property must be supported doesn't mean it has to be declared. In fact, the Web Services for J2EE 1.1 specification discourages the use of the USERNAME_PROPERTY and PASSWORD _PROPERTY in J2EE, because the J2EE container usually handles authentication automatically.

Table 12-1. Constants Representing Standard Properties

javax.xml.rpc.Call Constant = Property Value

Support Required

Definition

ENCODINGSTYLE_URI_PROPERTY = javax.xml.rpc.encodingstyle.namespace.uri

No

Declares the encoding style used. The default value is SOAP 1.1 Encoding: http://schemas.xmlsoap.org/soap/encoding/.

OPERATION_STYLE_PROPERTY = javax.xml.rpc.soap.operation.style

No

Declares the operation style. The only values accepted are "rpc" and "document".

SOAPACTION_USE_PROPERTY = javax.xml.rpc.soap.http.soapaction.use

No

Declares whether or not the SOAPAction header is used in the HTTP request. The value can be true or false. The default is false.

SOAPACTION_URI_PROPERTY = javax.xml.rpc.soap.http.soapaction.uri

No

If SOAPAction is used, declares the value of the SOAPAction.

SESSION_MAINTAIN_PROPERTY = javax.xml.rpc.j session.maintain

Yes

Declares whether the Call object must support session tracking (for example, cookies). The value can be true or false. The default is false.

USERNAME_PROPERTY = javax.xml.rpc.security.auth.username

Yes

Declares the user name for authentication.

PASSWORD_PROPERTY = javax.xml.rpc.security.auth.password

Yes

Declares the password for authentication.

JAX-RPC also defines the javax.xml.rpc.NamespaceConstants type, which contains a bunch of other SOAP 1.1 XML namespace URIs and prefix constants that are commonly used (see Table 12-2).

While the URI constants defined by the NamespaceConstants class are very useful, the prefix constants are less important. In XML, and therefore SOAP, you can pretty much use any prefixes you want. The industry tends to use a common set, however, such as those prefixes used in NamespaceConstants. The exception is the prefix used for the Envelope namespace, which varies from one vendor to the next, and includes prefixes like SOAP-ENV, env, soapenv, and others. Again, it's not that important. You can use any prefix you want.

Constructing QName objects is kind of a pain, so JAX-RPC provides the javax.xml.rpc.encoding.XMLType class that defines a couple of dozen final static QName constant values for the most common XML schema and SOAP 1.1 types. Table 12-3 lists them.

Table 12-2. Namespace Constants

javax.xml.rpc.NamespaceConstants

String Value

NSPREFIX_SOAP_ENVELOPE

soapenv

NSURI_SOAP_ENVELOPE

http://schemas.xmlsoap.org/soap/envelope/

NSPREFIX_SOAP_ENCODING

soapenc

NSURI_SOAP_ENCODING

http://schemas.xmlsoap.org/soap/encoding/

NSPREFIX_SCHEMA_XSD

xsd

NSURI_SCHEMA_XSD

http://www.w3.org/2001/XMLSchema

NSPREFIX_SCHEMA_XSI

xsi

NSURI_SCHEMA_XSI

http://www.w3.org/2001/XMLSchema-instance

NSURI_SOAP_NEXT_ACTOR

http://schemas.xmlsoap.org/soap/actor/next

Table 12-3. QName Constants Available in javax.xml.rpc.encoding.XMLType

Constant Field

Value

XSD_STRING

xsd:string

XSD_FLOAT

xsd:float

XSD_BOOLEAN

xsd:boolean

XSD_DOUBLE

xsd:double

XSD_INTEGER

xsd:integer

XSD_INT

xsd:int

XSD_LONG

xsd:long

XSD_SHORT

xsd:short

XSD_DECIMAL

xsd:decimal

XSD_BASE64

xsd:base64Binary

XSD_HEXBINARY

xsd:hexBinary

XSD_BYTE

xsd:byte

XSD_DATETIME

xsd:dateTime

XSD_QNAME

xsd:Qname

SOAP_STRING

soapenc:string

SOAP_FLOAT

soapenc:float

SOAP_BOOLEAN

soapenc:boolean

SOAP_DOUBLE

soapenc:double

SOAP_INT

soapenc:int

SOAP_LONG

soapenc:long

SOAP_SHORT

soapenc:short

SOAP_BASE64

soapenc:base64

SOAP_BYTE

soapenc:byte

SOAP_ARRAY

soapenc:Array

The constants that have an xsd prefix belong to the XML schema namespace (xmlns:xsd="http://www.w3.org/2001/XMLSchema"), while the constants prefixed with soapenc belong to the SOAP 1.1 Encoding namespace (xmlns: soapenc="http://schemas.xmlsoap.org/soap/encoding").

Other than the soapenc:Array type, the only difference between a SOAP Encoding type and an XML schema document type is that the SOAP Encoding types are nillable; that is, they can take on null values. Because they are nillable, they are mapped to Java's primitive wrapper types, which can be null, rather than to the underlying primitive types, which can't. Using JAX-RPC with SOAP encoding is covered in Appendix G.

Wrapping Up

The first section of this chapter explained the basics of using generated stubs to access SOAP-based Web services. I say “the basics” because it didn't touch on several other topics you'll probably need to know over the course of your development work. For example, how are complex types, arrays, faults, and OUT and INOUT parameters handled? These types of questions are addressed in Chapter 15. Other topics are also important for you to know, like message handlers used to process header blocks, covered in Chapter 14.

Generated stubs are going to be the mainstay of your J2EE Web service client development. As you use generated stubs (or any other JAX-RPC API) you may occasionally encounter problems making them work with other Web service platforms. Web services interoperability is a moving target that has not been fully addressed by Web service technologies. SOAP and WSDL certainly go a long way toward interoperability, but there are enough “corner cases” to cause at least a few interoperability headaches. The WS-I Basic Profile 1.0 has done much to smooth out the interoperability wrinkles that tend to pop up.

DII is an interesting API that can be useful for tool vendors, but is generally not very useful to the average application developer. You are much better off using generated stubs, as they provide a simpler and more productive programming model and you can optimize them to be much faster than DII. It should be noted, however, that some JAX-RPC implementations actually use the DII libraries inside generated stubs and dynamic proxies to marshal method calls into SOAP messages. This approach allows the vendor to reuse DII, but it can decrease performance compared to optimized static code in generated stubs.



[1] In some cases the binding may also influence the definition of the endpoint interface.

[2] This statement is true of JAX-RPC stubs that use HTTP 1.1, which is a connectionless protocol. All JAX-RPC providers must support HTTP 1.1 with SOAP, but may also support other protocols.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.82.83