Realizing Strategies for Document-Based Web Services With JAX-WS 2.0: Part 3 in a Series

   
By Sameer Tyagi, December 2005  

Articles Index

Part 1 and Part 2 of this series discussed different patterns and strategies for building Document-Based Web Services with Java Platform, Enterprise Edition (Java EE, formerly known as J2EE) technologies such as the Java API for XML-Based RPC (JAX-RPC) as well as how they can interoperate with Microsoft .NET technology.

This white paper, Part 3 of the series, will examine how to realize some of the same strategies with the Java API for XML-Based Web Services (JAX-WS) 2.0, a successor to JAX-RPC 1.1. JAX-WS 2.0 has been developed in the Java Community Process program through JSR 224 and extends JAX-RPC with new functionality as well as integration with Java Architecture for XML Binding (JAXB) 2.0 ( JSR 222). JAX-WS 2.0 is part of Java EE 5 as well as Java Platform, Standard Edition (Java SE) 6. It is available for download independently as well as through Project GlassFish, an open-source Java EE 5 application server.

To recap, the Document-Based Web Services pattern can be realized using the following eight strategies, each with its own advantages and disadvantages:

  • Using XML in the SOAP body
    • Starting with a Web Services Description Language (WSDL)
    • Starting with Java code
  • Using string in the SOAP body
  • Using base64-encoded or raw bytes in the SOAP body
  • Switching off data binding
  • Using the xsd:any element in WSDL
  • Using the xsd:anyType in WSDL
  • Using an external Uniform Resource Identifier (URI) to reference the business document
  • Using message attachments in the SOAP message

Table 1 summarizes the various strategies for choosing a type to represent the XML documents being exchanged with a document-oriented web service. These topics are covered in detail in Part 1 as well as in the Java BluePrints Solutions Catalog. This white paper will further explore six of the eight strategies listed here. For information on using string in the SOAP body and using an external URI to reference the business document, see Part 1 and Part 2 of the series.

Table 1: Comparison of Various Strategies
 
 
Strategy
Advantages
Disadvantages
Using XML in the SOAP body
  • Interoperability.
  • Validate against schema if XML docs are used.
  • Better performance than encoded formatting styles.
  • Service interface clearly describes the types of documents expected. This makes the WSDL file easier for clients to understand.
  • Cannot use custom bindings or binding frameworks directly.
  • Endpoint receives object representation; if you want the XML, you have to reconstruct it.
Using string in the SOAP body
  • Simple, same as writing a "hello world" application.
  • Simple to develop clients.
  • No issues with interoperability.
  • Schema validation offered by the runtime cannot be used, and errors with the document will not be picked up until the service has read the document in memory and attempted to process it.
  • Service interface is not descriptive because the document type is just a general string. Loss of business context. The payload context is not described in the WSDL, and there has to be an out-of-band negotiation on that.
  • Memory intensive: The entire XML document is read into memory as a string for each request.
Using base64-encoded or raw bytes in the SOAP body
  • This may be useful when the XML contains characters or declarations that are not supported either by the SOAP message infoset or by the runtime implementation. Examples of these are Document Type Definition (DTD) declarations, binary data, locale-specific character encoding, and so on.
  • Interoperability: Both parties need to know out of band what the data is.
  • Increased size: Base64 increases the size by 33%.
Switching off data binding
  • Integration with third-party frameworks.
  • The XML document is received in its entirety.
  • Building REST-ful services.
  • The behavior may be specific to the implementation for JAX-RPC 1.1 runtimes.
  • Loss of business context: The payload context is not described in the WSDL, and there has to be an out-of-band negotiation on that.
Using the xsd:any element in WSDL
  • The mapping of the xsd:any element has been standardized to map to SOAPElement for JAX-RPC 1.1.
  • Even though an element is named in the WSDL (for example, BusinessDocumentRequest) and the business document passed appears inside these elements on the wire, the web service client can still work with complete XML documents and maintain schema integrity without having to include document content as under these elements. Note: This is not the case with the anyType strategy.
  • Can be used with binding frameworks.
  • Schemas can be evolved independently.
  • Requires developers to work at the lower levels of XML because they now have to work with creating and manipulating SOAPElement objects.
  • There is no cohesiveness between the WSDL and the documents because the schemas defining the documents are not referenced directly. 
  • Schemas need to be negotiated out of band. Both the service provider and the consumer need prior knowledge of the payload contents because the WSDL file does not describe the schema of the expected documents.
