Use collection types with SOAP and JAX-RPC

This article introduces a couple of techniques that you can use to build interoperable Web services that take and return object collections. This is the first of a series of articles covering this subject. Future articles will cover more detailed scenarios. (This intermediate-level article was first published by IBM developerWorks, May 28, 2004, at http://www.ibm.com/developerWorks).

It is quite common for applications to manipulate collections of objects, passing them as arguments to functions and returning them when the processing is complete. There are many ways to represent these collections in most programming languages. They can be expressed as ordered vectors, unordered groups, linked lists, trees, graphs or other forms that the programming language supports. The Java language provides an entire library of collection types in the java.util package. The question, however, is how should you pass collections across a Web services invocation?

Can I use my favorite Java collection type?

To illustrate some issues brought about by the use of standard Java collection classes, let’s suppose you want to create a customer service with a getCustomers operation. You might create a JavaBean that returns a collection of Customer objects based on some search criteria. You peruse the collection classes that the Java language offers and decide to use the java.util.LinkedList class. Your CustomerService class might look like the code mock-up in Listing 1:

Listing 1. The CustomerService class using a LinkedList

import java.util.LinkedList;

public class TheCustomerService {
  public LinkedList getCustomers(String queryString) {
    Customer customer1, customer2;
    /* … retrieve customers for query … */
    LinkedList list = new LinkedList();
    /* iterate over the result set assigning to the list */
    list.add(customer1);
    list.add(customer2);
    return list;
  }
}

The next step is to expose this class as a Web service, and so you choose JAX-RPC-compliant tooling to generate the required artifacts, such as the WSDL file that describes the service interface. This is where the trouble starts. The JAX-RPC specification does not define a mapping for the LinkedList class. Instead, it mentions that each implementation can decide if and how this class is mapped and how instances of the class are serialized to and from XML.

For example, Listing 2, below, shows what the WebSphere Application Server tooling generates as the XML Schema for the class listed in Listing 1:

{mospagebreak title=Listing 2. The generated XML Schema for the CustomerService class.}

<schema elementFormDefault=”qualified”
  targetNamespace=”http://pack”
  xmlns=”http://www.w3.org/2001/XMLSchema”
  xmlns:apachesoap=”http://xml.apache.org/xml-soap”
  xmlns:impl=”http://pack” xmlns:intf=”http://pack”
  xmlns:wsdl=”http://schemas.xmlsoap.org/wsdl/”>
   <element name=”getCustomers”>
  <complexType>
    <sequence>
      <element name=”queryString”
        nillable=”true”
        type=”xsd:string”/>
       </sequence>
  </complexType>
   </element>
   <complexType name=”ArrayOfXSDAnyType”>
  <sequence>
    <element maxOccurs=”unbounded”
        minOccurs=”0″
        name=”item”
        nillable=”true”
        type=”xsd:anyType”/>
  </sequence>
   </complexType>
   <element name=”getCustomersResponse”>
  <complexType>
    <sequence>
      <element name=”getCustomersReturn”
          nillable=”true”
          type=”impl:ArrayOfXSDAnyType”/>
    </sequence>
  </complexType>
   </element>
</schema>   

As you can see, the tooling generates a complex type definition for something called ArrayOfXSDAnyType, which is, in turn, an unbounded sequence of type <xsd:anyType>. Why didn’t it generate an element of type xsd:linkedList to contain your Customers? It’s because there is no xsd:linkedList. There is no xsd:hashMap, no xsd:treeSet, no xsd:vector and no xsd:stack. All of those ficticious types would have some implied functioniality associated with them beyond storing an ordered list of objects. Each language, tool or environment is at liberty to provide arbitrary collection constructs that do one of the following:

- Implement variations of the implied functionality
- Omit that type of collection
- Define others not included in Java collections.

So how do you exchange collections of objects in an interoperable fashion? The answer is using an array.

The solution? Plain arrays…

While some of the collection classes might actually work in your specific environment (such as a Java to Java environment), they might not work in others. Thus, we recommend against using those classes on the service interface. The only way in which collections of objects should be transferred between Web services is as an array. The WS-I Basic Profile describes how arrays (which are still a language-specific construct) are mapped to XML and how this is described in the XML Schema, making its handling interoperable across multiple environments. Listing 3 shows what the changed CustomerService JavaBean looks like:

{mospagebreak title=Listing 3. The CustomerService class using a simple array.}

public class TheCustomerService {
  public Customer[] getCustomers(String queryString) {
    Customer customer1, customer2;
    /* … retrieve customers for query … */
    /* create an array large enough to hold the result set */
    Customer[] customers = new Customer[2];
    /* iterate over the result set assigning to the array */
    customers[0] = customer1;
    customers[1] = customer2;
    return customers;
  }
}

As you can see, we have replaced the LinkedList with an array of type Customer. Listing 4 shows the resulting XML Schema in the WSDL definition:

Listing 4. The updated XML Schema for the CustomerService class.

<schema elementFormDefault=”qualified”
  targetNamespace=http://pack
  xmlns=http://www.w3.org/2001/XMLSchema
  xmlns:apachesoap=”http://xml.apache.org/xml-soap”
  xmlns:impl=”http://pack” xmlns:intf=”http://pack”
  xmlns:wsdl=”http://schemas.xmlsoap.org/wsdl/”>
  <element name=”getCustomers”>
    <complexType>
      <sequence>
        <element name=”queryString”
            nillable=”true”
            type=”xsd:string”/>
      </sequence>
    </complexType>
  </element>
  <complexType name=”Customer”>
    <sequence>
      <element name=”customerID”
          nillable=”true”
          type=”xsd:string”/>
    </sequence>
  </complexType>
  <element name=”getCustomersResponse”>
    <complexType>
      <sequence>
        <element maxOccurs=”unbounded”
          name=”getCustomersReturn”
          type=”impl:Customer”/>
      </sequence>
    </complexType>
  </element>
</schema>


This resulting schema definition and the SOAP message that is generated for this service are language-neutral and follow the WS-I Basic Profile.

{mospagebreak title=Create a wrapper interface}

So far, so good. But what if you have an existing class that exposes language-specific Java collection classes in its method signatures, and you still want to expose this class via a Web service?

One relatively easy way of dealing with this situation is to create a wrapper around your service implementation, converting the collection interface into an array interface. For example, let’s consider the original LinkedList-based CustomerService implementation listed above. We create a class with methods that invoke the original collection-based implementation but return Java arrays instead of the collections. The wrapper class might look like the one in Listing 5:

Listing 5. The wrapper class.

public class TheCustomerServiceWrapper {
  protected TheCustomerServiceLinkedList innerService =
  new TheCustomerServiceLinkedList();

  public Customer[] getCustomers(String queryString) {
    return (Customer[]) innerService.getCustomers(queryString
    ).toArray(new Customer[0]);
  }
}


Now you can create the appropriate Web services artifacts based on this wrapper class, which internally uses the existing collection-based implementation.

Summary

The Java programming language offers a variety of collection classes for different types of collections of objects. None of these, however, are language-neutral, and serializing instances of them into XML is difficult and sometimes impossible. The only recommended way to expose object collections is to use arrays. The WS-I Basic Profile also describes and recommends this approach.

Using Java’s collection classes in the signatures of the public methods of your classes is generally a poor choice, regardless of the implications associated with Web services, because doing so dilutes the “contract” between invoker and provider. These classes invariably store the objects they “collect” as generic references to java.lang.Object. This prevents static-type checking by the compiler and introduces new classes of runtime errors. In cases where an implementation already exists, you can still use an array to expose it as an interoperable Web service by using wrapper classes.

Note that this practice agrees nicely with our good practice recommendation that business logic be coded in a Java class and wrapped in a session EJB to be exposed as a Web service.

{mospagebreak title=Resources}

Download the source code used in this article.

Find a number of Web services programming tips from developerWorks: http://www-106.ibm.com/developerworks/views/webservices/
articles.jsp?sort_order=desc&expand=&sort_by=
Date&show_abstract=true&view_by
=Search&search_by=tip%3A
 


Familiarize yourself with the WS-I Basic Profile: http://ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html

About the authors

Andre Tost works as a Solution Architect in the WebSphere Business Development group, where he helps IBM’s Strategic Alliance partners integrate their applications with WebSphere Studio. His special focus is on Web services technology throughout the WebSphere product family. Before his current assignment, he spent ten years in various development and architecture roles in IBM software development, most recently for the WebSphere Business Components product. Originally from Germany, he now lives and works in Rochester, Minnesota. In his spare time, he likes to spend time with his family and play and watch soccer whenever possible. You can contact Andre at atost@us.ibm.com

Tony Cowan is a senior consultant with the IBM Software Services for WebSphere (ISSW) team in IBM. He has been consulting in distributed system development for over 11 years and has lead IBM teams on many projects with fortune 1000 companies. Tony currently focuses on teaching Web services and Web services security to IBM consultants and customers. A frequent speaker at technical events, one of Tony’s primary objectives at IBM is to bring real customer requirements to the IBM development teams to assist in aligning IBM products with real world needs. You can contact Tony at ttcowan@us.ibm.com
 

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort