CHAPTER 1: Developing a Consumer with Celtix

Table of Contents

Generating the Stub Code 2

Implementing a Celtix Client 5

Setting Connection Properties with Contexts 8

Asynchronous Invocation Model 9

Generating the Stub Code

The starting point for developing a service consumer (or client) in Celtix 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 Celtix clients, the wsdl2java utility can generate the following kinds of code:

Basic HelloWorld WSDL contract

Listing 1 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://objectweb.org/hello_world_soap_http" 

    xmlns="http://schemas.xmlsoap.org/wsdl/" 

    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 

    xmlns:tns="http://objectweb.org/hello_world_soap_http"

    xmlns:x1="http://objectweb.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://objectweb.org/hello_world_soap_http/types" 

            xmlns="http://www.w3.org/2001/XMLSchema"

            elementFormDefault="qualified">

            <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="string"/>

                    </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">

        ...

    </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>

Listing 1: HelloWorld WSDL Contract

The Greeter port type from Listing 1 defines the following WSDL operations:

Listing 1 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 Celtix wsdl2soap or wsdl2xml utilities. Likewise, the SOAPService service can be generated automatically by running the Celtix wsdl2service utility.

Generating the Stub Code

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

wsdl2java -ant -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 Listing 1. The -ant option generates an ant build.xml file, for use with the ant build utility. The -client option generates starting point code for a client main() function.

The preceding wsdl2java command generates the following Java packages:

This package name is generated from the http://objectweb.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.

This package name is generated from the http://objectweb.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:

Implementing a Celtix Client

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

Generated Service Class

Listing 2 shows the typical outline a generated service class, ServiceName, which extends the javax.xml.ws.Service base class.

// Java

public class ServiceName extends javax.xml.ws.Service {


    ...

    public ServiceName(URL wsdlLocation, QName serviceName) { }


    public ServiceName() { }


    public Greeter getPortName() { }

    .

    .

    .

}

Listing 2: Outline of a Generated Service Class

The ServiceName class in Listing 2 defines the following 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:

For example, Listing 3 shows the Greeter service endpoint interface, which is generated from the Greeter port type defined in Listing 1. For simplicity, Listing 3 omits the standard JAXB and JAX-WS annotations.

// Java

/* 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;

}

Listing 3: The Greeter Service Endpoint Interface

Client Main Function

Listing 4 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.

// Java

package demo.hw.client;


import java.io.File;

import java.net.URL;

import javax.xml.namespace.QName;

import org.objectweb.hello_world_soap_http.Greeter;

import org.objectweb.hello_world_soap_http.PingMeFault;

import org.objectweb.hello_world_soap_http.SOAPService;


public final class Client {


    private static final QName SERVICE_NAME 

        = new QName("http://objectweb.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); 

    }


}

Listing 4: Client Implementation Code

The Client.main() function from Listing 4 proceeds as follows:

// Java

        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 getPortName() 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.

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:

Setting a Request Context

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

// Java

// Set request context property.

java.util.Map<String, Object> requestContext =

    ((javax.xml.ws.BindingProvider)port).getRequestContext();

requestContext.put(ContextPropertyNamePropertyValue);


// Invoke an operation.

port.SomeOperation();

Listing 5: Setting a Request Context Property on the Client Side

You have to cast the port object to javax.xml.ws.BindingProvider type 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 type and values of arbitrary type. Use the java.util.Map.put() method 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 in Listing 6.

// Java

// 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);

Listing 6: Reading a Response Context Property on the Client Side

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

Contexts Supported by Celtix

Celtix supports the following context properties:

Context Property Name

Context Property Type

org.objectweb.celtix.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES

org.objectweb.celtix.ws.addressing.AddressingProperties

Table 1: Celtix Context Properties

Asynchronous Invocation Model

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

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

WSDL Contract for Asynchronous Example

Listing 7 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.

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://objectweb.org/hello_world_async_soap_http" xmlns:x1="http://objectweb.org/hello_world_async_soap_http/types" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://objectweb.org/hello_world_async_soap_http" name="HelloWorld">

    <wsdl:types>

        <schema targetNamespace="http://objectweb.org/hello_world_async_soap_http/types" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:x1="http://objectweb.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>