JiBX Unwrapped document/literal

Code generation for JiBX data binding converts operations defined by a Web service to method calls. In the most general case of document/literal (doc/lit) Web services the generated methods each take a single parameter object and return a single result object. This type of interface can be painful for developers because it adds both a layer of indirection and potentially a large number of extra classes (one input and one output class for each generated method).

Fortunately, there's an alternative way of generating methods that gives a much more usable API for many Web services. This alternative is called unwrapping, and the service definitions that it applies to are called wrapped definitions. The key difference that qualifies a service definition as wrapped is the structure of the input and output elements used for operations.

Here's a sample wrapped WSDL (partial) by way of an example:

<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
    
  <wsdl:types>
  
    <schema elementFormDefault="qualified"
        targetNamespace="http://ws.sosnoski.com/library/types"
        xmlns="http://www.w3.org/2001/XMLSchema">
        
      <element name="getBook">
        <complexType>
          <sequence>
            <element name="isbn" type="string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="getBookResponse">
        <complexType>
          <sequence>
            <element name="book" minOccurs="0" type="tns:BookInformation"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="addBook">
        <complexType>
          <sequence>
            <element name="type" type="string"/>
            <element name="isbn" type="string"/>
            <element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
            <element name="title" type="string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="addBookResponse">
        <complexType>
          <sequence>
            <element name="success" type="boolean"/>
          </sequence>
        </complexType>
      </element>
      
      <complexType name="BookInformation">
        <sequence>
          <element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
          <element name="title" type="string"/>
        </sequence>
        <attribute name="type" use="required" type="string"/>
        <attribute name="isbn" use="required" type="string"/>
      </complexType>
      
    </schema>

  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>

  <wsdl:message name="getBookResponse">
    <wsdl:part element="wns:getBookResponse" name="parameters"/>
  </wsdl:message>

  <wsdl:message name="addBookRequest">
    <wsdl:part element="wns:addBook" name="parameters"/>
  </wsdl:message>
  
  <wsdl:message name="addBookResponse">
    <wsdl:part element="wns:addBookResponse" name="parameters"/>
  </wsdl:message>

  <wsdl:portType name="Library">

    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>

    <wsdl:operation name="addBook">
      <wsdl:input message="wns:addBookRequest" name="addBookRequest"/>
      <wsdl:output message="wns:addBookResponse" name="addBookResponse"/>
    </wsdl:operation>

  </wsdl:portType>
  ...
</wsdl:definitions>

This WSDL defines a service with just two operations: getBook and addBook. The getBook operation takes a getBook element as input, and returns a getBookResponse element as output, while addBook takes an addBook element as input and returns an addBookResponse as output. Each of these input and output elements in turn consists of a sequence of child elements, with some of the child elements defined directly using standard schema types and others referencing user-defined schema types.

As I said up front, this WSDL qualifies for unwrapped handling using JiBX. Here's the body of the client interface generated when using unwrapping (the -uw option for WSDL2Java):

    public interface LibraryJibxUnwrapped {
          
             
        /**
         * Auto generated method signatures
         * @param type* @param isbn* @param author* @param title
         */
         public boolean addBook(
         java.lang.String type,java.lang.String isbn,java.lang.String[] author,java.lang.String title) throws java.rmi.RemoteException
          
                       
             ;
             
             
        /**
         * Auto generated method signatures
         * @param isbn
         */
         public com.sosnoski.ws.library.jibx.beans.Book getBook(
         java.lang.String isbn) throws java.rmi.RemoteException
          
                       
             ;
             

        
       //
       }

You can see that the JiBX code generation converted the operations into simple method call interfaces without introducing any extraneous objects (see JiBX Document/Literal Example for the interface generated when unwrapping is not used). The server-side interface is the same.

The key points that allow unwrapped handling with JiBX are:

  1. Each operation either accepts no input, or the input consists of a single element.
  2. Each input element is defined as a schema complexType consisting of a sequence of any number of child elements.
  3. Each operation either generates no output, or the output consists of a single element.
  4. Each output element is defined as a schema complexType consisting of a sequence that's either empty or contains a single child element.
  5. The child elements of both inputs and outputs are defined using type references, rather than an embedded type definitions.

You also need to supply an appropriate JiBX binding definition (using the -Ebindingfile {file} parameter for WSDL2Java - see JiBX Codegen Integration - WSDL2Java usage for more details). This must define abstract mappings for the complexTypes referenced by child elements of the inputs and outputs, with a type-name attribute matching the schema complexType name. If the child elements reference schema simpleType definitions the binding must also define a formats for each simpleType, with a label attribute matching the schema simpleType name. The binding definition must also specify the force-classes='true' attribute on the binding element.

For example, here's a binding definition that matches the above WSDL:

<binding force-classes="true" xmlns:tns="http://ws.sosnoski.com/library/types">

  <namespace uri="http://ws.sosnoski.com/library/types" default="elements"/>
  
  <mapping abstract="true" class="com.sosnoski.ws.library.jibx.beans.Book"
      type-name="tns:BookInformation">
    <value name="type" style="attribute" field="m_type"/>
    <value name="isbn" style="attribute" field="m_isbn"/>
    <collection field="m_authors">
      <value name="author"/>
    </collection>
    <value name="title" field="m_title"/>
  </mapping>
  
</binding>

And here's the actual com.sosnoski.ws.library.jibx.beans.Book class:

package com.sosnoski.ws.library.jibx.beans;

public class Book
{
    private String m_type;
    private String m_isbn;
    private String m_title;
    private String[] m_authors;
    
    public Book() {}

    public String getType() {
        return m_type;
    }
    
    public String getIsbn() {
        return m_isbn;
    }
    
    public String getTitle() {
        return m_title;
    }
    
    public String[] getAuthors() {
        return m_authors;
    }
}

The JiBX code generation for Axis2 currently requires that classes coresponding to unwrapped child elements (such as com.sosnoski.ws.library.jibx.beans.Book, in this case) provide public default (no-argument) constructors.

JiBX handling allows the child elements of both inputs and outputs to be optional (with nillable='true', minOccurs='0', or both), providing the binding converts these child elements to object types rather than primitive types. It also allows repeated child elements (with minOccurs='unbounded', or any value of minOccurs greater than one), representing the repeated elements as arrays of the corresponding object or primitive types.