Download
FAQ History |
![]() ![]() ![]() |
API
Search Feedback |
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 thecommon/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 aString
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:
- Write the code for the service endpoint interface, service implementation class, and server handler classes
- Create the
jaxrpc-ri.xml
file.Read by
wsdeploy
, thejaxrpc-ri.xml
file has information about the service handlers. For details see the section, The Service jaxrpc-ri.xml File.- Create the
web.xml
file.- Compile the code from step 1.
- Package the
web.xml
file,jaxrpc-ri.xml
file, and compiled classes, into a raw WAR file (raw.war
).- Run
wsdeploy
to 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.xml
file.This file is read by
wscompile
and contains information about the client handler. For details see the section, The Client config.xml File.- Compile the client handler.
- Run
wscompile
to generate the service endpoint interface and client-side classes.The
wscompile
command 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>
/jwstutorial13/examples/jaxrpc/advanced/handler
directory and type the following commands: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 thereClient Handler
The
ClientHandler1
instance processes the SOAP request before it is transmitted to the service endpoint. The source code forClientHandler1
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
:
GenericHandler
is 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,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 theHandler
instance to initialize itself. Typically, you'll implement theinit
method to connect to resources such as databases. You can set instance variables in theinit
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'sdestroy
method is invoked. If you'd connected to resources in theinit
method, you might need to disconnect from them in thedestroy
method.The
init
method ofClientHandler1
follows. It fetches the value of the property name, which was set in theconfig.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. BecausegetHeaders
is declared asabstract
inGenericHandler
, it must be implemented inClientHandler1
:The handleRequest Method of ClientHandler1
A handler may process a SOAP request, response, or fault. Defined by the
Handler
interface, thehandleRequest
,handleResponse
, andhandleFault
methods perform the actual processing of the SOAP messages. TheClientHandler1
class implements thehandleRequest
method, listed at the end of this section.The
handleRequest
method ofClientHandler1
gets access to the SOAP message from theSOAPMessageContext
parameter. (For more information about the SOAP APIs, see Chapter 13.) The method adds twoSOAPHeaderElement
objects to the SOAP request:loggerElement
andnameElement
. TheloggerElement
header will be processed byServiceHandler1
and thenameElement
header byServiceHandler2
. TheServiceHandler1
instance will check the logging level thatClientHandler1
set by invokingloggerElement.setValue
. TheServiceHandler2
instance will retrieve theDuke
string from the header thatClientHandler1
specified 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
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 aMustUnderstand
fault will be thrown. If the server handler uses a different actor than the header element, then theMustUnderstand
fault will not be thrown. All of the handlers in this example use the default actor "next."ClientHandler1
adds header elements and invokessetMustUnderstand(true)
onloggerElement
. TheServerHandler1
program will processloggerElement
accordingly.Here is the
handleRequest
method 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
ant
build-client
task runs thewscompile
command, which reads theconfig.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. TherunAt
attribute of the<chain>
subelement specifies that this handler will run on the client side. TheclassName
attribute 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 theHandlerInfo
parameter of theinit
method. InClientHandler1
, theinit
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
andServerHandler2
. 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
init
method ofServerHandler1
fetches the value of the propertyname
, which was set in thejaxrpc-ri.xml
file. Theinit
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 nameloginfo
. This element was added to the request message by the client handler. ThehandleRequest
method 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.xml
file must declare thatServerHandler1
understands this particular element. (See The Service jaxrpc-ri.xml File.) Without this declaration, aMustUnderstand
fault will be thrown.To process the
loginfo
element,handleRequest
invokesgetValue
and prints out a message if the log level is greater than 5. BecauseClientHandler1
set the level to 10,handleRequest
prints the message.Here is the code for the
handleRequest
method 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
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 samejaxrpc-ri.xml
file.To fetch the header blocks of the message, the
init
method ofServerHandler2
invokesinfo.getHeaders
, which returns a array ofQName
(qualified name) objects. Next, theinit
method gets the value of thename
property, which was declared in thejaxrpc-ri.xml
file. ThegetHeaders
method ofServerHandler2
returns an array ofQName
objects that represent the header blocks. Here is the code for theinit
andgetHeaders
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 ofServerHandler1
andServerHandler2
began similarly. They both get the SOAP header from theSOAPMessageContext
and then iterate the header elements until matching a name that was set byClientHandler1
. InServerHandler2
, the matching header element name isclientname
. From this header element,handleRequest
ofServerHandler2
extracts and then prints theString
thatClientHandler1
passed to theaddTextNode
method. Here is a partial code listing of thehandleRequest
method 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
ant
build
task compiles and packages the service files, including the handlers. During this task thewsdeploy
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 therunAt
attribute. Because the service endpoint has two handlers, the<handlerChains>
element encloses two<handler>
elements.The first
<handler>
element declares thatServerHandler1
understands theloginfo
header. This declaration is required becauseClientHandler1
invokedsetMustUnderstand(true)
onloggerElement
. The attributes of theServerHandler1
<handler>
element correspond to the parameters of theenvelope.createName
method 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.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:The
run
task executes theHelloClient
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 : 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-service
task.
- Get the WSDL file.
Typically, you'd create a WSDL file with a development tool. For this example, the
conf/HelloWorldService.wsdl
file has been provided for you.- Generate the service endpoint interface and the other server-side classes.
ant task:
generate-server
This task runs
wscompile
with the-import
,-model
, and-f:wsi
options. Thewscompile
tool 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.java
code implements the service endpoint interface (HelloIF
) generated in the preceding step. TheHelloImpl.java
file 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.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.
- Compile the service implementation class.
ant task:
compile-server-classes
- Create the raw WAR file.
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 namedjaxrpc-DocumentLitHelloService-raw.war
.- Create a deployable (cooked) WAR file.
ant task:
build-war
To cook the raw WAR file, you run the
wsdeploy
command. The cooked WAR file is namedjaxrpc-DocumentLitHelloService.war
. (For more information aboutwsdeploy
, see the section process-war.)- Deploy the WAR file.
ant task:
deploy
This action deploys the
HelloWorldService
at the following URL: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. Theant
generate-server
task runswscompile
as follows:Table Table 12-1 describes these options.
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 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-client
task.
- Make sure that the service has been deployed.
When you ran the
ant
deploy
command, a WSDL file was deployed with the service. In the next step, thewscompile
command that generates client files refers to the deployed WSDL file.- Generate service endpoint interface and client-side classes.
ant task:
generate-client
This task runs
wscompile
with the-gen:client
and-f:wsi
options. Thewscompile
tool 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
wscompile
in 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
HelloClient
program resides in thesrc/client/
subdirectory.- Compile the client program and classes.
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:The
-keep
and-f:wsi
options are described in Table 12-2. The-gen:client
option instructswscompile
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. TheHelloClient
program creates a stub as follows:The
wscompile
tool generates theHelloIF_Stub
andHelloWorldService_Impl
classes and places them in thebuild/classes/server/
subdirectory. To construct theHelloIF_Stub
name,wscompile
appends_Stub
to theportType
name defined in the WSDL file:To create the
HelloWorldService_Impl
name,wscompile
appends_Impl
to 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
wscompile
generate 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/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 definessayHello
with theSayHelloResponse
andSayHello
wrapper classes:The name of the wrapper class for the return type is the capitalized name of the method plus
Response
. For example, theSayHelloResponse
class is the return type for thesayHello
method. TheSayHelloResponse
class is a wrapper for aString
variable, which can be accessed through a getter and a setter. The listing forSayHelloResponse
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 listingSayHello
that follows, note that it wraps twoString
parameters 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
sayHello
method, theHelloClient
program wraps the twoString
parameters in theSayHello
class. To fetch theString
returned by the method, the program invokesgetResult
on theSayHelloResponse
wrapper class. TheHelloClient
program invokessayHello
as follows:Wrapper Classes for the reverseArray Method
The
reverseArray
method accepts anint
array and returns the array in reverse order. The service endpoint interface,HelloIF
, definesreverseArray
as follows:public hello.ReverseArrayResponse reverseArray(hello.ReverseArray parameters) throws java.rmi.RemoteException;The
reverseArray
method uses three wrapper classes:The
ReverseArray
andReverseArrayResponse
classes contain theIntArrayTest
class, which in turn contains anint
array. In effect, theint
array is wrapped twice.In the client, setting the method parameter requires two steps:
Similarly, getting the method return value also requires two steps:
The
HelloClient
program invokesreverseArray
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
, theIntArrayTest.java
file resides in thebuild/classes/client/
subdirectory. In theIntArrayTest
listing that follows, note that theint
array field may be set either with a constructor or thesetIntArray
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 wrapsIntArrayTest
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, theReverseArrayResponse
class 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
wscompile
tool generates theValueType
class and source code files, placing them in thebuild/classes/server/
andbuild/classes/client
subdirectories. 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
changeValueType
method wrapsValueType
in theChangeValueType
parameter and theChangeValueTypeResponse
return value. The service endpoint interface defineschangeValueType
as follows:public hello.ChangeValueTypeResponse changeValueType(hello.ChangeValueType parameters) throws java.rmi.RemoteException;The following code listing shows how the
HelloImpl
class implements thechangeValueType
method. (TheHelloImpl.java
file is in thesrc/server/
subdirectory.) ThechangeValue
method extracts theValueType
parameter from theChangeValueType
wrapper class by invoking thegetValueType_1
method. Then the method alters theValueType
state by invoking the setter method for each property. Finally, the method returns the alteredValueType
wrapped 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
HelloClient
program invokes thechangeValueType
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
andChangeValueType
classes and source code reside in thebuild/client/classes
andbuild/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
andCalendar
), anint
array, and another value type. TheHelloIF
interface defineschangeComplexValueType
as follows:public hello.ChangeComplexValueTypeResponse changeComplexValueType(hello.ChangeComplexValueType parameters) throws java.rmi.RemoteException;
ChangeComplexValueTypeResponse
andChangeComplexValueType
are 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; . . .
IntArrayTest
is a wrapper for anint
array:
BaseFooObject
is another value type:public class BaseFooObject { protected java.math.BigInteger first; protected double second; protected boolean third; . . .In the
HelloClient
program, thetestComplexValueType
method invokes service'schangeComplexValueType
method. ThetestComplexValueType
method creates aValueTypeWObjectMemberAObjectMemberArray
object and passes it as the parameter forchangeComplexValueType
. To extract the values in theChangeComplexValueTypeResponse
object returned bychangeComplexValueType
, thetestComplexValueType
method 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
HelloClient
program calls thesayHello
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 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
HelloClient
program invokes theoneWayValueType
method on a separate service namedHelloWorldOneWayService
. This service was created along withHelloWorldService
when you typedant
build
andant
deploy
. Although the two services share the same WAR file,HelloWorldOneWayService
has a separate WSDL file (HelloWorldOneWayService.wsdl
) andwscompile
configuration files. Thewscompile
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 ofwscompile
when generating the WSDL file.The
HelloWorldOneWayService
has the following endpoint address URL:The oneWayValueType Method
The
HelloClient
invokesoneWayValueType
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 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>
/jwstutorial13/examples/jaxrpc/advanced/dynamic/
directory and type the following:The
run
task executes theProxyHelloClient
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 : 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
ant
build
task executes thegenerate-service-interface
subtask, which runswscompile
as follows: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 theconf/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 bywscompile
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:
- 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
createService
method 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
wscompile
tool generatedHelloIF.class
, the service endpoint interface. Because the client code refers toHelloIF.class
, you must runwscompile
before compiling the client.To invoke the
sayHello
method, the program does the following:
- Instantiates the parameter of the remote call.
SayHello request = new SayHello(" Duke says: " +
"Java is Everywhere!");The
SayHello
andSayHelloResponse
wrapper classes are also generated bywscompile
.- Invokes
sayHello
on 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,
wscompile
reads the deployed WSDL and generates the value types needed byDIIHelloClient
andDIINoWSDLHelloClient
. 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:To run
DIIHelloClient
, type: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 : trueTo run
DIINoWSDLHelloClient
, type:The
DIINoWSDLHelloClient
programs displays the same lines asDIIHelloClient
, except for the following:Generating the DII Client Files with wscompile
The
ant
build
task executes thegenerate-service-interface
subtask, which runs thiswscompile
command:The
wscompile
command andconfig-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 bywscompile
. However, like all clients that invoke operations that are literal (not encoded), the DII clients need the wrapper classes thatwscompile
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 theCall
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:
- 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
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.- Creates the service.
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.- Creates a
QName
object that represents the service port.
port = new QName(BODY_NAMESPACE_VALUE, portName);
To set up the
Call
object and make the remote invocation,DIIHelloClient
performs these steps:
- Creates the
Call
object.
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 thesayHello
method on theCall
object.- Sets properties on the
Call
object.
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};
SayHello
is a wrapper class generated bywscompile
.- Invokes the remote
sayHello
method.
SayHelloResponse response =
(SayHelloResponse) call.invoke(params);Like the
SayHello
parameter class, theSayHelloResponse
class is generated bywscompile
.- Gets and prints the string returned by
sayHello
.
System.out.println("Response is: " + response.getResult());
The DIINoWSDLHelloClient Code
Unlike the
DIIWSDLHelloClient
program described in the preceding section, theDIINoWSDLHelloClient
of this section does not configure theCall
object at runtime with information retrieved from the WSDL file. BecauseDIINoWSDLHelloClient
does not access the WSDL file, it must programatically configure theCall
object with the following:To prepare the service, the
DIINoWSDLHelloClient
program 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;
DIINoWSDLHelloClient
initializes these variables the same way asDIIWSDLHelloClient
, with the exception ofUrlString
. In DIINoWSDLHelloClient,UrlString
designates the target endpoint address, not the WSDL file location.- Creates the service.
ServiceFactory factory = ServiceFactory.newInstance();
service = factory.createService(new QName(serviceName));This
createService
method invocation does not include a parameter for the WSDL file.- Creates a
QName
object that represents the service port.
port = new QName(portName);
To configure the
Call
object and invoke thesayHello
method,DIINoWSDLHelloClient
does 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
Call
object.
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
QName
object that names the request parameter.
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 forsayHello
under the<types>
element, in the<message>
element namedHelloIF_sayHello
.- Adds a parameter to the
Call
object.
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 namedHelloIF_sayHello
. For DII clients that don't access the WSDL file at runtime,addParameter
must specify the parameter class, in this casehello.SayHello.class
.- Specifies the
QName
object for the call response.
QName RESPONSE_QNAME =
new QName(TYPE_NAMESPACE_VALUE, "sayHelloResponse");- Sets the return type on the
Call
object.
call.setReturnType(RESPONSE_QNAME,
hello.SayHelloResponse.class);Note that
setReturnType
specifiesSayHelloResponse.class
for the return type.- Instantiates and loads the parameter request.
SayHello request = new SayHello(" Duke says: ",
"Java is Everywhere!");
Object[] params = {request};- Invokes the remote
sayHello
method.
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.