In Section 5.2.6, it was noted that the SOAP binding for an individual operation or for all operations defined within a portType element can have a style attribute that takes either of the values rpc or document. However, not much was said about the actual differences between these two styles. This section looks at how RPC- and document-style operations are represented in SOAP messages as well as at the support that JAX-RPC provides for each.
Perhaps not surprisingly, when you create client-side stubs and server-side ties for a web service for which the definition is supplied in the form of a Java interface, each method in the interface is mapped to an RPC-style operation. The distinguishing feature of an RPC-style operation is the way in which it is represented as a SOAP message, which is specified in section 7 of the SOAP 1.1 specification. For example, Figure 6-2 shows the message that is sent as a result of invoking the following method from the BookQuery interface from the book web service created in Chapter 2:
public String getAuthor(String name) throws RemoteException;
As you can see, the SOAP body contains a single element that is named for the operation being invoked. This element is in the namespace associated with the web service, which is provided in the config.xml file supplied to wscompile, as shown in Example 2-9. The arguments required for the method call are nested as child elements. In this case, a single element containing the name of the book for which the author name is required is added to the getAuthor element. In the case of a method that requires more than one argument, each has a corresponding child element (commonly referred to as an "accessor"), the order of which is the same as that of the arguments that they represent in the method signature.
The SOAP specification states that each accessor should be named for its corresponding argument. It is clear, however, that JAX-RPC does not strictly follow this rule, since the accessor for the title argument in Figure 6-2 has been given the name String_1. The practical reason for the use of this rather strange name is that wscompile sees only the compiled class file for the BookQuery interface. Since class files do not contain information about the names used in the source code for method arguments, the best wscompile can do is to use the argument type (String) together with a numeric suffix to create a unique accessor name. Since the name assigned to the part element that describes this argument (which is shown in Example 6-20) in the WSDL description of the service created at the same time as the client stubs will be the same as that used in the SOAP message, it really doesn't matter that it doesn't happen to be the same as the name that the developer used in the Java method signature.[8]
[8] This situation exists only when wscompile is supplied with a Java interface definition as its starting point. When a WSDL description of a service is used to create a Java interface definition and client-side stubs, the argument names that wscompile generates match the part names used in the WSDL document.
<!-- Extracts from the message elements: --> <message name="BookQuery_getAuthor"> <part name="String_1" type="xsd:string"/> </message> <message name="BookQuery_getAuthorResponse"> <part name="result" type="xsd:string"/> </message> <!-- Extracts from the portType element: --> <operation name="getAuthor" parameterOrder="String_1"> <input message="tns:BookQuery_getAuthor"/> <output message="tns:BookQuery_getAuthorResponse"/> </operation>
It is clear from the WSDL extracts shown in Example 6-20 that the presence of the getAuthor element is actually part of the RPC binding for this operation, because the generic WSDL definition of the getAuthor operation indicates only that it requires a message labeled BookQuery_getAuthor that consists of a single part called String_1. As you'll see in the next section, wrapper elements such as getAuthor are a characteristic of the SOAP section 7 representation of RPC-style operations, and are not used for document-style operations.
The result of the method call is returned in a response message that is constructed according to similar rules. A typical response message resulting from an invocation of the getAuthor operation is shown in Figure 6-3.
In this case, the SOAP body contains a single element whose name is formed by appending Response to the name of the operation from the original request message. Although this convention is commonly used, it is not mandatory � in fact, any name could be used, since the response message is implicitly associated with the preceding request. Like the getAuthor element shown in Figure 6-2, getAuthorResponse is a wrapper element that is required by the SOAP binding for RPC-style operations, and does not appear in the WSDL description of the response message. The SOAP section 7 rules require the wrapper element to contain an accessor for the value that will become the return value of the method call, followed by accessors that provide the values for any output parameters, if there are any. In this case, since the getAuthor method does not have any output parameters, only the method call result accessor is present. By convention, this accessor is called result, but this is not a requirement. If the return type of the method is void, then the wrapper element contains only accessors for the output parameters. The wrapper for a method that has no return value and no output parameters therefore has no child elements.
In some cases, the wrapper element that represents the operation will be followed by other elements that contain data referenced from one or more of the accessors. For example, consider the getBookInfo( ) method from the book web service:
public abstract BookInfo[] getBookInfo( ) throws RemoteException;
A reference to the array of returned BookInfo elements is encoded as an accessor within a wrapper element called getBookInfo, but the array itself and the individual BookInfo objects that it contains are placed in the SOAP message body, as shown in Figure 6-4.
The discussion of the WSDL soap:body element in Chapter 5 mentioned the related attributes use and encodingStyle. The use attribute specifies whether the data types associated with the parts of a SOAP message are encoded according to some set of encoding rules specified by the encodingStyle attribute or whether they are included literally. In most cases, RPC-style operations specify use="encoded" and supply the URI http://schemas.xmlsoap.org/soap/encoding/ as the value of the encodingStyle attribute to indicate that the message content is encoded according to the SOAP section 5 encoding rules that were outlined in Chapter 3. WSDL also allows the use of literal encoding with RPC-style operations, but JAX-RPC implementations are not required to support it. All WSDL files created by wscompile from Java interface definitions contain only RPC-style operations encoded using SOAP section 5 rules. Literal encoding is most often used with document-style operations, which are covered next.
Although RPC-style operations are very common, some web services are defined in terms of the XML documents that the client and server exchange rather than in terms of programming-language operations. An operation defined in this way is referred to as a document-style operation. As an example of a web service using document-style operations, a company that allows clients to place orders over the Internet might create an XML Schema that defines a type to represent a purchase order and another to represent an order confirmation. A client application is expected to build a purchase order document and send it to the server, which, following validation, stock checks, credit checks, and so on, then builds and returns an order confirmation document. By contrast, an RPC-style version of this service represents each field in the purchase order as an input method parameter, and each field of the order confirmation as an output parameter or return value. Of course, this can quickly become unmanagable if the number of required arguments is large.
In Chapter 3, the SAAJ API was used to build a book image web service, which required a client to create a SOAP message with a particular set of elements in the body part that would specify a list of books for which it required the cover images. The service implementation used the SAAJ APIs to examine the XML document in the SOAP body part, extract the book titles, and construct a reply message containing the required images. The operations provided by the book image web service are all examples of document-style operations. In order to demonstrate the SOAP encoding rules, all of the data types used by its operations were encoded using the SOAP section 5 rules. However, document-style web services are not required to use any particular encoding rules. Also, since such services are usually defined by an XML Schema that explicitly specifies the data type of each message part, it is more usual for document-style operations to use the so-called literal encoding. In the literal encoding, the XML elements that make up a SOAP message do not carry explicit type qualification. For an example of the difference between encoded and literal use, an element that contains floating-point content looks like the following when encoded using the SOAP section 5 encoding rules:
<price xsi:type="xsd:double">29.95</price>
whereas a literal encoding would omit the typing attribute:
<price>29.95</price>
The use of literal encoding relies on the XML Schema document for the service containing a definition such as the following:
<element name="price" type= "xsd:double"/>
This makes typing information in the message itself redundant.
As you saw in Chapter 3, creating web services with SAAJ requires quite a lot of low-level coding. Fortunately, JAX-RPC developers can avoid this work because JAX-RPC includes support for document-style operations that allows you to delegate to the runtime the hard work of constructing and interpreting the SOAP messages. However, this support works only if the messages in the WSDL definition of the service are constructed in such a way that they look very much like those that are generated for RPC-style operations. The easiest way to explain how document-style operations are supported is to look at an example�in this case, yet another version of the book service that we have been using throughout this chapter.
Since wscompile always maps the methods of a Java interface definition to RPC-style operations, the only way to create a web service that contains document-style operations is to start by putting together a WSDL definition (or importing one in the case of an existing service) from which the Java interface definition can be generated.[9] Since WSDL files are quite verbose, we'll show only short extracts of the file that contains the definitions for the document-style book web service. You can find a complete listing in the Appendix. It is important to bear in mind throughout the following discussion that the WSDL has been carefully constructed so that it satisfies certain criteria that make it possible for the JAX-RPC runtime to generate and consume the messages that correspond to the SOAP bindings that it contains. You'll see what these criteria are as the discussion proceeds.
[9] This is true at the time of this writing. However, a future version of JAX-RPC is likely to contain an enhancement that allows document-style messages to be created for a service endpoint defined in terms of a Java interface.
Let's start by looking at the portType element, which defines the operations that the service provides, and the binding element for one of those operations. The relevant extracts from the WSDL document are shown in Example 6-21.
<portType name="DocBookQuery"> <operation name="getBookCount" parameterOrder=""> <input message="tns:DocBookQuery_getBookCount"/> <output message="tns:DocBookQuery_getBookCountResponse"/> </operation> <operation name="getBookTitle" parameterOrder="index"> <input message="tns:DocBookQuery_getBookTitle"/> <output message="tns:DocBookQuery_getBookTitleResponse"/> </operation> <operation name="getBookAuthor" parameterOrder="title author"> <input message="tns:DocBookQuery_getBookAuthor"/> <output message="tns:DocBookQuery_getBookAuthorResponse"/> </operation> <operation name="getBookInfo" parameterOrder="title result"> <input message="tns:DocBookQuery_getBookInfo"/> <output message="tns:DocBookQuery_getBookInfoResponse"/> </operation> <operation name="getStockInfo" parameterOrder="title"> <input message="tns:DocBookQuery_getStockInfo"/> <output message="tns:DocBookQuery_getStockInfoResponse"/> </operation> </portType> <binding name="DocBookQueryBinding" type="tns:DocBookQuery"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="getBookCount"> <soap:operation soapAction=""/> <input> <soap:body use="literal" namespace="urn:jwsnut.chapter6.docbookservice/wsdl/DocBookQuery"/> </input> <output> <soap:body use="literal" namespace="urn:jwsnut.chapter6.docbookservice/wsdl/DocBookQuery"/> </output> </operation> <!-- Additional operation bindings not shown --> </binding>
Looking first at the soap:binding element, you can see that the style attribute has the value document. This indicates that any operation that does not explicitly state otherwise is a document-style operation. Since none of the soap:operation elements in this file have their own style attribute, it follows that all of the operations of this web service use the document style. Next, look at the soap:body elements for both operations. In both cases, the use attribute of these elements has the value literal. Since all of the soap:body elements in this file are the same as the ones shown here, every operation provided by this service is document-style with literal encoding.
Now let's look specifically at the getBookTitle operation. The definitions of the input and output messages for this operation are shown in Example 6-22.
<message name="DocBookQuery_getBookTitlet"> <part name="body" element="typesns:BookTitleRequest"/> </message> <message name="DocBookQuery_getBookTitleResponse"> <part name="result" element="typesns:BookTitleResponse"/> </message>
The input message consists of a single part called body that represents an element of type BookTitleRequest. As noted in Chapter 5, a part element can refer to either an element or a type definition that may be either in the types section of the WSDL document or in an imported schema (see Section 5.2.9 for information on how to import external schema information into a WSDL document). In this case, the part element refers to a concrete element definition via the element attribute, rather than using a type attribute to reference an abstract type. Although either is acceptable, according to both the WSDL and JAX-RPC specifications, at the time of this writing, the JAX-RPC reference implementation does not allow the use of the type attribute for a message part associated with a document-style operation.
The next step is to look at the definition of the BookTitleRequest element. Before doing this, however, it is useful to review the way in which message parts are defined for RPC-style operations by looking at the WSDL definition for the getAuthor operation in the RPC-based book web service shown in Example 6-20. The input message for that operation looks like this:
<message name="BookQuery_getAuthor"> <part name="String_1" type="xsd:string"/> </message>
which resulted in the body of the SOAP message being constructed as follows:
<env:Body> <ans1:getAuthor xmlns:ans1="urn:jwsnut.chapter2.bookservice/wsdl/BookQuery"> <String_1 xsi:type="xsd:string">Java Swing</String_1> </ans1:getAuthor> </env:Body>
Since the operation in this case was RPC-style, the element for the String_1 part was not placed directly in the SOAP body but inside a wrapper element named for the operation itself. For document-style RPC, however, this does not happen. Instead, the element that corresponds to each part of the input or output message is added directly to the SOAP body, with no wrapper element. The problem with this is that the JAX-RPC runtime always expects to receive an outer wrapper element that corresponds to the requested operation, with nested accessors that provide the values of the operation arguments. This remains true even for document-style web services. Since this wrapper is not added automatically when the SOAP message is built, for a document-style operation it must be included as part of the schema definition of the element itself. Bearing this in mind, suitable definitions for the BookTitleRequest and BookTitleResponse elements are shown in Example 6-23
<element name="BookTitleRequest"> <complexType> <sequence> <element name="index" type="xsd:int"/> </sequence> </complexType> </element> <element name="BookTitleResponse"> <complexType> <sequence> <element name="result" type="xsd:string"/> </sequence> </complexType> </element>
The XML Schema definition for the BookTitleRequest element states that it is a compound type that contains a single nested child element called index, whose associated value must be an integer. Similarly, the BookTitleResponse element must contain a string-valued child element called result. Since the WSDL extract in Example 6-22 indicates that the BookTitleRequest and BookTitleResponse elements are placed directly in the SOAP body, the body part for a typical request message for the getBookTitle operation constructed from these definitions would look like this:
<env:Body> <ns:BookTitleRequest env:encodingStyle=""> <index>11</index> </ns:BookTitleRequest> </env:Body>
The prefix ns corresponds to the namespace within which the types used in the WSDL document are defined. As you can see, this looks very much like an RPC-style request message for an operation called BookTitleRequest that requires a single argument called index with the value 11. The data type of the index argument is not explicitly specified in the index element, since the encoding style is literal and therefore SOAP encoding rules do not apply.
|
Summarizing this discussion, in order for a document-style web service to be compatible with JAX-RPC, it must satisfy the following criteria:
The input and output messages must each define only a single part, so that only one element is placed directly in the SOAP body. This restriction is placed by the JAX-RPC reference implementation and does not appear in the specification. Therefore, it may not apply to other implementations and may disappear in future revisions of the reference implementation.
The part element must use the element attribute rather than the type attribute. This is also a restriction that is present in the reference implementation but not in the specification; therefore, it may be lifted in later releases.
The element referenced by the part must be a complexType constructed as a sequence that contains one nested element for each parameter that the operation requires.
It is entirely possible that you will come across third-party service definitions that do not meet these criteria. In this case, it may not be possible for you to use JAX-RPC to create a client for such a service and you will need to use SAAJ instead.
To proceed with the implementation after creating a WSDL definition for your web service, you need to generate the corresponding Java interface definition. You can do this by using wscompile with the -import argument in the usual way. The example source code for this book contains an implementation of this web service, which you can use by opening a command window and making chapter6\docbookservice your working directory. To generate the Java interface definition, type the command:
ant generate-interface
This command runs wscompile with the appropriate command-line arguments, and places the following generated files in the directory chapter6\docbookservice\generated\interface\ora\jwsnut\chapter6\docbookservice:
BookAuthorRequest.java
BookAuthorResponse.java
BookCountRequest.java
BookCountResponse.java
BookInfo.java
BookInfoRequest.java
BookTitleRequest.java
BookTitleResponse.java
DocBookQuery.java
StockInfoRequest.java
StockInfoResponse.java
The file DocBookQuery.java contains the Java interface definition corresponding to the DocBookQuery portType. The other files contain the Java source code for the complex types, such as BookTitleRequest and BookTitleResponse, that represent the parts of the input and output messages used by the document-style operations defined by this service. The generated Java interface is shown in Example 6-24.
public interface DocBookQuery extends java.rmi.Remote { public int getBookCount( ) throws java.rmi.RemoteException; public java.lang.String getBookTitle(int index) throws java.rmi.RemoteException; public java.lang.String getBookAuthor(java.lang.String title) throws java.rmi.RemoteException; public ora.jwsnut.chapter6.docbookservice.BookInfo getBookInfo(java.lang.String title) throws java.rmi.RemoteException; public javax.xml.soap.SOAPElement getStockInfo(java.lang.String title) throws java.rmi.RemoteException; }
Taking the getBookTitle( ) method as a typical example, it is interesting to note that even though the definition in the WSDL document for the operation that corresponds to this method used the complex types BookTitleRequest and BookTitleResponse to describe the content of its input and output messages, the Java classes that correspond to these complex types do not appear in the method definition. An inspection of the WSDL extracts shown in Example 6-22 and Example 6-23, which contain the definitions relevant to the getBookTitle operation, would lead you to expect that the generated getBookTitle( ) method would look like this:
public BookTitleResponse getBookTitle(.BookTitleRequest request) throws RemoteException
The method signature that is actually generated is as follows:
public String getBookTitle(int index) throws RemoteException;
This is, of course, a much better reflection of the intent of the operation, and is identical to the hand-created definition used by the original RPC-based version of this service shown earlier in this chapter. On the other hand, the declaration of the getBookInfo( ) method does include a class generated from the complex type that appears in the definition of the output message for its corresponding operation:
public BookInfo getBookInfo(String title) throws RemoteException;
So how is the actual method call signature determined? Let's discuss how the JAX-RPC reference implementation works.
In the case of the method arguments, the complex type associated with the input message is examined and an argument is added for each field within it, for which JAX-RPC defines a standard mapping to XML. In the case of the getBookTitle operation, for example, the BookTitleRequest type (the definition of which is shown in Example 6-23 earlier in this chapter) contains only a single field of type xsd:int. Since JAX-RPC maps xsd:int to the Java primitive type int, the generated method will have a single argument of type int, the name of which is taken from the name attribute of its element definition.
For a method that has a return value, the method definition depends on the complex type that represents the output message of the operation. In the case of the getBookTitle operation, the BookTitleResponse type (also shown in Example 6-23) has only a single child element of type xsd:String; the generated method therefore returns a String value. The output message for the getBookInfo operation, however, consists of a part that references the element definition shown in Example 6-25.
<element name="BookInfo"> <complexType> <sequence> <element name="title" type="xsd:string"/> <element name="author" type="xsd:string"/> <element name="editor" type="xsd:string"/> <element name="price" type="xsd:double"/> <element name="stock" type="xsd:int"/> </sequence> </complexType> </element>
In cases such as this, in which there is more than one field in the output message, the Java method returns an instance of the value type class generated from the BookInfo element, which acts as a convenient holder for all five return values. An alternative approach is to generate a Holder class for the BookInfo item and use the following method signature:
public void getBookInfo(String title, BookInfoHolder holder) throws RemoteException;
This is a more general solution, since it continues to work even if the output message for the operation contains more than one part. However, at the time of this writing�since the reference implementation allows only messages associated with document-style operations to have a single part�this generality is not required and method signatures utilizing Holder classes in this way are not generated.
The last method in the generated DocBookQuery interface is a little different from the others:
public javax.xml.soap.SOAPElement getStockInfo(java.lang.String title) throws RemoteException;
This method returns the number of copies of a given book that are in stock. The return value should, therefore, be an integer, and you might expect the generated method to look like this:
public int getStockInfo( ) throws RemoteException;
Instead of returning a Java primitive int, however, this method is defined to return an object of type javax.xml.soap.SOAPElement, which is a class defined by the SAAJ API (and described in Chapter 3) that represents an XML element in a SOAP message. To see why this return value is used instead of int, it is necessary to look at the parts of the WSDL document that define the output message for the getStockInfo operation, which are shown in Example 6-26.
<element name="StockInfoResponse">
<complexType>
<sequence>
<element name="stock" type="xsd:nonNegativeInteger"/>
</sequence>
</complexType>
</element>
<message name="DocBookQuery_getStockInfoResponse">
<part name="result" element="typesns:StockInfoResponse"/>
</message>
<!-- Operation definition (from portType) -->
<operation name="getStockInfo" parameterOrder="title">
<input message="tns:DocBookQuery_getStockInfo"/>
<output message="tns:DocBookQuery_getStockInfoResponse"/>
</operation>
These definitions amount to a statement that says the output message contains an element called stock with XML Schema type xsd:nonNegativeInteger. Perhaps surprisingly, although JAX-RPC maps xsd:int to a Java primitive int, it does not have a standard mapping to a Java type for xsd:nonNegativeInteger. When, as here, there is no mapping for an element in literal mode, JAX-RPC uses a SOAPElement to represent it in the generated Java interface. As a result, you'll need to use SAAJ APIs in your client application or service implementation code to provide or extract the return value of the getStockInfo( ) method, as you'll see later in this section.
The schema type for this return value was chosen deliberately in order to demonstrate the way in which JAX-RPC handles types for which it does not have a mapping. The same stock information is also available in the BookInfo object returned from the getBookInfo( ) method, the XML schema definition for which was shown in Example 6-25. In this case, however, for the sake of simplicity, the data type of the stock quantity was defined as xsd:int. Had it instead been declared as xsd:nonNegativeInteger, then the stock field of the generated BookInfo class would have been of type SOAPElement rather then int, as shown in the following code extract (the differences from the actual generated code are highlighted):
// How the BookInfo class would have looked had "stock" // been defined with schema type xsd:nonNegativeInteger // (code not affected by this change is not shown) public class BookInfo { private java.lang.String title; private java.lang.String author; private java.lang.String editor; private double price; private SOAPElement stock; public BookInfo( ) { } public BookInfo(java.lang.String title, java.lang.String author, java.lang.String editor, double price, SOAPElement stock) { this.title = title; this.author = author; this.editor = editor; this.price = price; this.stock = stock; } public SOAPElement getStock( ) { return stock; } public void setStock(SOAPElement stock) { this.stock = stock; } }
As with our earlier examples, in order to provide the service described in the WSDL document, you have to create a class that implements the DocBookQuery interface. The fact that the SOAP messages that are exchanged between the service and client applications are document-style messages and do not use the SOAP section 5 encoding rules is taken care of by the generated stub and tie classes, and is therefore not visible either to the service or to code in the client application. Most of the code that implements the service is very similar to that shown earlier in this chapter and in Chapter 2, and can be found in the directory chapter6\docbookservice\server\ora\jwsnut\chapter6. Nevertheless, there are some differences in the implementation and packaging of the service that you need to be aware of.
As far as the implementation is concerned, the only method of the service interface that is affected by the fact that the operation that it corresponds to is a document-style operation using literal encoding is getStockInfo( ). This gets the quantity of a given book that is currently in stock, which must return a SOAPElement instead of the int that is required for an RPC-style operation. The implementation details of this method are shown in Example 6-27.
public SOAPElement getStockInfo(String title) { BookInfo book = findBook(title); SOAPElement element = null; if (book != null) { try { if (factory == null) { factory = SOAPFactory.newInstance( ); } element = factory.createElement("stock"); element.addTextNode(String.valueOf(book.getStock( ))); } catch (SOAPException ex) { // Just return null in this case element = null; } } return element; }
As you can see, this method uses the SAAJ SOAPFactory class, described in Chapter 3, to create the SOAPElement that will become its return value. For the sake of efficiency, a single SOAPFactory instance is created when this method is first called. The SOAPElement's name must be taken from the value of the name attribute for the corresponding element in the WSDL document. As you can see from Example 6-26, this means that the element must be called stock. Once the SOAPElement is created, the actual stock quantity value is included by adding a text node with the quantity as its value. The SOAPElement that this code creates corresponds to XML that looks like this:
<stock>10</stock>
The client code that would use the value returned from this method is also slightly more complicated than it would be for an RPC-style operation, since it needs to use the SOAPElement getValue( ) method to access the content of the text node that it contains:
SOAPElement element = bookQuery.getStockInfo(title); int stock = Integer.parseInt(element.getValue( ));
The only other point of interest in the construction of the service is the way in which it is packaged for deployment. Recall from Chapter 2 that packaging is a two-step process:
A portable WAR file is created, containing the implementation-independent classes that contain the code for the service itself, together with any other classes and resources that it requires.
The content of the portable WAR file is used by wsdeploy or j2eec to generate a deployable WAR file that contains implementation-dependent server-side ties and serializers, as well as the developer-supplied classes and resources from the portable WAR file.
In principle, this process should not be any different for a service that contains document/literal operations than it is for any of the other examples that you have seen so far, which contained only RPC-style operations. However, there is one small part of the process for creating the WAR file for JWSDP deployment (but not for J2EE 1.4 deployment) that is a little different in this case.
In order for the wsdeploy utility to create a deployable archive, it reads the jaxrpc-ri.xml file from the portable archive, and uses the information that it contains to generate server-side ties and serializer classes that know how to convert between the data types used in the Java interface definition and their XML representation in SOAP messages. In order to create the serializers and tie classes, wsdeploy uses Java reflection to introspect on the methods and arguments of the Java interface definition indicated by the interface attribute of the endpoint element. The problem with this, of course, is that there is nothing in the class file to indicate whether any of the methods of the Java interface correspond to document-style operations. In the absence of this information, wsdeploy assumes that they are all RPC-style. This causes it to create a WSDL document, ties, and serializers that are suitable for an RPC-style service using the SOAP encoding. These classes will not work properly for document-style operations.
To avoid this potential problem, it is necessary to indicate explicitly to wsdeploy which operations have document-style semantics and which are RPC-style. One way to do this is to have it parse the WSDL document, since this shows clearly the semantics of each operation. Unfortunately, wsdeploy does not parse WSDL documents, but it will accept a model file as an alternative source of information in place of a Java interface definition. As noted in Chapter 2, a model file contains a binary representation of a service, its ports, port types, operations, messages, and types built by wscompile while parsing the WSDL document. It is therefore equivalent to the WSDL definition. You can refer wsdeploy to the model file for an endpoint by setting the model attribute of the endpoint element in the jaxrpc-ri.xml file.
|
Assuming that you have a model file, you simply need to place it in the portable web archive and include a line in the jaxrpc-ri.xml file indicating where it can be found, as shown in Example 6-28. Whenever an endpoint element in a jaxrpc-ri.xml file contains a model attribute, the model file that it refers to is used in preference to the Java interface class as a source of information regarding the web service to be deployed. It is still necessary to supply the interface attribute, however, because the class file that it refers to is used by JAXRPCServlet at runtime.
<?xml version="1.0" encoding="UTF-8"?> <webServices xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd" version="1.0" targetNamespaceBase="urn:jwsnut.chapter6.docbookservice/wsdl/" typeNamespaceBase="urn:jwsnut.chapter6.docbookservice/types/"> <endpoint name="DocBookQuery" displayName="DocBookQuery Port" description="Document-style Book Query Port" model="/WEB-INF/model" interface="ora.jwsnut.chapter6.docbookservice.DocBookQuery" implementation= "ora.jwsnut.chapter6.docbookservice.DocBookServiceServant"/> <endpointMapping endpointName="DocBookQuery" urlPattern="/DocBookQuery"/> </webServices>
Note that the model file is placed in the WEB-INF directory of the portable web archive. This is an appropriate place for it because it will then appear in the WEB-INF directory of the deployed web service, where it is protected from direct access from HTTP clients.
The remaining point to clear up is how the model file is created. Fortunately, this is very simple � all you need to do is use the -model argument when running the wscompile utility. In the Ant buildfile for this example, this can be done when running wscompile to generate the Java interface definition from the WSDL document:
wscompile -gen:server -model output/interface/model -s generated/interface
-d output/interface config.xml
All of this does not apply when deploying a service to a J2EE 1.4 application server because, as mentioned in Chapter 2, the deployable WAR archive for J2EE 1.4 includes a WSDL definition of the service along with a mapping file that allows you to define the way in which the WSDL definition is mapped to Java interfaces. Unlike jaxrpc-ri.xml, these files clearly indicate whether each operation is RPC- or document-style, and whether it uses literal or RPC-style encoding.
In fact, all of the JAX-RPC examples in this book are packaged with a model file. This is not strictly necessary in most cases, since the information that wsdeploy can obtain by reflecting on the endpoint interface classes is usually sufficient. The model file is actually included in every case so that the same example source code can be used whether you choose to run the examples with the JWSDP or on the J2EE 1.4 platform (in which the model file can be used in lieu of the J2EE 1.4 XML mapping file). In the text that accompanies the example, we'll point out where including the model file would have been a requirement even if it wasn't for this compatibility issue.
To build and deploy this web service, with chapter6\docbookservice as your working directory, type the command:
ant deploy
The example source code also contains a simple application client that uses this service, which you can run using the following commands:
ant compile-client ant run-client
If you supply the -f:nodatabinding option to wscompile, it generates a Java interface in which the SOAP-level interface of a document-style operation is exposed directly, instead of attempting to map the content of the SOAP messages to method arguments and return values. In the case of the example used in this section, the Java interface resulting from the use of this option would be as shown in Example 6-29, which you should compare to Example 6-24.
public interface DocBookQuery extends java.rmi.Remote { public javax.xml.soap.SOAPElement getBookCount(javax.xml.soap.SOAPElement body) throws RemoteException; public javax.xml.soap.SOAPElement getBookTitle(javax.xml.soap.SOAPElement body) throws RemoteException; public javax.xml.soap.SOAPElement getBookAuthor(javax.xml.soap.SOAPElement title) throws RemoteException; public javax.xml.soap.SOAPElement getBookInfo(javax.xml.soap.SOAPElement title) throws RemoteException; public javax.xml.soap.SOAPElement getStockInfo(javax.xml.soap.SOAPElement title) throws RemoteException; }
Turning off data binding in this way requires you to both create the SOAP elements to be included in the outgoing messages, and decode the responses using low-level code written using the SAAJ API. Therefore, this is not for the faint-hearted!