JAX-WS Client Development Options - 6.3

Talend ESB Service Developer Guide

EnrichVersion
6.3
EnrichProdName
Talend Data Fabric
Talend Data Services Platform
Talend ESB
Talend MDM Platform
Talend Open Studio for ESB
Talend Real-Time Big Data Platform
task
Design and Development
Installation and Upgrade
EnrichPlatform
Talend ESB

WSDL2Java generated Client

One of the most common scenarios is that where you have a service which you may or not manage and this service has a WSDL. In this case you'll often want to generate a client from the WSDL. This provides you with a strongly typed interface by which to interact with the service. Once you've generated a client, typical usage of it will look like so:

HelloService service = new HelloService();
Hello client = service.getHelloHttpPort();
String result = client.sayHi("Joe");

The WSDL2Java tool will generate JAX-WS clients from your WSDL. You can run WSDL2java one of three ways:

For more in depth information read Developing a JAX-WS consumer or see the Hello World demos inside the distribution.

JAX-WS Proxy

Instead of using a wsdl2java-generated stub client directly, you can use Service.create to create Service instances, the following code illustrates this process:

import java.net.URL;
import javax.xml.ws.Service;
...

URL wsdlURL = new URL("http://localhost/hello?wsdl");
QName SERVICE_NAME = new QName("http://apache.org/hello_world_soap_http", 
   "SOAPService");
Service service = Service.create(wsdlURL, SERVICE_NAME);
Greeter client = service.getPort(Greeter.class);
String result = client.greetMe("test");

JAX-WS Dispatch APIs

JAX-WS provides the "dispatch" mechanism which makes it easy to dynamically invoke services which you have not generated a client for. Using the Dispatch mechanism you can create messages (which can be JAXB objects, Source objects, or a SAAJMessage) and dispatch them to the server. A simple example might look like this:

import java.net.URL;
import javax.xml.transform.Source;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
...

URL wsdlURL = new URL("http://localhost/hello?wsdl");
Service service = Service.create(wsdlURL, new QName("HelloService"));
Dispatch<Source> disp = service.createDispatch(new QName("HelloPort"),
   Source.class, Service.Mode.PAYLOAD);

Source request = new StreamSource("<hello/>")
Source response = disp.invoke(request);

NOTE: you can also use dispatches without a WSDL.

For more in depth information see the Hello World demos inside the distribution.

Usage Modes

Overview

Dispatch objects have two usage modes :

  • Message mode

  • Message Payload mode (Payload mode)

The usage mode you specify for a Dispatch object determines the amount of detail is passed to the user level code.

Message mode

In message mode , a Dispatch object works with complete messages. A complete message includes any binding specific headers and wrappers. For example, a consumer interacting with a service that requires SOAP messages would need to provide the Dispatch object's invoke() method a fully specified SOAP message. The invoke() method will also return a fully specified SOAP message. The consumer code is responsible for completing and reading the SOAP message's headers and the SOAP message's envelope information.

Note

Message mode is not ideal when you wish to work with JAXB objects.

You specify that a Dispatch object uses message mode by providing the value java.xml.ws.Service.Mode.MESSAGE when creating the Dispatch object.

Payload mode

In payload mode , also called message payload mode, a Dispatch object works with only the payload of a message. For example, a Dispatch object working in payload mode works only with the body of a SOAP message. The binding layer processes any binding level wrappers and headers. When a result is returned from invoke() the binding level wrappers and headers are already striped away and only the body of the message is left.

Note

When working with a binding that does not use special wrappers, such as the Artix ESB XML binding, payload mode and message mode provide the same results.

You specify that a Dispatch object uses payload mode by providing the value java.xml.ws.Service.Mode.PAYLOAD when creating the Dispatch object.

Data Types

Overview

Dispatch objects, because they are low-level objects, are not optimized for using the same JAXB generated types as the higher level consumer APIs. Dispatch objects work with the following types of objects:

  • javax.xml.transform.Source

  • javax.xml.soap.SOAPMessage

  • javax.activation.DataSource

  • JAXB

Using Source objects

A Dispatch object can accept and return objects that are derived from the javax.xml.transform.Source interface. Source objects are low level objects that hold XML documents. Each Source implementation provides methods that access the stored XML documents and manipulate its contents. The following objects implement the Source interface:

  • DOMSource

  • SAXSource

  • StreamSource

    Important

    When using Source objects the developer is responsible for ensuring that all required binding specific wrappers are added to the message. For example, when interacting with a service expecting SOAP messages, the developer must ensure that the required SOAP envelope is added to the outgoing request and that the SOAP envelope's contents are correct.

Using SOAPMessage objects

Dispatch objects can use javax.xml.soap.SOAPMessage objects when the following conditions are true:

  • the Dispatch object is using the SOAP binding.

  • the Dispatch object is using message mode.

Using DataSource objects

Dispatch objects can use objects that implement the javax.activation.DataSource interface when the following conditions are true:

  • the Dispatch object is using the HTTP binding.

  • the Dispatch object is using message mode.

