Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

Advanced JAX-RPC Examples

This section describes the code examples in the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/ directory. To understand these examples, you should already be familiar with the following:

The following sections describe the advanced examples:


Note: Before proceeding, make sure that you've followed the instructions in Setting Up for setting the PATH variable and editing the common/build.properties file.


SOAP Message Handlers Example

JAX-RPC makes it easy to develop Web services and clients because it shields application developers from the underlying SOAP messages. Instead of writing code to build and parse SOAP messages, application developers merely implement the service methods and invoke them from remote clients. The handler processing is hidden from the JAX-RPC client and service implementation code.

However, there are times when you might want to access the SOAP messages that flow between JAX-RPC clients and services. For example, for auditing purposes, you might want to log every invocation of a service method. Or, you might want to encrypt remote calls at the SOAP message level. These logging and encrypting operations can be implemented with SOAP message handlers.

A SOAP message handler is a stateless instance that accesses SOAP messages representing RPC requests, responses, or faults. Tied to service endpoints, handlers enable you to process SOAP messages and to extend the functionality of the service. For a given service endpoint, one or more handlers may reside on the server and client.

Handler Processing

A SOAP request is processed as follows:

A SOAP response is processed in this order:

A SOAP fault is processed in the same manner as a SOAP response.

Clients and servers may have multiple handlers, which are configured into handler chains. For example, in a client handler chain with three handlers, the first handler processes the SOAP request, then the second processes the request, and then the third. When the third handler finishes, the request is sent to the server.

Overview of Handler Example

The client and service implementation code are quite simple. The client invokes the service method named helloServer, which echoes a String back to the client.

The client JAR file includes the following classes:

The service WAR includes these classes:

Handler Programming Model

The process for building the service with a handler requires these steps:

  1. Write the code for the service endpoint interface, service implementation class, and server handler classes
  2. Create the jaxrpc-ri.xml file.
  3. Read by wsdeploy, the jaxrpc-ri.xml file has information about the service handlers. For details see the section, The Service jaxrpc-ri.xml File.

  4. Create the web.xml file.
  5. Compile the code from step 1.
  6. Package the web.xml file, jaxrpc-ri.xml file, and compiled classes, into a raw WAR file (raw.war).
  7. Run wsdeploy to cook the raw WAR file and create a deployable WAR file (handler.war).
  8. Deploy the WAR file.
  9. The deployed service has this endpoint address URL:

    http://localhost:8080/handler/test 
    

For a client with a handler, do the following:

  1. Code the client program and the handler.
  2. Create the config.xml file.
  3. This file is read by wscompile and contains information about the client handler. For details see the section, The Client config.xml File.

  4. Compile the client handler.
  5. Run wscompile to generate the service endpoint interface and client-side classes.
  6. The wscompile command accesses the deployed WSDL file specified in config.xml.

  7. Compile the client program.

The client is now ready to be run.

Building and Running the Handler Example

To build and run this example, go to the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/handler directory and type the following commands:

ant build
ant deploy
ant build-client
ant run 

The ant run tasks executes the client program, which should display the following:

ClientHandler1: name = My Client Handler
ClientHandler1: adding loggerElement
ClientHandler1: adding nameElement
HandlerSampleClient: hi there
HandlerSampleClient: all done. 

At runtime, the example handlers and service implementation write the following lines to the <JWSDP_HOME>/logs/launcher.server.log file:

ServerHandler1: name = server1
ServerHandler2: name = server2
ServerHandler1: important message (level 10)
ServerHandler2: Request is from Duke
TestHandlerImpl: helloServer() message = hi there 

Client Handler

The ClientHandler1 instance processes the SOAP request before it is transmitted to the service endpoint. The source code for ClientHandler1 is in the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/handler/client/ directory.

Like the other handlers in this example, ClientHandler1 is a subclass of javax.xml.rpc.handler.GenericHandler:

public class ClientHandler1 extends GenericHandler . . . 

GenericHandler is an abstract class that implements javax.xml.rpc.handler.Handler, an interface that defines the methods required by any handler. Because it provides default implementations for these methods, GenericHandler makes it easy to write your own handlers.

Of the several methods implemented by GenericHandler, ClientHandler1 overrides only these methods:

The init Method of ClientHandler1

The init life-cycle method enables the Handler instance to initialize itself. Typically, you'll implement the init method to connect to resources such as databases. You can set instance variables in the init method, but be aware that a handler is stateless-- the next time a handler is invoked, it might have a different state. Prior to termination, the handler's destroy method is invoked. If you'd connected to resources in the init method, you might need to disconnect from them in the destroy method.

The init method of ClientHandler1 follows. It fetches the value of the property name, which was set in the config.xml file. (See The Client config.xml File.)

public void init(HandlerInfo info) {
    handlerInfo = info;
    name = (String) info.getHandlerConfig().get("name");
    System.out.println("ClientHandler1: name = " + name);
} 
The getHeaders Method of ClientHandler1

The getHeaders method retrieves the header blocks of the message processed by this handler. Because getHeaders is declared as abstract in GenericHandler, it must be implemented in ClientHandler1:

public QName[] getHeaders() {
    return handlerInfo.getHeaders();
} 
The handleRequest Method of ClientHandler1

A handler may process a SOAP request, response, or fault. Defined by the Handler interface, the handleRequest, handleResponse, and handleFault methods perform the actual processing of the SOAP messages. The ClientHandler1 class implements the handleRequest method, listed at the end of this section.

The handleRequest method of ClientHandler1 gets access to the SOAP message from the SOAPMessageContext parameter. (For more information about the SOAP APIs, see Chapter 13.) The method adds two SOAPHeaderElement objects to the SOAP request: loggerElement and nameElement. The loggerElement header will be processed by ServiceHandler1 and the nameElement header by ServiceHandler2. The ServiceHandler1 instance will check the logging level that ClientHandler1 set by invoking loggerElement.setValue. The ServiceHandler2 instance will retrieve the Duke string from the header that ClientHandler1 specified when calling nameElement.addTextNode.

A handler is associated with a SOAP actor (role). If a header element is targeted at a specific actor and the header element has the MustUnderstand attribute set to 1, and if a server request handler uses that actor, then the server handler must process the element. In this case, if the server handler of the targeted actor does not process the header, then a MustUnderstand fault will be thrown. If the server handler uses a different actor than the header element, then the MustUnderstand fault will not be thrown. All of the handlers in this example use the default actor "next." ClientHandler1 adds header elements and invokes setMustUnderstand(true) on loggerElement. The ServerHandler1 program will process loggerElement accordingly.

