JaVa
   

JAX-RPC Development

We have just covered how data can be transferred over the wire, along with the rules and associated mechanics governing that. In this section, we will look at how services can be developed and realized using JAX-RPC and the steps involved in doing so. Developing and consuming a JAX-RPC service can be categorized into five steps:

  1. Service definition

  2. Service implementation
  3. Service deployment
  4. Service description
  5. Service consumption

In walking through these steps we will develop the service example introduced in . The example illustrates a bill payment service developed by Flute Bank as part of its online operations.

Service Definition

The term service definition is used to refer to the abstraction that defines the publicly surfaced view of the service. The service definition is represented as a Java interface that exposes the service's operations. The service definition is also called a remote interface, because it must extend the java.rmi.Remote interface, and because all methods in it must throw a java.rmi.RemoteException. The code below shows the BillPay Web service:

package com.flutebank.billpayservice;
import java.util.Date;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface BillPay extends Remote {
 public PaymentConfirmation schedulePayment(Date date, String nickName, double
 amount) throws ScheduleFailedException, RemoteException;
 public PaymentDetail[] listScheduledPayments() throws RemoteException;
 public double getLastPayment(String nickname) throws RemoteException;
}


The methods in the interface must have valid JAX-RPC data types (disussed earlier) as arguments and return types. If they are not a supported data type (e.g. java.util.Map), then appropriate serializers and deserializers must be available, so that these types can be marshaled and unmarshalled to and from their corresponding XML representations. The data type can also be a holder class. Holders and pluggable serializers are covered later in this chapter.

An implemenatation will usually verify this type information at compile time and warn the developer if it is not correct. A request sent with incorrect type information at runtime will generate a SOAP fault, because it will not be able to unmarshall the XML.

Service Implementation

The service implementation, also known as a servant, is the concrete representation of the abstract service definition; it is a class that provides the implementation or the service definition. The Java class must have a default constructor and must implement the remote interface that defines the service. Listing 10.2 shows the implementation for the BillPay service.

