Version 2

    Providing a document style service endpoint

     

    Imagine a world, where two business partners agree on the exchange of complex business documents

    that are well defined in XML schema. You send me a document describing your purchase order,

    I respond (immediately or later) with a document that describes the status of your purchase

    order. No need to agree on such low level details as operation names and their associated

    parameters.

     

    The technology that is used for exchange of these complex documents is document/literal web

    services. The payload of the SOAP message is an XML document that can be validated against XML

    schema. No wrapping RPC element, no individual parameters.

     

    The service endpoint interface

     

    Let's take a more complex SEI than in the previous example:

     

    public interface OrderProcess extends Remote
    {
       OrderResponse processOrder(OrderItem[] items, Person person) throws RemoteException, OrderException;
    }
    

     

    All parameter and return types are complex java beans and we even use an array of them as an input parameter.

     

    Generating required deployment artifacts

     

       wscompile -cp output/classes -gen:server -f:documentliteral -mapping jaxrpc-mapping.xml -keep config.xml
    

     

    This time we use -f:documentliteral and the -keep option to instruct wscompile to keep the generated java sources.

     

    <configuration
      xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
    
      <service name="OrderProcessService"
        targetNamespace="http://org.jboss.test.webservice/samples2"
        typeNamespace="http://org.jboss.test.webservice/samples2/types"
        packageName="org.jboss.test.webservice.samples2">
        <interface name="org.jboss.test.webservice.samples2.OrderProcess"></interface>
      </service>
    
    </configuration>
    

     

    The Web Service Description

     

    The WSDL for our SEI is shown next

     

    <definitions name="OrderProcessService" targetNamespace="http://org.jboss.test.webservice/samples2" 
      xmlns:tns="http://org.jboss.test.webservice/samples2" xmlns="http://schemas.xmlsoap.org/wsdl/" 
      xmlns:ns2="http://org.jboss.test.webservice/samples2/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    
      <types>
        <schema targetNamespace="http://org.jboss.test.webservice/samples2/types" 
          xmlns:tns="http://org.jboss.test.webservice/samples2/types" 
          xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
          xmlns="http://www.w3.org/2001/XMLSchema">
    
          <complexType name="processOrder">
            <sequence>
              <element name="arrayOfOrderItem_1" type="tns:OrderItem" nillable="true" minOccurs="0" maxOccurs="unbounded"></element>
              <element name="Person_2" type="tns:Person" nillable="true"></element>
            </sequence>
          </complexType>
          <complexType name="OrderItem">
            <sequence>
              <element name="name" type="string" nillable="true"></element>
              <element name="quantity" type="int"></element>
            </sequence>
          </complexType>
          <complexType name="Person">
            <sequence>
              <element name="age" type="int"></element>
              <element name="name" type="string" nillable="true"></element>
            </sequence>
          </complexType>
          <complexType name="processOrderResponse">
            <sequence>
              <element name="result" type="tns:OrderResponse" nillable="true"></element>
            </sequence>
          </complexType>
          <complexType name="OrderResponse">
            <sequence>
              <element name="items" type="tns:OrderItem" nillable="true" minOccurs="0" maxOccurs="unbounded"></element>
              <element name="message" type="string" nillable="true"></element>
            </sequence>
          </complexType>
          <complexType name="OrderException">
            <sequence>
              <element name="message" type="string" nillable="true"></element>
            </sequence>
          </complexType>
          <element name="processOrder" type="tns:processOrder"></element>
          <element name="processOrderResponse" type="tns:processOrderResponse"></element>
          <element name="OrderException" type="tns:OrderException"></element>
        </schema>
      </types>
    
      <message name="OrderProcess_processOrder">
        <part name="parameters" element="ns2:processOrder"></part>
      </message>
      <message name="OrderProcess_processOrderResponse">
        <part name="result" element="ns2:processOrderResponse"></part>
      </message>
      <message name="OrderException">
        <part name="OrderException" element="ns2:OrderException"></part>
      </message>
    
      <portType name="OrderProcess">
        <operation name="processOrder">
          <input message="tns:OrderProcess_processOrder"/>
          <output message="tns:OrderProcess_processOrderResponse"></output>
          <fault name="OrderException" message="tns:OrderException"></fault>
        </operation>
      </portType>
    
      <binding name="OrderProcessBinding" type="tns:OrderProcess">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"></soap:binding>
        <operation name="processOrder">
          <soap:operation soapAction=""></soap:operation>
          <input>
            <soap:body use="literal"></soap:body>
          </input>
          <output>
            <soap:body use="literal"></soap:body>
          </output>
          <fault name="OrderException">
            <soap:fault name="OrderException" use="literal"></soap:fault>
          </fault>
        </operation>
      </binding>
    
      <service name="OrderProcessService">
        <port name="OrderProcessPort" binding="tns:OrderProcessBinding">
          <soap:address location="REPLACE_WITH_ACTUAL_URL"></soap:address>
        </port>
      </service>
    </definitions>
    

     

    There are a few things to notice about this wsdl. First and most importantly the messages involved in the

    operation only use a single part and that part is defined by an element definition in the schema. That

    means the entire payload of the service is defined in schema. There is no wrapping RPC element and no

    individual parameters. The same is true for the response message.

     

     

    Also note, that the XML element name of the incoming message is the same as the operation name.

    This is a convenient convention and not yet required by spec.

     

    Java to XML mapping

     

    Let's have a look how the mapping of the request/response structures are defined in jaxrpc-mapping.xml.

    For readability resons, I won't show the entire mapping file.

     

      <java-xml-type-mapping>
        <java-type>org.jboss.test.webservice.samples2.OrderProcess_processOrder_RequestStruct</java-type>
        <root-type-qname xmlns:typeNS="http://org.jboss.test.webservice/samples2/types">typeNS:processOrder</root-type-qname>
        <qname-scope>complexType</qname-scope>
        <variable-mapping>
          <java-variable-name>arrayOfOrderItem_1</java-variable-name>
          <xml-element-name>arrayOfOrderItem_1</xml-element-name>
        </variable-mapping>
        <variable-mapping>
          <java-variable-name>Person_2</java-variable-name>
          <xml-element-name>Person_2</xml-element-name>
        </variable-mapping>
      </java-xml-type-mapping>
    
      <java-xml-type-mapping>
        <java-type>org.jboss.test.webservice.samples2.OrderProcess_processOrder_ResponseStruct</java-type>
        <root-type-qname xmlns:typeNS="http://org.jboss.test.webservice/samples2/types">typeNS:processOrderResponse</root-type-qname>
        <qname-scope>complexType</qname-scope>
        <variable-mapping>
          <java-variable-name>result</java-variable-name>
          <xml-element-name>result</xml-element-name>
        </variable-mapping>
      </java-xml-type-mapping>
    

     

    Generated request/response structure objects

     

    wscompile generated the request/response structures and we look at them next. First the request structure

     

    public class OrderProcess_processOrder_RequestStruct {
        protected org.jboss.test.webservice.samples2.OrderItem[] arrayOfOrderItem_1;
        protected org.jboss.test.webservice.samples2.Person Person_2;
        
        public OrderProcess_processOrder_RequestStruct() {
        }
        
        public OrderProcess_processOrder_RequestStruct(org.jboss.test.webservice.samples2.OrderItem[] 
     arrayOfOrderItem_1, org.jboss.test.webservice.samples2.Person Person_2) {
            this.arrayOfOrderItem_1 = arrayOfOrderItem_1;
            this.Person_2 = Person_2;
        }
        
        public org.jboss.test.webservice.samples2.OrderItem[] getArrayOfOrderItem_1() {
            return arrayOfOrderItem_1;
        }
        
        public void setArrayOfOrderItem_1(org.jboss.test.webservice.samples2.OrderItem[] arrayOfOrderItem_1) {
            this.arrayOfOrderItem_1 = arrayOfOrderItem_1;
        }
        
        public org.jboss.test.webservice.samples2.Person getPerson_2() {
            return Person_2;
        }
        
        public void setPerson_2(org.jboss.test.webservice.samples2.Person Person_2) {
            this.Person_2 = Person_2;
        }
    }
    

     

    and now the response structure

     

    public class OrderProcess_processOrder_ResponseStruct {
        protected org.jboss.test.webservice.samples2.OrderResponse result;
        
        public OrderProcess_processOrder_ResponseStruct() {
        }
        
        public OrderProcess_processOrder_ResponseStruct(org.jboss.test.webservice.samples2.OrderResponse result) {
            this.result = result;
        }
        
        public org.jboss.test.webservice.samples2.OrderResponse getResult() {
            return result;
        }
        
        public void setResult(org.jboss.test.webservice.samples2.OrderResponse result) {
            this.result = result;
        }
    }
    

     

    You see, that they nicely wrap the input/return parameters from the SEI.

     

    The endpoint as web application

     

    A java service endpoint is deployed as a web application. Therefore we need a web.xml deployment descriptor, which

    we will have to code by hand.

     

    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
      version="2.4">
    
      <servlet>
        <servlet-name>OrderProcess</servlet-name>
        <servlet-class>org.jboss.test.webservice.samples2.OrderProcessImpl</servlet-class>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>OrderProcess</servlet-name>
        <url-pattern>/OrderProcess</url-pattern>
      </servlet-mapping>
    
    </web-app>
    

     

    Note, that we declare the endpoint implemenation bean in the <servlet-class> element, but it is actually not

    a servlet at all.

     

     

     

     

     

    The webservices deployment descriptor

     

    One more descriptor is needed that glues everything together. It is called webservices.xml and must also be coded

    by hand. Here it is

     

    <webservices
      xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"
      version="1.1">
    
      <webservice-description>
        <webservice-description-name>OrderProcess</webservice-description-name>
        <wsdl-file>WEB-INF/wsdl/OrderProcessService.wsdl</wsdl-file>
        <jaxrpc-mapping-file>WEB-INF/jaxrpc-mapping.xml</jaxrpc-mapping-file>
        <port-component>
          <port-component-name>PortComponent</port-component-name>
          <wsdl-port>OrderProcessPort</wsdl-port>
          <service-endpoint-interface>org.jboss.test.webservice.samples2.OrderProcess</service-endpoint-interface>
          <service-impl-bean>
            <servlet-link>OrderProcess</servlet-link>
          </service-impl-bean>
        </port-component>
      </webservice-description>
    </webservices>
    

     

    Clients connecting to document style endpoints

     

    Although there is better support on the client side for wrapping/unwrapping document style messages.

    I would recommend using a simmilar technique and using the generated request/response structures on the client

    as well.

     

       /** Test a valid access */
       public void testValidAccess() throws Exception
       {
          InitialContext iniCtx = getClientContext();
          Service service = (Service)iniCtx.lookup("java:comp/env/service/OrderProcess");
          OrderProcess endpoint = (OrderProcess)service.getPort(OrderProcess.class);
    
          Person p = new Person("Tom", 3);
          OrderItem i0 = new OrderItem("Ferrari", 1);
          OrderItem i1 = new OrderItem("Twix", 10);
          OrderItem i2 = new OrderItem("IceCream", 3);
    
          OrderResponse res = endpoint.processOrder(new OrderItem[] { i0, i1, i2 }, p);
          assertEquals(3, res.getItems().length);
          assertEquals(i0, res.getItems()[0]);
          assertEquals(i1, res.getItems()[1]);
          assertEquals(i2, res.getItems()[2]);
          assertEquals("approved", res.getMessage());
       }
    
       /** Test a invalid access */
       public void testNullPerson() throws Exception
       {
          InitialContext iniCtx = getClientContext();
          Service service = (Service)iniCtx.lookup("java:comp/env/service/OrderProcess");
          OrderProcess endpoint = (OrderProcess)service.getPort(OrderProcess.class);
    
          OrderItem i1 = new OrderItem("Ferrari", 1);
          OrderItem i2 = new OrderItem("Twix", 10);
          OrderItem i3 = new OrderItem("IceCream", 3);
    
          try
          {
             endpoint.processOrder(new OrderItem[] { i1, i2, i3 }, null);
             fail("OrderException expected");
          }
          catch (OrderException e)
          {
             // ignore expected exception
          }
       }