16.2. A Sample Web Service

This section describes a sample web service, including the interfaces, WSDL, and JAX-RPC mapping file required. Note that all of this is required to deploy the service in Geronimo (either as a web services client or server). However, it is not necessary to generate the "stubs" that implement the web services interfaces and are used for a normal web services client -- Geronimo takes care of that under the covers.

The sample service lists reviews for a product, where each review is uniquely identified by the e-mail address of the reviewer. For any product, you can get a list of reviewers, a list of all reviews, or the review by a particular reviewer.

16.2.1. The Service Endpoint Interface

The interface for the web service is fairly straightforward:

package reviews;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * The web services interface for product reviews.
 */
public interface ProductReviews extends Remote {
    /**
     * Gets the e-mail addresses of the reviewers who have
     * reviewed the specified product.
     */
    public String[] getReviewers(Integer productId)
            throws RemoteException;
    /**
     * Gets all the reviews for a particular product.
     */
    public ReviewData[] getReviews(Integer productId)
            throws RemoteException;
    /**
     * Gets a specific reviewer's review for the specified
     * pruduct (if there is one)
     */
    public ReviewData getReview(Integer productId,
                                String reviewer)
            throws RemoteException;
}

Note the following things:

  • Though the methods are similarly named, none of them are overloaded

  • This is a non-trivial example, with arrays and a JavaBean

  • All of the arguments and return types for the interface and the JavaBean used by the interface are either primitives or basic, supported Java types such as the Object wrappers for primitives and java.lang.String.

  • As we'll see, this simple web service interface, with 3 methods and a total of 4 arguments, requires about 160 lines of WSDL and about 150 lines of JAX-RPC mapping.

The ReviewData JavaBean looks like this:

package reviews;

/**
 * Holds all the information about a review.
 */
public class ReviewData {
    private Integer productId;
    private String reviewerEmail;
    private String reviewerName;
    private Float rating;
    private String reviewTitle;
    private String reviewText;

    /**
     * Empty constructor required for JavaBeans (and for
     * JAX-RPC compatibility)
     */
    public ReviewData() {
    }

    public ReviewData(Integer productId, String reviewerEmail,
                      String reviewerName, Float rating,
                      String reviewTitle, String reviewText) {
        this.productId = productId;
        this.reviewerEmail = reviewerEmail;
        this.reviewerName = reviewerName;
        this.rating = rating;
        this.reviewTitle = reviewTitle;
        this.reviewText = reviewText;
    }

    // Getters and setters for all properties go here
}

16.2.2. The WSDL

The WSDL corresponding to the service endpoint interface looks like this. Notice the three methods on the interface are operations in portType in the WSDL, each has a request and response message, and it uses the Document/Literal wrapped WSDL style (for example, the payload is defined by a schema in the types section, and each message is made up of a single element whose type in the schema has the same name as the message does and also matches the operation input or output name).

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