DataSource objects provide a mechanism for working with MIME typed data from a variety of sources including URLs, files, and byte arrays.

Using JAXB objects

While Dispatch objects are intended to be low level API that allows you to work with raw messages, they also allow you to work with JAXB objects. To work with JAXB objects a Dispatch object must be passed a JAXBContext that knows how to marshal and unmarshal the JAXB objects in use. The JAXBContext is passed when the Dispatch object is created.

You can pass any JAXB object understood by the JAXBContext object as the parameter to the invoke() method. You can also cast the returned message into any JAXB object understood by the JAXBContext object.

Working with Dispatch Objects

Procedure

To use a Dispatch object to invoke a remote service you do the following:

  1. Create a Dispatch object.

  2. Construct a request message.

  3. Call the proper invoke() method.

  4. Parse the response message.

Creating a Dispatch object

To create a Dispatch object do the following:

  1. Create a Service object to represent the wsdl:service element defining the service on which the Dispatch object will make invocations.

  2. Create the Dispatch object using the Service object's createDispatch() method.

    public Dispatch<T> createDispatch(QName portName, 
       java.lang.Class<T> type, Service.Mode mode) throws WebServiceException;

    Note

    If you are using JAXB objects the method signature for createDispatch() is:

    public Dispatch<T> createDispatch(QName portName, 
       javax.xml.bind.JAXBContext context, Service.Mode mode)
       throws WebServiceException;

The following table describes the parameters for createDispatch() .

Parameter

Description

portName

Specifies the QName of the wsdl:port element that represent the service provider on which the Dispatch object will make invocations.

type

Specifies the data type of the objects used by the Dispatch object.

mode

Specifies the usage mode for the Dispatch object.

The code below creates a Dispatch object that works with DOMSource objects in payload mode.

package com.mycompany.demo;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;

public class Client {
   public static void main(String args[]) {
      QName serviceName = new QName("http://org.apache.cxf", 
         "stockQuoteReporter");
      Service s = Service.create(serviceName);

      QName portName = new QName("http://org.apache.cxf", 
         "stockQuoteReporterPort");
      Dispatch<DOMSource> dispatch = s.createDispatch(portName,
         DOMSource.class,
         Service.Mode.PAYLOAD);
   ...
  }
}

Constructing request messages

When working with Dispatch objects requests must be built from scratch. The developer is responsible for ensuring that the messages passed to a Dispatch object match a request that the targeted service provider can process. This requires precise knowledge about the messages used by the service provider and what, if any, header information it requires.

This information can be provided by a WSDL document or an XMLSchema document that defines the messages. While service providers vary greatly there are a few guidelines that can be followed:

  • The root element of the request is based in the value of the name attribute of the wsdl:operation element that corresponds to the operation being invoked.

    Warning

    If the service being invoked uses doc/literal bare messages, the root element of the request will be based on the value of name attribute of the wsdl:part element referred to by the wsdl:operation element.

  • The root element of the request will be namespace qualified.

  • If the service being invoked uses rpc/literal messages, the top-level elements in the request will not be namespace qualified.

    Important

    The children of top-level elements may be namespace qualified. To be certain you will need to check their schema definitions.

  • If the service being invoked uses rpc/literal messages, none of the top-level elements can be null.

  • If the service being invoked uses doc/literal messages, the schema definition of the message determines if any of the elements are namespace qualified.

For more information about how services use XML messages see the WS-I Basic Profile.

Synchronous invocation

For consumers that make synchronous invocations that generate a response, you use the Dispatch object's invoke() method shown bellow.

T invoke(T msg) throws WebServiceException;

The type of both the response and the request passed to the invoke() method are determined when the Dispatch object is created. For example if you created a Dispatch object using createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE) the response and the request would both be SOAPMessage objects.

Note

When using JAXB objects, the response and the request can be of any type the provided JAXBContext object can marshal and unmarshal. Also, the response and the request can be different JAXB objects.

The code below makes a synchronous invocation on a remote service using a DOMSource object.

// Creating a DOMSource Object for the request
DocumentBuilder db = DocumentBuilderFactory.newDocumentBuilder();
Document requestDoc = db.newDocument();
Element root = requestDoc.createElementNS(
   "http://org.apache.cxf/stockExample", "getStockPrice");
root.setNodeValue("DOW");
DOMSource request = new DOMSource(requestDoc);

// Dispatch disp created previously
DOMSource response = disp.invoke(request);
Asynchronous invocation

Dispatch objects also support asynchronous invocations. As with the higher level asynchronous APIs discussed in Chapter 4, Dispatch objects can use both the polling approach and the callback approach.

When using the polling approach the invokeAsync() method returns a Response<t> object that can be periodically polled to see if the response has arrived.

Response <T> invokeAsync(T msg)
throws WebServiceException;

When using the callback approach the invokeAsync() method takes an AsyncHandler implementation that processes the response when it is returned.

Future<?> invokeAsync(T msg, AsyncHandler<T> handler)
throws WebServiceException;

Note

As with the synchronous invoke() method, the type of the response and the type of the request are determined when you create the Dispatch object.

Oneway invocation

When a request does not generate a response, you make remote invocations using the Dispatch object's invokeOneWay() .

void invokeOneWay(T msg)
throws WebServiceException;

The type of object used to package the request is determined when the Dispatch object is created. For example if the Dispatch object is created using createDispatch(portName, DOMSource.class, Service.Mode.PAYLOAD) the request would be packaged into a DOMSource object.

Note

When using JAXB objects, the response and the request can be of any type the provided JAXBContext object can marshal and unmarshal. Also, the response and the request can be different JAXB objects.

The code below makes a one way invocation on a remote service using a JAXB object.

// Creating a JAXBContext and an Unmarshaller for the request
JAXBContext jbc = JAXBContext.newInstance("org.mycompany.StockExample");
Unmarshaller u = jbc.createUnmarshaller();

// Read the request from disk
File rf = new File("request.xml");
GetStockPrice request = (GetStockPrice)u.unmarshal(rf);

// Dispatch disp created previously
disp.invokeOneWay(request);
Operation Determination

When using a Dispatch client with a WSDL file, the operation name will be set under one of the following cases.

  • The javax.xml.ws.handler.MessageContext.WSDL_OPERATION property of the request context is set with the operation QName.

  • The addressing feature is enabled (in the bus or at the endpoint) and there is a matching operation to the payload element.

  • The "find.dispatch.operation" property of the request context is set to Boolean.TRUE and there is a matching operation to the payload element. This property is not set by default. Determining the correct operation will affect the processing such as setting the correct SOAPAction or WS-Addressing's Action.

Developing a Consumer

Generating the Stub Code

The starting point for developing a service consumer (or client) in CXF is a WSDL contract, complete with port type, binding, and service definitions. You can then use the wsdl2java utility to generate the Java stub code from the WSDL contract. The stub code provides the supporting code that is required to invoke operations on the remote service. For CXF clients, the wsdl2java utility can generate the following kinds of code:

  • Stub code - supporting files for implementing a CXF client.

  • Client starting point code - sample client code that connects to the remote service and invokes every operation on the remote service.

  • Ant build file - a build.xml file intended for use with the ant build utility. It has targets for building and for running the sample client application.

Basic HelloWorld WSDL contract

The below shows the HelloWorld WSDL contract. This contract defines a single port type, Greeter , with a SOAP binding, Greeter_SOAPBinding , and a service, SOAPService , which has a single port, SoapPort .

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="HelloWorld" 
   targetNamespace="http://apache.org/hello_world_soap_http" 
   xmlns="http://schemas.xmlsoap.org/wsdl/" 
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
   xmlns:tns="http://apache.org/hello_world_soap_http"
   xmlns:x1="http://apache.org/hello_world_soap_http/types"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <wsdl:types>
      <schema targetNamespace=
         "http://apache.org/hello_world_soap_http/types" 
         xmlns="http://www.w3.org/2001/XMLSchema"
         xmlns:tns="http://apache.org/hello_world_soap_http/types"
            elementFormDefault="qualified">
         <simpleType name="MyStringType">
            <restriction base="string">
               <maxLength value="30" />
            </restriction>
         </simpleType>
         <element name="sayHi">
            <complexType/>
         </element>
         <element name="sayHiResponse">
            <complexType>
               <sequence>
                  <element name="responseType" type="string"/>
               </sequence>
            </complexType>
         </element>
         <element name="greetMe">
            <complexType>
               <sequence>
                  <element name="requestType" type="tns:MyStringType"/>
               </sequence>
            </complexType>
         </element>
         <element name="greetMeResponse">
            <complexType>
               <sequence>
                  <element name="responseType" type="string"/>
               </sequence>
            </complexType>
         </element>
         <element name="greetMeOneWay">
            <complexType>
               <sequence>
                  <element name="requestType" type="string"/>
               </sequence>
            </complexType>
         </element>
         <element name="pingMe">
            <complexType/>
         </element>
         <element name="pingMeResponse">
            <complexType/>
         </element>
         <element name="faultDetail">
            <complexType>
               <sequence>
                  <element name="minor" type="short"/>
                  <element name="major" type="short"/>
               </sequence>
            </complexType>
         </element>
      </schema>
   </wsdl:types>
<wsdl:message name="sayHiRequest">
      <wsdl:part element="x1:sayHi" name="in"/>
   </wsdl:message>
   <wsdl:message name="sayHiResponse">
      <wsdl:part element="x1:sayHiResponse" name="out"/>
   </wsdl:message>
   <wsdl:message name="greetMeRequest">
      <wsdl:part element="x1:greetMe" name="in"/>
   </wsdl:message>
   <wsdl:message name="greetMeResponse">
      <wsdl:part element="x1:greetMeResponse" name="out"/>
   </wsdl:message>
   <wsdl:message name="greetMeOneWayRequest">
      <wsdl:part element="x1:greetMeOneWay" name="in"/>
   </wsdl:message>
   <wsdl:message name="pingMeRequest">
      <wsdl:part name="in" element="x1:pingMe"/>
   </wsdl:message>
   <wsdl:message name="pingMeResponse">
      <wsdl:part name="out" element="x1:pingMeResponse"/>
   </wsdl:message>       
   <wsdl:message name="pingMeFault">
      <wsdl:part name="faultDetail" element="x1:faultDetail"/>
   </wsdl:message>  
   <wsdl:portType name="Greeter">
      <wsdl:operation name="sayHi">
         <wsdl:input message="tns:sayHiRequest" name="sayHiRequest"/>
         <wsdl:output message="tns:sayHiResponse" name="sayHiResponse"/>
      </wsdl:operation>
         
      <wsdl:operation name="greetMe">
         <wsdl:input message="tns:greetMeRequest" 
            name="greetMeRequest"/>
         <wsdl:output message="tns:greetMeResponse" 
            name="greetMeResponse"/>
      </wsdl:operation>
         
      <wsdl:operation name="greetMeOneWay">
         <wsdl:input message="tns:greetMeOneWayRequest" 
            name="greetMeOneWayRequest"/>
      </wsdl:operation>

      <wsdl:operation name="pingMe">
         <wsdl:input name="pingMeRequest" message="tns:pingMeRequest"/>
         <wsdl:output name="pingMeResponse" message="tns:pingMeResponse"/>
         <wsdl:fault name="pingMeFault" message="tns:pingMeFault"/>
      </wsdl:operation> 
   </wsdl:portType>
<wsdl:binding name="Greeter_SOAPBinding" type="tns:Greeter">
      <soap:binding style="document" 
         transport="http://schemas.xmlsoap.org/soap/http"/>
        
      <wsdl:operation name="sayHi">
         <soap:operation soapAction="" style="document"/>
         <wsdl:input name="sayHiRequest">
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output name="sayHiResponse">
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
        
      <wsdl:operation name="greetMe">
         <soap:operation soapAction="" style="document"/>
         <wsdl:input name="greetMeRequest">
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output name="greetMeResponse">
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
        
      <wsdl:operation name="greetMeOneWay">
         <soap:operation soapAction="" style="document"/>
         <wsdl:input name="greetMeOneWayRequest">
            <soap:body use="literal"/>
         </wsdl:input>
      </wsdl:operation>

      <wsdl:operation name="pingMe">
         <soap:operation style="document"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
         <wsdl:fault name="pingMeFault">
            <soap:fault name="pingMeFault" use="literal"/>
         </wsdl:fault>
      </wsdl:operation>
        
   </wsdl:binding>

   <wsdl:service name="SOAPService">
      <wsdl:port binding="tns:Greeter_SOAPBinding" name="SoapPort">
         <soap:address 
            location="http://localhost:9000/SoapContext/SoapPort"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

The Greeter port type from the example above defines the following WSDL operations:

  • sayHi - has a single output parameter, of xsd:string .

  • greetMe - has an input parameter, of xsd:string , and an output parameter, of xsd:string .

  • greetMeOneWay - has a single input parameter, of xsd:string . Because this operation has no output parameters, CXF can optimize this call to be a oneway invocation (that is, the client does not wait for a response from the server).

  • pingMe - has no input parameters and no output parameters, but it can raise a fault exception.

The above example also defines a binding, Greeter_SOAPBinding , for the SOAP protocol. In practice, the binding is normally generated automatically - for example, by running either of the CXF wsdl2soap or wsdl2xml utilities. Likewise, the SOAPService service can be generated automatically by running the CXF wsdl2service utility.

Generating the stub code

After defining the WSDL contract, you can generate client code using the CXF wsdl2java utility. Enter the following command at a command-line prompt:

wsdl2java -client -d ClientDir hello_world.wsdl

Where ClientDir is the location of a directory where you would like to put the generated files and hello_world.wsdl is a file containing the contract shown in the above WSDL. The -client option generates starting point code for a client main() method.

The preceding wsdl2java command generates the following Java packages:

  • org.apache.hello_world_soap_http This package name is generated from the http://apache.org/hello_world_soap_http target namespace. All of the WSDL entities defined in this target namespace (for example, the Greeter port type and the SOAPService service) map to Java classes in the corresponding Java package.

  • org.apache.hello_world_soap_http.types This package name is generated from the http://apache.org/hello_world_soap_http/types" target namespace. All of the XML types defined in this target namespace (that is, everything defined in the wsdl:types element of the HelloWorld contract) map to Java classes in the corresponding Java package.

