Schema Binding for Java Web Services
May 26, 2004
Sometimes you just wish software architects weren't so creative or, at least, were more coordinated. It seems that's what happened with SOAP, the standard format for web services messages. The SOAP specification started out defining a lot of protocol-specific XML structures that now, in hindsight, could obviously better be delegated to the W3C XML Schema language, the whole purpose of which is to define the structure of XML documents. It's a disconnect that the Java community is still working out.
In this article we'll explore the oft-misunderstood relationship between Java web services platforms and the W3C XML Schema (WXS) binding frameworks that convert between XML and XML-agnostic objects. And we'll look at how to leverage schema binding using various Java Web services toolkits and runtimes.
Reconciling Web services with XML Schema
In May 2000, when the World Wide Web Consortium published SOAP 1.1, the W3C XML Schema specification was still only in draft stages.
SOAP 1.1 encourages a way of encoding XML that relies on placing simple type values
in
element bodies. But where SOAP encoding, as it is called, really digresses from WXS
is in its use of in-document references and a SOAP-specific Array
type.
SOAP 1.1 also suggests a way of associating SOAP messages with remote procedure calls. RPC imposes certain conventions and restrictions on the XML elements that correspond to parameters or return values and the method-matching XML elements that automatically wrap them.
By the time in May 2001 the W3C finalized its XML Schema specification, more WXS-reliant alternatives to SOAP encoding and RPC style had arisen.
As opposed to SOAP encoding, literal XML usage says XML should be written
precisely the way it's defined in a schema. No more complicated references or SOAP-encoded
arrays. In a Web Services Description
Language (WSDL) 1.1 document, a use
attribute in certain SOAP-specific
elements can be set to encoded
or literal
.
As opposed to RPC style, document style tells a runtime to write the XML exactly
as it's defined in the WSDL. In the WSDL, each message
element should contain a
single part
element, which in turn refers to an element defined in a schema.
Conveniently, the XML schemata appear directly -- or indirectly via imports -- under
the
types
element of a WSDL, using a path like so:
/wsdl:definitions/wsdl:types/xsd:schema
In WSDL, a style
attribute in certain SOAP-binding elements can be set to
"rpc"
or "document"
.
Document/literal Web services are well on their way to replacing RPC/encoded as an industry standard. The Web Services Interoperability Organization (WS-I), a consortium whose goal is make sure Web services and clients play nice together, published in August 2003 a set of guidelines called the Basic Profile Version 1.0a. The Basic Profile prohibits the use of SOAP encoding; it does allow RPC style, although the RPC/literal combination has never caught on.
The nice thing about document/literal is that it forces the actual payload of a SOAP
message to be defined entirely using W3C XML Schema, independent of SOAP and WSDL.
The child
of the soap:Body
element, as shown in the SOAP message structure below, can
stand alone as its own XML document:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" > <soap:Header> <!-- header element(s) --> </soap:Header> <soap:Body> <!-- body element defined independent of SOAP --> </soap:Body> </soap:Envelope>
This is the kind of encapsulation that should make a design patterns aficionado go weak at the knees. Not only could the SOAP body's contents readily be validated against a schema, but if the web services platform leverages a schema binding framework with a public API, the same schema can be used to serialize XML not just to a SOAP message but to any destination: a file, as a node within a larger document, whatever.
Here's another nice thing, if you consider a whole can of worms a nice thing. Once you're using a full-fledged schema binding framework, you can customize how you bind between Java classes and WXS data types. Consider the beginning of a JavaBean-style class definition below:
public class BasicDataBean implements java.io.Serializable { private String key; public String getKey() { return key; } public void setKey(String key) { this.key = key; } ...
Simple question: do you define the key
property as an XML element or
attribute? With SOAP encoding, you have no choice but to make it an element. With
literal
use, there are two ways to go:
- Just as with SOAP encoding, the binding toolkit makes the choice for you. This is what the open-source Web services platform Apache Axis 1.2 does.
- The toolkit gives you a choice.
If you don't want to control the XML in your web service, the former, default-only approach is an acceptable option. But if you do want to exploit the schema's constructs or you're generating a client for a service that does, then the latter approach, involving a real schema binding framework, is required.
A Java binding framework provides a schema-to-Java tool, possibly also a Java-to-schema tool. If a web services provider incorporates the schema binding provider, then WSDL-to-Java and Java-to-WSDL tools should perform a superset of the schema-only tools' functionality.
So where do the binding/mapping customizations go? Some "to-Java" tools place them in the plumbing of the generated implementation classes themselves, although that tends to rule out a "Java-to" reverse conversion. Other to-Java tools produce an extra output besides source code, namely a proprietary XML mappings file, which in turn becomes an extra input to the corresponding Java-to tool (if present). Accordingly, the runtime checks the customizations as it translates between XML instance documents and objects.
Employing the same XML serializer on both ends
We're ready to try to exploit the synergy between schema binding and web services. We'll use what we might call a naïve implementation, where we define the same XML document for publishing over the Web service and for serializing to disk. Suppose the service endpoint interface defines these methods:
public void update(FooDocument foo) throws java.rmi.RemoteException; public FooDocument getFoo() throws java.rmi.RemoteException;
The FooDocument
class corresponds to a foo.xsd
schema definition
that gets imported into the web service's generated WSDL. The web service runtime
receives
an update
SOAP message and automatically deserializes the XML into a
FooDocument
instance. (Normally, you'll use a "wrapped" interpretation of a
document-style web service where the request soap:Body
element's child is not
the foo
element but rather an extra wrapper element that the Web service
platform has automatically defined in the WSDL.)
The implementation of update
should do something like:
public void update(FooDocument foo) throws java.rmi.RemoteException { QName qname = new javax.xml.namespace.QName( Pk_Foo.NAMESPACE, "foo", Pk_Foo.PREFIX); mPersist.writeObject2Document(qname, getFile(mDataRoot, Pk_Foo.FILENAME)); }
The member variable mPersist
is an instance of a Persistence
interface we define:
public interface Persistence { public void writeObject2Document(QName qname, File file) throws SomeCustomException; public Object readDocument2Object(Class klass, QName qname, File file) throws SomeCustomException; } }
You can then implement the Persistence
interface using the same binding
framework the Web services platform uses. The writeObject2Document
implementation obtains a writer or output stream and calls an appropriate object
serialization method in the binding framework API. Likewise,
readDocument2Object
, which is called by getFoo
, calls the
appropriate deserialization method. The translation between an entire XML document
and an
entire object graph should be accomplished in a few method calls; no need to write
DOM or
SAX code or otherwise get your hands dirty with the XML.
There are a few reasons this example might be considered a naïve implementation:
- Usually your persistence store is not the file system but a relational database where you probably don't want to spew text documents.
- Even then, you might have a slicker way of saving the XML document than by going directly to disk.
- The data structure you send across the wire perhaps shouldn't be as verbose as the one you save to disk.
The state of W3C XML Schema binding support
The following table shows a sampling of web services implementations together with the XML Schema binding components they use, plus how those components customize the bindings between XML Schema data types and Java classes:
Web services platform | XML Schema binding framework | Where customizations are stored |
webMethods Glue 5.0.1 | Its own proprietary Electric XML+. | .map files. |
Systinet WASP Server for Java 4.7.1 (new version is 5.0) | Its own. | .xmap files. |
Apache Axis 1.2 beta | Its own. | No customizations allowed. |
Apache Axis 1.2 beta with Castor | Castor. | The Castor-generated classes, or a mapping file for composed classes. |
BEA WebLogic Server 8.1 SP2 | XMLBeans. | The generated XMLBeans classes. |
Glue, with Electric XML+, and WebLogic Server, with XMLBeans, seamlessly allow you to serialize XML interchangeably within Web services and without. WASP requires some do-it-yourself to use its XML serializer independent of SOAP. The Axis+Castor marriage is more of a kludge.
It's also worth mentioning the Java Web Services Developer Pack 1.3. The JWSDP contains Sun's reference implementation of the JAX-RPC 1.1 specification for mediating between the Java world and the SOAP/WSDL worlds. The Java API for XML-based RPC, now in version 1.1, has had to evolve from its RPC/encoded legacy, but its default bindings do influence the other SOAP engines listed above.
The JWSDP also contains Sun's implementation of the Java Architecture for XML Binding (JAXB) v1.0.2, a standards-based attempt at a binding framework. But even though the JWSDP is Basic Profile-conformant, there's no real integration between the JAX-RPC and JAXB implementations. To accomplish what's described in our nave example, you'll have to wait until the JWSDP implements the 2.0 versions of the JAX-RPC and JAXB specifications, which at long last are being coordinated. Final drafts are expected by the end of 2004.