<wsdl:definitions targetNamespace="geronimo-book:reviews"
         xmlns:ger="geronimo-book:reviews"
         xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
         xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema"
            targetNamespace="geronimo-book:reviews"
            elementFormDefault="qualified">
      <element name="getReviewers">
        <complexType>
          <sequence>
            <element name="productId" type="xsd:int" />
          </sequence>
        </complexType>
      </element>
      <element name="getReviewersResponse">
        <complexType>
          <sequence>
            <element name="getReviewersReturn" type="xsd:string"
                                        maxOccurs="unbounded" />
          </sequence>
        </complexType>
      </element>

      <element name="getReviews">
        <complexType>
          <sequence>
            <element name="productId" type="xsd:int" />
          </sequence>
        </complexType>
      </element>
      <element name="getReviewsResponse">
        <complexType>
          <sequence>
            <element name="getReviewsReturn"
                  type="ger:ReviewData" maxOccurs="unbounded" />
          </sequence>
        </complexType>
      </element>

      <element name="getReview">
        <complexType>
          <sequence>
            <element name="productId" type="xsd:int" />
            <element name="reviewer" type="xsd:string" />
          </sequence>
        </complexType>
      </element>
      <element name="getReviewResponse">
        <complexType>
          <sequence>
            <element name="getReviewReturn"
                     type="ger:ReviewData" />
          </sequence>
        </complexType>
      </element>

      <complexType name="ReviewData">
        <sequence>
          <element name="productId" type="xsd:int"/>
          <element name="reviewerEmail" type="xsd:string"/>
          <element name="reviewerName" nillable="true"
                                       type="xsd:string"/>
          <element name="rating" nillable="true"
                                 type="xsd:float"/>
          <element name="reviewTitle" nillable="true"
                                      type="xsd:string"/>
          <element name="reviewText" nillable="true"
                                     type="xsd:string"/>
        </sequence>
      </complexType>
    </schema>
  </wsdl:types>

  <wsdl:message name="getReviewersRequest">
    <wsdl:part name="parameters" element="ger:getReviewers"/>
  </wsdl:message>
  <wsdl:message name="getReviewersResponse">
    <wsdl:part name="parameters"
               element="ger:getReviewersResponse"/>
  </wsdl:message>
  <wsdl:message name="getReviewsRequest">
    <wsdl:part name="parameters" element="ger:getReviews"/>
  </wsdl:message>
  <wsdl:message name="getReviewsResponse">
    <wsdl:part name="parameters" 
               element="ger:getReviewsResponse"/>
  </wsdl:message>
  <wsdl:message name="getReviewRequest">
    <wsdl:part name="parameters" element="ger:getReview"/>
  </wsdl:message>
  <wsdl:message name="getReviewResponse">
    <wsdl:part name="parameters"
               element="ger:getReviewResponse"/>
  </wsdl:message>

  <wsdl:portType name="ProductReviewsType">
    <wsdl:operation name="getReviewers">
       <wsdl:input name="getReviewersRequest" 
                   message="ger:getReviewersRequest"/>
       <wsdl:output name="getReviewersResponse"
                    message="ger:getReviewersResponse"/>
    </wsdl:operation>
    <wsdl:operation name="getReviews">
       <wsdl:input name="getReviewsRequest"
                   message="ger:getReviewsRequest"/>
       <wsdl:output name="getReviewsResponse"
                    message="ger:getReviewsResponse"/>
    </wsdl:operation>
    <wsdl:operation name="getReview">
       <wsdl:input name="getReviewRequest"
                   message="ger:getReviewRequest"/>
       <wsdl:output name="getReviewResponse"
                    message="ger:getReviewResponse"/>
    </wsdl:operation>
  </wsdl:portType>

  <wsdl:binding name="ProductReviewsSoapBinding"
                type="ger:ProductReviewsType">
    <wsdlsoap:binding style="document"
              transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getReviewers">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="getReviewersRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getReviewersResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getReviews">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="getReviewsRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getReviewsResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getReview">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="getReviewRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getReviewResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>

  <wsdl:service name="ProductReviewsService">
    <wsdl:port name="ProductReviews"
               binding="ger:ProductReviewsSoapBinding">
      <wsdlsoap:address
  location="http://localhost:8080/test/services/ProductReviews"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

16.2.3. The Client Service Interface

The client "service" interface is used to look up a port, or in the case, an instance of ProductReviews that is connected to a particular web service instance. The service interface is pretty straightforward:

package reviews.client;

import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import reviews.ProductReviews;

/**
 * The client service interface for the ProductReviews web
 * service.
 */
public interface ProductReviewsService extends Service {
    public String getProductReviewsAddress();

    public ProductReviews getProductReviews()
            throws ServiceException;

    public ProductReviews getProductReviews(URL portAddress)
            throws ServiceException;
}

Normally for a web services client we'd only start with the WSDL, and so we'd need to provide the "port" interface too, but in this case we happen to have it (the ProductReviews interface from above).

16.2.4. The JAX-RPC Mapping File

The JAX-RPC mapping file maps all the Java interfaces, beans, and methods to the corresponding elements in the WSDL file. Unfortunately, all the sections here are required for this service using the heavyweight JAX-RPC mapping format, even though the content seems fairly obvious.

Note which values using a namespace prefix, and note that the method mappings use the wrapped-element element to indicate that the Document/Literal wrapped format will be used (this avoids the need to explicitly map the complexTypes in the WSDL schema that are bound to request or response messages).

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

