Chapter 2 introduced the concepts behind JAX-RPC and demonstrated how to create simple JAX-RPC applications starting with a service endpoint defined in the form of a Java interface. This chapter builds on the discussion of SOAP messaging in Chapter 3 and of WSDL in Chapter 5 to show you how to do much more with JAX-RPC. This is quite a long chapter, which introduces many features of the JAX-RPC API and the
|
|
|
|
The book web service example that was used in Chapter 2
One way to create a web service client from a WSDL file is to start by pointing the
Example 6-1 shows the content of a config.xml file that can be used with wscompile to generate client-side artifacts from a WSDL document.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="http://localhost:8000/Books/BookQuery?WSDL"
packageName="ora.jwsnut.chapter6.wsdlbookservice"/>
</configuration>
The wsdl element has two attributes:
A URI that gives the location of the WSDL document. In most cases, this is a URL. Here, it is the URL of the WSDL file generated for the book web service developed in Chapter 2 and deployed in the Tomcat or J2EE web container. The wscompile utility also accepts a filename so that you can reference a WSDL document held locally, such as one downloaded from a registry.
The
The command line used to generate client-side artifacts from WSDL is the same as that required when you supply Java interface definitions, since wscompile distinguishes the two cases based only on the content of config.xml :
wscompile -gen:client -keep -s generated/client -d output/client
-classpath
classpath
config.xml
To see what this generates for the WSDL document corresponding to the book web service, you must first start the web container and deploy the book web service as described in Chapter 2.
|
Open a command window, make your working directory chapter6\wsdlbookservice relative to the example source code for this book, and type the command:
ant generate-client
This buildfile target runs the wscompile command line shown above, the output from which is written to the following directories:
|
Name |
Directory |
|---|---|
|
Java source code |
chapter6\wsdlbookservice\generated\client\ora\jwsnut\chapter6\wsdlbookservice |
|
Compiled class files |
chapter6\wsdlbookservice\output\client\ora\jwsnut\chapter6\wsdlbookservice |
where the ora\jwsnut\chapter6\wsdlbookservice suffix is determined by the value of the packageName attribute of the wsdl element, as shown in Example 6-1. Table 6-1 lists some of the Java source files that are created by this command.
|
Source |
Generated files |
|---|---|
|
Service |
BookService.java |
|
BookService_Impl.java |
|
|
BookService_SerializerRegistry.java |
|
|
Exception |
BookServiceException.java |
|
BookServiceException_SOAPSerializer.java |
|
|
BookServiceException_SOAPBuilder.java |
|
|
Value type |
BookInfo.java |
|
BookInfo_SOAPSerializer.java |
|
|
BookInfo_SOAPBuilder.java |
|
|
BookQuery interface |
BookQuery.java |
|
BookQuery_Stub.java |
|
|
BookQuery_getAuthor_RequestStruct.java |
|
|
BookQuery_getAuthor_ResponseStruct.java |
|
|
BookQuery_getAuthor_RequestStruct_SOAPBuilder.java |
|
|
BookQuery_getAuthor_ResponseStruct_SOAPBuilder.java |
|
|
BookQuery_getAuthor_RequestStruct_SOAPSerializer.java |
|
|
BookQuery_getAuthor_ResponseStruct_SOAPSerializer.java |
|
|
BookQuery_getBookCount_RequestStruct.java |
|
|
BookQuery_getBookCount_ResponseStruct.java |
|
|
BookQuery_getBookCount_RequestStruct_SOAPSerializer.java |
|
|
BookQuery_getBookCount_ResponseStruct_SOAPSerializer.java |
It is interesting to compare this list of source files with the content of Table 2-5 in Chapter 2, which shows what is generated when you start from a Java interface definition. You'll see that you get
By using the WSDL document generated from the Java service definitions for the book web service as the input to wscompile , we have performed a round-trip from Java source code to WSDL and back again. However, the source code that we end up with does not exactly match what we started with ” for one thing, if you compare the content of the generated BookInfo.java file with that created manually in Chapter 2, you'll notice that the parameter order for the constructor is different. The JAX-RPC specification does not require implementations to create exactly the same source code as the result of a round-trip such as this, and, in the real world, you are unlikely to ever need to do this.
Another difference that is not apparent from the source code ”but is nevertheless very important ”is that when you get the stub for the
BookQuery
interface using a
BookService
object generated from a WSDL file, its target endpoint address may already have been set. To
// Get a reference to the stub and set the service address BookService_Impl service = new BookService_Impl( ); BookQuery bookQuery = (BookQuery)service.getBookQueryPort( ); ((Stub)bookQuery)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, args[0]); BookInfo[] books = bookQuery.getBookInfo( );
The code required to get the same list of books using the classes generated from the WSDL for this service, which you can find in the file chapter6\wsdlbookservice\client\ora\jwsnut\chapter6\client\WSDLBookServiceClient.java , is slightly different:
BookService_Impl service = new BookService_Impl( ); BookQuery bookQuery = (BookQuery)service.getBookQueryPort( ); BookInfo[] books = bookQuery.getBookInfo( );
The new code does not explicitly reference the stub class and does not set its ENDPOINT_ADDRESS_PROPERTY . There is no need to set the web service address because the stub that you obtain from the getBookQueryPort( ) method is preconfigured with this information, which is obtained from the soap:address element within the port element corresponding to the BookQuery portType in the WSDL file, if one exists. You can find the relevant portion of the WSDL document by pointing your web browser at the URL http://localhost:8000/Books/BookQuery?WSDL :
<service name="BookService">
<port name="BookQueryPort" binding="tns:BookQueryBinding">
<soap:address xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
location="http://localhost:8000/Books/BookQuery"/>
</port>
</service>
Note, however, that not all WSDL documents need contain a port element. As noted in Section 5.2.9.2, it is useful to create a WSDL document that describes a generic service (such as an electronic book store) without specifying its actual location. Service providers can then create and advertise their own WSDL documents to import the generic definition and additionally supply the location information for their implementation of that service. If you generate the client-side artifacts from the generic WSDL document (for any conforming electronic book store), then there will be no addressing information with which to preconfigure the stubs.
You can compile and run this example using the commands:
ant compile-client ant run-client
As a result, you should see the same list of books as that returned by the original client developed in Chapter 2.
|
To get the editor, author, or price of a specific book instead of the complete list, you can use the CLIENT_ARGS property to supply the required command-line arguments. Here are two examples:
ant -DCLIENT_ARGS="author Java Swing" run-client ant -DCLIENT_ARGS="editor J2ME in a Nutshell" run-client
Notice that, since the deployed WSDL file for this service contains the service address, this version of the client does not require the address to be given as a command-line argument.
Because the JAX-RPC specification requires only stubs to support
For example, suppose a service defines a one-way operation to make a log entry, requiring a single string argument. The input message for this operation might be defined like this:
<message name="LogRequest"> <part name="String_1" type="xsd:string"/> </message>
The operation might be defined as
<operation name="makeLogEntry" parameterOrder="String_1"> <input message="tns:LogRequest"/> </operation>
To convert this to a form acceptable to wscompile , you need to add an empty message:
<message name="LogEmptyResponse"/>
and then reference it from the operation element:
<operation name="makeLogEntry" parameterOrder="String_1">
<input message="tns:LogRequest"/>
<output message="tns:LogEmptyResponse"/>
</operation>
|
|