Listing 10.2: Implementation for Flute Bank's BillPay service
package com.flutebank.billpayservice;
import java.util.Date;
public class BillPayImpl implements BillPay {
public BillPayImpl(){}
public PaymentConfirmation schedulePayment(Date date, String nickName, double
 amount) throws ScheduleFailedException {
 // invoke business logic like EJBs here
 return new PaymentConfirmation(81263767,"Sprint PCS", amount);
 }
public PaymentDetail[] listScheduledPayments() {
 // lookup the detail objects and other business logic from EJBs here
 PaymentDetail details[]=new PaymentDetail[1];
 PaymentDetail dummy= new PaymentDetail("Digital Credit
 Union","Credit",2000, new Date());
 details[0]=dummy;
 return details;
 }
public double getLastPayment(String nickname) {
 // lookup the detail objects and other business logic from EJBs for this
 // nickname based on the callers user id
 if(nickname.equalsIgnoreCase("my cable tv provider"))
 return 829;
 else
 return 272;
 }
}



Java End example

Services are deployed in a JAX-RPC runtime, which is a container that implements the JAX-RPC specifications. By default, the runtime will just invoke the methods corresponding to the RPC request in the Java implemenatation. The service implementation can choose to provide hooks to allow the runtime to manage the service's lifecycle and allow the container to invoke callbacks on the service when major lifecycle events occur. The "hook" is defined as a javax.xml.rpc.server.ServiceLifeCycle interface that the service can implement. The container will then invoke methods on this service appropriately, via this interface. The interface defines an init(Object context) and a destroy() method:

public interface ServiceLifecycle{
 public void init(Object obj) throws ServiceException;
 public void destroy();
}


The behavior of these methods is similar to the init() and destroy() methods in a servlet. When the implementation is first instantiated, the init() method is invoked, and a context object passed to it, the destroy() method is called before the implementation needs to be removed (e.g., at shutdown or during a resource crunch). These methods are good places to initialize and release expensive resources, such as database connections and remote references. The context is defined as an Object, to allow for different endpoint types to be used, as we will see later (e.g., the context will be different for an HTTP endpoint and a JMS endpoint).

As with a servlet, an implementation should not hold a client-specific state in instance variables, because the runtime can invoke methods from multiple threads. Architects should also avoid synchronizing the methods themselves. There are other ways to maintain client state, as discussed in the next section.

Service Deployment

We mentioned earlier that a service is deployed in a JAX-RPC runtime. A service endpoint is the perimeter where the SOAP message is received and the response dispatched. It is the physical entity exposed to service consumers that essentially services client requests. An endpoint is provided by the runtime and is not written by developers. An endpoint is bound to the transport protocol. Because a runtime is required to support an HTTP transport, JAX-RPC also defines the behavior of an endpoint for this protocol as a Java servlet, as Screenshot shows.

Java Click To expand
Screenshot: Service deployment
Java Start Sidebar

Even though a JAX-RPC runtime must support HTTP, it can use other transports as well. The JAX-RPC architecture is designed to be transport-independent, even though it describes the way HTTP is used if it is chosen as transport.

Java End Sidebar

The servlet receives the SOAP message as the HTTP request, determines the servant to use for servicing that request, and delegates to it or its proxy representation (the tie). Once the service has done its work, the servlet is responsible for packaging the SOAP message and sending it back over HTTP. The exact implementation of the servlet endpoint is left up to the runtime. The reference implementation contains a single servlet (com.sun.xml.rpc.server .http.JAXRPCServlet) that delegates to a tie, based on the xrpcc-generated properties file (we will see this later in the chapter). Because the endpoint is a servlet, it requires a Servlet 2.2—compliant container. Also, the packaging and deployment to the endpoint of the service has to be the standard J2EE WAR file, with its defined structure (WEB-INF/classes and the web.xml file, etc.) If a service implementation implements the ServiceLifeCycle interface, the context object passed in the init() is of type javax.xml.rpc.server.ServletEndpointContext:

public interface ServletEndpointContext{
 public MessageContext getMessageContext();
 public Principal getUserPrincipal();
 public HttpSession getHttpSession();
 public ServletContext getServletContext();
}


This context provides methods to access the MessageContext, Principal, HttpSession and ServletContext objects associated with the user. The listing below shows an example of how this can be used. These objects are good places for maintaining different kinds of state information:

public class BillPayImpl implements BillPay, ServiceLifecycle {
 private ServletEndpointContext ctx;
public void init(java.lang.Object context){
 ctx=(ServletEndpointContext)context;
 }
public PaymentDetail[] listScheduledPayments() {
 SOAPMessageContext msgctx= (SOAPMessageContext) (ctx.getMessageContext());
 HttpSession session = ctx.getHttpSession();
 ServletContext servletctx= ctx.getServletContext()
 // other code
 }
}


The usage of the ServletEndpointContext is analogous to the SessionContext and EntityContext in EJBs.

Service Description

Once the service is defined, implemented, and ready for deployment as an endpoint, it also must be described clearly for service consumers. This is where WDSL comes in. Based on the service definition, the WSDL document describes the service, its operations, arguments, return types, and the schema for the data types used in them.

xrpcc Internals

The JAX-RPC reference implementation comes with the xrpcc (XML-based RPC Compiler) tool, which reads a tool-specific XML configuration file and generates the client- or server-side bindings shown in Screenshot. A developer can start with

Java Click To expand
Screenshot: xrpcc artifacts

Listing 10.3 shows the format for the XML configuration file xrpcc reads.

Listing 10.3: xrpcc configuration in the reference implementation
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.oracle.com/xml/ns/jax-rpc/ri/config">
<service name="" packageName="" targetNamespace="" typeNamespace="">
 <interface name="" servantName="" soapAction="" soapActionBase="">
 <handlerChains>
 <chain runAt=" " roles="">
 <handler className="" headers="">
 <property name="" value=""/>
 </handler>
 </chain>
 </handlerChains>
</interface>
 <typeMappingRegistry>
 <import>
 <schema namespace="" location=""/>
 </import>
 <typeMapping encodingStyle="">
 <entry schemaType=""
 javaType=""
 serializerFactory=""
 deserializerFactory=""/>
 </typeMapping>
 <additionalTypes>
 <class name=""/>
 </additionalTypes>
 </typeMappingRegistry>
 <handlerChains>
 <chain runAt="client" roles="">
 <handler className=""headers="">
 <property name=""value=""/>
 </handler>
 </chain>
 </handlerChains>
 <namespaceMappingRegistry>
 <namespaceMapping namespace=""
 packageName=""/>
 </namespaceMappingRegistry>
 </service>
</configuration>




Java End example

Key XML elements of the configuration are discussed below. Some refer to concepts covered later in the chapter (e.g., handlers and typemappings). Service Element. This describes the overall service. Only one service can be defined in the XML descriptor, to prevent potential name clashes in the generated code for the different services and the types they use.

Interface element. This defines details about the interface the service supports. A service can have multiple interfaces.

Handlerchain element. Defines information about handlers for this service. The handler element can be defined inside a service. If so, it is available to all interfaces inside the interface element, in which case it is specific only to that interface.

Typemapping registry element.

Namespace mapping registry.

The -both option of the xrpcc tool can be used to generate stubs and ties together. Alternatively, the server and client code can be generated separately, using the -server and -client options. Note that the -keep option must be used to retain the WSDL file. One of the artifacts xrpcc generates when it reads the XML descriptor is an additional configuration file. So, what is this new configuration file? Remember, the service is being deployed in a servlet container, where the runtime-provided endpoint exists (the reference implementation defined the endpoint as a com.sun.xml.rpc.server.http.JAX-RPCServlet). This configuration file is used to hook the endpoint with the service implementation. It is just an implementation detail that, like xrpcc itself, is specific to the reference implementation and is not a part of the specifications. Other vendors may use a completely different tool with its own mechanism.

Java Start Sidebar

The final version of the Hava WSDP and JAX pack include a tool called wscompile. Currently there is no difference in the behavior of wscompile and xrpcc; however in future versions wscompile is likely to evolve, where as xrpcc may not.

Java End Sidebar

Java-WSDL Mappings

In , we discussed the WSDL structure, the role of vendor tools, and the significance of a standard specification to map WSDL elements to Java (and vice versa). To understand this mapping, let us revisit the role of WSDL elements from that chapter (Screenshot).

Java Click To expand
Screenshot: WSDL elements and dynamic interaction of a service and its consumer.

A Web service exposes groups of business operations for service consumers to use. Operations are grouped together to form portTypes. To invoke an operation, the consumer sends an input message containing the input data. It gets an output message containing the data that results from the business processing, or a fault if a problem occurs. The input and output messages may have multiple data items in them; each is called a part. The wire protocol used for the invocation and the format of the input and output messages on the wire for that protocol are specified in a binding element. The service exposes itself to consumers through one or more ports, each of which specifies a network address where the service is located and the binding to use with that port. A service may render itself though several ports, where each port has a different binding (e.g., the same service may expose itself via SOAP/HTTP and SOAP/SMTP). JAX-RPC defines the mapping of Java to WSDL data types, and vice versa. This is the mapping used by xrpcc when generating a WSDL file or consuming it. Table 10.6 summarizes this mapping. Listings 10.4 and 10.5 show a complete example of a service definition and its corresponding WSDL generated on the basis of these mappings.

Table 10.6: Data Type Mapping between Java and WSDL

Java type

WSDL mapping

Package

WSDL document

