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:
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.
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:
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.
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.
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.
| Name | Size | Download method |
|---|---|---|
| ws-tip-codingcode.ear | 36.0 KB | HTTP |
Information about download methods
- Download the source code used in this article.
- Find a number of Web services programming tips from developerWorks.
- Familiarize yourself with the WS-I Basic Profile.
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.
Comments (Undergoing maintenance)





