Monday, March 3, 2008

Best practices to implement interoperable web services in a Service-oriented Architecture (SOA)

SOA (service-oriented architecture) is an increasingly popular concept and it has become a buzzword. The World Wide Web Consortium (W3C) refers to SOA simply as 'A set of components which can be invoked, and whose interface descriptions can be published and discovered'.

CBDI Forum provides a better definition of SOA as: The policies, practices, frameworks that enable application functionality to be provided and consumed as sets of services published at a granularity relevant to the service consumer. Services can be invoked, published and discovered, and are abstracted away from the implementation using a single, standards-based form of interface.

You can see Understanding Service-Oriented Architecture for a complete introduction to SOA by CBDI. The base element of a SOA is a Service with the following basic properties:

  • · The service provides well defined functionality with high value for the consumer.
  • · The interface contract for the service is platform-independent (loose-coupled and interoperable).
  • · The service can be dynamically located and invoked.
  • · The service is self-contained. That is, the service maintains its own state.

The most important aspect of a service-oriented architecture is that it separates the service's implementation from its interface. Service consumers view a service simply as an endpoint that supports a particular request format or contract. The way the service executes tasks given to it by service consumers is irrelevant.

One of the ways to implement such kind of services is by using Web Services.

At the core of Web Services are the standards WSDL and SOAP. The aim of this article is to provide some best practices in developing web services compliant with the SOA requirement mentioned above to define platform-independent interfaces.

Choosing the right Web Service model