  • Sample extract code

  • The namespace definition in a WSDL is mapped to a Java package name.

Java type

WSDL mapping

Interface

wsdl:portType

  • Sample extract code

 

 public interface BillPay extends java.rmi.Remote {
 // methods here
 }

 

 <portType >
 // operations here
 </portType>

Java type

WSDL mapping

Method

wsdl:operation

  1. The WSDL operation name is the same as the method name.

  2. Overloaded methods can map to multiple operations with the same name or unique names that are implementation-specific.

  • Sample extract code

 

 public interface BillPay extends java.rmi.Remote {
 public PaymentDetail[] listScheduledPayments()
 throws RemoteException;
 public PaymentConfirmation schedulePayment(
 Date date, String payee, double amt)
 throws ScheduleFailedException, RemoteException;
 public double getLastPayment(String nickname) throws
 RemoteException;
 }

 

<portType >
 <operation >
 // input output messages for this operation
</operation>
 <operation >
 // input output messages for this operation
 </operation>
<operation >
 // input output messages for this operation
</portType>

Java type

WSDL mapping

Extended interface

wsdl:portType

with a complete set of inherited operations.

  • Sample extract code

 

 Public interface LinkedBillPay extends BillPay {
 public String getStatus() throws
 java.rmi.RemoteException, StatusUnavilableException;
 }
 <portType >
 <operation >
 // input output messages for this operation
 </operation>
 <operation >
 // input output messages for this operation
 </operation>
 <operation >
 // input output messages for this operation
 </operation>
 </operation>
 </portType>

Java type

WSDL mapping

Method arguments

wsdl:input and corresponding wsdl:message elements.

  • Sample extract code

 

