|
Download
FAQ History |
|
API
Search Feedback |
Advanced JAX-RPC Examples
This section describes the code examples in the
<INSTALL>/jwstutorial12/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
PATHvariable and editing thecommon/build.propertiesfile.
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 aStringback 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:
- Write the code for the following:
- service endpoint interface
- service implementation class
- server handler classes
- Create the
jaxrpc-ri.xmlfile.Read by
wsdeploy, thejaxrpc-ri.xmlfile has information about the service handlers. For details see the section, The Service jaxrpc-ri.xml File.- Create the
web.xmlfile.- Compile the code from step 1.
- Package the
web.xmlfile,jaxrpc-ri.xmlfile, and compiled classes, into a raw WAR file (raw.war).- Run
wsdeployto cook the raw WAR file and create a deployable WAR file (handler.war).- Deploy the WAR file.
The deployed service has this endpoint address URL:
For a client with a handler, do the following:
- Code the client program and the handler.
- Create the
config.xmlfile.This file is read by
wscompileand contains information about the client handler. For details see the section, The Client config.xml File.- Compile the client handler.
- Run
wscompileto generate the service endpoint interface and client-side classes.The
wscompilecommand accesses the deployed WSDL file specified inconfig.xml.- 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>/jwstutorial12/examples/jaxrpc/advanced/handlerdirectory and type the following commands:The
antruntasks 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.logfile:ServerHandler1: name = server1 ServerHandler2: name = server2 ServerHandler1: important message (level 10) ServerHandler2: Request is from Duke TestHandlerImpl: helloServer() message = hi thereClient Handler
The
ClientHandler1instance processes the SOAP request before it is transmitted to the service endpoint. The source code forClientHandler1is in the<INSTALL>/jwstutorial12/examples/jaxrpc/advanced/handler/client/directory.Like the other handlers in this example,
ClientHandler1is a subclass of javax.xml.rpc.handler.GenericHandler:
GenericHandleris an abstract class that implementsjavax.xml.rpc.handler.Handler, an interface that defines the methods required by any handler. Because it provides default implementations for these methods,GenericHandlermakes it easy to write your own handlers.Of the several methods implemented by
GenericHandler,ClientHandler1overrides only these methods:The init Method of ClientHandler1
The
initlife-cycle method enables theHandlerinstance to initialize itself. Typically, you'll implement theinitmethod to connect to resources such as databases. You can set instance variables in theinitmethod, 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'sdestroymethod is invoked. If you'd connected to resources in theinitmethod, you might need to disconnect from them in thedestroymethod.The
initmethod ofClientHandler1follows. It fetches the value of the property name, which was set in theconfig.xmlfile. (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
getHeadersmethod retrieves the header blocks of the message processed by this handler. BecausegetHeadersis declared asabstractinGenericHandler, it must be implemented inClientHandler1:The handleRequest Method of ClientHandler1
A handler may process a SOAP request, response, or fault. Defined by the
Handlerinterface, thehandleRequest,handleResponse, andhandleFaultmethods perform the actual processing of the SOAP messages. TheClientHandler1class implements thehandleRequestmethod, listed at the end of this section.The
handleRequestmethod ofClientHandler1gets access to the SOAP message from theSOAPMessageContextparameter. (For more information about the SOAP APIs, see Chapter 13.) The method adds twoSOAPHeaderElementobjects to the SOAP request:loggerElementandnameElement. TheloggerElementheader will be processed byServiceHandler1and thenameElementheader byServiceHandler2. TheServiceHandler1instance will check the logging level thatClientHandler1set by invokingloggerElement.setValue. TheServiceHandler2instance will retrieve theDukestring from the header thatClientHandler1specified when callingnameElement.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
mustUnderderstandattribute 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 aMustUnderStandFaultwill be thrown. If the server handler uses a different actor than the header element, then theMustUnderStandFaultwill not be thrown. All of the handlers in this example use the default actor "next."ClientHandler1adds header elements and invokessetMustUnderstand(true)onloggerElement. TheServerHandler1program will processloggerElementaccordingly.Here is the
handleRequestmethod ofClientHandler1: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
antbuild-clienttask runs thewscompilecommand, which reads theconfig.xmlfile. 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. TherunAtattribute of the<chain>subelement specifies that this handler will run on the client side. TheclassNameattribute of the<handler>subelement identifiesclient.ClientHandler1. The optional<property>element assigns a name and value to a property that is passed to the handler instance in theHandlerInfoparameter of theinitmethod. InClientHandler1, theinitmethod prints out the property value.The
config.xmlfile 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:
ServerHandler1andServerHandler2. The source code for these handlers is in theexamples/jaxrpc/advanced/handler/service/subdirectory.ServerHandler1
This handler processes the SOAP request message that has been transmitted to the service endpoint.
The
initmethod ofServerHandler1fetches the value of the propertyname, which was set in thejaxrpc-ri.xmlfile. Theinitmethod follows:public void init(HandlerInfo info) { handlerInfo = info; name = (String) info.getHandlerConfig().get("name"); System.out.println("ServerHandler1: name = " + name); }The
handleRequestmethod iterates through the header elements until it finds one with the element nameloginfo. This element was added to the request message by the client handler. ThehandleRequestmethod must process the element and then detach (remove) it because of the following two reasons: First, the client handler invokedsetMustUnderstand(true)on the element. Second, the server handler is using the actor ("next") targeted by the header element. Also, thejaxrpc-ri.xmlfile must declare thatServerHandler1understands this particular element. (See The Service jaxrpc-ri.xml File.) Without this declaration, aMustUnderStandFaultwill be thrown.To process the
loginfoelement,handleRequestinvokesgetValueand prints out a message if the log level is greater than 5. BecauseClientHandler1set the level to 10,handleRequestprints the message.Here is the code for the
handleRequestmethod ofServerHandler1: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
ServerHandler1finishes processing the SOAP request,ServerHandler2is 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 samejaxrpc-ri.xmlfile.To fetch the header blocks of the message, the
initmethod ofServerHandler2invokesinfo.getHeaders, which returns a array ofQName(qualified name) objects. Next, theinitmethod gets the value of thenameproperty, which was declared in thejaxrpc-ri.xmlfile. ThegetHeadersmethod ofServerHandler2returns an array ofQNameobjects that represent the header blocks. Here is the code for theinitandgetHeadersmethods: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
handleRequestmethods ofServerHandler1andServerHandler2began similarly. They both get the SOAP header from theSOAPMessageContextand then iterate the header elements until matching a name that was set byClientHandler1. InServerHandler2, the matching header element name isclientname. From this header element,handleRequestofServerHandler2extracts and then prints theStringthatClientHandler1passed to theaddTextNodemethod. Here is a partial code listing of thehandleRequestmethod ofServerHandler2:. . . 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
antbuildtask compiles and packages the service files, including the handlers. During this task thewsdeploycommand reads the jaxrpc-ri.xmlfile to get information about the service.In
jaxrpc-ri.xml, he<chain>subelement of<handlerChains>specifies server for therunAtattribute. Because the service endpoint has two handlers, the<handlerChains>element encloses two<handler>elements.The first
<handler>element declares thatServerHandler1understands theloginfoheader. This declaration is required becauseClientHandler1invokedsetMustUnderstand(true)onloggerElement. The attributes of theServerHandler1<handler>element correspond to the parameters of theenvelope.createNamemethod invoked inClientHandler1.The second
<handler>element is forServerHandler2. 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.xmlfile:<?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>/jwstutorial12/examples/jaxrpc/advanced/stubs/directory.Building and Running the Advanced Static Example
To build, deploy, and run this example, go to the
<INSTALL>/jwstutorial12/examples/jaxrpc/advanced/stubs/directory and type the following:The
runtask executes theHelloClientprogram, 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 : trueService 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-servicetask.
- Get the WSDL file.
Typically, you'd create a WSDL file with a development tool. For this example, the
conf/HelloWorldService.wsdlfile has been provided for you.- Generate the service endpoint interface and the other server-side classes.
ant task:
generate-serverThis task runs
wscompilewith the-import,-model, and-f:wsioptions. Thewscompiletool stores the generated classes and corresponding source code in thebuild/classes/server/subdirectory.For more information, see the sections Generating WS-I Compliant Service Files with wscompile and Service Endpoint Interface Generated by wscompile.
- Code the service implementation class.
Located in the
src/server/subdirectory, theHelloImpl.javacode implements the service endpoint interface (HelloIF) generated in the preceding step. TheHelloImpl.javafile is included with the tutorial, so in this example you don't have to code it. If you did have writeHelloImpl.java, you'd first examine the generatedHelloIF.javacode 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.
- Compile the service implementation class.
ant task:
compile-server-classes- Create the raw WAR file.
ant task:
create-raw-warThis 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 namedjaxrpc-DocumentLitHelloService-raw.war.- Create a deployable (cooked) WAR file.
ant task:
build-warTo cook the raw WAR file, you run the
wsdeploycommand. The cooked WAR file is namedjaxrpc-DocumentLitHelloService.war. (For more information aboutwsdeploy, see the section process-war.)- Deploy the WAR file.
ant task:
deployThis action deploys the
HelloWorldServiceat the following URL:Generating WS-I Compliant Service Files with wscompile
Because the client and server in this example are WS-I compliant,
wscompileis run with the-f:wsioption. Theantgenerate-servertask runswscompileas follows:Table Table 12-1 describes these options.
The
wscompilecommand 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 forconf/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-clienttask.
- Make sure that the service has been deployed.
When you ran the
antdeploycommand, a WSDL file was deployed with the service. In the next step, thewscompilecommand that generates client files refers to the deployed WSDL file.- Generate service endpoint interface and client-side classes.
ant task:
generate-clientThis task runs
wscompilewith the-gen:clientand-f:wsioptions. Thewscompiletool stores the generated classes and corresponding source code in thebuild/classes/client/subdirectory.For more information, see the sections Generating the Static Stub Client Files with wscompile and Service Endpoint Interface Generated by wscompile.
- Identify the signatures of the remote methods.
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
wscompilein the previous step.- Identify the wrapper classes.
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.- Code the client program.
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
HelloClientprogram resides in thesrc/client/subdirectory.- Compile the client program and classes.
ant task:
compile-client-classesThis 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-clienttask runs wscompile as follows:The
-keepand-f:wsioptions are described in Table 12-2. The-gen:clientoption instructswscompileto 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
wscompiletool generates class files required by the static stub client. TheHelloClientprogram creates a stub as follows:The
wscompiletool generates theHelloIF_StubandHelloWorldService_Implclasses and places them in thebuild/classes/server/subdirectory. To construct theHelloIF_Stubname,wscompileappends_Stubto theportTypename defined in the WSDL file:To create the
HelloWorldService_Implname,wscompileappends_Implto the service name, which the WSDL file specifies as follows: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
wscompilegenerate a service endpoint interface namedHelloIF: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 thebuild/classes/server/andbuild/classes/clientsubdirectories.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
HelloIFinterface definessayHellowith theSayHelloResponseandSayHellowrapper classes:The name of the wrapper class for the return type is the capitalized name of the method plus
Response. For example, theSayHelloResponseclass is the return type for thesayHellomethod. TheSayHelloResponseclass is a wrapper for aStringvariable, which can be accessed through a getter and a setter. The listing forSayHelloResponsefollows.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 listingSayHellothat follows, note that it wraps twoStringparameters and provides a getter and setter for eachString.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
sayHellomethod, theHelloClientprogram wraps the twoStringparameters in theSayHelloclass. To fetch theStringreturned by the method, the program invokesgetResulton theSayHelloResponsewrapper class. TheHelloClientprogram invokessayHelloas follows:Wrapper Classes for the reverseArray Method
The
reverseArraymethod accepts anintarray and returns the array in reverse order. The service endpoint interface,HelloIF, definesreverseArrayas follows:public hello.ReverseArrayResponse reverseArray(hello.ReverseArray parameters) throws java.rmi.RemoteException;The
reverseArraymethod uses three wrapper classes:The
ReverseArrayandReverseArrayResponseclasses contain theIntArrayTestclass, which in turn contains anintarray. In effect, theintarray is wrapped twice.In the client, setting the method parameter requires two steps:
Similarly, getting the method return value also requires two steps:
The
HelloClientprogram invokesreverseArrayin 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, theIntArrayTest.javafile resides in thebuild/classes/client/subdirectory. In theIntArrayTestlisting that follows, note that theintarray field may be set either with a constructor or thesetIntArraymethod.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
ReverseArrayparameter class wrapsIntArrayTestprovides 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
ReverseArrayclass, theReverseArrayResponseclass wrapsIntArrayTest: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: aboolean, anInteger, and aString. Each of these properties has a getter and setter method.The
wscompiletool generates theValueTypeclass and source code files, placing them in thebuild/classes/server/andbuild/classes/clientsubdirectories. Here is the source code forValueType: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
changeValueTypemethod wrapsValueTypein theChangeValueTypeparameter and theChangeValueTypeResponsereturn value. The service endpoint interface defineschangeValueTypeas follows:public hello.ChangeValueTypeResponse changeValueType(hello.ChangeValueType parameters) throws java.rmi.RemoteException;The following code listing shows how the
HelloImplclass implements thechangeValueTypemethod. (TheHelloImpl.javafile is in thesrc/server/subdirectory.) ThechangeValuemethod extracts theValueTypeparameter from theChangeValueTypewrapper class by invoking thegetValueType_1method. Then the method alters theValueTypestate by invoking the setter method for each property. Finally, the method returns the alteredValueTypewrapped in aChangeValueTypeResponse.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
HelloClientprogram invokes thechangeValueTypemethod.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
ChangeValueTypeandChangeValueTypeclasses and source code reside in thebuild/client/classesandbuild/server/classessubdirectories. 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
StringandCalendar), anintarray, and another value type. TheHelloIFinterface defineschangeComplexValueTypeas follows:public hello.ChangeComplexValueTypeResponse changeComplexValueType(hello.ChangeComplexValueType parameters) throws java.rmi.RemoteException;
ChangeComplexValueTypeResponseandChangeComplexValueTypeare wrappers forValueTypeWObjectMemberAObjectMemberArray, 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; . . .
IntArrayTestis a wrapper for anintarray:
BaseFooObjectis another value type:public class BaseFooObject { protected java.math.BigInteger first; protected double second; protected boolean third; . . .In the
HelloClientprogram, thetestComplexValueTypemethod invokes service'schangeComplexValueTypemethod. ThetestComplexValueTypemethod creates aValueTypeWObjectMemberAObjectMemberArrayobject and passes it as the parameter forchangeComplexValueType. To extract the values in theChangeComplexValueTypeResponseobject returned bychangeComplexValueType, thetestComplexValueTypemethod invokes several getter methods. Here is the source code fortestComplexValueType: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
HelloClientprogram calls thesayHellomethod, 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 isvoid.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
HelloClientprogram invokes theoneWayValueTypemethod on a separate service namedHelloWorldOneWayService. This service was created along withHelloWorldServicewhen you typedantbuildandantdeploy. Although the two services share the same WAR file,HelloWorldOneWayServicehas a separate WSDL file (HelloWorldOneWayService.wsdl) andwscompileconfiguration files. Thewscompilecommand-line options are the same for both services.In the programming model for this example, the WSDL file already exists before you run
wscompileto generate the server-side files. If you were to start with a service endpoint interface, you would use the-f:useonewayoperationsoption ofwscompilewhen generating the WSDL file.The
HelloWorldOneWayServicehas the following endpoint address URL:The oneWayValueType Method
The
HelloClientinvokesoneWayValueTypein 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,
HelloOnewWayImplimplements 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 forValueType. The source code and class files, generated bywscompile, are in thebuild/classes/client/andbuild/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>/jwstutorial12/examples/jaxrpc/advanced/dynamic/directory and type the following:The
runtask executes theProxyHelloClientprogram, 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 : trueGenerating 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
antbuildtask executes thegenerate-service-interfacesubtask, which runswscompileas follows:This
wscompilecommand 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
wscompilecommand reads the service description from the WSDL file specified by theconf/config-client.xmlfile:<?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
wscompilecommand 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 bywscompileor on files made available by the service developers.The ProxyHelloClient Code
The source code for this client is in the
<INSTALL>/jwstutorial12/examples/jaxrpc/advanced/dynamic/src/client/directory.To set up the dynamic proxy, the
ProxyHelloClientprogram performs these steps:
- Sets program variables to match corresponding values in the WSDL file.
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 ElementBODY_NAMESPACE_VALUE<definitions>serviceName<service>portName<port>- Sets the endpoint URL that denotes the location of the WSDL file that is deployed with the service.
String UrlString =
"http://localhost:8080/jaxrpc-DocumentLitHelloService/ hello?WSDL";The endpoint address and the WSDL must be supplied by the service developer or deployer.
- Creates the service.
URL helloWsdlUrl = new URL(UrlString);
Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(BODY_NAMESPACE_VALUE, serviceName));The
createServicemethod invocation has two parameters: aQName(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.- Creates the dynamic proxy.
HelloIF proxy = null;
...
proxy = (HelloIF) helloService.getPort(
new QName(BODY_NAMESPACE_VALUE, portName),
hello.HelloIF.class);The
wscompiletool generatedHelloIF.class, the service endpoint interface. Because the client code refers toHelloIF.class, you must runwscompilebefore compiling the client.To invoke the
sayHellomethod, the program does the following:
- Instantiates the parameter of the remote call.
SayHello request = new SayHello(" Duke says: " +
"Java is Everywhere!");The
SayHelloandSayHelloResponsewrapper classes are also generated bywscompile.- Invokes
sayHelloon the dynamic proxy:
SayHelloResponse response = proxy.sayHello(request);- Gets and prints the string returned by
sayHello.
System.out.println("Response is: " + response.getResult());Advanced DII Client Example
This section demonstrates two DII clients:
During development,
wscompilereads the deployed WSDL and generates the value types needed byDIIHelloClientandDIINoWSDLHelloClient. 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>/jwstutorial12/examples/jaxrpc/advanced/dii/directory and type the following:To run
DIIHelloClient, type:The
DIIHelloClientprogram 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 : trueTo run
DIINoWSDLHelloClient, type:The
DIINoWSDLHelloClientprograms displays the same lines asDIIHelloClient, except for the following:Generating the DII Client Files with wscompile
The
antbuildtask executes thegenerate-service-interfacesubtask, which runs thiswscompilecommand:The
wscompilecommand andconfig-client.xmlfile 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 bywscompile. However, like all clients that invoke operations that are literal (not encoded), the DII clients need the wrapper classes thatwscompilegenerates. 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
DIIHelloClientprogram uses information in the deployed WSDL file to automatically configure theCallobject 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>/jwstutorial12/examples/jaxrpc/advanced/dii/src/client/directory.To set up the service,
DIIHelloClientdoes the following:
- Initializes program variables.
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
UrlStringobject 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.- Creates the service.
service =
factory.createService(new java.net.URL(UrlString),
new QName(BODY_NAMESPACE_VALUE, serviceName));Note that the first parameter of
createServicedesignates the URL of the WSDL file. At runtime, the service will be configured with information fetched from the WSDL.- Creates a
QNameobject that represents the service port.
port = new QName(BODY_NAMESPACE_VALUE, portName);To set up the
Callobject and make the remote invocation,DIIHelloClientperforms these steps:
- Creates the
Callobject.
QName operation = new QName("sayHello");
Call call = service.createCall(port, operation);In this step, the client creates a
Callobject for the port (HelloIFPort) and the remote method (sayHello). In a later step, the client will invoke thesayHellomethod on theCallobject.- Sets properties on the
Callobject.
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.
- Instantiates and loads the parameter request.
SayHello request = new SayHello(" Duke says: ",
"Java is Everywhere!");
Object[] params = {request};
SayHellois a wrapper class generated bywscompile.- Invokes the remote
sayHellomethod.
SayHelloResponse response =
(SayHelloResponse) call.invoke(params);Like the
SayHelloparameter class, theSayHelloResponseclass is generated bywscompile.- Gets and prints the string returned by
sayHello.
System.out.println("Response is: " + response.getResult());The DIINoWSDLHelloClient Code
Unlike the
DIIWSDLHelloClientprogram described in the preceding section, theDIINoWSDLHelloClientof this section does not configure theCallobject at runtime with information retrieved from the WSDL file. BecauseDIINoWSDLHelloClientdoes not access the WSDL file, it must programatically configure theCallobject with the following:To prepare the service, the
DIINoWSDLHelloClientprogram performs these steps:
- Initializes program variables.
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;
DIINoWSDLHelloClientinitializes these variables the same way asDIIWSDLHelloClient, with the exception ofUrlString. In DIINoWSDLHelloClient,UrlStringdesignates the target endpoint address, not the WSDL file location.- Creates the service.
ServiceFactory factory = ServiceFactory.newInstance();
service = factory.createService(new QName(serviceName));This
createServicemethod invocation does not include a parameter for the WSDL file.- Creates a
QNameobject that represents the service port.
port = new QName(portName);To configure the
Callobject and invoke thesayHellomethod,DIINoWSDLHelloClientdoes the following:
- Creates the Call object.
QName operation =
new QName(BODY_NAMESPACE_VALUE, "sayHello");
Call call = service.createCall(port, operation);- Sets the endpoint address of the target service.
call.setTargetEndpointAddress(endpoint);In the WSDL file, the endpoint address is in the
<soap:address>element.- Sets properties on the
Callobject.
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");- Creates the
QNameobject that names the request parameter.
QName REQUEST_QNAME =
new QName(TYPE_NAMESPACE_VALUE, "sayHello");The
TYPE_NAMESPACE_VALUEstring designates the namespace for the data types defined within the WSDL file. The WSDL file specifies the type information forsayHellounder the<types>element, in the<message>element namedHelloIF_sayHello.- Adds a parameter to the
Callobject.
call.addParameter("parameters", REQUEST_QNAME,
hello.SayHello.class, ParameterMode.IN);The
parametersargument matches the name of the WSDL<part>subelement that is contained in the<message>element namedHelloIF_sayHello. For DII clients that don't access the WSDL file at runtime,addParametermust specify the parameter class, in this casehello.SayHello.class.- Specifies the
QNameobject for the call response.
QName RESPONSE_QNAME =
new QName(TYPE_NAMESPACE_VALUE, "sayHelloResponse");- Sets the return type on the
Callobject.
call.setReturnType(RESPONSE_QNAME,
hello.SayHelloResponse.class);Note that
setReturnTypespecifiesSayHelloResponse.classfor the return type.- Instantiates and loads the parameter request.
SayHello request = new SayHello(" Duke says: ",
"Java is Everywhere!");
Object[] params = {request};- Invokes the remote
sayHellomethod.
SayHelloResponse response =
(SayHelloResponse) call.invoke(params);- Gets and prints the string returned by
sayHello.
System.out.println("Response is: " + response.getResult());
|
Download
FAQ History |
|
API
Search Feedback |
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.