A Web Service is described by its WSDL (http://www.w3.org/TR/wsdl) having six major elements:

  • Types: provides data type definitions used to describe the messages exchanged.
  • Message: represents an abstract definition of the data being transmitted. A message consists of logical parts, each of which is associated with a definition within some type system.
  • portType: a set of abstract operations. Each operation refers to an input message and output messages.
  • Binding: specifies concrete protocol and data format specifications for the operations and messages defined by a particular portType.
  • Port: specifies an address for a binding, thus defining a single communication endpoint.
  • Service: used to aggregate a set of related ports.

In particular binding contains some very interesting information:

· style: this attribute (of the sub-element soap:operation) specifies if the message represents a Remote Procedure Call (RPC) - the invocation of a remote method with parameters and returned values - rather than a Document - a message where all the information is encapsulated in one XML document defined by an XML schema. Each style has a corresponding standardized SOAP body message structure (see SOAP specs: http://www.w3.org/TR/soap).

· Use: this attribute (of the sub-element soap:body) specifies the message encoding/decoding methods used (these terms describe the change from an in-memory structure of objects into an XML data stream, and back). The permitted values are Encoded or Literal. The Encoded style uses SOAP encoding rules to map the abstract data types to concrete data. On the contrary, using Literal encoding the abstract data type produces the concrete data.

Combining the binding style and encoding style provides four different models of web services having different communication models:

  • RPC/encoded
  • RPC/literal
  • Document/encoded
  • Document/literal

RPC/encoded is the most used but it is not compliant to WS-I (The Web Services Interoperability Organization is an open industry effort chartered to promote Web Services interoperability across platforms, applications, and programming languages. See http://www.ws-i.org).

RPC/literal is not used very much although it is WS-I compliant, while the Document/encoded model has never been really specified (and is not WS-I compliant).

The Document/literal model, even if it is not very standardized, adheres best to SOA in terms of Loose Coupling (a concept related to the degree in which components depend on one another) and Interoperability. In fact this model provides:

  • An effective loose-coupled interface: the contract is fully described by its xsd schema therefore providing message validation and avoiding any possible conflicts on encoding/decoding
  • An effectively interoperable system: as we will see in the next paragraph this model is supported by the biggest IT players in the web services area: Microsoft, Bea, IBM, and Apache Axis. Moreover Microsoft effectively supports this and only this model. This model is WS-I compliant.

The Document/literal Web Services model

In the WSDL specs there’s another important sub-element of binding called operation where the attributes name represents the unique name of an operation that can be invoked on a service. In the case of RPC/* Web Services there’s an operation with its unique name for each method exposed while for those of type Document/* this relation has not been explicitly defined.

The drawback is that it is not possible to invoke different functionalities within the same document/literal-based web service.

To solve this issue Microsoft has defined a specific document/literal-based web service with the following characteristics:

  1. The soap: body element contains one and only one xml message
  2. The first element of the message is an operation name (!)
  3. Each operation corresponds exactly to one method exposed by the web service.

These requirements solve the issue. There is no specification that defines this wrapped style even though it is supported not only by Microsoft but also by IBM Web Sphere, Apache Axis 1.2 and BEA Weblogic.

So the only choice right now, in order to interoperate among those platforms, is to make educated guesses to try to understand how they work based on the WSDL and the SOAP messages they generate.

Interoperability tests executed permit us to also define the following:

4.The xml messages to be sent with the operation name element are fully defined by their schemas included in the WSDL types element. To provide the maximum interoperability XSD is used (see http://www.w3.org/XML/Schema):

There’s also the following constraint to be considered coming from .NET platform (probably related to a bug in the MS WSDL generator tool):

5.All the XSD schemas defined under the WSDL types element and describing the messages contract must not refer to the same targetnamespace

Axis 1.2 beta release is not basically compliant to the last 2 requirements because so far it doesn’t require providing the xsd schema. On the contrary Axis simply automatically generates a very generic schema that is included into the WSDL. Axis provides a workaround because it allows the replacement of the automatically generated WSDL with another one provided at deployment time by the developer.

Although these missing functionalities force the developer to build the WSDL himself, at runtime the interoperability for document/literal web services is ensured by the fact that Axis follows requirement (1) and (2).

A complete example

In this section we’re going to apply the requirements listed above to implement a Document/literal Web Service in SOA.

Let’s consider a Web Service exposing an operation called getResourceProfile. This operation defines a contract made of two xml messages:

  • An input xml message defined by the ResourceProfile.In element:
  • The relative output xml message defined by the ResourceProfile.Out element:

Here is the complete XSD schema for the two elements:

<xs:schema targetNamespace="http://next.best.italy.hp.com/trx" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://next.best.italy.hp.com/trx" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">

<xs:complexType name="resourceType">

<xs:all>

<xs:element name="id" type="xs:string" nillable="false"/>

<xs:element name="dn" type="xs:string"/>

<xs:element name="type" type="xs:string"/>

<xs:element name="description" type="xs:string" minOccurs="0"/>

<xs:element name="version" type="xs:string" minOccurs="0"/>

<xs:element name="url" type="xs:anyURI"/>

xs:all>

xs:complexType>

<xs:element name="ResourceProfile.In">

<xs:complexType>

<xs:all>

<xs:element name="resourceId" type="xs:string" nillable="false"/>

<xs:element name="view" type="xs:token" nillable="false"/>

xs:all>

xs:complexType>

xs:element>

<xs:element name="ResourceProfile.Out">

<xs:complexType>

<xs:sequence>

<xs:element name="resourceProfile">

<xs:complexType>

<xs:sequence minOccurs="0" maxOccurs="unbounded">

<xs:element name="resource" type="resourceType"/>

xs:sequence>

<xs:attribute name="view" type="xs:string" use="required"/>

xs:complexType>

xs:element>

xs:sequence>

xs:complexType>

xs:element>

xs:schema>

Notice that each xml message is composed of only one top element as required by requirement (1). Now, to match requirement (2) and (3), it is necessary to wrap each message with an operation element. Here is the fragment of the XSD defining it:

xmlns:s=http://www.w3.org/2001/XMLSchema

xmlns:trx="http://next.best.italy.hp.com/trx" elementFormDefault="qualified"

targetNamespace="http://www.openuri.org/">

namespace="http://next.best.italy.hp.com/trx" />

name="getResourceProfile">

ref="trx:ResourceProfile.In" />

getResourceProfileResponse">

ref="trx:ResourceProfile.Out" />

It is important to notice that two different targetNamespace have been defined to adhere to requirement (5).

Finally, for requirement (4), it is now necessary to include all such schemas into the Web service WSDL type’s element.

Here is a fragment of the WSDL produced:

WSDL

<types>

<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema"

xmlns:trx="http://next.best.italy.hp.com/trx" elementFormDefault="qualified"

targetNamespace="http://www.openuri.org/">

<s:import namespace="http://next.best.italy.hp.com/trx" />

<s:element name="getResourceProfile">

<s:complexType>

ResourceProfile.In">

…..

- <s:sequence>

<s:element ref="trx:ResourceProfile.In" />

s:sequence>

s:complexType>

s:schema>

….

types>

<binding name="ResourcesProfilerSoap" type="s0:ResourcesProfilerSoap">

<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

<operation name="getResourceProfile">

<soap:operation soapAction="http://www.openuri.org/getResourceProfile"

style="document" />

<input>

<soap:body use="literal" />

input>

<output>

<soap:body use="literal" />

output>

operation>

binding>

We have now completely described the steps necessary to generate the WSDL. We finish by showing two fragments of the correct SOAP messages that are exchanged by the Web Service.

This is the body of a SOAP message invoking the getResourceProfile operation:

<soapenv:Body>

<getResourceProfile xmlns="http://www.openuri.org/">

sportello

all

And this is a fragment of the SOAP body of the reply message generated:

<soapenv:Body>

<getResourceProfileResponse xmlns="http://www.openuri.org/">

...

No comments: