Web services are meant to expose functionality to a wide range of clients, most not under the administrative domain of the service provider. Since over time clients come to rely on a Web service's interface, how is a service provider to evolve that interface without breaking existing clients? Dru Devore offers a solution in a recent blog post.
In a recent blog post, Dru Devore states the problem in this way:
Lets say it is safe to say that you need to make Web Services backwards compatible for as long as possible. To do this you cannot change an existing operation, parameter, or return object. Also, you want to keep a concise operation list. So if you had an operation named getXYZ() which returns an object which you are certain will change to meet the business needs how do you do this?
He then lists a few possible solutions, including passing emtpy fields as part of the interface, using string arrays (simple ones or multi-dimensional arrays), and the Service Data Object mechanism described in JSR 235. He finds that these possibilities don't really address the service evolution problem, and then proposes a solution he terms self-describing data object (SDDO):
[An SDDO object] uses a Map ... to hold the data and an array of ... Key-Value data objects [that allow] the XML parser to think that the data is an array. In the get() method for the data array, it converts the values in the Map to KVData objects and puts them into an array and returns them. In the set() method for the data array ... simply puts the data back into the Map for easier data access.
The KVData object is a simple object which only holds 3 Strings: key, value, and dataType. The key is the key name for the data, the value is the data value, and the dataType is the value.getClass().getName().
To instantiate the data on the other side, I use reflection to create the object and set the data using the getDeclaredConstructor(String.class) method. Using this method you can recreate any object that can be created with the same String value the object returns from the toString() method.
Dru lists several of the advantages and disadvantages of this approach to defining versionable services. The biggest disadvantage is that,
An SDDO without an clearly defined set of attributes will be very difficult to understand.
I think that's more than a minor problem: Web service interfaces are already hard to understand, and every ounce of clarity in the interface is needed for a service to be usable. At the minimum, a client developer should be able to understand parameters of the service interface just by looking at the interface definition.
Thus, while SDDOs might offer a better solution than empty strings or string arrays in the service interface, they also illustrate just how complex Web service evolution is.
What are your thoughts about evolving long-running services? And what do you think of Dru's self-describing data objects solution?
> Dru lists several of the advantages and disadvantages > of this approach to defining versionable services. The > biggest disadvantage is that, > An SDDO without an clearly defined set of attributes will > be very difficult to understand.
One of the key issues with missing type information in web services is the inability to use type information to resolve versioning issues. In the world of Java mobile code, you can use interfaces to change your API over time, while keeping the old API in place.
The benefit of mobile code is that you can send the clients a smart proxy, and have the smart proxy, locally, provide the compatible implementation, while always supporting just a single server implementation (which has to support the needs of the various versions naturally).
When you try to make everything a string, then the string is all you have to help you understand versioning. Being able to insert type names and other descriptions of differences into the API as a standard part of the software system, is very valuable in these cases.