Using the xsd:anyType element in WSDL
  • Allows the action and the payload to be passed together. This can be useful when creating a polymorphic processor that accepts multiple document types with the same actions, for example, a single service that performs a search action on a purchase order and an invoice, each of which conforms to different schemas.
  • JAX-RPC specification does not define standard Java mapping for the xsd:anyType, so not all implementations will behave like the Java WSDP and map this to a SOAPElement. In fact, support for the xsd:anyType is optional for an implementation.
  • Because the anyType element actually defines the data type for a named element in the WSDL, the business document being passed in the SOAP body is located inside this element identified in the WSDL. For example, the PurchaseOrder is inside the BusinessDocumentRequest element. This means that the document being passed now must either (a) have its root element identified in the WSDL or (b) be constructed appropriately or wrapped in the element on the fly.
Using an external URI to reference the business document
  • Minimal payload size.
  • Data can be dynamically generated when requested.
  • Data can be cached in proxy servers.
  • Useful for repeated reads of the same data.
  • Service provider can obtain the document later when it needs it.
  • Service provider can use binding API such as JAXB directly.
  • Additional network hops.
  • Data may become stale.
  • Client side needs web server access to serve up documents.
  • Server side needs HTTP client to download documents.
  • Lots of out-of-band negotiation, many security implications.