The stub files generated by the wsdl2java command fall into the following categories:

  • Classes representing WSDL entities (in the org.apache.hello_world_soap_http package) - the following classes are generated to represent WSDL entities:

    • Greeter is a Java interface that represents the Greeter WSDL port type. In JAX-WS terminology, this Java interface is a service endpoint interface.

    • SOAPService is a Java class that represents the SOAPService WSDL service element.

    • PingMeFault is a Java exception class (extending java.lang.Exception ) that represents the pingMeFault WSDL fault element.

  • Classes representing XML types (in the org.apache.hello_world_soap_http.types package) - in the HelloWorld example, the only generated types are the various wrappers for the request and reply messages. Some of these data types are useful for the asynchronous invocation model.

Implementing a CXF Client

This section describes how to write the code for a simple Java client, based on the WSDL contract above. To implement the client, you need to use the following stub classes:

  • Service class (that is, SOAPService ).

  • Service endpoint interface (that is, Greeter ).

Generated service class

The next example shows the typical outline a generated service class, ServiceName , which extends the javax.xml.ws.Service base class.

Example 9. Outline of a Generated Service Class

public class ServiceName extends javax.xml.ws.Service {
   ...
   public ServiceName(URL wsdlLocation, QName serviceName) { } 
   public ServiceName() { }
   public Greeter getPortName() { }
   ...
}

The ServiceName class above defines the following methods:

  • Constructor methods - the following forms of constructor are defined:

    • ServiceName(URL wsdlLocation, QName serviceName) constructs a service object based on the data in the serviceName service in the WSDL contract that is obtainable from wsdlLocation .

    • ServiceName() is the default constructor, which constructs a service object based on the service name and WSDL contract that were provided at the time the stub code was generated (for example, when running the CXF wsdl2java command). Using this constructor presupposes that the WSDL contract remains available at its original location.

  • get_PortName_() methods - for every PortName port defined on the ServiceName service, CXF generates a corresponding get_PortName_() method in Java. Therefore, a wsdl:service element that defines multiple ports will generate a service class with multiple get_PortName_() methods.

Service endpoint interface

For every port type defined in the original WSDL contract, you can generate a corresponding service endpoint interface in Java. A service endpoint interface is the Java mapping of a WSDL port type. Each operation defined in the original WSDL port type maps to a corresponding method in the service endpoint interface. The operation's parameters are mapped as follows:

  1. The input parameters are mapped to method arguments.

  2. The first output parameter is mapped to a return value.

  3. If there is more than one output parameter, the second and subsequent output parameters map to method arguments (moreover, the values of these arguments must be passed using Holder types).

The next example shows the Greeter service endpoint interface, which is generated from the Greeter port type defined in the WSDL above. For simplicity, this example omits the standard JAXB and JAX-WS annotations.

Example 10. The Greeter Service Endpoint Interface

/* Generated by WSDLToJava Compiler. */

package org.objectweb.hello_world_soap_http;
  ...
public interface Greeter {
   public java.lang.String sayHi(); 
   public java.lang.String greetMe(java.lang.String requestType);
   public void greetMeOneWay(java.lang.String requestType);
   public void pingMe() throws PingMeFault;
}

Client main function

This example shows the Java code that implements the HelloWorld client. In summary, the client connects to the SoapPort port on the SOAPService service and then proceeds to invoke each of the operations supported by the Greeter port type.

package demo.hw.client;

import java.io.File;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.hello_world_soap_http.Greeter;
import org.apache.hello_world_soap_http.PingMeFault;
import org.apche.hello_world_soap_http.SOAPService;

public final class Client {

   private static final QName SERVICE_NAME = 
      new QName("http://apache.org/hello_world_soap_http", 
      "SOAPService");

   private Client() {}

   public static void main(String args[]) throws Exception {
      if (args.length == 0) {
         System.out.println("please specify wsdl");
         System.exit(1);
      }

      URL wsdlURL;
      File wsdlFile = new File(args[0]);
      if (wsdlFile.exists()) {
         wsdlURL = wsdlFile.toURL();
      } else {
         wsdlURL = new URL(args[0]);
      }

      System.out.println(wsdlURL);
      SOAPService ss = new SOAPService(wsdlURL, SERVICE_NAME);
      Greeter port = ss.getSoapPort();
      String resp;

      System.out.println("Invoking sayHi...");
      resp = port.sayHi();
      System.out.println("Server responded with: " + resp);
      System.out.println();

      System.out.println("Invoking greetMe...");
      resp = port.greetMe(System.getProperty("user.name"));
      System.out.println("Server responded with: " + resp);
      System.out.println();

      System.out.println("Invoking greetMeOneWay...");
      port.greetMeOneWay(System.getProperty("user.name"));
      System.out.println("No response from server as method is OneWay");
      System.out.println();
 
      try {
         System.out.println("Invoking pingMe, expecting exception...");
         port.pingMe();
      } catch (PingMeFault ex) {
         System.out.println(
            "Expected exception: PingMeFault has occurred.");
         System.out.println(ex.toString());
      }
      System.exit(0);
   }
}