Here is the handleRequest method of ClientHandler1:

public boolean handleRequest(MessageContext context) {
    try {
        SOAPMessageContext smc = (SOAPMessageContext) context;
        SOAPMessage message = smc.getMessage();
        SOAPPart soapPart = message.getSOAPPart();
        SOAPEnvelope envelope = soapPart.getEnvelope();
        SOAPHeader header = message.getSOAPHeader();
        if (header == null) {
            header = envelope.addHeader();
        }

        System.out.println
            ("ClientHandler1: adding loggerElement");
        SOAPHeaderElement loggerElement =
           header.addHeaderElement
           (envelope.createName("loginfo",
           "ns1", "http://example.com/headerprops"));
        loggerElement.setMustUnderstand(true);
        loggerElement.setValue("10");

        System.out.println
            ("ClientHandler1: adding nameElement");
        SOAPHeaderElement nameElement =
            header.addHeaderElement
            (envelope.createName("clientname",
            "ns1", "http://example.com/headerprops"));
        nameElement.addTextNode("Duke");

    } catch (Exception e) {
        throw new JAXRPCException(e);
    }
    return true;
} 
The Client config.xml File

The ant build-client task runs the wscompile command, which reads the config.xml file. Within this file, you declare handlers in <handlerChains>, an element that represents an ordered list of handlers. Because the client has only one handler, <handlerChains> contains a single <handler> subelement. The runAt attribute of the <chain> subelement specifies that this handler will run on the client side. The className attribute of the <handler> subelement identifies client.ClientHandler1. The optional <property> element assigns a name and value to a property that is passed to the handler instance in the HandlerInfo parameter of the init method. In ClientHandler1, the init method prints out the property value.

The config.xml file follows:

<?xml version="1.0" encoding="UTF-8"?>
<configuration 
        xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
        
  <wsdl location="http://localhost:8080/handler/test?WSDL"
         packageName="handlersample">
    <handlerChains>
      <chain runAt="client">
        <handler className="client.ClientHandler1">
           <property name="name" value="My Client Handler"/>
        </handler>
      </chain>
    </handlerChains>
    
  </wsdl>
</configuration> 

Server Handlers

This example has two service handlers: ServerHandler1 and ServerHandler2. The source code for these handlers is in the examples/jaxrpc/advanced/handler/service/ subdirectory.

ServerHandler1

This handler processes the SOAP request message that has been transmitted to the service endpoint.

The init method of ServerHandler1 fetches the value of the property name, which was set in the jaxrpc-ri.xml file. The init method follows:

public void init(HandlerInfo info) {
    handlerInfo = info;
    name = (String) info.getHandlerConfig().get("name");
    System.out.println("ServerHandler1: name = " + name);
} 

The handleRequest method iterates through the header elements until it finds one with the element name loginfo. This element was added to the request message by the client handler. The handleRequest method must process the element and then detach (remove) it because of the following two reasons: First, the client handler invoked setMustUnderstand(true) on the element. Second, the server handler is using the actor ("next") targeted by the header element. Also, the jaxrpc-ri.xml file must declare that ServerHandler1 understands this particular element. (See The Service jaxrpc-ri.xml File.) Without this declaration, a MustUnderstand fault will be thrown.

To process the loginfo element, handleRequest invokes getValue and prints out a message if the log level is greater than 5. Because ClientHandler1 set the level to 10, handleRequest prints the message.

Here is the code for the handleRequest method of ServerHandler1:

public boolean handleRequest(MessageContext context) {
    try {

        SOAPMessageContext smc = (SOAPMessageContext) context;
        SOAPMessage message = smc.getMessage();
        SOAPHeader header = message.getSOAPHeader();

        if (header != null) {
            Iterator iter = header.examineAllHeaderElements();
            while (iter.hasNext()) {
                SOAPElement element = (SOAPElement) iter.next();
                if 
(element.getElementName().getLocalName().equals("loginfo")) {

                    int logLevel =
                       Integer.parseInt(element.getValue());
                    if (logLevel > 5) {
                        System.out.println
                           ("ServerHandler1: important " +
                            "message (level " + logLevel + ")");
                    } else {
                        // message not important enough to log
                    }
                    element.detachNode();
                    break;
               }
            }
        }

    } catch (Exception e) {
        throw new JAXRPCException(e);
    }

    return true;
} 
ServerHandler2

After ServerHandler1 finishes processing the SOAP request, ServerHandler2 is invoked and processes the same request. Because both handlers are for the same service endpoint, they are packaged in the same WAR file and specified in the same jaxrpc-ri.xml file.

To fetch the header blocks of the message, the init method of ServerHandler2 invokes info.getHeaders, which returns a array of QName (qualified name) objects. Next, the init method gets the value of the name property, which was declared in the jaxrpc-ri.xml file. The getHeaders method of ServerHandler2 returns an array of QName objects that represent the header blocks. Here is the code for the init and getHeaders methods:

public void init(HandlerInfo info) {
    qnames = info.getHeaders();
    name = (String) info.getHandlerConfig().get("name");
    System.out.println("ServerHandler2: name = " + name);
}

public QName[] getHeaders() {
    return qnames;
} 

The handleRequest methods of ServerHandler1 and ServerHandler2 began similarly. They both get the SOAP header from the SOAPMessageContext and then iterate the header elements until matching a name that was set by ClientHandler1. In ServerHandler2, the matching header element name is clientname. From this header element, handleRequest of ServerHandler2 extracts and then prints the String that ClientHandler1 passed to the addTextNode method. Here is a partial code listing of the handleRequest method of ServerHandler2:

. . .
if (header != null) {
    Iterator iter = header.examineAllHeaderElements();
    while (iter.hasNext()) {
        SOAPElement element = (SOAPElement) iter.next();
        if 
(element.getElementName().getLocalName().equals("clientname"))
        {
            String clientName = element.getValue();
            System.out.println
                ("ServerHandler2: Request is from " +
                clientName);
        }
 } 
. . . 
The Service jaxrpc-ri.xml File

The ant build task compiles and packages the service files, including the handlers. During this task the wsdeploy command reads the jaxrpc-ri.xml file to get information about the service.

In jaxrpc-ri.xml, he <chain> subelement of <handlerChains> specifies server for the runAt attribute. Because the service endpoint has two handlers, the <handlerChains> element encloses two <handler> elements.

The first <handler> element declares that ServerHandler1 understands the loginfo header. This declaration is required because ClientHandler1 invoked setMustUnderstand(true) on loggerElement. The attributes of the ServerHandler1 <handler> element correspond to the parameters of the envelope.createName method invoked in ClientHandler1.

The second <handler> element is for ServerHandler2. For requests, by default multiple handlers are invoked in the order in which they appear in the <handlerChains> element. For responses and faults, the handlers are invoked in the reverse order.

Here is the jaxrpc-ri.xml file:

<?xml version="1.0" encoding="UTF-8" ?>
<webServices
    xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
    version="1.0"
    targetNamespaceBase="http://com.test/wsdl"
    typeNamespaceBase="http://com.test/types"
    urlPatternBase="/test">

    <endpoint
        name="MyHandlerApp"
        displayName="An application for testing handlers"
        description="...description of app"
        interface="service.HandlerTest"
        implementation="service.HandlerTestImpl">

    <handlerChains>
      <chain runAt="server"> 
        <handler className="service.ServerHandler1"
            headers="ns1:loginfo"
            xmlns:ns1="http://example.com/headerprops">
          <property name="name" value="server1"/>
        </handler>

        <handler className="service.ServerHandler2">
          <property name="name" value="server2"/>
        </handler>
      </chain>
    </handlerChains>
    </endpoint>

    <endpointMapping
        endpointName="MyHandlerApp"
        urlPattern="/test"/>
</webServices> 

Advanced Static Stub Example

This example demonstrates the following:

The files for this example are in the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/stubs/ directory.

Building and Running the Advanced Static Example

To build, deploy, and run this example, go to the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/stubs/ directory and type the following:

ant build
ant deploy
ant build-client
ant run 

The run task executes the HelloClient program, which should display the following lines:

Running echo String from a staticstub client.
Hi there Duke!!!

Running SimpleValueType from a staticstub client.
Echoing the boolean set in ValueType by server :false
Echoing the integer set in ValueType by server :54
Echoing the string set in ValueType by server  :Server Entry : 
Test Data

Running the one way operation using ValuType from a staticstub 
client

Running ComplexValuType from a staticstub client
Client output for testComplexValueType : 12345
Client output for testComplexValueType : 4.0
Client output for testComplexValueType : true

Original unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray : 129
Modified unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray  : 258

Running int[] from a staticstub client
Client output for testSimpleIntArray   : true 

Service Programming Model for the Advanced Static Stub Example

With JWSDP, to create a JAX-RPC service you begin with either a service endpoint interface or a WSDL file. In the example described in Creating a Web Service with JAX-RPC, you started with a service endpoint interface. In this example, you'll start with a WSDL file before building the service.

The steps that follow outline the programming model for creating a WS-I compliant service when starting with a WSDL file follow. Steps 2, 4, 5, and 6 are performed by subtasks of the ant build-service task.

  1. Get the WSDL file.
  2. Typically, you'd create a WSDL file with a development tool. For this example, the conf/HelloWorldService.wsdl file has been provided for you.

  3. Generate the service endpoint interface and the other server-side classes.
  4. ant task: generate-server

    This task runs wscompile with the -import, -model, and -f:wsi options. The wscompile tool stores the generated classes and corresponding source code in the build/classes/server/ subdirectory.

    For more information, see the sections Generating WS-I Compliant Service Files with wscompile and Service Endpoint Interface Generated by wscompile.

  5. Code the service implementation class.
  6. Located in the src/server/ subdirectory, the HelloImpl.java code implements the service endpoint interface (HelloIF) generated in the preceding step. The HelloImpl.java file is included with the tutorial, so in this example you don't have to code it. If you did have write HelloImpl.java, you'd first examine the generated HelloIF.java code for the signatures of the methods that you need to implement.

    This approach might seem backwards, because with the help of an IDE such as SunONE Studio, you'd probably code the service implementation first and then generate the service endpoint interface and WSDL file. However, if you are developing a client for someone else's service, you might want to start with a WSDL to create a simple service for unit testing.

  7. Compile the service implementation class.
  8. ant task: compile-server-classes

  9. Create the raw WAR file.
  10. ant task: create-raw-war

    This step packages the server-side files into a raw WAR file. The term raw indicates that this WAR file is not ready for deployment. In this example, the raw WAR file resides in the build/war/ subdirectory and is named jaxrpc-DocumentLitHelloService-raw.war.

  11. Create a deployable (cooked) WAR file.
  12. ant task: build-war

    To cook the raw WAR file, you run the wsdeploy command. The cooked WAR file is named jaxrpc-DocumentLitHelloService.war. (For more information about wsdeploy, see the section process-war.)

  13. Deploy the WAR file.
  14. ant task: deploy

    This action deploys the HelloWorldService at the following URL:

    http://localhost:8080/jaxrpc-DoclitHelloService/hello 
    

Generating WS-I Compliant Service Files with wscompile

Because the client and server in this example are WS-I compliant, wscompile is run with the -f:wsi option. The ant generate-server task runs wscompile as follows:

wscompile -keep -import  
          -model model-DocumentLitHelloService.xml.gz 
          -f:wsi conf/config-server.xml 

Table Table 12-1 describes these options.

Table 12-2 wscompile Options for WS-I Compliance
Option
Description
-keep
Keep all generated files, including the source files. These files reside in the build/classes/server/hello/ subdirectory.
-import
Import the service description from the WSDL file listed in the configuration file (conf/config-server.xml) and then generate service interfaces and value types.
-model
Write information about the internal data structures used by the service to a model file. The wsdeploy command gets information from the model file that it needs for generating runtime classes. The model file is implementation specific and is not portable.
-f:wsi
Generate files for a WS-I compliant service or client.

The wscompile command requires a configuration file. When you start with a WSDL file, the configuration file must have a <wsdl> element, which specifies the location of the WSDL file. Here is the listing for conf/config-server.xml, the configuration file used in this example:

<?xml version="1.0" encoding="UTF-8"?>
<configuration
    xmlns="http://java.sun.com/xml/ns/jax-rpc/pi/config">
    <wsdl
        location="conf/HelloWorldService.wsdl"
        packageName="hello" />
</configuration> 

Client Programming Model for the Advanced Static Stub Example

When creating a client for a Web service, you usually begin with the WSDL file that's been made available by the service developers. The steps that follow briefly describe the process for creating WS-I compliant client when starting with a WSDL file. Note that steps 2 and 6 are subtasks of the ant build-client task.

  1. Make sure that the service has been deployed.
  2. When you ran the ant deploy command, a WSDL file was deployed with the service. In the next step, the wscompile command that generates client files refers to the deployed WSDL file.

  3. Generate service endpoint interface and client-side classes.
  4. ant task: generate-client

    This task runs wscompile with the -gen:client and -f:wsi options. The wscompile tool stores the generated classes and corresponding source code in the build/classes/client/ subdirectory.

    For more information, see the sections Generating the Static Stub Client Files with wscompile and Service Endpoint Interface Generated by wscompile.

  5. Identify the signatures of the remote methods.
  6. A remote client may call the methods defined in the service endpoint interface. To identify the method signatures, examine the source code of the interface, which was generated by wscompile in the previous step.

  7. Identify the wrapper classes.
  8. For literal (not encoded) operations, the return types and parameters are within wrapper classes. When writing the client program, you'll need to know how to get and set the fields contained in the wrapper classes. Therefore, you'll need to examine the wrapper source code, also generated by wscompile. For an example, see the section Wrapper Classes for the sayHello Method.

  9. Code the client program.
  10. Now that you're familiar with the remote method signatures and wrapper classes, you're ready to write the client code. In this example, the provided source code for the HelloClient program resides in the src/client/ subdirectory.

  11. Compile the client program and classes.
  12. ant task: compile-client-classes

    This task compiles the source code created in the previous step. After compilation, the client is ready to run.

Generating the Static Stub Client Files with wscompile

The generate-client task runs wscompile as follows:

wscompile -keep -gen:client -f:wsi conf/config-client.xml 

The -keep and -f:wsi options are described in Table 12-2. The -gen:client option instructs wscompile to generate files for a client.

In the following listing of the configuration file (conf/config-client.xml), note that the <wsdl> element specifies the WSDL file that was deployed with the service.

<?xml version="1.0" encoding="UTF-8"?>
<configuration
    xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
    <wsdl location=
    "http://localhost:8080/jaxrpc-DocumentLitHelloService/
hello?WSDL"
        packageName="hello" />
</configuration> 

The wscompile tool generates class files required by the static stub client. The HelloClient program creates a stub as follows:

stub = (HelloIF_Stub)
    (new HelloWorldService_Impl().getHelloIFPort());  

The wscompile tool generates the HelloIF_Stub and HelloWorldService_Impl classes and places them in the build/classes/server/ subdirectory. To construct the HelloIF_Stub name, wscompile appends _Stub to the portType name defined in the WSDL file:

<portType name="HelloIF"> 

To create the HelloWorldService_Impl name, wscompile appends _Impl to the service name, which the WSDL file specifies as follows:

<service name="HelloWorldService">  

Service Endpoint Interface Generated by wscompile

The service endpoint interface defines the methods that a remote client can invoke. In this example, both invocations of wscompile generate a service endpoint interface named HelloIF:

package hello; 
 
public interface HelloIF extends java.rmi.Remote { 
    public hello.ChangeComplexValueTypeResponse
      changeComplexValueType(hello.ChangeComplexValueTyp
       parameters) throws java.rmi.RemoteException; 
    public hello.ChangeValueTypeResponse
        changeValueType(hello.ChangeValueType parameters) 
        throws java.rmi.RemoteException; 
    public hello.ReverseArrayResponse
        reverseArray(hello.ReverseArray parameters) 
        throws java.rmi.RemoteException; 
    public hello.SayHelloResponse 
         sayHello(hello.SayHello parameters)
         throws java.rmi.RemoteException; 
}  

Note that HelloIF.java, as well as the source code for other generated files, is in the build/classes/server/ and build/classes/client subdirectories.

Wrapper Classes for the sayHello Method

Because this example uses literal (not encoded), the parameters and return types of remote methods must be enclosed in wrapper classes. Because the client in this section is WS-I compliant, the HelloIF interface defines sayHello with the SayHelloResponse and SayHello wrapper classes:

public hello.SayHelloResponse 
    sayHello(hello.SayHello parameters) 
    throws java.rmi.RemoteException; 

The name of the wrapper class for the return type is the capitalized name of the method plus Response. For example, the SayHelloResponse class is the return type for the sayHello method. The SayHelloResponse class is a wrapper for a String variable, which can be accessed through a getter and a setter. The listing for SayHelloResponse follows.

package hello; 
 
 
public class SayHelloResponse { 
    protected java.lang.String result; 
     
    public SayHelloResponse() { 
    } 
     
    public SayHelloResponse(java.lang.String result) { 
        this.result = result; 
    } 
     
    public java.lang.String getResult() { 
        return result; 
    } 
     
    public void setResult(java.lang.String result) { 
        this.result = result; 
    } 
}  

The parameters for the remote call are wrapped in a single class, SayHello. The name of the class is the capitalized name of the method. In the listing SayHello that follows, note that it wraps two String parameters and provides a getter and setter for each String.

package hello; 
 
 
public class SayHello { 
    protected java.lang.String string_1; 
    protected java.lang.String string_2; 
     
    public SayHello() { 
    } 
     
    public SayHello(java.lang.String string_1, 
       java.lang.String string_2) { 
        this.string_1 = string_1; 
        this.string_2 = string_2; 
    } 
     
    public java.lang.String getString_1() { 
        return string_1; 
    } 
     
    public void setString_1(java.lang.String string_1) { 
        this.string_1 = string_1; 
    } 
     
    public java.lang.String getString_2() { 
        return string_2; 
    } 

public void setString_2(java.lang.String string_2) { 
        this.string_2 = string_2; 
    } 
}  

When it invokes the sayHello method, the HelloClient program wraps the two String parameters in the SayHello class. To fetch the String returned by the method, the program invokes getResult on the SayHelloResponse wrapper class. The HelloClient program invokes sayHello as follows:

System.out.println(stub.sayHello(new SayHello
    ("Hi there ", "Duke!!!")).getResult());  

Wrapper Classes for the reverseArray Method

The reverseArray method accepts an int array and returns the array in reverse order. The service endpoint interface, HelloIF, defines reverseArray as follows:

public hello.ReverseArrayResponse 
    reverseArray(hello.ReverseArray parameters) 
    throws java.rmi.RemoteException; 

The reverseArray method uses three wrapper classes:

The ReverseArray and ReverseArrayResponse classes contain the IntArrayTest class, which in turn contains an int array. In effect, the int array is wrapped twice.

In the client, setting the method parameter requires two steps:

  1. Setting the int array contained in IntArrayTest.
  2. Setting the IntArrayTest in ReverseArray.

Similarly, getting the method return value also requires two steps:

  1. Getting the IntArrayTest from ReverseArrayResponse.
  2. Getting the int array from IntArrayTest.

The HelloClient program invokes reverseArray in the following code:

int[] intArray = {1,4,7,9,11};
IntArrayTest param = new IntArrayTest(intArray);
ReverseArrayResponse result1 = 
    stub.reverseArray(new ReverseArray(param));
IntArrayTest result = result1.getResult(); 
int[] rintArray = result.getIntArray();
System.out.println
    ("\nRunning int[] from a staticstub client ");
System.out.println("Client output for testSimpleIntArray   : "
     + java.util.Arrays.equals(intArray, rintArray)); 

Generated by wscompile, the IntArrayTest.java file resides in the build/classes/client/ subdirectory. In the IntArrayTest listing that follows, note that the int array field may be set either with a constructor or the setIntArray method.

package hello; 
 
 
public class IntArrayTest { 
    protected int[] intArray; 
     
    public IntArrayTest() { 
    } 
     
    public IntArrayTest(int[] intArray) { 
        this.intArray = intArray; 
    } 
     
    public int[] getIntArray() { 
        return intArray; 
    } 
     
    public void setIntArray(int[] intArray) { 
        this.intArray = intArray; 
    } 
}  

The ReverseArray parameter class wraps IntArrayTest provides a getter, setter, and constructors:

package hello; 
 
 
public class ReverseArray { 
    protected hello.IntArrayTest intArrayTest_1; 
     
    public ReverseArray() { 
    } 
     
    public ReverseArray(hello.IntArrayTest intArrayTest_1) { 
        this.intArrayTest_1 = intArrayTest_1; 
    } 
     
    public hello.IntArrayTest getIntArrayTest_1() { 
        return intArrayTest_1; 
    } 
     
    public void setIntArrayTest_1(hello.IntArrayTest
       intArrayTest_1) { 
        this.intArrayTest_1 = intArrayTest_1; 
    } 
}  

Like the ReverseArray class, the ReverseArrayResponse class wraps IntArrayTest:

package hello; 
 
 
public class ReverseArrayResponse { 
    protected hello.IntArrayTest result; 
     
    public ReverseArrayResponse() { 
    } 
     
    public ReverseArrayResponse(hello.IntArrayTest result) { 
        this.result = result; 
    } 
     
    public hello.IntArrayTest getResult() { 
        return result; 
    } 
     
    public void setResult(hello.IntArrayTest result) { 
        this.result = result; 
    } 
}  

Wrapper Classes for the changeValueType Method

A value type is a user-defined class with a state that may be passed as a method parameter or return type. A value type often represents a logical entity such as a customer or a purchase order. This example demonstrates the use of a class named ValueType, a user-defined class with a state that consists of three properties: a boolean, an Integer, and a String. Each of these properties has a getter and setter method.

The wscompile tool generates the ValueType class and source code files, placing them in the build/classes/server/ and build/classes/client subdirectories. Here is the source code for ValueType:

package hello; 
 
 
public class ValueType { 
    protected boolean boolProperty; 
    protected java.lang.Integer integerProperty; 
    protected java.lang.String stringProperty; 
     
    public ValueType() { 
    } 
     
    public ValueType(boolean boolProperty, java.lang.Integer
      integerProperty, java.lang.String stringProperty) { 
        this.boolProperty = boolProperty; 
        this.integerProperty = integerProperty; 
        this.stringProperty = stringProperty; 
    } 
     
    public boolean isBoolProperty() { 
        return boolProperty; 
    } 
     
    public void setBoolProperty(boolean boolProperty) { 
        this.boolProperty = boolProperty; 
    } 
     
    public java.lang.Integer getIntegerProperty() { 
        return integerProperty; 
    } 
     
    public void setIntegerProperty(java.lang.Integer
       integerProperty) { 
        this.integerProperty = integerProperty; 
    } 
     
    public java.lang.String getStringProperty() { 
        return stringProperty; 
    } 
     
    public void setStringProperty(java.lang.String
       stringProperty) { 
        this.stringProperty = stringProperty; 
    } 
}  

The changeValueType method wraps ValueType in the ChangeValueType parameter and the ChangeValueTypeResponse return value. The service endpoint interface defines changeValueType as follows:

public hello.ChangeValueTypeResponse 
    changeValueType(hello.ChangeValueType parameters) 
    throws java.rmi.RemoteException; 

The following code listing shows how the HelloImpl class implements the changeValueType method. (The HelloImpl.java file is in the src/server/ subdirectory.) The changeValue method extracts the ValueType parameter from the ChangeValueType wrapper class by invoking the getValueType_1 method. Then the method alters the ValueType state by invoking the setter method for each property. Finally, the method returns the altered ValueType wrapped in a ChangeValueTypeResponse.

public ChangeValueTypeResponse changeValueType(ChangeValueType 
evt) {

    System.out.println("in echoValue type : " +
        evt.getValueType_1());
    ValueType vt = evt.getValueType_1();
    String str = vt.getStringProperty();
    vt.setStringProperty( "Server Entry : " + str); 
    vt.setIntegerProperty(new Integer(54)); 
    vt.setBoolProperty(false); 
    return (new ChangeValueTypeResponse( vt ));
} 

The following code snippet shows how the HelloClient program invokes the changeValueType method.

ValueType vt = stub.changeValueType(new 
ChangeValueType(valueType)).getResult();
System.out.println
    ("Echoing the boolean set in ValueType by server :" 
    + vt.isBoolProperty());
System.out.println
    ("Echoing the integer set in ValueType by server :" 
    + vt.getIntegerProperty().intValue());
System.out.println
    ("Echoing the string set in ValueType by server  :" 
    + vt.getStringProperty()); 

The ChangeValueType and ChangeValueType classes and source code reside in the build/client/classes and build/server/classes subdirectories. If you examine the source code, you can see the getter and setter methods that are invoked by the implementation class (HelloImpl) and client program (HelloClient).

Wrapper Classes for the changeComplexValueType Method

This method shows how to pass a value type that contains several simple types (such as String and Calendar), an int array, and another value type. The HelloIF interface defines changeComplexValueType as follows:

public hello.ChangeComplexValueTypeResponse
     changeComplexValueType(hello.ChangeComplexValueType
     parameters) throws java.rmi.RemoteException; 

ChangeComplexValueTypeResponse and ChangeComplexValueType are wrappers for ValueTypeWObjectMemberAObjectMemberArray, a complex type:

public class ValueTypeWObjectMemberAObjectMemberArray {
    protected java.util.Calendar calender1;
    protected java.util.Calendar calender2;
    protected long longUnsignedInt;
    protected hello.BaseFooObject myValueType;
    protected hello.IntArrayTest simpleArray;
    protected java.lang.String simpleString;
. . . 

IntArrayTest is a wrapper for an int array:

public class IntArrayTest { 
    protected int[] intArray; 
. . . 

BaseFooObject is another value type:

public class BaseFooObject {
    protected java.math.BigInteger first;
    protected double second;
    protected boolean third;
. . . 

In the HelloClient program, the testComplexValueType method invokes service's changeComplexValueType method. The testComplexValueType method creates a ValueTypeWObjectMemberAObjectMemberArray object and passes it as the parameter for changeComplexValueType. To extract the values in the ChangeComplexValueTypeResponse object returned by changeComplexValueType, the testComplexValueType method invokes several getter methods. Here is the source code for testComplexValueType:

public void testComplexValueType() throws Exception {
    BaseFooObject baseFoo = 
       new BaseFooObject(new BigInteger("12345"), 04, true);
    int[] intArray = {1,4,7,9,11};
    IntArrayTest simpleArray = new IntArrayTest(intArray);

    ValueTypeWObjectMemberAObjectMemberArray param =
       new ValueTypeWObjectMemberAObjectMemberArray(
       new java.util.GregorianCalendar(), 
       new java.util.GregorianCalendar(), 129, baseFoo,
       simpleArray, "fooString");
    ChangeComplexValueTypeResponse result1 =
    stub.changeComplexValueType
        (new ChangeComplexValueType(param));
    ValueTypeWObjectMemberAObjectMemberArray result =
        result1.getResult();
    BaseFooObject rfoo = result.getMyValueType();

    System.out.println
    ("\nRunning ComplexValuType from a staticstub client ");
    System.out.println
    ("Client output for testComplexValueType : " + 
    rfoo.getFirst().toString());
    System.out.println
    ("Client output for testComplexValueType : " + 
    rfoo.getSecond());
    System.out.println
    ("Client output for testComplexValueType : " + 
    rfoo.isThird());

    System.out.println
    ("\nOriginal unsigned long in " + "
    ValueTypeWObjectMemberAObjectMemberArray : 129");
    System.out.println
    ("Modified unsigned long in + " +
    ValueTypeWObjectMemberAObjectMemberArray  : "
    + result.getLongUnsignedInt());
} 

One-Way Invocations From a Static Stub

Most remote calls use the synchronous request/response model. For example, when the HelloClient program calls the sayHello method, it waits until the method completes before resuming execution. At a lower level, on the client side the JAX-RPC implementation sends a SOAP request to the service and then waits. While the client is waiting, the service receives the request, processes it, and sends back a SOAP response. When the client receives the SOAP response it resumes execution. With the synchronous model, the client always waits for the response message, even if the return type of the method is void.

In contrast, with the one-way model the client sends a SOAP request to the service and then continues execution upon receiving an HTTP response. However, the client does not wait for a SOAP response. The service processes the request but does not send back a SOAP response. Because the client does not receive a SOAP response, it does not know whether or not the service completed the remote call. This limitation might be acceptable for some applications, for example, a monitoring application that frequently checks the status of a system.

The One-Way Service

To demonstrate the one-way model, in this example the HelloClient program invokes the oneWayValueType method on a separate service named HelloWorldOneWayService. This service was created along with HelloWorldService when you typed ant build and ant deploy. Although the two services share the same WAR file, HelloWorldOneWayService has a separate WSDL file (HelloWorldOneWayService.wsdl) and wscompile configuration files. The wscompile command-line options are the same for both services.

In the programming model for this example, the WSDL file already exists before you run wscompile to generate the server-side files. If you were to start with a service endpoint interface, you would use the -f:useonewayoperations option of wscompile when generating the WSDL file.

The HelloWorldOneWayService has the following endpoint address URL:

http://localhost:8080/jaxrpc-DoclitHelloService/oneway 
The oneWayValueType Method

The HelloClient invokes oneWayValueType in the following code:

. . .
onewayStub = (HelloOneWayIF_Stub)
(new HelloWorldOneWayService_Impl().getHelloOneWayIFPort());
valueType = new ValueType(true,new Integer(23),"Test Data");
. . .
onewayStub.oneWayValueType(new OneWayValueType(valueType));
. . . 

On the server side, HelloOnewWayImpl implements the method as follows:

package hello;

public class HelloOneWayImpl implements HelloOneWayIF {

    public void oneWayValueType(OneWayValueType evt) {
        ValueType vt = evt.getValueType_1();
       System.out.println("OneWay boolean value from client :"
           + vt.isBoolProperty());
       System.out.println("OneWay integer value from client :"
           + vt.getIntegerProperty().intValue()); 
       System.out.println("OneWay string value from client : "
           + vt.getStringProperty()); 
    }
} 

The parameter of the method is OneWayValueType, a wrapper class for ValueType. The source code and class files, generated by wscompile, are in the build/classes/client/ and build/classes/server/ subdirectories.

Advanced Dynamic Proxy Example

This section shows how to build and run a dynamic proxy client that uses doc/literal and is WS-I compliant. If you are unfamiliar with dynamic proxies, you should first read the information in an earlier section, Dynamic Proxy Client Example.

Building and Running the Advanced Dynamic Proxy Example

Because this client invokes methods on the service described in the section Advanced Static Stub Example, you must deploy the service before proceeding. To build and run the dynamic proxy client, go to the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/dynamic/ directory and type the following:

ant build
ant run 

The run task executes the ProxyHelloClient program, which should display the following lines:

Running echo String from a dii client.
Response is:  Duke says: Java is Everywhere!

Running SimpleValueType from a dii client using WSDL

Original ValueType is:
Echoing the boolean set in ValueType by client :true
Echoing the integer set in ValueType by client :23
Echoing the string set in ValueType by client  :Test Data

The response from the Server is:
Echoing the boolean set in ValueType by server :false
Echoing the integer set in ValueType by server :54
Echoing the string set in ValueType by server  :Server Entry : 
Test Data
Running ChangeComplexValueType from a dii client.

Running ComplexValuType from a dii client using WSDL
Client output for testComplexValueType : 12345
Client output for testComplexValueType : 4.0
Client output for testComplexValueType : true

Original unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray : 129
Modified unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray  : 258

Running int[] from a dii client using WSDL
Client output for testSimpleIntArray   : true 

Generating the Dynamic Proxy Client Files with wscompile

This example has the same programming model as that described in the section Client Programming Model for the Advanced Static Stub Example. However, in this dynamic proxy example the ant build task executes the generate-service-interface subtask, which runs wscompile as follows:

wscompile -keep -import -f:wsi conf/config-client.xml 

This wscompile command generates the service endpoint interface and value types needed by the client. Generated wrapper classes are required because the client invokes operations that are literal (not encoded). For descriptions of the command options, see Table 12-1.

The wscompile command reads the service description from the WSDL file specified by the conf/config-client.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration
    xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
    <wsdl
        location="http://localhost:8080/jaxrpc-
DocumentLitHelloService/hello?WSDL"
        packageName="hello" />
</configuration> 

The preceding wscompile command reads the same WSDL file as the command shown in the section Generating the Static Stub Client Files with wscompile. The two commands generate the same service endpoint interface and value types. For information about these generated files, see the section Service Endpoint Interface Generated by wscompile, as well as the subsequent sections on wrapper classes. Client developers should not write their own code as a substitute for the generated files. Instead, they should either rely on the files generated by wscompile or on files made available by the service developers.

The ProxyHelloClient Code

The source code for this client is in the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/dynamic/src/client/ directory.

To set up the dynamic proxy, the ProxyHelloClient program performs these steps:

  1. Sets program variables to match corresponding values in the WSDL file.
  2. private static String BODY_NAMESPACE_VALUE =
    "http://hello.org/wsdl";
    String serviceName = "HelloWorldService";
    String portName = "HelloIFPort";

    Table 12-3 identifies the XML elements in the WSDL file that match the preceding variables

    .

    Table 12-3 ProxyHelloClient Variables and Corresponding WSDL Elements
    Program Variable
    WSDL Element
    BODY_NAMESPACE_VALUE
    <definitions>
    serviceName
    <service>
    portName
    <port>

  3. Sets the endpoint URL that denotes the location of the WSDL file that is deployed with the service.
  4. String UrlString =
    "http://localhost:8080/jaxrpc-DocumentLitHelloService/ hello?WSDL";

    The endpoint address and the WSDL must be supplied by the service developer or deployer.

  5. Creates the service.
  6. URL helloWsdlUrl = new URL(UrlString);

    Service helloService =
    serviceFactory.createService(helloWsdlUrl,
    new QName(BODY_NAMESPACE_VALUE, serviceName));

    The createService method invocation has two parameters: a QName (qualified name) representing the name of the service and a URL designating the location of the WSDL file. At runtime, the service will be configured with information fetched from the WSDL file.

  7. Creates the dynamic proxy.
  8. HelloIF proxy = null;
    ...
    proxy = (HelloIF) helloService.getPort(
    new QName(BODY_NAMESPACE_VALUE, portName),
    hello.HelloIF.class);

    The wscompile tool generated HelloIF.class, the service endpoint interface. Because the client code refers to HelloIF.class, you must run wscompile before compiling the client.

To invoke the sayHello method, the program does the following:

  1. Instantiates the parameter of the remote call.
  2. SayHello request = new SayHello(" Duke says: " +
    "Java is Everywhere!");

    The SayHello and SayHelloResponse wrapper classes are also generated by wscompile.

  3. Invokes sayHello on the dynamic proxy:
  4. SayHelloResponse response = proxy.sayHello(request);

  5. Gets and prints the string returned by sayHello.
  6. System.out.println("Response is: " + response.getResult());

Advanced DII Client Example

This section demonstrates two DII clients:

During development, wscompile reads the deployed WSDL and generates the value types needed by DIIHelloClient and DIINoWSDLHelloClient. Both clients use doc/literal, are WS-I compliant, and invoke methods on the service deployed in the section Advanced Static Stub Example. For an introduction to DII, see the section Dynamic Invocation Interface (DII) Client Example.

Building and Running the Advanced DII Example

To build the clients, go to the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/dii/ directory and type the following:

ant build 

To run DIIHelloClient, type:

ant run 

The DIIHelloClient program should display the following lines:

Running echo String from a dii client.
Response is:  Duke says: Java is Everywhere!

Running SimpleValueType from a dii client using WSDL

Original ValueType is:
Echoing the boolean set in ValueType by client :true
Echoing the integer set in ValueType by client :23
Echoing the string set in ValueType by client  :Test Data

The response from the Server is:
Echoing the boolean set in ValueType by server :false
Echoing the integer set in ValueType by server :54
Echoing the string set in ValueType by server  :Server Entry : 
Test Data
Running ChangeComplexValueType from a dii client.

Running ComplexValuType from a dii client using WSDL
Client output for testComplexValueType : 12345
Client output for testComplexValueType : 4.0
Client output for testComplexValueType : true

Original unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray : 129
Modified unsigned long in 
ValueTypeWObjectMemberAObjectMemberArray  : 258

Running int[] from a dii client using WSDL
Client output for testSimpleIntArray   : true 

To run DIINoWSDLHelloClient, type:

ant run-no-wsdl 

The DIINoWSDLHelloClient programs displays the same lines as DIIHelloClient, except for the following:

Running ComplexValueType from a dii client not using WSDL 

Generating the DII Client Files with wscompile

The ant build task executes the generate-service-interface subtask, which runs this wscompile command:

wscompile -keep -import -f:wsi conf/config-client.xml 

The wscompile command and config-client.xml file of this example are identical to those used in Generating the Dynamic Proxy Client Files with wscompile. Unlike the dynamic proxy client, these DII clients do not need the service endpoint interface (HelloIF) generated by wscompile. However, like all clients that invoke operations that are literal (not encoded), the DII clients need the wrapper classes that wscompile generates. For information on wrapper classes, see the section Advanced Static Stub Example. Client developers should not write their own code as a substitute for the generated files.

The DIIHelloClient Code

At runtime, the DIIHelloClient program uses information in the deployed WSDL file to automatically configure the Call object on which it invokes remote methods. The items automatically configured include parameters, return types, and the target endpoint address.

The source code for DIIHelloClient is in the <INSTALL>/jwstutorial13/examples/jaxrpc/advanced/dii/src/client/ directory.

To set up the service, DIIHelloClient does the following:

  1. Initializes program variables.
  2. private static String BODY_NAMESPACE_VALUE =
    "http://hello.org/wsdl";
    private static String ENCODING_STYLE_PROPERTY =
    "javax.xml.rpc.encodingstyle.namespace.uri";

    String UrlString =
    "http://localhost:8080/jaxrpc-DocumentLitHelloService/hello?WSDL";

    String serviceName = "HelloWorldService";
    String portName = "HelloIFPort";
    Service service = null;
    QName port = null;

    The UrlString object denotes the location of the WSDL file that will be accessed by the client at runtime. The WSDL file specifies the target endpoint address in the <soap:address> element. See Table 12-3 for more information on the WSDL elements that match the other initialized variables.

  3. Creates the service.
  4. service =
    factory.createService(new java.net.URL(UrlString),
    new QName(BODY_NAMESPACE_VALUE, serviceName));

    Note that the first parameter of createService designates the URL of the WSDL file. At runtime, the service will be configured with information fetched from the WSDL.

  5. Creates a QName object that represents the service port.
  6. port = new QName(BODY_NAMESPACE_VALUE, portName);

To set up the Call object and make the remote invocation, DIIHelloClient performs these steps:

  1. Creates the Call object.
  2. QName operation = new QName("sayHello");
    Call call = service.createCall(port, operation);

    In this step, the client creates a Call object for the port (HelloIFPort) and the remote method (sayHello). In a later step, the client will invoke the sayHello method on the Call object.

  3. Sets properties on the Call object.
  4. call.setProperty(Call.SOAPACTION_USE_PROPERTY
    new Boolean(true));
    call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");

    call.setProperty(ENCODING_STYLE_PROPERTY, "");
    call.setProperty(Call.OPERATION_STYLE_PROPERTY,
    "document");

    The previous two lines of code set the encoding/operation style to doc/literal. To learn more about these properties, refer to the SOAP and WSDL documents listed in Further Information.

  5. Instantiates and loads the parameter request.
  6. SayHello request = new SayHello(" Duke says: ",
    "Java is Everywhere!");
    Object[] params = {request};

    SayHello is a wrapper class generated by wscompile.

  7. Invokes the remote sayHello method.
  8. SayHelloResponse response =
    (SayHelloResponse) call.invoke(params);

    Like the SayHello parameter class, the SayHelloResponse class is generated by wscompile.

  9. Gets and prints the string returned by sayHello.
  10. System.out.println("Response is: " + response.getResult());

The DIINoWSDLHelloClient Code

Unlike the DIIWSDLHelloClient program described in the preceding section, the DIINoWSDLHelloClient of this section does not configure the Call object at runtime with information retrieved from the WSDL file. Because DIINoWSDLHelloClient does not access the WSDL file, it must programatically configure the Call object with the following:

To prepare the service, the DIINoWSDLHelloClient program performs these steps:

  1. Initializes program variables.
  2. private static String BODY_NAMESPACE_VALUE =
    "http://hello.org/wsdl";
    private static String TYPE_NAMESPACE_VALUE =
    "http://hello.org/types";
    private static String ENCODING_STYLE_PROPERTY =
    "javax.xml.rpc.encodingstyle.namespace.uri";

    String UrlString =
    "http://localhost:8080/jaxrpc-DocumentLitHelloService/hello?WSDL";

    String serviceName = "HelloWorldService";
    String portName = "HelloIFPort";
    Service service = null;
    QName port = null;

    DIINoWSDLHelloClient initializes these variables the same way as DIIWSDLHelloClient, with the exception of UrlString. In DIINoWSDLHelloClient, UrlString designates the target endpoint address, not the WSDL file location.

  3. Creates the service.
  4. ServiceFactory factory = ServiceFactory.newInstance();
    service = factory.createService(new QName(serviceName));

    This createService method invocation does not include a parameter for the WSDL file.

  5. Creates a QName object that represents the service port.
  6. port = new QName(portName);

To configure the Call object and invoke the sayHello method, DIINoWSDLHelloClient does the following:

  1. Creates the Call object.
  2. QName operation =
    new QName(BODY_NAMESPACE_VALUE, "sayHello");
    Call call = service.createCall(port, operation);

  3. Sets the endpoint address of the target service.
  4. call.setTargetEndpointAddress(endpoint);

    In the WSDL file, the endpoint address is in the <soap:address> element.

  5. Sets properties on the Call object.
  6. call.setProperty(Call.SOAPACTION_USE_PROPERTY,
    new Boolean(true));
    call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");
    call.setProperty(ENCODING_STYLE_PROPERTY, "");
    call.setProperty(Call.OPERATION_STYLE_PROPERTY,
    "document");

  7. Creates the QName object that names the request parameter.
  8. QName REQUEST_QNAME =
    new QName(TYPE_NAMESPACE_VALUE, "sayHello");

    The TYPE_NAMESPACE_VALUE string designates the namespace for the data types defined within the WSDL file. The WSDL file specifies the type information for sayHello under the <types> element, in the <message> element named HelloIF_sayHello.

  9. Adds a parameter to the Call object.
  10. call.addParameter("parameters", REQUEST_QNAME,
    hello.SayHello.class, ParameterMode.IN);

    The parameters argument matches the name of the WSDL <part> subelement that is contained in the <message> element named HelloIF_sayHello. For DII clients that don't access the WSDL file at runtime, addParameter must specify the parameter class, in this case hello.SayHello.class.

  11. Specifies the QName object for the call response.
  12. QName RESPONSE_QNAME =
    new QName(TYPE_NAMESPACE_VALUE, "sayHelloResponse");

  13. Sets the return type on the Call object.
  14. call.setReturnType(RESPONSE_QNAME,
    hello.SayHelloResponse.class);

    Note that setReturnType specifies SayHelloResponse.class for the return type.

  15. Instantiates and loads the parameter request.
  16. SayHello request = new SayHello(" Duke says: ",
    "Java is Everywhere!");
    Object[] params = {request};

  17. Invokes the remote sayHello method.
  18. SayHelloResponse response =
    (SayHelloResponse) call.invoke(params);

  19. Gets and prints the string returned by sayHello.
  20. System.out.println("Response is: " + response.getResult());

Divider
Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

All of the material in The Java(TM) Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.