Using message attachments in the SOAP message
  • Useful for documents that might conform to schemas expressed in languages not supported by the web service endpoint or that are prohibited from being present within a SOAP message infoset (such as DTD <!DOCTYPE> for a DTD-based schema.
  • Useful for large documents (can be compressed and decompressed).
  • Additional facilities can be built on the attachments using handlers, for example, a client-side handler that adds custom encryption using passwords to the attachment and then compresses it using the GZIP algorithm. The corresponding server-side handler decompresses the content from the attachment and decrypts it using the same password-based encryption algorithm.
  • Interoperability: Not all vendors support attachments. For example, .NET uses its own mechanism through an extensions pack and cannot handle MIME attachments based on the Web Services Interoperability (WS-I) Attachment Profile.
 
Using XML in the SOAP Body: Starting With WSDL

In this strategy, the web service is deployed with document-literal formatting, and the XML document that needs to be exchanged is passed in the body of the SOAP message. When starting with WSDL, just as in Part 1, we first generate the server-side code using a runtime-provided tool. In JAX-RPC, we used wscompile. In JAX-WS 2.0, this has been replaced by a new tool called wsimport, which is used to generate JAX-WS portable artifacts such as the following:

  • Service endpoint interface (SEI)
  • Service
  • Exception class mapped from wsdl:fault and soap:headerfault
  • Asynchronous response bean derived from response wsdl:message
  • JAXB-generated value types based on schema types

These artifacts can be packaged in a WAR file with the WSDL and schema documents, along with the endpoint implementation to be deployed. Code Sample 1 shows the Ant target used in the sample code file Doc-Literal-From-WSDL\build.xml.

Code Sample 1: Ant Target for Importing the WSDL

<!-- Generate the server bindings --> 
          <pre><target name="generate-server-from-wsdl" depends="init">
                  

  <wsimport
                  

    fork="true"
                  

    debug="${debug}"
                  

    verbose="${verbose}"
                  

    keep="${keep}"
                  

    base="${build.server.classes}"
                  

    wsdlFile="${service.wsdl}">
                  

  <binding dir="./configsjaxws" includes="config-server.xml"/>
                  

  <binding dir="./configsjaxws" includes="jaxb.xjb"/>
                  

 </wsimport>
                  

</target>
                
 

Unlike JAX-RPC, JAX-WS delegates the binding of schema types to JAXB 2.0. The wsimport tool facilitates this by allowing a custom binding file to used in the configuration. Our configuration contains the following binding information, which allows the code generated by JAXB 2.0 to be generated in specifically named packages for the data types defined in the schemas.

Code Sample 2: Custom Configuration File Used With wsimport

<jxb:bindings version="1.0"
   xmlns:jxb=
                  http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/jaxb/index.html
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                
 <jxb:bindings schemaLocation="wsdl/PurchaseOrder.xsd" node="//xsd:schema"> 
  <jxb:schemaBindings>
    <jxb:package name="com.examples.types.po"/>
  </jxb:schemaBindings>
 </jxb:bindings>
 <jxb:bindings schemaLocation="wsdl/PurchaseOrderStatus.xsd" node="//xsd:schema">
  <jxb:schemaBindings>
    <jxb:package name="com.examples.types.postatus"/>
   </jxb:schemaBindings>
 </jxb:bindings>
 <jxb:bindings schemaLocation="wsdl/POProcessingProblem.xsd" node="//xsd:schema">
  <jxb:schemaBindings>
    <jxb:package name="com.examples.types.poproblem"/>
   </jxb:schemaBindings>
  </jxb:bindings>
                   

</jxb:bindings> 
                
 

With the generated code, we write our implementation class and annotate it using the @WebService annotation, defined in JSR 181. The @WebService annotation and its attributes are used to specify the name of the web service, its service name, WSDL file location, and so on. In our case, we specify only the endpointInterface attribute to point to the generated interface and leave all the attributes at the default values. The interface specified in the endpointInterface is used to determine the abstract WSDL contract ( portType and bindings). With this annotation in place, the code is almost identical to what we used in Code Sample 11 in Part 1 of this series.

Code Sample 3: Endpoint Implementation With Annotation

package com.examples.docliteral;
import java.rmi.RemoteException;
                  

import java.util.Date;
                  

import java.util.List;
                  

// import generated types
                  

import com.examples.types.po.*;
                  

import com.examples.types.poproblem.*;
                  

import com.examples.types.postatus.*;
                
 @javax.jws.WebService(endpointInterface="com.examples.docliteral.IPurchaseOrder")
                  

public class PurchaseOrderService{
                
 public PurchaseOrderStatus acceptPO(PurchaseOrder order) throws ProcessingFault {
                  

   if (order == null) {
                  

          throw new ProcessingFault("Web service was passed a null purchase order",
                  

                                     new POProcessingProblem());
                  

   } else if (order.getItems() == null ||
                  

              order.getPoID() == null ||
                  

              order.getBillTo() == null) {
                  

          throw new ProcessingFault("Purchase order cannot have null fields",
                  

                                     new POProcessingProblem());
                  

}
                  

   System.out.println("PurchaseOrder received in the web service");
                  

   System.out.println("Order ID " + order.getPoID());
                  

   System.out.println("Order creation date " + order.getCreateDate());
                  

   System.out.println("Ship To Address " + order.getShipTo());
                  

   System.out.println("Bill To Address " + order.getBillTo());
                  

   System.out.println("Order Contents :");
                  

   List<LineItem> items = order.getItems();
                  

   for (int i = 0; i < items.size(); i++)
                  

    System.out.println(items.get(i)); 
   PurchaseOrderStatus status = new PurchaseOrderStatus();
                  

   status.setTimestamp(new Date().toString());
                  

   status.setOrderid("ABC" + System.currentTimeMillis());
                  

   return status;
                  

  }
                  

}
                
 

With the code now generated, we package everything into a WAR file along with the web.xml shown in Code Sample 4. We will use the same web.xml for subsequent examples as well. This web.xml delegates the incoming requests to the JAX-WS runtime using a servlet and a listener.

Code Sample 4: web.xml Descriptor

<?xml version="1.0" encoding="UTF-8"?>
                  

<web-app version="2.4" xmlns="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/j2ee/index.html"
                  

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  

   xsi:schemaLocation="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/j2ee/index.html http://bit.ly/7KcNwH">
                  

<display-name>Sample Service</display-name>
                  

  <listener>
                  

    <listener-class>
                  

                com.sun.xml.ws.transport.http.servlet.WSServletContextListener
                  

    </listener-class>
                  

  </listener>
                  

  <servlet>
                  

    <display-name>POServiceImpl</display-name>
                  

    <servlet-name>POServiceImpl</servlet-name>
                  

    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
                  

  </servlet>
                  

  <servlet-mapping>
                  

    <servlet-name>POServiceImpl</servlet-name>
                  

    <url-pattern>/jaxrpc</url-pattern>
                  

  </servlet-mapping>
                  

</web-app>
                
 

The second descriptor is sun-jaxws.xml, which defines the endpoints and contains implementation- or container-specific details about the specific endpoint. It is used by the runtime to determine what class to use to process the incoming requests. This descriptor is shown in Code Sample 5 and will also be reused for subsequent examples.

Code Sample 5: The sun-jaxws.xml Descriptor

<?xml version="1.0" encoding="UTF-8"?>
                  

<endpoints xmlns="# " version="2.0">
                  

<endpoint name="Example"
                  

   interface="com.examples.docliteral.IPurchaseOrder"
                  

   implementation="com.examples.docliteral.PurchaseOrderService"
                  

   wsdl="WEB-INF/wsdl/POService.wsdl"
                  

   service="{http://www.examples.com/wsdl/poservice}POService"
                  

   port="{http://www.examples.com/wsdl/poservice}IPurchaseOrderPort"
                  

   url-pattern="/jaxrpc"/>
                  

</endpoints>
                
 
Using XML in the SOAP Body: Starting With Java Code

We can pass the document as XML in the SOAP body by starting with Java code also. JAX-WS uses the same JSR 181 annotations in the source code to mark the code as a web service. To process the annotations at compile time, JAX-WS uses the J2SE 5.0 annotation processing tool called Apt and ships with the corresponding Ant task. The Apt tool provides a facility for programmatically processing the annotations that JSR 175 added to Java technology. In brief, JSR 175 allows programmers to declare new kinds of structured modifiers that can be associated with program elements, fields, methods, classes, and so on. The Apt tool generates the portable artifacts used in JAX-WS services. To build a web service from Java code, we simply add the @WebService annotation shown in Code Sample 6 to the code from Part 1 and use the Ant task shown in Code Sample 7.

Code Sample 6: Service Implementation With Annotations

package com.examples.docliteral;
                  

import java.util.Date; 
          <pre>
                   @javax.jws.WebService                     
public class PurchaseOrderService { public PurchaseOrderStatus acceptPO(PurchaseOrder order) throws POProcessingProblem { // Other code not shown }
 

It is important when using apt with JAX-WS to specify all of the JAR files in the distributed JAX-WS bundle in the class path passed to apt. The -sourcepath <path> option must also be provided so that apt and the JAX-WS annotation processor can find all types referenced by a web service endpoint implementation class. For more information on the Apt tool, please refer to the Apt documentation. The Ant task used to process the annotations and compile the code is shown in Code Sample 7.

Code Sample 7: Ant Task for Processing Java Code

<target name="build-server-java" depends="init">
                  

 <apt
                  

    fork="true" 
    debug="${debug}"
                  

    verbose="${verbose}"
                  

    base="${build.server.classes}"
                  

    sourceBase="${build.server.classes}"
                  

    sourcePath="${basedir}/src">
                  

 <classpath>
                  

   <path refid="jaxws.classpath"/>
                  

   <path refid="compile.classpath"/>
                  

   <pathelement location="${basedir}/src"/>
                  

 </classpath>
                  

 <option key="nd" value="${build.server.classes}"/>
                  

 <option key="verbose" value="${verbose}"/>
                  

 <source dir="${basedir}/src">
                  

   <include name="**/server/*.java"/>
                  

 </source>
                  

</apt>  <!-- copy handlers descriptor file -->
                  

<copy todir="${build.classes.home}">
                  

 <fileset dir="${basedir}/src">
                  

  <include name="**/server/**/*.xml"/>
                  

 </fileset>
                  

</copy>
                  

</target>
                
 
Using Base64-Encoded or Raw Bytes in the SOAP Body

In this strategy, the XML document structure is either base64-encoded or passed as raw bytes to the endpoint. In Part 1, we used the xsd:base64Binary data type to represent the XML document in the WSDL file. JAX-WS includes full support for SOAP Message Transmission Optimization Mechanism (MTOM), which is a W3C proposed recommendation for optimizing the transmission and wire format of SOAP messages. With MTOM and XML-Binary Optimized Packaging (XOP), data of this type are extracted from the SOAP message and placed in the MIME package. Code Sample 8 shows the WSDL extract with the base64Binary type. This is similar to the string-based "hello world" example, because the endpoint doesn't really know what the contents of the payload are and they are not described by any schema definition.

Code Sample 8 shows the WSDL extract, and Code Sample 9 shows the SOAP request on the wire with the XML document referenced from the SOAP body with an xop:Include. The complete example is available in the sample code directory RawBytes-MTOM.

Code Sample 8: WSDL Extract Showing base64Binary Data Type

   <types>
                  

               <schema targetNamespace="http://www.examples.com/types"
                  

             xmlns:tns="http://www.examples.com/types"
                  

             xmlns="http://www.w3.org/2001/XMLSchema"
                  

             xmlns:xmime="http://www.w3.org/2005/05/xmlmime">
                  

                  <element name="sayRawHello" type="base64Binary" />
                  

                        <element name="sayRawHelloResponse" type="base64Binary" />
                  

                </schema>
                  

     </types>
                  

      <message name="IStringService_sayRawHello">
                  

         <part name="parameters" element="ns2:sayRawHello"/>
                  

       </message>
                  

    <message name="IStringService_sayRawHelloResponse">
                  

         <part name="result" element="ns2:sayRawHelloResponse"/>
                  

   </message>
                  

    <portType name="IStringService">
                  

            <operation name="sayRawHello">
                  

                      <input message="tns:IStringService_sayRawHello"/>
                  

                   <output message="tns:IStringService_sayRawHelloResponse"/>
                  

          </operation>
                  

  </portType>
                  

   <binding name="IStringServiceBinding" type="tns:IStringService">
                  

          <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
                  

         <operation name="sayRawHello">
                  

                      <soap:operation/>
                  

                     <input>
                  

                               <soap:body use="literal"/>
                  

                  </input>
                  

                      <output>
                  

                              <soap:body use="literal"/>
                  

                  </output>
                  

             </operation>
                  

  </binding>
                
 

Code Sample 9: SOAP Message on the Wire Showing XOP and Multipart Packaging

POST /mtomservice-war/jaxws HTTP/1.1
Content-Length: 1763
SOAPAction: ""
Content-Type: Multipart/Related; type="application/xop+xml";
                  

              boundary="----=_Part_0_5525185.1133991209879";
                  

              start-info="text/xml"
Accept: text/xml, application/xop+xml, text/html,
                  

        image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Java/1.5.0_01
Host: localhost:9090
Connection: keep-alive        
                
------=_Part_0_5525185.1133991209879
           Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
<?xml version="1.0" ?>
                  

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  

                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  

                  xmlns:ns1="http://www.examples.com/types">
                  

  <soapenv:Body>
                  

   <ns1:sayRawHello>
                  

    <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
                  

         href="cid:c5eff26e-a558-4334-a96a-f73cdb0853be@www.examples.com">
                  

     </xop:Include>
                  

   </ns1:sayRawHello>
                  

  </soapenv:Body>
                  

  </soapenv:Envelope>
------=_Part_0_5525185.1133991209879
                   

Content-Type: application/octet-stream
Content-ID: <c5eff26e-a558-4334-a96a-f73cdb0853be@www.examples.com>
Content-transfer-encoding: binary
                
<?xml version="1.0" encoding="UTF-8"?>
   tns:PurchaseOrderDocument xmlns:tns="http://www.examples.com/types" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://www.examples.com/types PurchaseOrder.xsd">
           
 <billTo>
   <street>1 Main Street</street>
   <city>Beverly Hills</city>
   <state>CA</state>
   <zipCode>90210</zipCode>
 </billTo>
<createDate>2004-03-27T12:21:02.055-05:00</createDate>
 <items>
   <itemname>Copier Paper</itemname> 
   <price>10</price>
   <quantity>2</quantity>
 </items>
                  

<items>
   <itemname>Toner</itemname>
   <price>920</price>
   <quantity>1</quantity>
 </items>
<poID>ABC-CO-19282</poID>
 <shipTo>
   <street>1 Main Street</street>
   <city>Beverly Hills</city>
   <state>CA</state>
   <zipCode>90210</zipCode>
 </shipTo>
                  

</tns:PurchaseOrderDocument>
                
 

Because there is a performance cost involved in creating the MIME package, for smaller data sets, the cost may be more than the value of not having to base64-encode and decode the data. For flexibility, the JAX-WS implementation contains a configurable property that allows developers to set the threshold size in KB, with a default of 1 KB, after which the MTOM package is created for the base64Binary data. The property can be set on the BindingProvider, for example, by the following:

((BindingProvider) stub).getRequestContext().put( 
   com.sun.xml.ws.developer.JAXWSProperties.MTOM_THRESHOLD_VALUE,0)
 
Switching Off Data Binding

In this strategy, the explicit binding of Java types is turned off. Java service endpoint interfaces (SEIs) provide a high-level Java technology-centric abstraction that hides the details of converting between Java objects and their XML representations for use in XML-based messages. The javax.xml.ws.Provider interface in JAX-WS offers a mechanism to endpoint implementations that want to work with the message level directly, rather than with their binding representation. In such implementations, the invoke(Source source) method is invoked by the runtime for each web service invocation, and a javax.xml.transform.Source object corresponding to the message representation is passed to it. Developers can choose whether the runtime should pass only the SOAP payload in this invocation or the entire SOAP message by marking up the code with the appropriate javax.xml.ws.ServiceMode annotation. A value=MESSAGE annotation indicates that the provider instance should receive and send entire protocol messages, that is, the entire SOAP message. If this annotation is omitted or used with @ServiceMode(value=PAYLOAD), then the provider instance will receive and send message payloads only, that is, the contents of a SOAP Body element.

Code Sample 10: Generic Endpoint Implementation With a Provider

package com.sun.demo;
import org.w3c.dom.Node;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
@javax.xml.ws.ServiceMode(value=Service.Mode.PAYLOAD)
@javax.xml.ws.WebServiceProvider(wsdlLocation="WEB-INF/wsdl/POService.wsdl")
public class PurchaseOrderService implements Provider<Source> {
                  

  public Source invoke(Source source) {
   try {
    DOMResult dom = new DOMResult();
    Transformer trans = TransformerFactory.newInstance().newTransformer();
    trans.transform(source, dom);
    Node node = dom.getNode();
    printNodeToConsole(node);
    DOMSource src = new DOMSource (node);
    return src;
    } catch(Exception e) {
    e.printStackTrace();
    throw new RuntimeException("Error in provider endpoint", e);
    }
   }
                
 public static void printNodeToConsole(Node n) {
                  

// Utility method code not shown here
                  

  }
                  

}
                
 

Just as is true in the server side, the client can operate directly at the XML message level, rather than binding to objects. In JAX-WS, the javax.xml.ws.Dispatch interface provides support for this mode of interaction. The javax.xml.ws.Dispatch is a low-level API that allows clients to construct messages or message payloads as XML, but it requires developers to have an intimate knowledge of the desired message or payload structure. The javax.xml.ws.Dispatch itself is a generic class that supports input and output of messages or message payloads of arbitrary types. It supports two usage modes, identified by the constants javax.xml.ws.Service.Mode.MESSAGE and javax.xml.ws.Service.Mode.PAYLOAD, respectively, which behave identically to the annotation we previously described for the server-side endpoint. Code Sample 11 shows the client for this strategy using the dispatch API.

Code Sample 11: Generic Client Implementation With the Dispatch API

public class DispatchClient {
                  

 public static void main(String[] args) {
  try {
                  

    QName qname= new QName("urn:ConcretePOService","IPurchaseOrderPort");
    Service service = Service.create(new 
    QName("urn:ConcretePOService","POService"));
    service.addPort(qname,new URI(SOAPBinding.SOAP11HTTP_BINDING),url);
    Dispatch<Source> sourceDispatch = service.createDispatch(qname,
                  

                                                             Source.class,
                  

                                                             Service.Mode.PAYLOAD);
                  

    DOMSource request = new DOMSource(createSOAPMessage("request.xml"));
                  

    Source result = sourceDispatch.invoke(request);
                  

    printNodeToConsole(result);
                  

  } catch (Exception jex) {
    jex.printStackTrace();
    }
   }
                
 
Using the xsd:any Element in WSDL

In this strategy, the XML schemas or data types used in the WSDL messages are not explicitly defined. Instead, the generic wildcard type of xsd:any is used to describe the data being passed back and forth without defining the contents of the complex types being used. The implementation with JAX-WS remains similar to that with JAX-RPC, except that the xsd:any is no longer mapped to javax.xml.soap.SOAPElement. Because JAXB 2.0 mappings are used, the Document Object Model (DOM) representation is still obtained, but it can be cast to a org.w3c.dom.Element object instead. In our implementation, we also use the javax.xml.ws.WebServiceContext interface, which makes it possible for the endpoint implementation to access contextual information pertaining to the request being served. The javax.xml.ws.WebServiceContext itself is a java.util.Map of entries, some of which are standardized using the key names shown in Table 2, and is used in Code Sample 12 to access the ServletContext and then the file to use as a response. This is identical to what we did with JAX-RPC in Code Sample 44 of Part 1 with its createSOAPMessage method and the javax.xml.rpc.server.ServletEndpointContext.

Table 2: Standard Properties in the javax.xml.ws.handler.MessageContext
 
 
Standard Property Name
Property Description

MessageContext.HTTP_REQUEST_HEADERS

The HTTP request headers.

MessageContext.HTTP_REQUEST_METHOD

The HTTP request method.

MessageContext.HTTP_RESPONSE_CODE

The HTTP response status code.

MessageContext.HTTP_RESPONSE_HEADERS

The HTTP response headers.

MessageContext.MESSAGE_ATTACHMENTS

The map of attachments to a message; key is the MIME Content-ID; value is a DataHandler.

MessageContext.MESSAGE_OUTBOUND_PROPERTY

The message direction: true for outbound messages, false for inbound.

MessageContext.SERVLET_CONTEXT

The servlet context object.

MessageContext.SERVLET_REQUEST

The servlet request object.

MessageContext.SERVLET_RESPONSE

The servlet response object.

MessageContext.SERVLET_SESSION

The servlet session object.

MessageContext.WSDL_DESCRIPTION

The input source for the WSDL document.

MessageContext.WSDL_INTERFACE

The name of the WSDL interface (2.0) or port type (1.1).

MessageContext.WSDL_OPERATION

The name of the WSDL operation.

MessageContext.WSDL_PORT

The name of the WSDL port.

MessageContext.WSDL_SERVICE

The name of the WSDL service.

 

Code Sample 12: Service Endpoint Implementation

@javax.jws.WebService(endpointInterface="com.examples.soapelement.IPurchaseOrder")
                  

public class PurchaseOrderService implements IPurchaseOrder {
                
  @javax.annotation.Resource 
  javax.xml.ws.WebServiceContext ctx;
public BusinessDocumentReply acceptPO(BusinessDocumentRequest request)
                  

                                               throws POProcessingProblem{
   if (request == null) {
     throw new POProcessingProblem("Web service was passed a null purchase order",null);
   }
                
 try{
   Element requestdata= (Element) request.getAny();
   // Print the request to the console.
   printNodeToConsole(requestdata);
   Element replydata = createSOAPMessage("/Response.xml");
   printNodeToConsole (replydata);
   BusinessDocumentReply reply = new BusinessDocumentReply();
   reply.setAny(replydata);
   return reply;
   }catch(Exception e){
      BusinessDocumentFault bdf= new BusinessDocumentFault();
      bdf.setAny(createExceptionXML("Web service could not construct a reply :" + e));
      throw new POProcessingProblem("Exception in processing your request", bdf);
   }
                  

}
                
                   

 private Document readFileCreateDocument(String filename) throws Exception {
   // Other code not shown
        ServletContext sContext =
                  

        (ServletContext)(ctx.getMessageContext().get(MessageContext.SERVLET_CONTEXT));
        InputStream in =sContext.getResourceAsStream(filename);
        Document xml = parser.parse(in);
                   

  // Other code not shown
                  

  }
}
                
 

The client implementation is shown in Code Sample 13. It is much like Code Sample 45 in Part 1, except that it uses the org.w3c.dom.Element.

Code Sample 13: Client Implementation Sending an XML Document

public class WSDLClient {
                  

 public static void main(String[] args) throws Exception {
   String url = "http://localhost:9090/anysoapelement/jaxrpc";
   if (args.length == 1) {
   url = args[0];
   }
                
 POService serviceproxy = new POService();
 IPurchaseOrder stub = serviceproxy.getIPurchaseOrderPort() ;
 ((BindingProvider) stub).getRequestContext().
                  

                            put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,url);
                  

 BusinessDocumentReply reply=sendPurchaseOrder(stub);
 Element payload= (Element)reply.getAny();
                  

 printNodeToConsole(payload);
   } 
   // Other methods not shown here
                  

}
                
 
Using the xsd:anyType in WSDL

In this strategy, the xsd:anyType element is used to represent the XML fragment being passed to the endpoint. With JAX-WS 2.0, the code remains much like the code in Part 1 but with a few minor changes. First, the @WebService annotation must be added to mark the class as an endpoint. Second, the generated interface no longer uses javax.xml.soap.SOAPElement as method parameters and arguments but uses java.lang.Object instead. As Code Sample 14 shows, the implementation class uses not the javax.xml.soap.SOAPElement but the org.w3c.dom.Element.

Code Sample 14: Service Endpoint Implementation Processing xsd:anyType

@javax.jws.WebService(endpointInterface="com.examples.soapelement.IPurchaseOrder")
                  

public class PurchaseOrderService implements IPurchaseOrder {
                
@javax.annotation.Resource
                  

javax.xml.ws.WebServiceContext ctx;
                
 public Object acceptPO(Object request) throws POProcessingProblem{
   if (request == null) {
      throw new POProcessingProblem("Web service was passed a null purchase order",null);
    }
                  

 try{
   Element requestdata= (Element) request;
   // Print the request to the console.
   printNodeToConsole(requestdata);
   Element replydata = createSOAPMessage("/Response.xml");
   printNodeToConsole (replydata);
   return replydata;
   }catch(Exception e){
     throw new POProcessingProblem("Exception in processing your request",
                  

                                    createExceptionXML("Web service could not
                  

                                                         construct a reply :" + e));
     }
   }
                  

  // Other methods not shown here
}
                
 
Using Message Attachments in the SOAP Message

The WS-I Attachments Profile describes two separate attachment mechanisms, both based on use of the WSDL 1.1 MIME binding:

  • wsiap:swaRef is a schema type that may be used in the abstract message description to indicate a reference to an attachment.
  • mime:content is a binding construct that may be used to bind a message part to an attachment.

JAXB 2.0 defines a mapping between MIME types and Java types. In JAX-WS, when a message part is bound using one or more mime:content elements in the WSDL and use of the additional metadata is enabled, then the JAXB mapping is customized to use the most specific type allowed by the set of MIME types described for the part in the binding. If the binding is not customized, it will use the default binding for the data types used in the WSDL.

The JAX-WS version of this strategy remains more or less same as the example in the Attachments-From-WSDL directory in Part 1, a downloadable zip archive. The endpoint implementation code for JAX-WS also remains unchanged from the JAX-RPC code in Part 1's Code Sample 53 and also appears in this white paper in Code Sample 15. The MIME mappings used by JAXB remain the same and map to a DataHandler and Source object, respectively. We do need to add the @WebService annotation to the implementation endpoint, as shown in Code Sample 15. What changes is the configuration. By default, JAXB will map the data types using its binding rules for data type in the WSDL -- in this case, byte[], because the XML data type is binary (or xsd:hexBinary). Additional metadata can be passed to JAXB that enables it to narrow this default mapping into a more meaningful representation for the endpoints to work with. To facilitate this, we specify in both the client and server configuration used by wsimport that it should use the corresponding Java data type for the type defined in the mime:content using the jaxws:enableMIMEContent binding declaration, as shown in Code Sample 16. This declaration must be supported by all JAX-WS runtimes.

Code Sample 15: Service Endpoint Implementation Processing Attachments

@javax.jws.WebService(endpointInterface="com.examples.attachmentservice.IPurchaseOrder")
                  

public class PurchaseOrderService {
                  

 
   public String storeDocumentService(DataHandler dh, String filename) {
   // Code not shown
    }
                
   public String storeDocumentXML(Source dh, String filename) {
   // Code not shown
    }
   }
 

Code Sample 16: Configuration Descriptor Enabling Binding of MIME Types

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
                  

<bindings  
                  

  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  

      xmlns:jaxb="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/jaxb/index.html"
                  

      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  

     wsdlLocation="wsdl/POService.wsdl"
                  

        xmlns="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/jaxws/index.html">    
                  

       
                  

  <bindings node="wsdl:definitions" >
                  

    <package name="com.examples.attachmentservice"/>
                  

   </bindings>
                  

  
                  

  <bindings node="wsdl:definitions/wsdl:binding
                  

                  [@name='IPurchaseOrderBinding']/wsdl:operation
                  

                  [@name='storeDocumentService']">
   <enableMIMEContent>true</enableMIMEContent>
  </bindings>
                
  <bindings node="wsdl:definitions/wsdl:binding
                  

                  [@name='IPurchaseOrderBinding']/wsdl:operation
                  

                  [@name='storeDocumentXML']">
   <enableMIMEContent>true</enableMIMEContent>
  </bindings>
                  

</bindings>
                
 

Additionally, when using a combination of the switched-off data-binding strategy along with attachments, the attachment contents can be retrieved from the message context using the javax.xml.ws.binding.attachments property. This represents a Map<String,DataHandler> of attachments to a message, in which the key is a unique identifier for the attachment and the value is a DataHandler for the attachment data.

Summary

In this document, we have explored some of the new features of JAX-WS 2.0 and seen how some of the strategies discussed for the Document-Based Web Services pattern in Parts 1 and 2 of this series can be implemented using this new technology -- often with only minor modifications. You can download all the sample code discussed here, including WSDL and build scripts.

Part 4 of this series will explore the interoperability aspect of the same strategies with Microsoft’s Windows Communication Foundation (formerly code-named Indigo).

For More Information
About the Author

Sameer Tyagi is a senior staff engineer at Sun Microsystems. He remains focused on architecture, design, and implementation of large-scale enterprise applications with Java technology. Among his publications are industry periodicals and books on Java and Java EE technologies, as well as a blog.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.