The Client.main() function from the above example proceeds as follows:

  1. The CXF runtime is implicitly initialized - that is, provided the CXF runtime classes are loaded. Hence, there is no need to call a special function in order to initialize CXF.

  2. The client expects a single string argument that gives the location of the WSDL contract for HelloWorld. The WSDL location is stored in wsdlURL .

  3. A new port object (which enables you to access the remote server endpoint) is created in two steps, as shown in the following code fragment:

    SOAPService ss = new SOAPService(wsdlURL, SERVICE_NAME);
    Greeter port = ss.getSoapPort();

    To create a new port object, you first create a service object (passing in the WSDL location and service name) and then call the appropriate get PortName () method to obtain an instance of the particular port you need. In this case, the SOAPService service supports only the SoapPort port, which is of Greeter type.

  4. The client proceeds to call each of the methods supported by the Greeter service endpoint interface.

  5. In the case of the pingMe() operation, the example code shows how to catch the PingMeFault fault exception.

Setting Connection Properties with Contexts

You can use JAX-WS contexts to customize the properties of a client proxy. In particular, contexts can be used to modify connection properties and to send data in protocol headers. For example, you could use contexts to add a SOAP header, either to a request message or to a response message. The following types of context are supported on the client side:

  • Request context - on the client side, the request context enables you to set properties that affect outbound messages. Request context properties are applied to a specific port instance and, once set, the properties affect every subsequent operation invocation made on the port, until such time as a property is explicitly cleared. For example, you might use a request context property to set a connection timeout or to initialize data for sending in a header.

  • Response context - on the client side, you can access the response context to read the property values set by the inbound message from the last operation invocation. Response context properties are reset after every operation invocation. For example, you might access a response context property to read header information received from the last inbound message.

Setting a request context

To set a particular request context property, ContextPropertyName , to the value, PropertyValue , use the code shown in the below example.

Example 11. Setting a Request Context Property on the Client Side

// Set request context property.
java.util.Map<String, Object> requestContext =
   ((javax.xml.ws.BindingProvider)port).getRequestContext();
requestContext.put(ContextPropertyName, PropertyValue);

// Invoke an operation.
port.SomeOperation();

You have to cast the port object to javax.xml.ws.BindingProvider in order to access the request context. The request context itself is of type, java.util.Map<String, Object> , which is a hash map that has keys of String and values of arbitrary type. Use java.util.Map.put() to create a new entry in the hash map.

Reading a response context

To retrieve a particular response context property, ContextPropertyName , use the code shown below.

Example 12. Reading a Response Context Property on the Client Side

// Invoke an operation.
port.SomeOperation();

// Read response context property.
java.util.Map<String, Object> responseContext =
   ((javax.xml.ws.BindingProvider)port).getResponseContext();
PropertyType propValue = (PropertyType) 
   responseContext.get(ContextPropertyName);

The response context is of type, java.util.Map<String, Object> , which is a hash map that has keys of type String and values of an arbitrary type. Use java.util.Map.get() to access an entry in the hash map of response context properties.

Supported contexts

CXF supports the following context properties:

Context Property Name

Context Property Type

org. apache. cxf. ws. addressing. JAXWSAConstants. CLIENT_ADDRESSING_PROPERTIES

org. apache. cxf. ws. addressing. AddressingProperties

Asynchronous Invocation Model

In addition to the usual synchronous mode of invocation, CXF also supports two forms of asynchronous invocation, as follows:

  • Polling approach - in this case, to invoke the remote operation, you call a special method that has no output parameters, but returns a javax.xml.ws.Response instance. The Response object (which inherits from the javax.util.concurrency.Future interface) can be polled to check whether or not a response message has arrived.

  • Callback approach - in this case, to invoke the remote operation, you call another special method that takes a reference to a callback object (of javax.xml.ws.AsyncHandler type) as one of its parameters. Whenever the response message arrives at the client, the CXF runtime calls back on the AsyncHandler object to give it the contents of the response message.

Both of these asynchronous invocation approaches are described here and illustrated by code examples.

Contract for asynchronous example

The below example shows the WSDL contract that is used for the asynchronous example. The contract defines a single port type, GreeterAsync , which contains a single operation, greetMeSometime .