<java-wsdl-mapping xmlns="http://java.sun.com/xml/ns/j2ee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:ger="geronimo-book:reviews"
           xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd"
           version="1.1">
  <package-mapping>
    <package-type>reviews.wsclient</package-type>
    <namespaceURI>geronimo-book:reviews</namespaceURI>
  </package-mapping>
  <java-xml-type-mapping>
    <java-type>reviews.ReviewData</java-type>
    <root-type-qname>ger:ReviewData</root-type-qname>
    <qname-scope>complexType</qname-scope>
    <variable-mapping>
      <java-variable-name>productId</java-variable-name>
      <xml-element-name>productId</xml-element-name>
    </variable-mapping>
    <variable-mapping>
      <java-variable-name>reviewerEmail</java-variable-name>
      <xml-element-name>reviewerEmail</xml-element-name>
    </variable-mapping>
    <variable-mapping>
      <java-variable-name>reviewerName</java-variable-name>
      <xml-element-name>reviewerName</xml-element-name>
    </variable-mapping>
    <variable-mapping>
      <java-variable-name>rating</java-variable-name>
      <xml-element-name>rating</xml-element-name>
    </variable-mapping>
    <variable-mapping>
      <java-variable-name>reviewTitle</java-variable-name>
      <xml-element-name>reviewTitle</xml-element-name>
    </variable-mapping>
    <variable-mapping>
      <java-variable-name>reviewText</java-variable-name>
      <xml-element-name>reviewText</xml-element-name>
    </variable-mapping>
  </java-xml-type-mapping>
  <service-interface-mapping>
    <service-interface>
      reviews.wsclient.ProductReviewService
    </service-interface>
    <wsdl-service-name>
      ger:ProductReviewsService
    </wsdl-service-name>
    <port-mapping>
      <port-name>ger:ProductReviews</port-name>
      <java-port-name>ProductReviews</java-port-name>
    </port-mapping>
  </service-interface-mapping>
  <service-endpoint-interface-mapping>
    <service-endpoint-interface>
      reviews.ProductReviews
    </service-endpoint-interface>
    <wsdl-port-type>ger:ProductReviewsType</wsdl-port-type>
    <wsdl-binding>ger:ProductReviewsSoapBinding</wsdl-binding>
    <service-endpoint-method-mapping>
      <java-method-name>getReviewers</java-method-name>
      <wsdl-operation>getReviewers</wsdl-operation>
      <wrapped-element />
      <method-param-parts-mapping>
        <param-position>0</param-position>
        <param-type>java.lang.Integer</param-type>
        <wsdl-message-mapping>
          <wsdl-message>ger:getReviewersRequest</wsdl-message>
          <wsdl-message-part-name>
            productId
          </wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <wsdl-return-value-mapping>
        <method-return-value>
          java.lang.String[]
        </method-return-value>
        <wsdl-message>ger:getReviewersResponse</wsdl-message>
        <wsdl-message-part-name>
          getReviewersReturn
        </wsdl-message-part-name>
      </wsdl-return-value-mapping>
    </service-endpoint-method-mapping>
    <service-endpoint-method-mapping>
      <java-method-name>getReviews</java-method-name>
      <wsdl-operation>getReviews</wsdl-operation>
      <wrapped-element />
      <method-param-parts-mapping>
        <param-position>0</param-position>
        <param-type>java.lang.Integer</param-type>
        <wsdl-message-mapping>
          <wsdl-message>ger:getReviewsRequest</wsdl-message>
          <wsdl-message-part-name>
            productId
          </wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <wsdl-return-value-mapping>
        <method-return-value>
          reviews.ReviewData[]
        </method-return-value>
        <wsdl-message>ger:getReviewsResponse</wsdl-message>
        <wsdl-message-part-name>
          getReviewsReturn
        </wsdl-message-part-name>
      </wsdl-return-value-mapping>
    </service-endpoint-method-mapping>
    <service-endpoint-method-mapping>
      <java-method-name>getReview</java-method-name>
      <wsdl-operation>getReview</wsdl-operation>
      <wrapped-element />
      <method-param-parts-mapping>
        <param-position>0</param-position>
        <param-type>java.lang.Integer</param-type>
        <wsdl-message-mapping>
          <wsdl-message>ger:getReviewRequest</wsdl-message>
          <wsdl-message-part-name>
            productId
          </wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <method-param-parts-mapping>
        <param-position>1</param-position>
        <param-type>java.lang.String</param-type>
        <wsdl-message-mapping>
          <wsdl-message>ger:getReviewRequest</wsdl-message>
          <wsdl-message-part-name>
            reviewer
          </wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <wsdl-return-value-mapping>
        <method-return-value>
          reviews.ReviewData
        </method-return-value>
        <wsdl-message>ger:getReviewResponse</wsdl-message>
        <wsdl-message-part-name>
          getReviewReturn
        </wsdl-message-part-name>
      </wsdl-return-value-mapping>
    </service-endpoint-method-mapping>
  </service-endpoint-interface-mapping>
</java-wsdl-mapping>