 public interface BillPay extends java.rmi.Remote {
 public PaymentDetail[] listScheduledPayments()
 throws RemoteException;
 public PaymentConfirmation schedulePayment(Date
 date, String payee, double amt)
 throws ScheduleFailedException, RemoteException;
 public double getLastPayment(String nickname) throws
 RemoteException;
 }
 <portType >
 <operation ">
 <input message="tns:BillPay_getLastPayment"/>
 // output message
 </operation>
 <operation >
 <input message="tns:BillPay_listScheduledPayments"/>
 // output message
 </operation>
 <operation >
 <input message="tns:BillPay_schedulePayment"/>
 // output message
 </operation>
 </portType>
 <message name="BillPay_getLastPayment">
 <part name="String_1" type="xsd:string"/></message>
 <message name="BillPay_listScheduledPayments"/>
 <message name="BillPay_schedulePayment">
 <part name="Date_1" type="xsd:dateTime"/>
 <part name="String_2" type="xsd:string"/>
 <part name="double_3" type="xsd:double"/></message>

Java type

WSDL mapping

Method returns

wsdl:output and corresponding wsdl:message elements.

  • Sample extract code

 

 public interface BillPay extends java.rmi.Remote {
 public PaymentDetail[] listScheduledPayments()
 throws RemoteException;
 public PaymentConfirmation schedulePayment(Date date, String payee,
 double amt)
 throws ScheduleFailedException, RemoteException;
 public double getLastPayment(String nickname) throws
 RemoteException;

 

 <message name="BillPay_getLastPaymentResponse">
 <part type="xsd:double"/></message>
 <message name="BillPay_listScheduledPaymentsResponse">
 <part type="tns:ArrayOfPaymentDetail"/></message>
 <message name="BillPay_schedulePaymentResponse">
 <part type="tns:PaymentConfirmation"/></message>
 <portType >
 <operation parameterOrder="String_1">
 // input message here
 <output message="tns:BillPay_getLastPaymentResponse"/>
 </operation>
 <operation parameterOrder="">
 // input message here
 <output message="tns:BillPay_listScheduledPaymentsResponse"/>
 </operation>
 <operation parameterOrder="Date_1
 String_2 double_3">
 <output message="tns:BillPay_schedulePaymentResponse"/>
 </operation></portType>

Java type

WSDL mapping

Checked exceptions

wsdl:fault

  1. wsdl:message name is the same as the exception name.

  2. RemoteExceptions are mapped to standard SOAP faults.

  3. The exception and its hierarchies get mapped to XML types in the schema, using the standard complexType extension mechanism.

  • Sample extract code

 

 public interface BillPay extends java.rmi.Remote {
 // other code
 public PaymentConfirmation schedulePayment(Date
 date, String payee, double amt)
 throws ScheduleFailedException, RemoteException;
 }
 <operation >
 // input and output elements
 <fault >
 <soap:fault encodingStyle="http://schemas.xmlsoap.org/soap/
 encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/>
 </fault>
 <soap:operation soapAction=""/>
 </operation>
 <message >
 <part type=
 "tns:ScheduleFailedException"/></message>

Java type

WSDL mapping

Java identifiers

XML name.

  • Sample extract code

  • Java identifiers are already legal XML names.

 

 public class PaymentDetail {
 private String payeeName;
 private String account;
 private double amt;
 private Date date;
 // other code

 