<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://apache.org/hello_world_async_soap_http"
   xmlns:x1="http://apache.org/hello_world_async_soap_http/types"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   targetNamespace="http://apache.org/hello_world_async_soap_http"
   name="HelloWorld">
   <wsdl:types>
      <schema targetNamespace=
         "http://apache.org/hello_world_async_soap_http/types"
          xmlns="http://www.w3.org/2001/XMLSchema"
          xmlns:x1="http://apache.org/hello_world_async_soap_http/types"
          elementFormDefault="qualified">
         <element name="greetMeSometime">
            <complexType>
               <sequence>
                  <element name="requestType" type="xsd:string"/>
               </sequence>
            </complexType>
         </element>
         <element name="greetMeSometimeResponse">
            <complexType>
               <sequence>
                  <element name="responseType" type="xsd:string"/>
               </sequence>
            </complexType>
         </element>      
      </schema>
   </wsdl:types>
   <wsdl:message name="greetMeSometimeRequest">
      <wsdl:part name="in" element="x1:greetMeSometime"/>
   </wsdl:message>
   <wsdl:message name="greetMeSometimeResponse">
      <wsdl:part name="out" element="x1:greetMeSometimeResponse"/>
   </wsdl:message>
   <wsdl:portType name="GreeterAsync">
      <wsdl:operation name="greetMeSometime">
         <wsdl:input name="greetMeSometimeRequest" 
             message="tns:greetMeSometimeRequest"/>
         <wsdl:output name="greetMeSometimeResponse" 
             message="tns:greetMeSometimeResponse"/>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="GreeterAsync_SOAPBinding" 
      type="tns:GreeterAsync">
      <soap:binding style="document" 
         transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="greetMeSometime">
         <soap:operation style="document"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="SOAPService">
      <wsdl:port name="SoapPort" 
         binding="tns:GreeterAsync_SOAPBinding">
      <soap:address 
         location="http://localhost:9000/SoapContext/SoapPort"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>
Generating the asynchronous stub code

The asynchronous style of invocation requires extra stub code (for example, dedicated asychronous methods defined on the service endpoint interface). This special stub code is not generated by default, however. To switch on the asynchronous feature and generate the requisite stub code, you must use the mapping customization feature from the WSDL 2.0 specification.

Customization enables you to modify the way the wsdl2java utility generates stub code. In particular, it enables you to modify the WSDL-to-Java mapping and to switch on certain features. Here, customization is used to switch on the asynchronous invocation feature. Customizations are specified using a binding declaration, which you define using a jaxws:bindings tag (where the jaxws prefix is tied to the http://java.sun.com/xml/ns/jaxws" namespace). There are two alternative ways of specifying a binding declaration:

  • External binding declaration - the jaxws:bindings element is defined in a file separately from the WSDL contract. You specify the location of the binding declaration file to the wsdl2java utility when you generate the stub code.

  • Embedded binding declaration - you can also embed the jaxws:bindings element directly in a WSDL contract, treating it as a WSDL extension. In this case, the settings in jaxws:bindings apply only to the immediate parent element.

This section considers only the first approach, the external binding declaration. The template for a binding declaration file that switches on asynchronous invocations is shown below.

Example 13. Template for an Asynchronous Binding Declaration

<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   wsdlLocation="@WSDL_LOCATION@/hello_world_async.wsdl"
   xmlns="http://java.sun.com/xml/ns/jaxws">
   <bindings node="wsdl:definitions">
      <enableAsyncMapping>true</enableAsyncMapping>
   </bindings>
</bindings>

Where AffectedWSDLContract specifies the URL of the WSDL contract that is affected by this binding declaration. The AffectedNode is an XPath value that specifies which node (or nodes) from the WSDL contract are affected by this binding declaration. You can set AffectedNode to wsdl:definitions , if you want the entire WSDL contract to be affected. The {jaxws:enableAsyncMapping}} element is set to true to enable the asynchronous invocation feature.

For example, if you want to generate asynchronous methods only for the GreeterAsync port type, you could specify <bindings node="wsdl:definitions/wsdl:portType[@name='GreeterAsync']"> in the preceding binding declaration.

Assuming that the binding declaration is stored in a file, async_binding.xml , you can generate the requisite stub files with asynchronous support by entering the following wsdl2java command:

wsdl2java -client -d ClientDir -b async_binding.xml hello_world.wsdl

When you run the wsdl2java command, you specify the location of the binding declaration file using the -b option. After generating the stub code in this way, the GreeterAsync service endpoint interface (in the file GreeterAsync.java ) is defined as shown below.

Example 14. Service Endpoint Interface with Methods for Asynchronous Invocations

/* Generated by WSDLToJava Compiler. */
package org.apache.hello_world_async_soap_http;
...
import java.util.concurrent.Future;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
...
public interface GreeterAsync {

   public Future<?> greetMeSometimeAsync(
      java.lang.String requestType,
      AsyncHandler<org.myorg.types.GreetMeSometimeResponse> asyncHandler
   );

   public Response<org.myorg.types.GreetMeSometimeResponse> 
      greetMeSometimeAsync(java.lang.String requestType);

   public java.lang.String greetMeSometime(java.lang.String requestType);
}

In addition to the usual synchronous method, greetMeSometime() , two asynchronous methods are also generated for the greetMeSometime operation, as follows:

  • greetMeSometimeAsync() method with Future<?> return type and an extra javax.xml.ws.AsyncHandler parameter - call this method for the callback approach to asynchronous invocation.

  • greetMeSometimeAsync() method with Response<GreetMeSometimeResponse> return type - call this method for the polling approach to asynchronous invocation.

The details of the callback approach and the polling approach are discussed in the following subsections.

Implementing an asynchronous client with the polling approach

