Version 2

    Message Endpoints

     

    Using web services for RPC style communication is what all the other wiki documentation is about. However, this is

    not where web services is going. After all, why would you use something as complex to setup and deploy

    as a web service if you only what to make a remote procedure call. The massive serialization overhead imposed

    by SOAP only adds pain to injury - your RPC will be slow. If you can't use RMI because of firewall restrictions

    and you want your components to communicate via HTTP, use the HTTP invoker not web services. Among the few valid

    reasons to use RPC style web services is that you have an interoperability issue with some other that requires you

    to do so.

     

     

     

    So what is the hype about web services in Java to Java communication if it only adds complexity and is slower

    than traditional RPC? The answer is document style web services.

     

     

     

    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.

     

     

     

    Here is an example:

     

       <env:Envelope>
         <env:Body>
           <ns1:Order xmlns:ns1='http://ws.sample'>
             <Customer>Kermit</Customer>
             <Item>Ferrari</Item>
           </ns1:Order>      
         </env:Body>
       </env:Envelope>
    

     

    The complex type for the Order element must be definied in schema and there must be a Java bean that represents

    the order. What the endpoint method is called is irrelevant, important is that there is a method that takes a single

    OrderBean parameter. The mapping between the Order element and the OrderBean is definied in

    jaxrpc-mapping.xml.

     

     

     

    So far so good, but how about routing messages to an endpoint where the message structure is unknown? Lets use a generic service endpoint interface like this:

     

       public interface MessageEndpoint extends Remote
       {
          public Element processElement(Element msg) throws RemoteException;
       }
    

     

    There is no defined schema mapping for javax.xml.dom.Element in the JAX-RPC specification. The schema type that probably makes most sense is xsd:anyType. Therefore the WSDL that describes the service could look like this:

     

      <definitions ...>
        <types>
          <schema ...>
            <element name="Order" type="xsd:anyType"></element>
            <element name="Response" type="xsd:anyType"></element>
          </schema>
        </types>
        <message name="MessageEndpoint_processElement">
          <part name="order" element="tns:Order"></part>
        </message>
        <message name="MessageEndpoint_processElementResponse">
          <part name="result" element="tns:Response"></part>
        </message>
        <portType name="MessageEndpoint">
          <operation name="processElement" parameterOrder="order">
            <input message="tns:MessageEndpoint_processElement"/>
            <output message="tns:MessageEndpoint_processElementResponse"></output>
          </operation>
        </portType>
        <binding name="MessageEndpointBinding" type="tns:MessageEndpoint">
          <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"></soap:binding>
          <operation name="processElement">
            <soap:operation soapAction=""></soap:operation>
            <input>
              <soap:body use="literal" namespace="http://ws.sample"></soap:body>
            </input>
            <output>
              <soap:body use="literal" namespace="http://ws.sample"></soap:body>
            </output>
          </operation>
        </binding>
        <service name="MessageEndpointService">
          <port name="MessageEndpointPort" binding="tns:MessageEndpointBinding">
            <soap:address location="REPLACE_WITH_ACTUAL_URL"></soap:address>
          </port>
        </service>
      </definitions>
    

     

    and the corresponding java mapping file could look like this:

     

      <java-wsdl-mapping ...>
        <package-mapping>
          <package-type>org.jboss.test.webservice.message</package-type>
          <namespaceURI>http://ws.sample</namespaceURI>
        </package-mapping>
        <service-interface-mapping>
          <service-interface>org.jboss.test.webservice.message.MessageEndpointService</service-interface>
          <wsdl-service-name xmlns:serviceNS="http://ws.sample">serviceNS:MessageEndpointService</wsdl-service-name>
          <port-mapping>
            <port-name>MessageEndpointPort</port-name>
            <java-port-name>MessageEndpointPort</java-port-name>
          </port-mapping>
        </service-interface-mapping>
        <service-endpoint-interface-mapping>
          <service-endpoint-interface>org.jboss.test.webservice.message.MessageEndpoint</service-endpoint-interface>
          <wsdl-port-type xmlns:portTypeNS="http://ws.sample">portTypeNS:MessageEndpoint</wsdl-port-type>
          <wsdl-binding xmlns:bindingNS="http://ws.sample">bindingNS:MessageEndpointBinding</wsdl-binding>
          <service-endpoint-method-mapping>
            <java-method-name>processElement</java-method-name>
            <wsdl-operation>processElement</wsdl-operation>
            <method-param-parts-mapping>
              <param-position>0</param-position>
              <param-type>org.w3c.dom.Element</param-type>
              <wsdl-message-mapping>
                <wsdl-message xmlns:wsdlMsgNS="http://ws.sample">wsdlMsgNS:MessageEndpoint_processElement</wsdl-message>
                <wsdl-message-part-name>Order</wsdl-message-part-name>
                <parameter-mode>IN</parameter-mode>
              </wsdl-message-mapping>
            </method-param-parts-mapping>
            <wsdl-return-value-mapping>
              <method-return-value>org.w3c.dom.Element</method-return-value>
              <wsdl-message xmlns:wsdlMsgNS="http://ws.sample">wsdlMsgNS:MessageEndpoint_processElementResponse</wsdl-message>
              <wsdl-message-part-name>Response</wsdl-message-part-name>
            </wsdl-return-value-mapping>
          </service-endpoint-method-mapping>
        </service-endpoint-interface-mapping>
      </java-wsdl-mapping>
    

     

    The service endpoint implementation will receive a SOAPElement that represents the order message. To work with that message, you would use the SAAJ API, simmilar to this

     

       public Element processElement(Element msg) throws RemoteException
       {
             SOAPElement soapElement = (SOAPElement)msg;
    
             SOAPFactory factory = SOAPFactory.newInstance();
    
             Name name = factory.createName("Order", PREFIX, NAMESPACE_URI);
             Name elementName = soapElement.getElementName();
             if (name.equals(elementName) == false)
                throw new IllegalArgumentException("Unexpected element: " + elementName);
    
             name = factory.createName("Customer");
             soapElement = (SOAPElement)soapElement.getChildElements(name).next();
             String elementValue = soapElement.getValue();
             if ("Customer".equals(soapElement.getLocalName()) && "Kermit".equals(elementValue) == false)
                throw new IllegalArgumentException("Unexpected element value: " + elementValue);
    
             soapElement = (SOAPElement)soapElement.getNextSibling();
             elementValue = soapElement.getValue();
             if ("Item".equals(soapElement.getLocalName()) && "Ferrari".equals(elementValue) == false)
                throw new IllegalArgumentException("Unexpected element value: " + elementValue);
    
             // Setup document builder
             DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
             docBuilderFactory.setNamespaceAware(true);
    
             // Prepare response
             DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
             Document doc = builder.parse(new ByteArrayInputStream(response.getBytes()));
    
             return doc.getDocumentElement();
       }
    

     

    Note: Even though element parameters are instances of javax.xml.soap.SOAPElement (as indicated by the cast above example), don't try to map to this type in the jaxrpc mapping file. Just map to org.w3c.dom.Element and then cast it.