 <types>
 // other code
 <complexType >
 <sequence>
 <element type="dateTime"/>
 <element type="string"/>
 <element type="string"/>
 <element type="double"/></sequence>
 </complexType>
 </types>
Listing 10.4: Source file for BillPay.java
package com.flutebank.billpayservice;
import java.util.*;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface BillPay extends Remote {
public PaymentConfirmation schedulePayment(Date date, String nickName, double amount)
throws ScheduleFailedException, RemoteException;
public PaymentDetail[] listScheduledPayments() throws RemoteException;
public double getLastPayment(String nickname) throws RemoteException;
}



Java End example
Listing 10.5: WSDL billservice.java corresponding to Listing 10.4
<?xml version="1.0" encoding="UTF-8"?>
<definitions targetNamespace="http://www.flutebank.com/xml"
xmlns:tns="http://www.flutebank.com/xml" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/
wsdl/soap/">
 <types>
 <schema targetNamespace="http://www.flutebank.com/xml" xmlns:wsdl="http://
schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.flutebank.com/xml" xmlns:xsi="http://
www.w3.org/2001/XMLSchema-instance" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/
encoding/" xmlns="http://www.w3.org/2001/XMLSchema">
 <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
 <complexType >
 <complexContent>
 <restriction base="soap-enc:Array">
 <attribute ref="soap-enc:arrayType" wsdl:arrayType=
 "tns:PaymentDetail[]"/>
 </restriction>
 </complexContent>
 </complexType>
 <complexType >
 <sequence>
 <element type="dateTime"/>
 <element type="string"/>
 <element type="string"/>
 <element type="double"/>
 </sequence>
 </complexType>
 <complexType >
 <sequence>
 <element type="int"/>
 <element type="string"/>
 <element type="double"/>
 </sequence>
 </complexType>
 <complexType >
 <sequence>
 <element type="string"/>
 <element type="string"/>
 </sequence>
 </complexType>
 </schema>
 </types>
 <message name="BillPay_getLastPayment">
 <part name="String_1" type="xsd:string"/>
 </message>
 <message name="BillPay_getLastPaymentResponse">
 <part type="xsd:double"/>
 </message>
 <message name="BillPay_listScheduledPayments"/>
 <message name="BillPay_listScheduledPaymentsResponse">
 <part type="tns:ArrayOfPaymentDetail"/>
 </message>
 <message name="BillPay_schedulePayment">
 <part name="Date_1" type="xsd:dateTime"/>
 <part name="String_2" type="xsd:string"/>
 <part name="double_3" type="xsd:double"/>
 </message>
 <message name="BillPay_schedulePaymentResponse">
 <part type="tns:PaymentConfirmation"/>
 </message>
 <message >
 <part type="tns:ScheduleFailedException"/>
 </message>
 <portType >
 <operation parameterOrder="String_1">
 <input message="tns:BillPay_getLastPayment"/>
 <output message="tns:BillPay_getLastPaymentResponse"/>
 </operation>
 <operation parameterOrder="">
 <input message="tns:BillPay_listScheduledPayments"/>
 <output message="tns:BillPay_listScheduledPaymentsResponse"/>
 </operation>
 <operation parameterOrder="Date_1 String_2
 double_3">
 <input message="tns:BillPay_schedulePayment"/>
 <output message="tns:BillPay_schedulePaymentResponse"/>
 <fault message=
 "tns:ScheduleFailedException"/>
 </operation>
 </portType>
 <binding type="tns:BillPay">
 <operation >
 <input>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </input>
 <output>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </output>
 <soap:operation soapAction=""/>
 </operation>
 <operation >
 <input>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </input>
 <output>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </output>
 <soap:operation soapAction=""/>
 </operation>
 <operation >
 <input>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </input>
 <output>
 <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </output>
 <fault >
 <soap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 use="encoded" namespace="http://www.flutebank.com/xml"/>
 </fault>
 <soap:operation soapAction=""/>
 </operation>
 <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
 </binding>
 <service >
 <port binding="tns:BillPayBinding">
 <soap:address location="http://127.0.0.1:9090/billpayservice/jaxrpc/BillPay"/>
 </port>
 </service>
</definitions>



Java End example

Service Consumption

Until now, we have seen how to define, implement, and deploy a JAX-RPC service. Let us now look at how such a service can be consumed. A service consumer represents the abstraction of the entity invoking the facilities of an existing service. Invocation modes for doing so fall into three broad categories:

The significant difference between one-way and nonblocking invocation is that in the former, the client will not receive a return value. As a bare minimum, JAX-RPC implementations must support the first two modes for client invocation and HTTP 1.1 as the transport binding for SOAP. The sematics of nonblocking RPC are quite complicated. For example, the client must inform the service of an endpoint to which the service can repond, and both parties must deal with issues of reliability and availability. If your app requires asynchronous communication, messaging is probably more appropriate. See for details. Let us now look at the mechanisms an RPC client can use to consume the service in these invocation modes. The client can be written to invoke the service using one of the following three mechanisms:

In , we described WSDL use cases and early/late binding patterns associated with them. The reader is encouraged to revisit that section before continuing. Recall the usage patterns:

The examples of clients in the following sections show how some of these patterns can be realized.

Clients Using Stubs

Screenshot introduced the concept of stubs. Clients locate the service endpoint by specifying a URI, then simply invoke the methods on a local object, a stub that represents the remote service. JAX-RPC stubs, or proxies, as they are sometime referred to, are very different from RMI-IIOP stubs. Keep the following in mind:

The tie represents the server-side skeleton for the implementation. It is used by the endpoint to communicate with the implementation and is generated using tools (such as xrpcc) when the implementation is deployed. Using stubs is also sometime referred to as static invocation, because the stub must know the remote interface about the service at compile time. It must have the class file representing the remote interface and the implementation available for stub generation to proceed. The client does not need the WSDL file describing the service at runtime. Stubs are specific to a particular runtime and are not portable across vendor implementations. The code in Listing 10.6 shows the fragment for invoking the Billpayservice developed previously.

Listing 10.6: Client using stubs
// import generated xrpcc classes + interface class + Helper classes for interface
 import com.flutebank.billpayservice.*;
 import java.util.Date;
public class StubClient {
 public static void main(String[] args) throws Exception {
 String endpoint="http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay";
 String namespace = "http://www.flutebank.com/xml";
 String wsldport = "BillPayPort";
 Billpayservice_Impl serviceproxy= new Billpayservice_Impl();
 BillPay_Stub stub=(BillPay_Stub)(serviceproxy.getBillPayPort());
 stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,endpoint);
 PaymentConfirmation conf= stub.schedulePayment(new Date(),
 "my account at sprint", 190);
 System.out.println("Payment was scheduled "+ conf.getConfirmationNum());
 PaymentDetail detail[]=stub.listScheduledPayments();
 for(int i=0;i<detail.length;i++) {
 System.out.println("Payee name "+ detail[i].getPayeeName());
 System.out.println("Account "+ detail[i].getAccount());
 System.out.println("Amount "+ detail[i].getAmt());
 System.out.println("Will be paid on "+ detail[i].getDate());
 }
 double lastpaid= stub.getLastPayment("my cable tv provider");
 System.out.println("Last payment was "+ lastpaid);
 }
}



Java End example

Before using a stub, a client must first obtain a reference to it. The exact mechanism is specific to the implementation. The reference implementation for the stub is obtained by instantiating the service implementation class. The code below shows the mechanism another vendor might use:

InitialContext ctx = new InitialContext();
Billpayservice service =
 (Billpayservice) ctx.lookup("myserver:soap:Billpayservice");
BillPay bill = service. getBillPayPort ();
Stub stub= ((Stub) bill;



The stub can be configured by passing it name-value pairs of properties. The javax.xml.rpc.Stub interface defines four standard properties to configure the stub, using the stub. _setProperty(java.lang.String name, java.lang.Object value) method:

Clients Using DII

The second way a consumer can access a service involves the use of dynamic invocation interface (DII) instead of static stubs. DII is a concept that, like most other things in JAX-RPC, should be familiar to CORBA developers. Unlike static invocation, which requires that the client app include a client stub, DII enables a client app to invoke a service whose data types were unknown at the time the client was compiled. This allows a client to discover interfaces dynamically—in other words, at runtime rather than compile time—and invoke methods on objects that implement those interfaces. JAX-RPC supports DII with the javax.xml.rpc.Call interface. A Call object can be created on a javax.xml.rpc.Service using the port name and service name. Then, during runtime, the following details are set:

This information is derived by looking at the WSDL file for the service. For example, the service name is the service > element, the portname is the port element, and so on. Listing 10.7 shows a DII client where a Call object is configured for the getLastPayment method.

Listing 10.7: Client using DII directly, where all parameters are known (WSDL is not passed)
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.ServiceFactory;
public class DIIClient_NoWSDL{
 public static void main(String[] args) throws Exception {
 String endpoint="http://127.0.0.1:9090/billpayservice/jaxrpc/BillPay";
 String namespace = "http://www.flutebank.com/xml";
 String schemanamespace = "http://www.w3.org/2001/XMLSchema";
 String serviceName = "Billpayservice";
 ServiceFactory factory = ServiceFactory.newInstance();
 // the Billpayservice service does not exist
 // (no stub, skeleton, or Service was generated by xrpcc)
 // but createService will return a Service object
 // that can be used to create the dynamic call
 Service service = (Service) factory.createService
 (new QName(namespace,serviceName));
 QName portName = new QName(namespace," BillPayPort");
 QName operationName = new QName(namespace," getLastPayment");
 Call call = service.createCall(portName, operationName);
 call.setTargetEndpointAddress(endpoint);
 call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY,
 "http://schemas.xmlsoap.org/soap/encoding/");
 QName paramtype = new QName(schemanamespace, "string");
 QName returntype = new QName(schemanamespace, "double");
 call.addParameter("String_1", paramtype, ParameterMode.IN);
 call.setReturnType(returntype);
 Object[] params = {"my cable tv provider"};
 Object lastpaid= (Double)call.invoke(params);
 System.out.println("Last payment was "+ lastpaid);
 }
}



Java End example

The client code wraps the DII request in a Call object. DII can be used directly, by passing these values (port, operation, location, and part information) to the Call, or indirectly, by passing the WSDL to the Call. Listing 10.7 shows how a DII client can be written using the former. (QName is a common class used to represent a qualified name in different XML APIs. The qualified name of an XML element consists of its namespace declaration and its local name in the namespace.) What is relevant in Listing 10.7 is that there is no coupling between the service interface and the client (e.g., see import statements). In indirect DII, only the port and operation names are knowm at compile time. The runtime will determine the type information about the part and location, based on the WSDL. In this case, the parameters and return types do not need to be configured using the addParameter or setReturnType method. Listing 10.8 shows a sample DII client using the WSDL.

Listing 10.8: Client using DII indirectly, where all parameters are not known (WSDL is dynamically inspected)
public class DIIClient_WSDL{
 public static void main(String[] args) throws Exception {
 String wsdllocation= http://127.0.0.1:9090/billpayservice/billpayservice.wsdl";
 String namespace = "http://www.flutebank.com/xml";
 String serviceName = "Billpayservice";
 ServiceFactory factory = ServiceFactory.newInstance();
 Service service = (Service) factory.createService
 (new URL(wsdllocation),new QName(namespace,serviceName));
 QName portName = new QName(namespace," BillPayPort");
 QName operationName = new QName(namespace," getLastPayment");
 Call call = service.createCall(portName, operationName);
 Object[] params = {"my cable tv provider"};
 Object lastpaid= (Double)call.invoke(params);
 System.out.println("Last payment was "+ lastpaid);
 }
}



Java End example

Note that neither use of DII generates stubs. WSDL with DII. When deciding whether to use WSDL or not in the client, keep in mind that though it may be more convenient to use, it requires an extra network call and processing overhead for the runtime to fetch and process the WSDL and perhaps even validate the call against the WSDL.

One of the major differences between static invocation and dynamic invocation is that, while both support synchronous communication, only DII supports one-way communication. From an API perspective, instead of using the invoke() method, DII can be used to invoke the invokeOneWay(java.lang.Object[] inputParams) method. Attempting to invoke a call.getOutputParams() in a one-way invocation will result in a JAX-RPCException.

Clients Using Dynamic Proxies

The JAX-RPC specification also specifies a third way for clients to access services: using the concept of dynamic proxy classes available in the standard J2SE Reflection API (the java.lang.reflect.Proxy class and the java.lang.reflect .InvocationHandler interface). A dynamic proxy class implements a list of interfaces specified at runtime. The client can use this proxy or façade as though it actually implemented these interfaces, although it actually delegates the invocation to the implementation. Classes allowing any method on any of these interfaces can be called directly on the proxy (after casting it). Thus, a dynamic proxy class is used to create a type-safe proxy object for an interface list without requiring pregeneration of the proxy class, as you would with compile-time tools. Listing 10.9 shows how a client can use dynamic proxies.

Listing 10.9: Client using dynamic proxies
// jaxrpc classes import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
// java classes import java.util.Date;
import java.net.URL;
// Interface class
import com.flutebank.billpayservice.BillPay;
public class DynamicProxyClient {
 public static void main(String[] args) throws Exception{
 String namespace = "http://www.flutebank.com/xml";
 String wsldport = "BillPayPort";
 String wsdlservice = "Billpayservice";
 String wsdllocation =
 "http://127.0.0.1:8080/billpayservice/billpayservice.wsdl";
 URL wsldurl = new URL(wsdllocation);
 ServiceFactory factory = ServiceFactory.newInstance();
 Service service = factory.createService(wsldurl,
 new QName(namespace, wsdlservice));
// make the call to get the stub corresponding to this service and interface
 BillPay stub = (BillPay) service.getPort(new QName(namespace,wsldport),
 BillPay.class);
// invoke methods on the service
 double lastpaid= stub.getLastPayment("my cable tv provider");
 System.out.println("Last payment was "+ lastpaid);
 }
}



Java End example

In Listing 10.9, there is no compile-time stub generation. The getPort() method will return the proxy, which is also required to implement the Stub interface at runtime—that is, the stub is generated internally at runtime. Again, CORBA developers will see the similarity in the above code with its counterpart:

BillPay stub = (BillPay)PortableRemoteObject.narrow(initial.lookup("Billpayservice"),
 BillPay.class);


Clients Using WSDL

Until now, we have seen how to start with a Java service definition and implement it as an XML-RPC Web service. One could also do the reverse:

Let us look at how to consume the Billpayservice Web service using the service's WSDL and the WSDL 1.1-compliant xrpcc tool. The client-side bindings are generated from the WSDL using xrpcc, with only a configuration file change:

<?xml version="1.0" encoding="UTF-8"?>
 <configuration xmlns="http://java.oracle.com/xml/ns/jax-rpc/ri/config">
 <wsdl location=http://127.0.0.1:9090/billpayservice/billpayservice.wsdl
 package >
 </wsdl>
 </configuration>


The client-side code is identical to the StubClient shown previously, except that

import generated.*; // generated classes by xrpcc from WSDL file
import java.util.Calendar;
public class WSDLClient {
 public static void main(String[] args) throws Exception {
 String namespace = "http://www.flutebank.com/xml";
 String wsldport = "BillPayPort";
 Billpayservice_Impl serviceproxy= new Billpayservice_Impl();
 BillPay_Stub stub=(BillPay_Stub)(serviceproxy.getBillPayPort());
 PaymentConfirmation conf=stub.schedulePayment(Calendar.getInstance(),
 "my account at sprint", 190);
 System.out.println("Payment was scheduled "+
 conf.getConfirmationNum());
 PaymentDetail detail[]=stub.listScheduledPayments();
 for(int i=0;i<detail.length;i++) {
 System.out.println("Payee name "+ detail[i].getPayeeName());
 System.out.println("Account "+ detail[i].getAccount());
 System.out.println("Amount "+ detail[i].getAmt());
 System.out.println("Will be paid on "+
 detail[i].getDate().getTime());
 }
 double lastpaid= stub.getLastPayment("my cable tv provider");
 System.out.println("Last payment was "+ lastpaid);
 }
}


What Client Is Right for Me?

Choosing either option shown above to implement the client affects only client-side development. When a server method is invoked, that server has no knowledge of whether a method was invoked via the conventional static stub mechanism, through DII, through proxies, or even by a non-Java client. From the server's perspective, it receives a SOAP request and generates a SOAP response; these are identical for all client types. For example, Listings 10.10a and 10.10b show SOAP request and response messages for the getLastPayment() method, which is identical for stubs, DII (with or without WSDL), dynamic proxies, or WSDL.

Listing 10.10a: SOAP request
POST /billpayservice/jaxrpc/BillPay HTTP/1.1
Content-Type: text/xml; charset="utf-8"
Content-Length: 506
SOAPAction: ""
User-Agent: Java1.3.1_01
Host: 127.0.0.1:9090
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns0="http://
www.flutebank.com/xml" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<env:Body>
<ns0:getLastPayment>
 <String_1 xsi:type="xsd:string">my cable tv provider</String_1>
</ns0:getLastPayment>
</env:Body>
</env:Envelope>



Java End example
Listing 10.10b: SOAP response
HTTP/1.1 200 OK Content-Type: text/xml; charset="utf-8"
SOAPAction: ""
Transfer-Encoding: chunked Date: Mon, 29 Jul 2002 19:28:50 GMT Server: Apache Coyote HTTP/1.1 Connector [1.0]
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns0="http://
www.flutebank.com/xml" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
 <env:Body>
 <ns0:getLastPaymentResponse>
 <result xsi:type="xsd:double">829.0</result>
 </ns0:getLastPaymentResponse>
 </env:Body>
</env:Envelope>



Java End example

In most practical situations, an enterprise will develop a service and publish its WSDL. Service consumers will use the WSDL and a vendor-provided tool (such as xrpcc) to generate client-side bindings and invoke the service. This has several advantages. There is no distribution of client code (e.g., remote interfaces), and in most cases, the tool will generate the serializers and deserializers, using the encoding scheme. For example, xrpcc generates the serializers and deserializers using the SOAP encoding scheme for PaymentDetail[] and PaymentDetail and maps the supported XML Schema types to Java. Using stubs directly has the disadvantage of having to share the Java interface (and interface-dependent classes) with the service consumer. However, in scenarios where services will be developed within the boundaries of the enterprise, static stubs are the preferred client model, along the same lines as above. The performance with stubs is also expected to be better, since all type casting information is built in. All that occurs at runtime is service invocation.

DII is quite attractive, because it allows dynamic creation and invocation of object requests. In most cases, the architect of an app knows the kind of objects the app will need to access, and if not, WSDL should suffice. In some cases, such as object browsers and object brokers, DII is useful, but we don't envision these as frequent.

In practical situations and architecturally, DII is also not completely dynamic. Let us explain this further. Enterprise-level Web services will be coarse-grained and will frequently deal with passing data objects, such as the JavaBean's (e.g., PaymentDetail or PaymentConfirmation, as in the StubClient.java example). Simple data types will not suffice. For a DII client to be able to invoke these services, it will need the classes at compile time for the objects being passed around. (For example, if the DII code above invoked the schedulePayment method, the result would be a PaymentConfirmation object). The question is, where do these classes come from? The alternatives include using the same classes as the service, producing a coupling, or producing the classes from WSDL, using a tool (xrpcc). Further, if the data type that needs to be passed around is a custom type and not a JavaBean (e.g., a vector), a serializer and deserializer would need to be written for it. All this offsets the benefits DII offers of being a "dynamic invocation" at runtime.


JaVa
Comments