The next example illustrates the polling approach to making an asynchronous operation call. Using this approach, the client invokes the operation by calling the special Java method, _OperationName_Async() , that returns a javax.xml.ws.Response<T> object, where T is the type of the operation's response message. The Response<T> object can be polled at a later stage to check whether the operation's response message has arrived.

Example 15. Polling Approach for an Asynchronous Operation Call

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_async_soap_http.GreeterAsync;
import org.apache.hello_async_soap_http.SOAPService;
import org.apache.hello_async_soap_http.types.GreetMeSometimeResponse;

public final class Client {
   private static final QName SERVICE_NAME
      = new QName("http://objectweb.org/hello_async_soap_http", 
      "SOAPService");

   private Client() {}

   public static void main(String args[]) throws Exception {
      ...
      // Polling approach:
      Response<GreetMeSometimeResponse> greetMeSomeTimeResp =
         port.greetMeSometimeAsync(System.getProperty("user.name"));
      while (!greetMeSomeTimeResp.isDone()) {
         Thread.sleep(100);
      }
      GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
      ...
      System.exit(0);
   }
}

The greetMeSometimeAsync() method invokes the greetMeSometimes operation, transmitting the input parameters to the remote service and returning a reference to a javax.xml.ws.Response<GreetMeSometimeResponse> object. The Response class is defined by extending the standard java.util.concurrency.Future<T> interface, which is specifically designed for polling the outcome of work performed by a concurrent thread. There are essentially two basic approaches to polling using the Response object:

  • Non-blocking polling - before attempting to get the result, check whether the response has arrived by calling the non-blocking Response<T>.isDone() method. For example:

    Response<GreetMeSometimeResponse> greetMeSomeTimeResp = ...;
    
    if (greetMeSomeTimeResp.isDone()) {
       GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
    }
  • Blocking polling - call Response<T>.get() right away and block until the response arrives (optionally specifying a timeout). For example, to poll for a response, with a 60 second timeout:

    Response<GreetMeSometimeResponse> greetMeSomeTimeResp = ...;
    
    GreetMeSometimeResponse reply = greetMeSomeTimeResp.get(60L,
       java.util.concurrent.TimeUnit.SECONDS);
Implementing an asynchronous client with the callback approach

An alternative approach to making an asynchronous operation invocation is to implement a callback class, by deriving from the javax.xml.ws.AsyncHandler interface. This callback class must implement a handleResponse() method, which is called by the CXF runtime to notify the client that the response has arrived. The below example shows an outline of the AsyncHandler interface that you need to implement.

Example 16. The javax.xml.ws.AsyncHandler Interface

package javax.xml.ws;

public interface AsyncHandler<T> {
   void handleResponse(Response<T> res);
}

In this example, a callback class, TestAsyncHandler , is defined as shown in the example below.

Example 17. The TestAsyncHandler Callback Class

package demo.hw.client;

import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;

import org.apache.hello_async_soap.types.GreetMeSometimeResponse;

public class TestAsyncHandler implements 
   AsyncHandler<GreetMeSometimeResponse> {
   private GreetMeSometimeResponse reply;

   public void handleResponse(Response<GreetMeSometimeResponse> 
      response) {
      try {
         reply = response.get();
      } catch (Exception ex) {
      ex.printStackTrace();
   }
}

   public String getResponse() {
      return reply.getResponseType();
   }
}

The implementation of handleResponse() shown in Example11 simply gets the response data and stores it in a member variable, reply . The extra getResponse() method is just a convenience method that extracts the sole output parameter (that is, responseType ) from the response.

Example12 illustrates the callback approach to making an asynchronous operation call. Using this approach, the client invokes the operation by calling the special Java method, _OperationName_Async() , that returns a java.util.concurrency.Future<?> object and takes an extra parameter of AsyncHandler<T> .

Example 18. Callback Approach for an Asynchronous Operation Call

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_async_soap_http.GreeterAsync;
import org.apache.hello_async_soap_http.SOAPService;
import org.apache.hello_async_soap_http.types.GreetMeSometimeResponse;

public final class Client {
   private static final QName SERVICE_NAME
      = new QName("http://apache.org/hello_world_async_soap_http", 
      "SOAPService");

   private Client() {}

   public static void main(String args[]) throws Exception {
       ...
       // Callback approach
       TestAsyncHandler testAsyncHandler = new TestAsyncHandler();
       System.out.println(
          "Invoking greetMeSometimeAsync using callback object...");
       Future<?> response = port.greetMeSometimeAsync(
          System.getProperty("user.name"), testAsyncHandler);
       while (!response.isDone()) {
         Thread.sleep(100);
       }
       resp = testAsyncHandler.getResponse();
       ...
       System.exit(0);
   }
}

The Future<?> object returned by greetMeSometimeAsync() can be used only to test whether or not a response has arrived yet - for example, by calling response.isDone() . The value of the response is only made available to the callback object, testAsyncHandler .