Building Web Services With Java PDF
Building Web Services With Java PDF
ID
?
href
IDREF
?
sku
*/string
?
name
string
?
company
string
?
street
string
?
city
string
?
state
string
?
postalCode
string
?
country
string
quantity
positiveInteger
+
item
itemType
?
description
string
po
poType
shipTo
addressType
order
i
Figure 2.3 Document structure defined by purchase order schema
54 Chapter 2 XML Primer
Listing 2.11 shows the basic structure of the SkatesTown PO schema.
Listing 2.11 Basic XML Schema Structure
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
targetNamespace=http://www.skatestown.com/ns/po>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Purchase order schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
...
</xsd:schema>
Schema are expressed in XML and designed with namespaces in mind from the ground
up. In this particular schema document, all elements belonging to the schema specifica-
tion are prefixed with xsd:. The prefixs name isnt important, but xsd: (which comes
from XML Schema Definition) is the convention. The prefix is associated with the
http://www.w3.org/2001/XMLSchema namespace, which identifies the W3C
Recommendation of the XML Schema specification. The default namespace of the doc-
ument is set to be http://www.skatestown.com/ns/po, the namespace of the
SkatesTown PO. The schema document needs both namespaces to distinguish between
XML elements that belong to the schema specification versus XML elements that
belong to POs. Finally, the targetNamespace attribute of the schema element identifies
the namespace of the documents that will conform to this schema. This is set to the PO
schema namespace.
The schema is enclosed by the xsd:schema element. The content of this element is
other schema elements that are used for element, attribute, and datatype definitions. The
annotation and documentation elements can be used liberally to attach auxiliary infor-
mation to the schema.
Associating Schemas with Documents
Schemas dont have to be associated with XML documents. For example, applications
can be preconfigured to use a particular schema when processing documents.
Alternatively, there is a powerful mechanism for associating schemas with documents.
Listing 2.12 shows how to associate the previous schema with a PO document.
55 XML Schemas
Listing 2.12 Associating Schemas with Documents
<?xml version=1.0 encoding=UTF-8?>
<po:po xmlns:po=http://www.skatestown.com/ns/po
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/po
http://www.skatestown.com/schema/po.xsd
id=43871 submitted=2004-01-05 customerId=73852>
...
</po:po>
First, because the PO schema identifies a target namespace, PO documents are required
to use namespaces to identify their elements. The PO document uses the po prefix for
this task.
Next, the document uses another namespacehttp://www.w3.org/2001/
XMLSchema-instancewhich has a special meaning. It defines a number of attributes
that are part of the schema specification. These attributes can be applied to elements in
instance documents to provide additional information to a schema-aware XML proces-
sor. By convention, most documents use the namespace prefix xsi: (for XML Schema:
Instance).
The binding between the PO document and its schema is established via the
xsi:schemaLocation attribute. This attribute contains a pair of values. The first value is
the namespace identifier whose schemas location is identified by the second value.
Typically, the second value is a URL, but specialized applications can use other types of
values, such as an identifier in a schema repository or a well-known schema name. If the
document used more than one namespace, the xsi:schemaLocation attribute would
contain multiple pairs of values.
Simple Types
Prior to the arrival of schemas, one of the biggest problems with XML processing was
that XML had no notion of datatypes, even for simple values such as the character data
content of an element or an attribute value. Because of this limitation, XML applications
included a large amount of validation code. For example, even a simple PO requires the
following validation rules, which are outside the scope of the XML Specification:
n
Attributes id and customerId of the po element must be positive integers.
n
Attribute submitted of the po element must be a date in the format yyyy-mm-dd.
n
Attribute quantity of the item element must be a positive integer.
n
Attribute sku (stock keeping unit) of the item element must be a string with this
format: three digits, followed by a dash, followed by two uppercase letters.
XML schemas address these issues in two ways. First, the specification comes with a large
set of predefined basic datatypes such as string, positiveInteger, and date, which you
56 Chapter 2 XML Primer
can use directly. For custom data types, such as the values of the sku attribute, the speci-
fication defines a powerful mechanism for defining new types. Table 2.2 shows some of
the commonly used predefined schema types with examples of their use.
Table 2.2 Predefined XML Schema Simple Types
Examples (Delimited
Simple Type by Commas) Notes
string Confirm this is electric
base64Binary GpM7
hexBinary 0FB7
integer -126789, -1, 0, 1, 126789
positiveInteger 1, 126789
negativeInteger -126789, -1
nonNegativeInteger 0, 1, 126789
nonPositiveInteger -126789, -1, 0
decimal -1.23, 0, 123.4, 1000.00
boolean true, false
1, 0
time 13:20:00.000
13:20:00.000-05:00
dateTime 1999-05-31T13:20:00.000-05:00
(May 31, 1999 at 1.20pm Eastern
Standard Time, which is 5 hours
behind Coordinated Universal
Time)
duration P1Y2M3DT10H30M12.3S (1 year,
2 months, 3 days, 10 hours, 30
minutes, and 12.3 seconds)
date 1999-05-31
Name shipTo XML Name type
QName po:USAddress XML Namespace QName
anyURI http://www.example.com/,
http://www.example.com/
doc.html#ID5
ID XML ID attribute type
IDREF XML IDREF attribute type
The information in this table comes from the XML Schema Primer.
A note on ID/IDREF attributes: An XML processor is required to generate an error if a
document contains two ID attributes with the same value or an IDREF with a value that
57 XML Schemas
has no matching ID value. This makes ID/IDREF attributes perfect for handling attributes
such as id and href in SkatesTowns PO address element.
Extending Simple Types
The process for creating new simple datatypes is straightforward. The new type must be
derived from a base type: a predefined schema type or another already-defined simple
type. The base type is restricted along a number of facets to obtain the new type. The facets
identify various characteristics of the types, such as
n
length, minLength, maxLengthThe exact, minimum, and maximum character
length of the value
n
patternA regular expression pattern for the value
n
enumerationA list of all possible values
n
whiteSpaceThe rules for handling whitespace in the value
n
minExclusive, minInclusive, maxInclusive, maxExclusiveThe range of
numeric values that are allowed
n
totalDigitsThe number of decimal digits in a numeric value
n
fractionDigitsThe number of decimal digits after the decimal point
Of course, not all facets apply to all types. For example, the notion of fraction digits
makes no sense for a date or a name. Tables 2.3 and 2.4 cross-link the predefined types
and the facets that are applicable for them.
Table 2.3 XML Schema Facets for Simple Types
Simple Type Facets
length minLength maxLength pattern enumeration whiteSpace
string
base64Binary
hexBinary
integer
positiveInteger
negativeInteger
nonNegativeInteger
nonPositiveInteger
decimal
boolean
time
dateTime
duration
date
58 Chapter 2 XML Primer
Name
QName
anyURI
ID
IDREF
The information in this table comes from the XML Schema Primer.
The facets listed in Table 2.4 apply only to simple types that have an implicit order.
Table 2.4 XML Schema Facets for Ordered Simple Types
Simple Types Facets
Max Max Min Min Total Fraction
Inclusive Exclusive Inclusive Exclusive Digits Digits
integer
positiveInteger
negativeInteger
nonNegativeInteger
nonPositiveInteger
decimal
time
dateTime
duration
date
The information in this table comes from the XML Schema Primer.
The syntax for creating new types is simple. For example, the schema snippet in Listing
2.13 defines a simple type for purchase order SKUs. The name of the type is skuType.
Its based on a string, and it restricts the string to the following pattern: three digits,
followed by a dash, followed by two uppercase letters.
Table 2.3 Continued
Simple Type Facets
length minLength maxLength pattern enumeration whiteSpace
59 XML Schemas
Listing 2.13 Using Patterns to Define a Strings Format
<xsd:simpleType name=skuType>
<xsd:restriction base=xsd:string>
<xsd:pattern value=\d{3}-[A-Z]{2}/>
</xsd:restriction>
</xsd:simpleType>
Listing 2.14 shows how to force purchase order IDs to be greater than 10,000 but less
than 100,000, and also how to define an enumeration of all U.S. states.
Listing 2.14 Using Ranges and Enumerations
<xsd:simpleType name=poIdType>
<xsd:restriction base=xsd:integer>
<xsd:minExclusive value=10000/>
<xsd:maxExclusive value=100000/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name=stateType>
<xsd:restriction base=xsd:string>
<xsd:enumeration value=AK/>
<xsd:enumeration value=AL/>
<xsd:enumeration value=AR/>
...
</xsd:restriction>
</xsd:simpleType>
Complex Types
In XML Schema, simple types define the valid choices for character-based content such
as attribute values and elements with character content. Complex types g, on the other
hand, define complex content models, such as those of elements that can have attributes
and nested children. Complex type definitions address both the sequencing and multi-
plicity of child elements as well as the names of associated attributes and whether theyre
required or optional.
The syntax for defining complex types is straightforward:
<xsd:complexType name=typeName>
<xsd:someTopLevelModelGroup>
<!-- Sequencing and multiplicity constraints for
child elements defined using xsd:element -->
</xsd:someTopLevelModelGroup>
<!-- Attribute declarations using xsd:attribute -->
</xsd:complexType>
60 Chapter 2 XML Primer
The element xsd:complexType identifies the type definition. There are many different
ways to specify the model group of the complex type. The most commonly used top-
level model group elements youll see are
n
xsd:sequenceA sequence of elements
n
xsd:choiceAllows one out of a number of elements
n
xsd:allAllows a certain set of elements to appear once or not at all but in any
order
n
xsd:groupReferences a model group that is defined someplace else
These could be further nested to create more complex model groups. The xsd:group
model group element is covered later in this chapter in the section Content Model
Groups.
Inside the model group specification, child elements are defined using xsd:element.
The model group specification is followed by any number of attribute definitions using
xsd:attribute.
For example, one possible way to define the content model of the PO address used in
the billTo and shipTo elements is shown in Listing 2.15. The name of the complex
type is addressType. Using xsd:sequence and xsd:element, it defines a sequence of
the elements name, company, street, city, state, postalCode, and country.
Listing 2.15 Schema Fragment for the Address Complex Type
<xsd:complexType name=addressType>
<xsd:sequence>
<xsd:element name=name type=xsd:string minOccurs=0/>
<xsd:element name=company type=xsd:string minOccurs=0/>
<xsd:element name=street type=xsd:string
maxOccurs=unbounded/>
<xsd:element name=city type=xsd:string/>
<xsd:element name=state type=xsd:string minOccurs=0/>
<xsd:element name=postalCode type=xsd:string
minOccurs=0/>
<xsd:element name=country type=xsd:string minOccurs=0/>
</xsd:sequence>
<xsd:attribute name=id type=xsd:ID/>
<xsd:attribute name=href type=xsd:IDREF/>
</xsd:complexType>
The multiplicities of these elements occurrences are defined using the minOccurs and
maxOccurs attributes of xsd:element. The value of zero for minOccurs renders an ele-
ments presence optional (? in the document structure diagrams). The default value for
minOccurs is 1. The special maxOccurs value unbounded is used for the street ele-
ment to indicate that at least one must be present (+ in the document structure dia-
grams).
61 XML Schemas
As we mentioned earlier, every element is associated with a type using the type
attribute xsd:element. In this example, all elements have simple character content of
type string, identified by the xsd:string type. It might seem unusual that the name-
space prefix is used inside an attribute value. Its true, the XML Namespaces specification
doesnt explicitly address this use of namespace prefixes. However, the idea is simple. A
schema can define any number of types. Some of them are built into the specification,
and others are user-defined. The only way to know for sure which type is being referred
to is to associate the type name with the namespace from which its coming. What better
way to do this than to prefix all references to the type with a namespace prefix?
After the model group definition come the attribute definitions. In this example,
xsd:attribute defines attributes id and href of types ID and IDREF, respectively. Both
attributes are optional by default.
Now, consider a slightly more complex example of a complex type definitionthe
po elements type (see Listing 2.16).
Listing 2.16 Schema Fragment for the Purchase Order Complex Type
<xsd:complexType name=poType>
<xsd:sequence>
<xsd:element name=billTo type=addressType/>
<xsd:element name=shipTo type=addressType/>
<xsd:element name=order>
<xsd:complexType>
<xsd:sequence>
<xsd:element name=item type=itemType
maxOccurs=unbounded/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name=id use=required
type=xsd:positiveInteger/>
<xsd:attribute name=submitted use=required
type=xsd:date/>
<xsd:attribute name=customerId use=required
type=xsd:positiveInteger/>
</xsd:complexType>
The poType introduces three interesting aspects of schemas:
n
It shows how easy it is to achieve basic reusability of types. Both the billTo and
shipTo elements refer to the addressType defined previously. Note that because
this is a user-defined complex type, a namespace prefix isnt necessary.
n
The association between elements and their types can be implicit. The order ele-
ments type is defined inline as a sequence of one or more item elements of type
62 Chapter 2 XML Primer
itemType. This is convenient because it keeps the schema more readable and pre-
vents the need to define a global type that is used in only one place.
n
The presence of attributes can be required through the use=required attribute-
value pair of the xsd:attribute element. To give default and fixed values to
attributes, you can also use the aptly named default and fixed attributes of
xsd:attribute.
The Purchase Order Schema
With the information gathered so far, we can completely define the SkatesTown pur-
chase order schema (Listing 2.17).
Listing 2.17 The Complete SkatesTown Purchase Order Schema (po.xsd)
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
targetNamespace=http://www.skatestown.com/ns/po>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Purchase order schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
<xsd:element name=po type=poType/>
<xsd:complexType name=poType>
<xsd:sequence>
<xsd:element name=billTo type=addressType/>
<xsd:element name=shipTo type=addressType/>
<xsd:element name=order>
<xsd:complexType>
<xsd:sequence>
<xsd:element name=item type=itemType
maxOccurs=unbounded/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name=id use=required
type=xsd:positiveInteger/>
<xsd:attribute name=submitted use=required
type=xsd:date/>
<xsd:attribute name=customerId use=required
type=xsd:positiveInteger/>
</xsd:complexType>
63 XML Schemas
<xsd:complexType name=addressType>
<xsd:sequence>
<xsd:element name=name type=xsd:string minOccurs=0/>
<xsd:element name=company type=xsd:string minOccurs=0/>
<xsd:element name=street type=xsd:string
maxOccurs=unbounded/>
<xsd:element name=city type=xsd:string/>
<xsd:element name=state type=xsd:string minOccurs=0/>
<xsd:element name=postalCode type=xsd:string
minOccurs=0/>
<xsd:element name=country type=xsd:string minOccurs=0/>
</xsd:sequence>
<xsd:attribute name=id type=xsd:ID/>
<xsd:attribute name=href type=xsd:IDREF/>
</xsd:complexType>
<xsd:complexType name=itemType>
<xsd:sequence>
<xsd:element name=description type=xsd:string
minOccurs=0/>
</xsd:sequence>
<xsd:attribute name=sku use=required>
<xsd:simpleType>
<xsd:restriction base=xsd:string>
<xsd:pattern value=\d{3}-[A-Z]{2}/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name=quantity use=required
type=xsd:positiveInteger/>
</xsd:complexType>
</xsd:schema>
Global Versus Local Elements and Attributes
Everything should look familiar except perhaps the standalone definition of the po ele-
ment after the schema annotation. This brings us to the important topic of local versus
global elements and attributes. Any element or attribute defined inside a complex type
definition is considered local to that definition. Conversely, any element or attribute
defined at the top level (as a child of xsd:schema) is considered global.
All global elements can be document roots. That is the main reason why most
schemas define a single global element. In the case of the SkatesTown PO, the po ele-
ment must be the root of the PO document and is hence defined as a global element.
Listing 2.17 Continued
64 Chapter 2 XML Primer
The notion of global attributes might not make much sense at first, but these
attributes are very convenient.You can use them (in namespace-prefixed form) on any
element in a document that allows them. The item priority attribute discussed in the
section XML Namespaces is defined with the short schema in Listing 2.18.
Listing 2.18 Defining the Priority Global Attribute Using a Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/priority
targetNamespace=http://www.skatestown.com/ns/priority
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
<xsd:attribute name=priority use=optional default=medium>
<xsd:simpleType>
<xsd:restriction base=xsd:string>
<xsd:enumeration value=low/>
<xsd:enumeration value=medium/>
<xsd:enumeration value=high/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:schema>
Basic Schema Reusability
The concept of reusability is important for XML Schema. Reusability deals with the
question of how to best leverage existing assets in new projects. In schemas, the assets
include element and attribute definitions, content model definitions, simple and complex
datatypes, and whole schemas. We can roughly break down reusability mechanisms into
two kinds: basic and advanced. The basic reusability mechanisms address the problems of
using existing assets in multiple places. Advanced reusability mechanisms address the
problems of modifying existing assets to serve needs that are different than those for
which the assets were originally designed.
This section will address the following basic reusability mechanisms:
n
Element references
n
Content model groups
n
Attribute groups
n
Schema includes
n
Schema imports
Element References
In XML Schema, you can define elements using a name and a type. Alternatively, ele-
ment declarations can refer to preexisting elements using the ref attribute of
65 XML Schemas
xsd:element as follows, where a globally defined comment element is reused for both
person and task complex types:
<xsd:element name=comment type=xsd:string/>
<xsd:complexType name=personType>
<xsd:sequence>
<xsd:element name=name type=xsd:string/>
<xsd:element ref=comment minOccurs=0/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name=taskType>
<xsd:sequence>
<xsd:element name=toDo type=xsd:string/>
<xsd:element ref=comment minOccurs=0/>
</xsd:sequence>
</xsd:complexType>
Content Model Groups
Element references are perfect for reusing the definition of a single element. However, if
your goal is to reuse all or part of a content model, then element groups are the way to
go. Element groups are defined using xsd:group and are referred to using the same
mechanism used for elements. The following schema fragment illustrates the concept. It
extends the previous example so that instead of a single comment element, public and
private comment elements are reused as a group:
<xsd:group name=comments>
<xsd:sequence>
<xsd:element name=publicComment type=xsd:string
minOccurs=0/>
<xsd:element name=privateComment type=xsd:string
minOccurs=0/>
</xsd:sequence>
</xsd:group>
<xsd:complexType name=personType>
<xsd:sequence>
<xsd:element name=name type=xsd:string/>
<xsd:group ref=comments/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name=taskType>
<xsd:sequence>
66 Chapter 2 XML Primer
<xsd:element name=toDo type=xsd:string/>
<xsd:group ref=comments/>
</xsd:sequence>
</xsd:complexType>
Attribute Groups
The same reusability mechanism can be applied to commonly used attribute groups. The
following example defines the ID/IDREF combination of id and href attributes as a ref-
erenceable attribute group. Its then applied to both the person and the task type:
<xsd:attributeGroup name=referenceable>
<xsd:attribute name=id type=xsd:ID/>
<xsd:attribute name=href type=xsd:IDREF/>
</xsd:attributeGroup>
<xsd:complexType name=personType>
<xsd:sequence>
<xsd:element name=name type=xsd:string/>
</xsd:sequence>
<xsd:attributeGroup ref=referenceable/>
</xsd:complexType>
<xsd:complexType name=taskType>
<xsd:sequence>
<xsd:element name=toDo type=xsd:string/>
</xsd:sequence>
<xsd:attributeGroup ref=referenceable/>
</xsd:complexType>
Schema Includes and Imports
Element references and groups as well as attribute groups provide reusability within the
same schema document. However, when youre dealing with very complex schema or
trying to achieve maximum reusability, youll often need to split a schema into several
documents. The schema include and import mechanisms allow these documents to refer-
ence one another.
Consider the scenario where SkatesTown is intent on reusing the schema definition
for its address type for a mailing list schema. SkatesTown must solve three small prob-
lems:
n
Put the address type definition in its own schema document
n
Reference this schema document from the purchase order schema document
n
Reference this schema document from the mailing list schema document
67 XML Schemas
Pulling the address definition into its own schema is as easy as a cut-and-paste operation
(see Listing 2.19). Even though this is a different document than the main purchase
order schema, they both define portions of the SkatesTown PO namespace. The binding
between schema documents and the namespaces they define isnt one-to-one. Its explic-
itly identified by the targetNamespace attribute of the xsd:schema element.
Listing 2.19 Standalone Address Type Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
targetNamespace=http://www.skatestown.com/ns/po>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Address type schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType name=addressType>
<xsd:sequence>
<xsd:element name=name type=xsd:string minOccurs=0/>
<xsd:element name=company type=xsd:string minOccurs=0/>
<xsd:element name=street type=xsd:string
maxOccurs=unbounded/>
<xsd:element name=city type=xsd:string/>
<xsd:element name=state type=xsd:string minOccurs=0/>
<xsd:element name=postalCode type=xsd:string
minOccurs=0/>
<xsd:element name=country type=xsd:string minOccurs=0/>
</xsd:sequence>
<xsd:attribute name=id type=xsd:ID/>
<xsd:attribute name=href type=xsd:IDREF/>
</xsd:complexType>
</xsd:schema>
Referring to this schema is also easy. Instead of having the address type definition inline,
the PO schema needs to include the address schema using the xsd:include element.
During the processing of the PO schema, the address schema will be retrieved and the
address type definition will become available (see Listing 2.20).
Listing 2.20 Referring to the Address Type Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
targetNamespace=http://www.skatestown.com/ns/po>
68 Chapter 2 XML Primer
<xsd:include
schemaLocation=http://www.skatestown.com/schema/address.xsd/>
...
</xsd:schema>
The mailing list schema is very simple. It defines a single mailingList element that
contains any number of contact elements whose type is address. Being an altogether
different schema than that used for POs, the mailing list schema uses a new namespace:
http://www.skatestown.com/ns/mailingList. Listing 2.21 shows one possible way to
define this schema.
Listing 2.21 Mailing List Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
targetNamespace=http://www.skatestown.com/ns/mailingList>
<xsd:include
schemaLocation=http://www.skatestown.com/schema/address.xsd/>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Mailing list schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
<xsd:element name=mailingList>
<xsd:sequence>
<xsd:element name=contact type=addressType
minOccurs=0 maxOccurs=unbounded/>
</xsd:sequence>
</xsd:element>
</xsd:schema>
This example uses xsd:include to bring in the schema fragment defining the address
type. There is no problem with that approach. However, there might be a problem with
authoring mailing-list documents. The root of the problem is that the mailingList and
contact elements are defined in one namespace (http://www.skatestown.com/ns/
mailingList), whereas the elements belonging to the address typename, company,
street, city, state, postalCode, and countryare defined in another
(http://www.skatestown.com/ns/po). Therefore, the mailing list document must refer-
ence both namespaces (see Listing 2.22).
Listing 2.20 Continued
69 XML Schemas
Listing 2.22 Mailing List That References Two Namespaces
<?xml version=1.0 encoding=UTF-8?>
<list:mailingList xmlns:list=http://www.skatestown.com/ns/mailingList
xmlns:addr=http://www.skatestown.com/ns/po
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/mailingList
http://www.skatestown.com/schema/mailingList.xsd
http://www.skatestown.com/ns/po
http://www.skatestown.com/schema/address.xsd>
<contact>
<addr:company>The Skateboard Warehouse</addr:company>
<addr:street>One Warehouse Park</addr:street>
<addr:street>Building 17</addr:street>
<addr:city>Boston</addr:city>
<addr:state>MA</addr:state>
<addr:postalCode>01775</addr:postalCode>
</contact>
</list:mailingList>
Ideally, when reusing the address type definition in the mailing list schema, we want to
hide the fact that it originates from a different namespace and treat it as a true part of
the mailing list schema. Therefore, the xsd:include mechanism isnt the right one to
use, because it makes no namespace changes. The reuse mechanism that will allow the
merging of schema fragments from multiple namespaces into a single schema is the
import mechanism. Listing 2.23 shows the new mailing list schema.
Listing 2.23 Importing Rather Than Including the Address Type Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/po
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:addr=http://www.skatestown.com/ns/po
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/po
http://www.skatestown.com/schema/address.xsd
targetNamespace=http://www.skatestown.com/ns/mailingList>
<xsd:import namespace=http://www.skatestown.com/ns/po/>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Mailing list schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
<xsd:element name=mailingList>
<xsd:sequence>
70 Chapter 2 XML Primer
<xsd:element name=contact type=addr:addressType
minOccurs=0 maxOccurs=unbounded/>
</xsd:sequence>
</xsd:element>
</xsd:schema>
Although the mechanism is simple to describe, it takes several steps to execute:
1. Declare the namespace of the address type definition and assign it the prefix addr.
2. Use the standard xsi:schemaLocation mechanism to hint about the location of
the address schema.
3. Use xsd:import instead of xsd:include to merge the contents of the PO name-
space into the mailing list namespace.
4. When referring to the address type, use its fully qualified name:
addr:addressType.
The net result is that the mailing list instance document has been simplified (see Listing
2.24).
Listing 2.24 Simplified Instance Document That Requires a Single Namespace
<?xml version=1.0 encoding=UTF-8?>
<list:mailingList xmlns:list=http://www.skatestown.com/ns/mailingList
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/mailingList
http://www.skatestown.com/schema/mailingList.xsd>
<contact>
<company>The Skateboard Warehouse</company>
<street>One Warehouse Park</street>
<street>Building 17</street>
<city>Boston</city>
<state>MA</state>
<postalCode>01775</postalCode>
</contact>
</list:mailingList>
Advanced Schema Reusability
The previous section demonstrated how you can reuse types and elements as is from the
same or a different namespace. This capability can go a long way in some cases, but many
real-world scenarios require more sophisticated reuse capabilities. Consider, for example,
the format of the invoice that SkatesTown will send to the Skateboard Warehouse based
on its PO (see Listing 2.25).
Listing 2.23 Continued
71 XML Schemas
Listing 2.25 SkatesTown Invoice Document
<?xml version=1.0 encoding=UTF-8?>
<invoice:invoice xmlns:invoice=http://www.skatestown.com/ns/invoice
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/invoice
http://www.skatestown.com/schema/invoice.xsd
id=43871 submitted=2004-01-05 customerId=73852>
<billTo id=addr-1>
<company>The Skateboard Warehouse</company>
<street>One Warehouse Park</street>
<street>Building 17</street>
<city>Boston</city>
<state>MA</state>
<postalCode>01775</postalCode>
</billTo>
<shipTo href=addr-1/>
<order>
<item sku=318-BP quantity=5 unitPrice=49.95>
<description>Skateboard backpack; five pockets</description>
</item>
<item sku=947-TI quantity=12 unitPrice=129.00>
<description>Street-style titanium skateboard.</description>
</item>
<item sku=008-PR quantity=1000 unitPrice=0.00>
<description>Promotional: SkatesTown stickers</description>
</item>
</order>
<tax>89.89</tax>
<shippingAndHandling>200</shippingAndHandling>
<totalCost>2087.64</totalCost>
</invoice:invoice>
The invoice document has many of the features of a PO document, with a few impor-
tant changes:
n
Invoices use a different namespace: http://www.skatestown.com/ns/invoice.
n
The root element of the document is invoice, not po.
n
The invoice element has three additional children: tax, shippingAndHandling,
and totalCost.
n
The item element has an additional attribute: unitPrice.
How can we leverage the work done to define the PO schema in defining the invoice
schema? This section will introduce the advanced schema reusability mechanisms that
make this possible.
72 Chapter 2 XML Primer
Design Principles
Imagine that purchase orders, addresses, and items were represented as classes in an
object-oriented programming language such as Java. We could create an invoice object
by subclassing item to invoiceItem (which adds unitPrice) and po to invoice (which
adds tax, shippingAndHandling, and totalCost). The benefit of this approach is that
any changes to related classes such as address will be automatically picked up by both
POs and invoices. Further, any changes in base types such as item will be automatically
picked up by derived types such as invoiceItem.
The following pseudocode shows how this approach might work:
public class Address { ... }
public class Item
{
public String sku;
public int quantity;
}
public class InvoiceItem extends Item
{
public double unitPrice;
}
public class PO
{
public int id;
public Calendar submitted;
public int customerId;
public Address billTo;
public Address shipTo;
public Item order[];
}
public class Invoice extends PO
{
public double tax;
public double shippingAndHandling;
public double totalCost;
}
Everything looks good except for one important detail.You might have noticed that
Invoice shouldnt subclass PO, because the order array inside an invoice object must
hold InvoiceItems and not just Item. The subclassing relationship will force you to
work with Items instead of InvoiceItems. Doing so will weaken static type-checking
and require constant downcasting, which is generally a bad thing in well-designed
object-oriented systems. A better design for the Invoice class, unfortunately, requires
some duplication of POs data members:
73 XML Schemas
public class Invoice
{
public int id;
public Calendar submitted;
public int customerId;
public Address billTo;
public Address shipTo;
public InvoiceItem order[];
public double tax;
public double shippingAndHandling;
public double totalCost;
}
Note that subclassing Item to get InvoiceItem is a good decision because InvoiceItem
is a pure extension of Item. It adds new data members; it doesnt require modifications
to Items data members, nor does it change the way theyre used.
Extensions and Restrictions
The analysis from object-oriented systems can be directly applied to the design of
SkatesTowns invoice schema. The schema defines the invoice element in terms of pre-
existing types such as addressType, and the invoices item type reuses the already-
defined purchase order item type via extension (see Listing 2.26).
Listing 2.26 SkatesTown Invoice Schema
<?xml version=1.0 encoding=UTF-8?>
<xsd:schema xmlns=http://www.skatestown.com/ns/invoice
targetNamespace=http://www.skatestown.com/ns/invoice
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:po=http://www.skatestown.com/ns/po>
<xsd:import namespace=http://www.skatestown.com/ns/po
schemaLocation=http://www.skatestown.cm/schema/po.xsd/>
<xsd:annotation>
<xsd:documentation xml:lang=en>
Invoice schema for SkatesTown.
</xsd:documentation>
</xsd:annotation>
<xsd:element name=invoice type=invoiceType/>
<xsd:complexType name=invoiceType>
<xsd:sequence>
<xsd:element name=billTo type=po:addressType/>
<xsd:element name=shipTo type=po:addressType/>
<xsd:element name=order>
74 Chapter 2 XML Primer
<xsd:complexType>
<xsd:sequence>
<xsd:element name=item type=itemType
maxOccurs=unbounded/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name=tax type=priceType/>
<xsd:element name=shippingAndHandling type=priceType/>
<xsd:element name=totalCost type=priceType/>
</xsd:sequence>
<xsd:attribute name=id use=required
type=xsd:positiveInteger/>
<xsd:attribute name=submitted use=required
type=xsd:date/>
<xsd:attribute name=customerId use=required
type=xsd:positiveInteger/>
</xsd:complexType>
<xsd:complexType name=itemType>
<xsd:complexContent>
<xsd:extension base=po:itemType>
<xsd:attribute name=unitPrice use=required
type=priceType/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name=priceType>
<xsd:restriction base=xsd:decimal>
<xsd:minInclusive value=0/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
By now the schema mechanics should be familiar. The beginning of the schema declares
the PO and invoice namespaces. The PO schema has to be imported because it doesnt
reside in the same namespace as the invoice schema.
The invoiceType schema address type is defined in terms of po:addressType, but
the order elements content is of type itemType and not po:itemType. Thats because
the invoices itemType needs to extend po:itemType and add the unitPrice attribute.
This happens at the next complex type definition. In general, the schema extension syn-
tax, although somewhat verbose, is easy to use:
Listing 2.26 Continued
75 XML Schemas
<xsd:complexType name=...>
<xsd:complexContent>
<xsd:extension base=...>
<!-- Optional extension content model -->
<!-- Optional extension attributes -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
The content model of extended types contains all the child elements of the base type
plus any additional elements added by the extension. Any attributes in the extension are
added to the attribute set of the base type.
Last but not least, the invoice schema defines a simple price type as a non-negative
decimal number. The definition happens via restriction of the lower boundary of the
decimal type using the same mechanism introduced in the section on simple types.
The restriction mechanism in schemas applies not only to simple types but also to
complex types. The syntax is similar to that of extension:
<xsd:complexType name=...>
<xsd:complexContent>
<xsd:restriction base=...>
<!-- Content model and attributes -->
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
The concept of restriction has a precise meaning in XML Schema. The declarations of
the type derived by restriction are very close to those of the base type but more limited.
There are several possible types of restrictions:
n
Multiplicity restrictions
n
Deletion of optional elements
n
Tighter limits on occurrence constraints
n
Provision of default values
n
Provision of types where there were none, or narrowing of types
For example, we can extend the address type by restriction to create a corporate address
that doesnt include a name:
<xsd:complexType name=corporateAddressType>
<xsd:complexContent>
<xsd:restriction base=addressType>
<xsd:sequence>
<!-- Add maxOccurs=0 to delete optional name element -->
<xsd:element name=name type=xsd:string
minOccurs=0 maxOccurs=0/>
76 Chapter 2 XML Primer
<!-- The rest is the same as in addressType -->
<xsd:element name=company type=xsd:string
minOccurs=0/>
<xsd:element name=street type=xsd:string
maxOccurs=unbounded/>
<xsd:element name=city type=xsd:string/>
<xsd:element name=state type=xsd:string
minOccurs=0/>
<xsd:element name=postalCode type=xsd:string
minOccurs=0/>
<xsd:element name=country type=xsd:string
minOccurs=0/>
</xsd:sequence>
<xsd:attribute name=id type=xsd:ID/>
<xsd:attribute name=href type=xsd:IDREF/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
The Importance of xsi:type
The nature of restriction is such that an application that is prepared to deal with the base
type can certainly accept the derived type. In other words, you can use a corporate
address type directly inside the billTo and shipTo elements of POs and invoices with-
out a problem. Sometimes, however, it might be convenient to identify the actual schema
type used in an instance document. XML Schema allows you to do this through the use
of the global xsi:type attribute. This attribute can be applied to any element to signal
its actual schema type, as Listing 2.27 shows.
Listing 2.27 Using xsi:type
<?xml version=1.0 encoding=UTF-8?>
<po:po xmlns:po=http://www.skatestown.com/ns/po
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://www.skatestown.com/ns/po
http://www.skatestown.com/schema/po.xsd
id=43871 submitted=2004-01-05 customerId=73852>
<billTo xsi:type=po:corporateAddressType>
<company>The Skateboard Warehouse</company>
<street>One Warehouse Park</street>
<street>Building 17</street>
<city>Boston</city>
<state>MA</state>
<postalCode>01775</postalCode>
</billTo>
...
</po:po>
77 XML Schemas
Although derivation by restriction doesnt require the use of xsi:type, derivation by
extension often does. The reason is that an application prepared for the base schema type
is unlikely to be able to process the derived type (it adds information) without a hint.
But why would such a scenario ever occur? Why would an instance document contain
data from a type derived by extension in a place where the schema expects a base type?
XML Schema allows derivation by extension to be used in cases where it really
shouldnt be used, as in the case of the invoice and PO datatypes. In these cases, you
must use xsi:type in the instance document to ensure successful validation. Consider a
scenario where the invoice type was derived by extension from the PO type:
<xsd:complexType name=invoiceType>
<xsd:complexContent>
<xsd:extension base=po:poType>
<xsd:element name=tax type=priceType/>
<xsd:element name=shippingAndHandling type=priceType/>
<xsd:element name=totalCost type=priceType/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Remember, extension doesnt change the content model of the base type; it can only
add to the content model. Therefore, this definition will make the item element inside
invoices of type po:itemType, not invoice:itemType. The use of xsi:type (see Listing
2.28) is the only way to add unit prices to items without violating the validity con-
straints of the document imposed by the schema. An imperfect analogy from program-
ming languages is that xsi:type provides the true type to downcast to when youre
holding a reference to a base type.
Listing 2.28 Using xsi:type to Correctly Identify Invoice Item Elements
<order>
<item sku=318-BP quantity=5 unitPrice=49.95
xsi:type=invoice:itemType>
<description>Skateboard backpack; five pockets</description>
</item>
<item sku=947-TI quantity=12 unitPrice=129.00
xsi:type=invoice:itemType>
<description>Street-style titanium skateboard.</description>
</item>
<item sku=008-PR quantity=1000 unitPrice=0.00
xsi:type=invoice:itemType>
<description>Promotional: SkatesTown stickers</description>
</item>
</order>
78 Chapter 2 XML Primer
This example shows a use of xsi:type that comes as a result of poor schema design. If,
instead of extending PO, the invoice type is defined on its own, the need for xsi:type
disappears. However, sometimes even good schema design doesnt prevent the need to
identify actual types in instance documents.
Imagine that, due to constant typos in shipping and billing address postal codes,
SkatesTown decides to become more restrictive in its document validation. The company
defines three types of addresses that are part of POs and schema. The types have the fol-
lowing constraints:
n
AddressSame as always
n
USAddressCountry isnt allowed, and the ZIP Code pattern \d{5}(-\d{4})?
is enforced
n
UKAddressCountry is fixed to UK, and the postal code pattern [0-9A-Z]{3}
[0-9A-Z]{3} is enforced
To get the best possible validation, SkatesTowns applications need to know the exact
type of address that is being used in a document. Without using xsi:type, the PO and
invoice schema will each have to define nine (three squared) possible combinations of
billTo and shipTo elements: billTo/shipTo, billTo/shipToUS, billTo/shipToUK,
billToUS/shipTo, and so on. Its better to stick with billTo and shipTo and use
xsi:type to get exact schema type information.
Theres More
This completes the whirlwind tour of XML Schema. Much material useful for data-
oriented applications falls outside the scope of what is included in this chapter; well
introduce some information throughout the book as needed.
Processing XML
So far, this chapter has introduced the key XML standards and explained how theyre
expressed in XML documents. The final section of the chapter focuses on processing
XML, with a quick tour of the specifications and APIs you need to know to be able to
generate, parse, and process XML documents in your Java applications.
Basic Operations
The basic XML processing architecture shown in Figure 2.4 consists of three key layers.
At far left are the XML documents an application needs to work with. At far right is the
application. In the middle is the infrastructure layer for working with XML documents,
which is the topic of this section.
79 Processing XML
Figure 2.4 Basic XML processing architecture
For an application to be able to work with an XML document, it must first be able to
parse the document. Parsing is a process that involves breaking the text of an XML docu-
ment into small identifiable pieces (nodes). Parsers break documents into pieces such as
start tags, end tags, attribute value pairs, chunks of text content, processing instructions,
comments, and so on. These pieces are fed into the application using a well-defined API
implementing a particular parsing model. Four parsing models are commonly used:
n
Pull parsing gThe application always has to ask the parser to give it the next
piece of information about the document. Its as if the application has to pull the
information out of the parser (hence the name of the model). The XML commu-
nity has not yet defined standard APIs for pull parsing. However, because pull pars-
ing is becoming popular, this could happen soon.
n
Push parsing gThe parser sends notifications to the application about the types
of XML document pieces it encounters during the parsing process. The notifica-
tions are sent in reading order, as they appear in the text of the document.
Notifications are typically implemented as event callbacks in the application code,
and thus push parsing is also commonly known as event-based parsing. The XML
community created a de facto standard for push parsing called Simple API for XML
(SAX) g. SAX is currently released in version 2.0.
n
One-step parsing gThe parser reads the whole XML document and generates a
data structure (a parse tree g) describing its contents (elements, attributes, PIs,
comments, and so on). The data structure is typically deeply nested; its hierarchy
mimics the nesting of elements in the parsed XML document. The W3C has
defined a Document Object Model (DOM) gfor XML. The XML DOM specifies
the types of objects that are included in the parse tree, their properties, and their
operations. The DOM is so popular that one-step parsing is typically referred to as
DOM parsing. The DOM is a language- and platform-independent API. It offers
many obvious benefits but also some hidden costs. The biggest problem with the
DOM APIs is that they often dont map well to the native data structures of pro-
gramming languages. To address this issue for Java, the Java community has started
working on a Java DOM (JDOM) specification whose goal is to simplify the
manipulation of document trees in Java by using object APIs tuned to the com-
mon patterns of Java programming.
Character Stream
Serializer
Parser
Standardized
XML APIs
Application XML Document(s)
80 Chapter 2 XML Primer
n
Hybrid parsing gThis approach combines characteristics of the other three
parsing models to create efficient parsers for special scenarios. For example, one
common pattern combines pull parsing with one-step parsing. In this model, the
application thinks its working with a one-step parser that has processed the whole
XML document from start to end. In reality, the parsing process has just begun. As
the application keeps accessing more objects on the DOM (or JDOM) tree, the
parsing continues incrementally so that just enough of the document is parsed at
any given point to give the application the objects it wants to see.
The reasons there are so many different models for parsing XML have to do with trade-
offs between memory efficiency, computational efficiency, and ease of programming.
Table 2.5 identifies some of the characteristics of the parsing models. In the table, control
of parsing refers to who manages the step-by-step parsing process. Pull parsing requires
that the application do that; in all other models, the parser takes care of this process.
Control of context refers to who manages context information such as the level of nesting
of elements and their location relative to one another. Both push and pull parsing dele-
gate this control to the application; all other models build a tree of nodes that makes
maintaining context much easier. This approach makes programming with DOM or
JDOM generally easier than working with SAX. The price is memory and computation-
al efficiency, because instantiating all these objects takes up time and memory. Hybrid
parsers attempt to offer the best of both worlds by presenting a tree view of the docu-
ment but doing incremental parsing behind the scenes.
Table 2.5 XML Parsing Models and Their Trade-offs
Model Control of Control of Memory Computational Ease of
parsing context efficiency efficiency programming
Pull Application Application High Highest Low
Push (SAX) Parser Application High High Low
One-step (DOM) Parser Parser Lowest Lowest High
One-step (JDOM) Parser Parser Low Low Highest
Hybrid (DOM) Parser Parser Medium Medium High
Hybrid (JDOM) Parser Parser Medium Medium Highest
In the Java world, a standardized APIJava API for XML Processing (JAXP) gexists
for instantiating XML parsers and parsing documents using either SAX or DOM.
Without JAXP, Java applications werent completely portable across XML parsers because
different parsers, despite following SAX and DOM, had different APIs for creation, con-
figuration, and parsing of documents. JAXP is currently released in version 1.2. It doesnt
support JDOM yet because the JDOM specification isnt complete at this point.
Although XML parsing addresses the problem of feeding data from XML documents
into applications, XML output addresses the reverse problemapplications generating
XML documents. At the most basic level, an application can directly output XML
81 Processing XML
markup. In Figure 2.4, this is indicated by the application working with a character
stream. This isnt difficult to do, but handling the basic syntax rules (attributes quoting,
special character escaping, and so on) can become cumbersome. In many cases, it might
be easier for the application to construct a data structure (DOM or JDOM tree) describ-
ing the XML document that should be generated. Then, the application can use a seriali-
zation gprocess to traverse the document tree and emit XML markup corresponding
to its elements. This capability isnt directly defined in the DOM and JDOM APIs, but
most XML toolkits make it very easy to do just that.
Data-Oriented XML Processing
When youre thinking about applications working with XML, its important to note that
all the mechanisms for parsing and generating XML described so far are syntax-oriented.
They force the application to work with concepts such as elements, attributes, and pieces
of text. This is similar to applications that use text files for storage being forced to work
with characters, lines, carriage returns (CR), and line feeds (LF).
Typically, applications want a higher-level view of their data. They arent concerned
with the physical structure of the data, be it characters and lines in the case of text files or
elements and attributes in the case of XML documents. They want to abstract this away
and expose the meaning or semantics of the data. In other words, applications dont want
to work with syntax-oriented APIs; they want to work with data-oriented APIs. Therefore,
typical data-oriented XML applications introduce a data abstraction layer between the
syntax-oriented parsing and output APIs and application logic (see Figure 2.5).
Syntax-oriented
APIs
Application
Data Abstraction
Layer Application Logic
Figure 2.5 Data abstraction layer in XML applications
When working with XML in a data-oriented manner, youll typically use one of two
approaches: operation-centric and data-centric.
The Operation-Centric Approach
The operation-centric approach works in terms of custom-built APIs for certain opera-
tions on the XML document. The implementation of these APIs hides the details of
XML processing. Only non-XML types are passed through the APIs.
Consider, for example, the task of SkatesTown trying to independently check the total
amount on the invoices its sending to its customers. From a Java application perspective,
a good way to implement an operation like this would be through the interface shown
in Listing 2.29.
82 Chapter 2 XML Primer
Listing 2.29 InvoiceChecker Interface
package com.skatestown.invoice;
import java.io.InputStream;
/**
* SkatesTown invoice checker
*/
public interface InvoiceChecker {
/**
* Check invoice totals.
*
* @param invoiceXML Invoice XML document
* @exception Exception Any exception returned during checking
*/
void checkInvoice(InputStream invoiceXML) throws Exception;
}
The implementation of checkInvoice() must do the following:
1. Obtain an XML parser.
2. Parse the XML from the input stream.
3. Initialize a running total to zero.
4. Find all order items, and calculate item subtotals by multiplying quantities and unit
prices. Add the item subtotals to the running total.
5. Add tax to the running total.
6. Add shipping and handling to the running total.
7. Compare the running total to the total on the invoice.
8. If there is a difference, throw an exception.
9. Otherwise, return.
The most important aspect of this approach is that any XML processing details are hid-
den from the application. It can happily deal with the InvoiceChecker interface, never
knowing or caring about how checkInvoice() works.
The Data-Centric Approach
An alternative is the data-centric approach. Data-centric XML computing reduces the
problem of working with XML documents to that of mapping the XML to and from
application data and then working with the data independently of its XML origins.
Application data covers the common datatypes developers work with every day: boolean
values, numbers, strings, date-time values, arrays, associative arrays (dictionaries, maps,
83 Processing XML
hash tables), database recordsets, and complex object types. Note that in this context,
DOM tree objects arent considered true application data because theyre tied to XML
syntax. The process of converting application data to XML is called marshalling g. The
XML is a serialized representation of the application data. The process of generating
application data from XML is called unmarshalling g.
For example, the XML invoice markup could be mapped to the set of Java classes
introduced in the schema section (see Listing 2.30).
Listing 2.30 Java Classes Representing Invoice Data
class Address { ... }
class Item { ... }
class InvoiceItem extends Item { ... }
class Invoice
{
int id;
Date submitted;
int customerId;
Address billTo;
Address shipTo;
InvoiceItem order[];
double tax;
double shippingAndHandling;
double totalCost;
}
Schema Compilers
The traditional approach for generating XML from application data has been to custom-
code the way data values become elements, attributes, and element content. The tradi-
tional approach of working with XML to produce application data has been to parse it
using a SAX or a DOM parser. Data structures are built from the SAX events or the
DOM tree using custom code. However, there are better ways to map data to and from
XML using technologies specifically built for marshalling and unmarshalling data to and
from XML. Enter schema compilation tools.
Schema compilers are tools that analyze XML schema and code-generate marshalling
and unmarshalling modules specific to the schema. These modules work with data struc-
tures tuned to the schema. Figure 2.6 shows the basic process for working with schema
compilers. The schema compiler needs to be invoked only once; then the application can
use the code-generated modules like any other API.
84 Chapter 2 XML Primer
Figure 2.6 Using a schema compiler.
Binding Customization
In some cases, the object types generated by the schema compiler offer a good enough
API for working with the types and elements described in the target schema. The appli-
cation can use these classes directly. Other cases may require customization of the default
binding of XML types to object types. That is where the binding customizations come
in: They provide additional information to the schema compiler about how the binding
between XML and application structures should happen.
There are two main reasons for applying customization:
n
To deal with predefined application data structuresThis reason applies in environments
where the application already has defined object types to represent the concepts
described in the schema. An example is a PO processing system that was designed
to receive inputs from a human-facing UI and an EDI data feed. Now, the system
must be extended to handle XML POs. The task is to map the XML of POs to
the existing application data structures. There is zero chance that the default map-
ping defined by the schema compiler will do this in a satisfactory manner.
n
To simplify the APIThis reason for applying customization is driven by program-
ming convenience. Sometimes the conventions of schema design dont map well to
the conventions of object-oriented design. For example, localized text is often rep-
resented in schema as a subelement with an xml:lang attribute identifying the
language. Most applications represent this construct as a string object property
whose value is determined by the active internationalization locale. Further, there
is often more than one way to express a schema type in a programming language.
For example, should an xsd:decimal be mapped to a BigDecimal, double, or
float in Java? The right answer depends on the application.
Common examples of customizations include the following:
n
Changing the names of namespaces, object types, and object properties; for exam-
ple, mapping the customerID attribute to the _cid object property.
n
Defining the type mapping, especially for simple types, as the previous
xsd:decimal mapping example suggested.
Marshaller Module
Schema Compiler
Target Schema
Binding
Customizations
Unmarshaller Module
T
a
r
g
e
t
X
M
L
F
o
r
m
a
t
A
p
p
l
i
c
a
t
i
o
n
D
a
t
a
codegen
codegen
85 Processing XML
n
Choosing which subelements to map to object properties and whether to map
them as simple types or as properties that are objects themselves, as the localized
text example suggested.
n
Specifying how repeated types should be mapped to collection types in program-
ming languages. For example, should the order items in POs be represented by a
simple array type, a dynamic array type, or some other data structure such as a list?
The Java community has defined a standard set of tools and APIs for mapping schema
types to Java data structures called Java Architecture for XML Binding (JAXB) g. JAXB
took a long time to develop because the problems it was trying to address were very
complex. Initially, the work targeted DTD-to-Java mapping. This, and the fact that JAXB
was JSR-31 in the Java Community Process (JCP), gives you an idea of how long JAXB
has taken to evolve. Because of its long history, JAXB isnt yet fully aligned with the lat-
est thinking about XML type mapping for Web services. The good news is that JAXB
now supports a significant part of XML Schema and is ready for production use. JAXB
2.0 will synchronize JAXB with JAX-RPC (the Java APIs for remote procedure calls
using Web services), which will make JAXB even better suited for use with Web services.
Chapters 3 and 5 (Implementing Web Services with Apache Axis) introduce
advanced data-mapping concepts specific to Web services as well as more sophisticated
mechanisms for working with XML. The rest of this section will offer a taste of XML
processing by implementing the checkInvoice() API described earlier using both a
SAX and a DOM parser as well as JAXB.
SAX-Based checkInvoice()
The basic architecture of the JAXP SAX parsing APIs is shown in Figure 2.7. It uses the
common abstract factory design pattern. First, you must create an instance of
SAXParserFactory that is used to create an instance of SAXParser. Internally, the parser
wraps a SAXReader object that is defined by the SAX API. JAXP developers typically
dont have to work directly with SAXReader. When the parsers parse() method is
invoked, the reader starts firing events to the application by invoking certain registered
callbacks.
SAXParser
Factory
SAXParser
SAX
Reader
XML
Content
Handler
Error
Handler
DTD
Handler
Entity
Handler
Figure 2.7 SAX parsing architecture
86 Chapter 2 XML Primer
Working with JAXP and SAX involves four important Java packages:
n
org.xml.saxDefines the SAX interfaces
n
org.xml.sax.extDefines advanced SAX extensions for DTD processing and
detailed syntax information
n
org.xml.sax.helpersDefines helper classes such as DefaultHandler
n
javax.xml.parsersDefines the SAXParserFactory and SAXParser classes
Here is a summary of the key SAX-related objects:
n
SAXParserFactoryA SAXParserFactory object creates an instance of the parser
determined by the system property javax.xml.parsers.SAXParserFactory.
n
SAXParserThe SAXParser interface defines several kinds of parse() methods.
In general, you pass an XML data source and a DefaultHandler object to the
parser, which processes the XML and invokes the appropriate methods in the han-
dler object.
n
DefaultHandlerNot shown in Figure 2.7, DefaultHandler implements all
SAX callback interfaces with null methods. Custom handlers subclass
DefaultHandler and override the methods theyre interested in receiving.
The following list contains the callback interfaces and some of their important methods:
n
ContentHandlerContains methods for all basic XML parsing events:
void startDocument()
Receive notification of the beginning of a document.
void endDocument()
Receive notification of the end of a document.
void startElement(String namespaceURI, String localName,
String qName, Attributes atts)
Receive notification of the beginning of an element.
void characters(char[] ch, int start, int length)
Receive notification of character data.
n
ErrorHandlerContains methods for receiving error notification. The default
implementation in DefaultHandler throws errors for fatal errors but does nothing
for nonfatal errors, including validation errors:
void error(SAXParseException exception)
Receive notification of a recoverable error (for example, a validation error).
void fatalError(SAXParseException exception)
Receive notification of a nonrecoverable error (for example, a well-formedness
error).
87 Processing XML
n
DTDHandlerContains methods for dealing with XML entities.
n
EntityResolverContains methods for resolving the location of external enti-
ties.
SAX defines an event-based parsing model. A SAX parser invokes the callbacks from
these interfaces as its working through the document. Consider the following sample
document:
<?xml version=1.0 encoding=UTF-8?>
<sampleDoc>
<greeting>Hello, world!</greeting>
</sampleDoc>
An event-based parser will make the series of callbacks to the application as follows:
start document
start element: sampleDoc
start element: greeting
characters: Hello, world!
end element: greeting
end element: sampleDoc
end document
Because of the simplicity of the parsing model, the parser doesnt need to keep much
state information in memory. This is why SAX-based parsers are fast and highly efficient.
The flip side to this benefit is that the application has to manage any context associated
with the parsing process. For example, for the application to know that the string Hello,
world! is associated with the greeting element, it needs to maintain a flag that is raised
in the start element event for greeting and lowered in the end element event. More
complex applications typically maintain a stack of elements that are in the process of
being parsed. Here are the SAX events with an added context stack:
start document ()
start element: sampleDoc (sampleDoc)
start element: greeting (sampleDoc, greeting)
characters: Hello, world! (sampleDoc, greeting)
end element: greeting (sampleDoc, greeting)
end element: sampleDoc (sampleDoc)
end document ()
With this information in mind, building a class to check invoice totals becomes relative-
ly simple (see Listing 2.31).
88 Chapter 2 XML Primer
Listing 2.31 SAX-Based Invoice Checker (InvoiceCheckerSAX.java)
package com.skatestown.invoice;
import java.io.InputStream;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;
/**
* Check SkatesTown invoice totals using a SAX parser.
*/
public class InvoiceCheckerSAX
extends DefaultHandler
implements InvoiceChecker
{
// Class-level data
// invoice running total
double runningTotal = 0.0;
// invoice total
double total = 0.0;
// Utility data for extracting money amounts from content
boolean isMoneyContent = false;
double amount = 0.0;
/**
* Check invoice totals.
* @param invoiceXML Invoice XML document
* @exception Exception Any exception returned during checking
*/
public void checkInvoice(InputStream invoiceXML) throws Exception {
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// Parse the input; we are the handler of SAX events
saxParser.parse(invoiceXML, this);
}
// SAX DocumentHandler methods
public void startDocument() throws SAXException {
runningTotal = 0.0;
total = 0.0;
isMoneyContent = false;
}
89 Processing XML
public void endDocument() throws SAXException {
// Use delta equality check to prevent cumulative
// binary arithmetic errors. In this case, the delta
// is one half of one cent
if (Math.abs(runningTotal - total) >= 0.005) {
throw new SAXException(
Invoice error: total is + Double.toString(total) +
while our calculation shows a total of +
Double.toString(Math.round(runningTotal * 100) / 100.0));
}
}
public void startElement(String namespaceURI,
String localName,
String qualifiedName,
Attributes attrs) throws SAXException {
if (localName.equals(item)) {
// Find item subtotal; add it to running total
runningTotal +=
Integer.valueOf(attrs.getValue(namespaceURI,
quantity)).intValue() *
Double.valueOf(attrs.getValue(namespaceURI,
unitPrice)).doubleValue();
} else if (localName.equals(tax) ||
localName.equals(shippingAndHandling) ||
localName.equals(totalCost)) {
// Prepare to extract money amount
isMoneyContent = true;
}
}
public void endElement(String namespaceURI,
String localName,
String qualifiedName) throws SAXException {
if (isMoneyContent) {
if (localName.equals(totalCost)) {
total = amount;
} else {
// It must be tax or shippingAndHandling
runningTotal += amount;
}
isMoneyContent = false;
}
}
public void characters(char buf[], int offset, int len)
Listing 2.31 Continued
90 Chapter 2 XML Primer
throws SAXException {
if (isMoneyContent) {
String value = new String(buf, offset, len);
amount = Double.valueOf(value).doubleValue();
}
}
}
InvoiceCheckerSAX must implement the InvoiceChecker interface in order to provide
the checkInvoice() functionality. It also subclasses DefaultHandler to obtain default
implementations for all SAX callbacks. This way, the implementation can focus on over-
riding only the relevant callbacks.
The class members runningTotal and total maintain state information about the
invoice during the parsing process. The class members isMoneyContent and amount are
necessary in order to maintain parsing context. Because events about character data are
independent of events about elements, we need the isMoneyContent flag to indicate
whether we should attempt to parse character data as a dollar amount for the tax,
shippingAndHandling, and totalCost elements. After we parse the text into a dollar
figure, we save it into the amount member variable and wait until the endElement()
callback to determine what to do with it.
The checkInvoice() method implementation shows how easy it is to use JAXP for
XML parsing. Parsing an XML document with SAX only takes three lines of code.
At the beginning of the document, we have to initialize all member variables. At the
end of the document, we check whether there is a difference between the running total
and the total cost listed on the invoice. If there is a problem, we throw an exception
with a descriptive message. Note that we cant use an equality check because no exact
mapping exists between decimal numbers and their binary representation. During the
many additions to runningTotal, a tiny error will be introduced in the calculation. So,
instead of checking for equality, we need to check whether the difference between the
listed and the calculated totals is significant. Significant in this case would be any amount
greater than half a cent, because a half-cent difference can affect the rounding of a final
value to a cent.
The parser pushes events about new elements to the startElement() method. If the
element we get a notification about is an item element, we can immediately extract the
values of the quantity and unitPrice attributes from its attributes collection.
Multiplying them together creates an item subtotal, which we add to the running total.
Alternatively, if the element is one of tax, shippingAndHandling, or totalCost, we
prepare to extract a money amount from its text content. All other elements are ignored.
When we receive end element notifications, we only need to process the ones where
we expect to extract a money amount from their content. Based on the name of the ele-
ment, we decide whether to save the amount as the total cost of the invoice or whether
to add it to the running total.
Listing 2.31 Continued
91 Processing XML
When we process character data and were expecting a dollar value, we extract the
element content, convert it to a double value, and save it in the amount class member for
use by the endElement() callback.
Note that we could have skipped implementing endElement() if we had also stored
the element name as a string member of the class or used an enumerated value. Then, we
would have decided how to use the dollar amount inside characters().
Thats all there is to it. Of course, this is a simple example. A real application would
have done at least two things differently:
n
It would have used namespace information and prefixed element names instead of
local names.
n
It would have defined its own exception type to communicate invoice validation
information. It would have also overridden the default callbacks for error() and
fatalError() and used these to collect better exception information.
Unfortunately, these extensions fall outside the scope of this chapter. The rest of the
book has several examples of building robust XML-processing software.
DOM-Based checkInvoice()
The basic architecture of the JAXP DOM parsing APIs is shown in Figure 2.8. This
architecture uses the same factory design pattern as the SAX API. An application uses the
javax.xml.parsers.DocumentBuilderFactory class to get a DocumentBuilder object
instance, and uses that to produce a document that conforms to the DOM specification.
The value of the system property javax.xml.parsers.DocumentBuilderFactory deter-
mines which factory implementation produces the builder. This is how JAXP enables
applications to work with different DOM parsers.
The important packages for working with JAXP and DOM are as follows:
n
org.w3c.domDefines the DOM programming interfaces for XML (and, option-
ally, HTML) documents, as specified by the W3C
n
javax.xml.parsersDefines DocumentBuilder and DocumentBuilderFactory
classes
The DOM defines APIs that allow applications to navigate XML documents and to
manipulate their content and structure. The DOM defines interfaces, not a particular
implementation. These interfaces are specified using the Interface Description Language
(IDL) so that any language can define bindings for them. Separate Java bindings are pro-
vided to make working with the DOM in Java easy.
The DOM has several levels and various facets within a level. In the fall of 1998,
DOM Level 1 was released. It provided the basic functionality to navigate and manipu-
late XML and HTML documents. DOM Level 2 builds upon Level 1 with more and
better-segmented functionality:
n
The DOM Level 2 Core APIs build on Level 1, fix some problem spots, and
define additional ways to navigate and manipulate the content and structure of
documents. These APIs also provide full support for namespaces.
92 Chapter 2 XML Primer
n
The DOM Level 2 Views API specifies interfaces that let programmers view alter-
nate presentations of the XML or HTML document.
n
The DOM Level 2 Style API specifies interfaces that let programmers dynamically
access and manipulate style sheets.
n
The DOM Level 2 Events API specifies interfaces that give programmers a generic
event system.
n
The DOM Level 2 Traversal-Range API specifies interfaces that let programmers
traverse a representation of the XML document.
n
The DOM Level 2 HTML API specifies interfaces that let programmers work
with HTML documents.
Document (DOM)
object
object object
object object
DocumentBuilder
Factory
Document
Builder
XML Data
Figure 2.8 DOM parsing architecture
All interfaces (apart from the Core ones) are optional. This is the main reason most
applications rely entirely on the DOM Core.You can expect parsers to support more of
the DOM soon. In fact, the W3C is currently working on DOM Level 3.
The DOM originated as an API for XML processing at a time when the majority of
XML applications were document-centric. As a result, the interfaces in the DOM
describe low-level syntax constructs in XML documents. This makes working with the
DOM for data-oriented applications somewhat cumbersome and is one of the reasons
the Java community is working on the JDOM APIs.
To better understand the XML DOM, you need to be familiar with the core inter-
faces and their most significant methods. Figure 2.9 shows a Universal Modeling
Language (UML) diagram describing some of them.
The root interface is Node. It contains methods for working with the node name
(getNodeName()), type (getNodeType()), and attributes (getNodeAttributes()). Node
types cover various XML syntax elements: document, element, attribute, character data,
text node, comment, processing instruction, and so on. All of these are shown in subclass
Node, but not all are shown in Figure 2.9. To traverse the document hierarchy, nodes can
access their parent (getParentNode()) as well as their children (getChildNodes()). Node
also has several convenience methods for retrieving the first and last child as well as the
previous and following sibling.
93 Processing XML
Figure 2.9 Key DOM interfaces and operations
The most important operations in Document involve creating nodes (at least one for
every node type); assembling these nodes into the tree (not shown); and locating ele-
ments by name, regardless of their location in the DOM (getElementsByTagName()).
This last API is convenient because it can save you from having to traverse the tree to
get to a particular node.
The rest of the interfaces in the figure are simple. Elements, attributes, and character
data each offer a few methods for getting and setting their data members. NodeList and
NamedNodeMap are convenience interfaces for dealing with collections of nodes and
attributes, respectively.
What Figure 2.9 doesnt show is that DOM Level 2 is fully namespace-aware and that
all DOM APIs have versions that take in namespace URIs. Typically, their name is the
same as the name of the original API with NS appended, such as Elements
getAttributeNS(String nsURI, String localName).
With this information in mind, building a class to check invoice totals becomes rela-
tively simple. The DOM implementation of InvoiceChecker is shown in Listing 2.32.
Listing 2.32 DOM-Based Invoice Checker (InvoiceCheckerDOM.java)
package com.skatestown.invoice;
import java.io.InputStream;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.CharacterData;
import javax.xml.parsers.DocumentBuilder;
interface
NodeList
+item(index:int):Node
+getLength():int
interface
Text
+splitText(offset:int)Text
interface
CharacterData
+getData():String
+setData(data:String):void
+getLength():int
interface
Node
+getNodeName():String
+getNodeValue():String
+setNodeValue(nodeValue:String):void
+getNodeType():short
+getParentNode():Node
+getChildNodes():NodeList
+getAttributes():NamedNodeMap
+getName():String
+getValue():String
+setValue(value:String):void
interface
Document
+createElement(tagName:String):Element
+createDocumentFragment():DocumentFragment
+createTextNode(data:String):Text
+createAttribute(name:String):Attr
+getElementsByTagName(tagname:String):NodeList
interface
NamedNodeMap
+getNamedItem(name:String):Node
+setNamedItem(arg:Node):Node
+item(index:int):Node
+getLength():int
interface
Attr
+getTagName():String
+getAttribute(name:String):String
+setAttribute(name:String,value:String):void
+hasAttribute(name:String):boolean
interface
Element
94 Chapter 2 XML Primer
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Check SkatesTown invoice totals using a DOM parser.
*/
public class InvoiceCheckerDOM implements InvoiceChecker {
/**
* Check invoice totals.
*
* @param invoiceXML Invoice XML document
* @exception Exception Any exception returned during checking
*/
public void checkInvoice(InputStream invoiceXML)
throws Exception
{
// Invoice running total
double runningTotal = 0.0;
// Obtain parser instance and parse the document
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(invoiceXML);
// Calculate order subtotal
NodeList itemList = doc.getElementsByTagName(item);
for (int i = 0; i < itemList.getLength(); i++) {
// Extract quantity and price
Element item = (Element)itemList.item(i);
Integer qty = Integer.valueOf(
item.getAttribute(quantity));
Double price = Double.valueOf(
item.getAttribute(unitPrice));
// Add subtotal to running total
runningTotal += qty.intValue() * price.doubleValue();
}
// Add tax
Node nodeTax = doc.getElementsByTagName(tax).item(0);
runningTotal += doubleValue(nodeTax);
// Add shipping and handling
Node nodeShippingAndHandling =
doc.getElementsByTagName(shippingAndHandling).item(0);
Listing 2.32 Continued
95 Processing XML
runningTotal += doubleValue(nodeShippingAndHandling);
// Get invoice total
Node nodeTotalCost =
doc.getElementsByTagName(totalCost).item(0);
double total = doubleValue(nodeTotalCost);
// Use delta equality check to prevent cumulative
// binary arithmetic errors. In this case, the delta
// is one half of one cent
if (Math.abs(runningTotal - total) >= 0.005)
{
throw new Exception(
Invoice error: total is + Double.toString(total) +
while our calculation shows a total of +
Double.toString(Math.round(runningTotal * 100) / 100.0));
}
}
/**
* Extract a double from the text content of a DOM node.
*
* @param node A DOM node with character content.
* @return The double representation of the nodes content.
* @exception Exception Could be the result of either a node
* that doesnt have text content being passed in
* or a node whose text content is not a number.
*/
private double doubleValue(Node node) throws Exception {
// Get the character data from the node and parse it
String value = ((CharacterData)node.getFirstChild()).getData();
return Double.valueOf(value).doubleValue();
}
}
InvoiceCheckerDOM must implement the InvoiceChecker interface in order to provide
the checkInvoice() functionality. Apart from this, its a standalone class. Also, note that
the class has no member data, because there is no need to maintain parsing context. The
context is implicit in the hierarchy of the DOM tree that will be the result of the
parsing process.
The factory pattern used here to parse the invoice is the same as the one from the
SAX implementation; it just uses DocumentBuilderFactory and DocumentBuilder.
Although the SAX parse method returns no data (it starts firing events instead), the
DOM parse() method returns a Document object that holds the complete parse tree of
the invoice document.
Listing 2.32 Continued
96 Chapter 2 XML Primer
Within the parse tree, the call to getElementsByTagName(item) retrieves a node
list of all order items. The loop iterates over the list, extracting the quantity and
unitPrice attributes for every item, obtaining an item subtotal, and adding this to the
running total.
The same getElementsByTagName() API combined with the utility function
doubleValue() extracts the amounts of tax, the shipping and handling, and the invoice
total cost.
Just as in the SAX example, the code has to use a difference check instead of a direct
equality check to guard against inexact decimal-to-binary conversions.
The class also defines a convenient utility function that takes in a DOM node that
should have only character content and returns the numeric representation of that con-
tent as a double. Any nontrivial DOM processing will typically require these types of
utility functions. It goes to prove that the DOM is very syntax-oriented and not con-
cerned about data.
Thats all it takes to process the invoice using DOM. Of course, this is a simple exam-
ple; just as in the SAX example, a real application would have done at least three things
differently:
n
It would have used namespace information and prefixed element names instead of
using local names.
n
It would have defined its own exception type to communicate invoice validation
information. It would have implemented try-catch logic inside the
checkInvoice() method in order to report more meaningful errors.
n
It would have either explicitly turned on validation of the incoming XML docu-
ment or traversed the DOM tree step by step from the document root to all the
elements of interest. Using getElementsByTagName() presumes that the structure
of the document (relative positions of elements) has already been validated. If this
is the case, its okay to ask for all item elements regardless of where they are in the
document. The example implementation took this approach for code readability
purposes.
These changes arent complex, but they would have increased the size and complexity of
the example beyond its goals as a basic introduction to DOM processing.
JAXB-Based checkInvoice()
The basic architecture of the JAXB implementation is shown in Figure 2.10. The various
components are similar to those described in the general architecture of XML processing
using schema compilers in Figure 2.7.
To use JAXB, you first have to invoke the schema compiler that comes with the dis-
tribution. The compiler that comes with the Sun distribution used in this example is
called xjc (XML-to-Java Compiler). The compiler is easy to use: It can produce an ini-
tial mapping of a schema to Java classes by looking at an XML schema, without requir-
ing any binding customization. To try this on the invoice schema, we have to execute the
command xjc invoice.xsd. The output of this command is shown in Listing 2.33.
97 Processing XML
Figure 2.10 JAXB architecture
Listing 2.33 xjc Output After Processing the Invoice Schema
C:\dev\projects\jaxb>xjc invoice.xsd
parsing a schema...
compiling a schema...
com\skatestown\ns\invoice\impl\InvoiceImpl.java
com\skatestown\ns\invoice\impl\InvoiceTypeImpl.java
com\skatestown\ns\invoice\impl\ItemTypeImpl.java
com\skatestown\ns\invoice\impl\JAXBVersion.java
com\skatestown\ns\invoice\impl\runtime\XMLSerializer.java
com\skatestown\ns\invoice\impl\runtime\SAXUnmarshallerHandler.java
com\skatestown\ns\invoice\impl\runtime\MSVValidator.java
com\skatestown\ns\invoice\impl\runtime\PrefixCallback.java
com\skatestown\ns\invoice\impl\runtime\UnmarshallingEventHandlerAdaptor.java
com\skatestown\ns\invoice\impl\runtime\UnmarshallerImpl.java
com\skatestown\ns\invoice\impl\runtime\Discarder.java
com\skatestown\ns\invoice\impl\runtime\SAXUnmarshallerHandlerImpl.java
com\skatestown\ns\invoice\impl\runtime\ValidatorImpl.java
com\skatestown\ns\invoice\impl\runtime\GrammarInfo.java
com\skatestown\ns\invoice\impl\runtime\DefaultJAXBContextImpl.java
com\skatestown\ns\invoice\impl\runtime\ErrorHandlerAdaptor.java
com\skatestown\ns\invoice\impl\runtime\AbstractUnmarshallingEventHandlerImpl.java
com\skatestown\ns\invoice\impl\runtime\GrammarInfoFacade.java
com\skatestown\ns\invoice\impl\runtime\MarshallerImpl.java
com\skatestown\ns\invoice\impl\runtime\UnmarshallingContext.java
com\skatestown\ns\invoice\impl\runtime\UnmarshallableObject.java
com\skatestown\ns\invoice\impl\runtime\ContentHandlerAdaptor.java
com\skatestown\ns\invoice\impl\runtime\NamespaceContext2.java
com\skatestown\ns\invoice\impl\runtime\ValidatingUnmarshaller.java
com\skatestown\ns\invoice\impl\runtime\SAXMarshaller.java
Application Code
Application
Interfaces and
Object Factory
package
javax.xml.blind
Implementation
Classes
Implementation
of javax.xml.bind
unmarshal
marshal
XML
Input
Document
XML
Output
Document
Binding
Compiler
Binding
Customizations
(optional)
XML
Schema
98 Chapter 2 XML Primer
com\skatestown\ns\invoice\impl\runtime\NamespaceContextImpl.java
com\skatestown\ns\invoice\impl\runtime\Util.java
com\skatestown\ns\invoice\impl\runtime\XMLSerializable.java
com\skatestown\ns\invoice\impl\runtime\ValidatableObject.java
com\skatestown\ns\invoice\impl\runtime\AbstractGrammarInfoImpl.java
com\skatestown\ns\invoice\impl\runtime\ValidationContext.java
com\skatestown\ns\invoice\impl\runtime\UnmarshallingEventHandler.java
com\skatestown\ns\po\impl\AddressTypeImpl.java
com\skatestown\ns\po\impl\ItemTypeImpl.java
com\skatestown\ns\po\impl\JAXBVersion.java
com\skatestown\ns\po\impl\PoImpl.java
com\skatestown\ns\po\impl\PoTypeImpl.java
com\skatestown\ns\invoice\Invoice.java
com\skatestown\ns\invoice\InvoiceType.java
com\skatestown\ns\invoice\ItemType.java
com\skatestown\ns\invoice\ObjectFactory.java
com\skatestown\ns\invoice\bgm.ser
com\skatestown\ns\invoice\jaxb.properties
com\skatestown\ns\po\AddressType.java
com\skatestown\ns\po\ItemType.java
com\skatestown\ns\po\ObjectFactory.java
com\skatestown\ns\po\Po.java
com\skatestown\ns\po\PoType.java
com\skatestown\ns\po\bgm.ser
com\skatestown\ns\po\jaxb.properties
After parsing, analyzing, and processing the invoice schema (and the PO schema on
which it depends), the compiler outputs 50 (!) files that fall into three categories: inter-
face files, implementation files, and supporting files.
Note that the namespace URIs for the invoice and PO schemas are mapped to the
Java package names com.skatestown.ns.po and com.skatestown.ns.invoice. Inside
these two packages are the interfaces generated for the schema types. A Java interface is
generated for every type and element in the schema. For example, invoiceType in the
schema is mapped to InvoiceType.java. The compiler also generates three supporting
files:
n
ObjectFactory.javaContains factory methods for each generated Java inter-
face. It allows you to programmatically construct new instances of Java objects rep-
resenting XML content.
n
jaxb.propertiesProvides information about the specific JAXB implementation
provider.
n
bgm.serContains a serialized representation of the schema information that can
be used for efficient on-the-fly validation during XML-to-Java and Java-to-XML
mapping.
Listing 2.33 Continued
99 Processing XML
All the files in the ...\impl packages are specific to the Sun JAXB implementation and
can be ignored.
Generated Interfaces
Lets look more closely at the interfaces generated by xjc, in particular the invoice and
invoice item types well need to work with to check the invoice totals (Listing 2.34). For
convenience purposes, weve reformatted the code and removed comments.
Listing 2.34 Example Generated Interfaces
package com.skatestown.ns.invoice;
public interface InvoiceType {
java.math.BigDecimal getTotalCost();
void setTotalCost(java.math.BigDecimal value);
com.skatestown.ns.invoice.InvoiceType.OrderType getOrder();
void setOrder(com.skatestown.ns.invoice.InvoiceType.OrderType value);
java.math.BigInteger getCustomerId();
void setCustomerId(java.math.BigInteger value);
java.math.BigDecimal getShippingAndHandling();
void setShippingAndHandling(java.math.BigDecimal value);
com.skatestown.ns.po.AddressType getShipTo();
void setShipTo(com.skatestown.ns.po.AddressType value);
com.skatestown.ns.po.AddressType getBillTo();
void setBillTo(com.skatestown.ns.po.AddressType value);
java.util.Calendar getSubmitted();
void setSubmitted(java.util.Calendar value);
java.math.BigInteger getId();
void setId(java.math.BigInteger value);
java.math.BigDecimal getTax();
void setTax(java.math.BigDecimal value);
public interface OrderType {
java.util.List getItem();
}
}
public interface ItemType
extends com.skatestown.ns.po.ItemType
100 Chapter 2 XML Primer
{
java.math.BigDecimal getUnitPrice();
void setUnitPrice(java.math.BigDecimal value);
}
package com.skatestown.ns.po;
public interface ItemType {
java.lang.String getSku();
void setSku(java.lang.String value);
java.lang.String getDescription();
void setDescription(java.lang.String value);
java.math.BigInteger getQuantity();
void setQuantity(java.math.BigInteger value);
}
As you can see, the structure of the schema types is directly expressed in the Java classes
with the appropriate type information. There is no sign of elements and attributes at the
XML syntax levelthey become properties of the Java classes. Working with repeated
types maps well to Java programming patterns. For example, order items are accessed via
java.util.List. We dont need to parse numbers; this is done by the JAXB implemen-
tation. Its easy to see why the JAXB implementation of checkInvoice() is likely to be
the simplest and most resilient to potential future changes in the XML schema, com-
pared to the SAX and DOM implementations.
JAXB Binding Customization
Only one thing about the default mapping generated by xjc doesnt seem quite right.
All numeric values in the schema are mapped to BigInteger and BigDecimal types. The
default rules of type mapping are meant to preserve as much information as possible.
Therefore, schema types with unbounded precision such as xsd:decimal and
xsd:positiveInteger are mapped to BigDecimal and BigInteger. The rules of JAXB
name and type mapping are complex, and unfortunately we dont have space to discuss
them here. However, we can address the number-mapping issue.
It would be nice to map to int and double in this example, because theyre more
convenient and efficient to use. To do so, we need to provide a binding customization to
the schema compiler (Listing 2.35).
Listing 2.35 Binding Customization for xjc (binding.xjb)
<jxb:bindings version=1.0
xmlns:jxb=http://java.sun.com/xml/ns/jaxb
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
Listing 2.34 Continued
101 Processing XML
<jxb:bindings schemaLocation=po.xsd node=/xsd:schema>
<jxb:bindings node=//xsd:complexType[@name=poType]/
xsd:attribute[@name=id]>
<jxb:javaType name=int
parseMethod=javax.xml.bind.DatatypeConverter.parseInt
printMethod=javax.xml.bind.DatatypeConverter.printInt/>
</jxb:bindings>
<jxb:bindings node=//xsd:attribute[@name=customerId]>
<jxb:javaType name=int
parseMethod=javax.xml.bind.DatatypeConverter.parseInt
printMethod=javax.xml.bind.DatatypeConverter.printInt/>
</jxb:bindings>
<jxb:bindings node=//xsd:attribute[@name=quantity]>
<jxb:javaType name=int
parseMethod=javax.xml.bind.DatatypeConverter.parseInt
printMethod=javax.xml.bind.DatatypeConverter.printInt/>
</jxb:bindings>
</jxb:bindings> <!-- schemaLocation=po.xsd node=/xsd:schema -->
<jxb:bindings schemaLocation=invoice.xsd node=/xsd:schema>
<jxb:bindings node=//xsd:complexType[@name=invoiceType]/
xsd:attribute[@name=id]>
<jxb:javaType name=int
parseMethod=javax.xml.bind.DatatypeConverter.parseInt
printMethod=javax.xml.bind.DatatypeConverter.printInt/>
</jxb:bindings>
<jxb:bindings node=//xsd:attribute[@name=customerId]>
<jxb:javaType name=int
parseMethod=javax.xml.bind.DatatypeConverter.parseInt
printMethod=javax.xml.bind.DatatypeConverter.printInt/>
</jxb:bindings>
<jxb:bindings node=//xsd:simpleType[@name=priceType]>
<jxb:javaType name=double
parseMethod=javax.xml.bind.DatatypeConverter.parseDouble
printMethod=javax.xml.bind.DatatypeConverter.printDouble/>
</jxb:bindings>
</jxb:bindings> <!-- schemaLocation=invoice.xsd node=/xsd:schema -->
</jxb:bindings>
In JAXBs case, you can insert binding customizations directly in the XML schema using
schema extensibility mechanisms or provide them in a separate XML document. The lat-
ter is a better practice in that the XML schema is a programming-language-independent
representation of data that shouldnt be encumbered by this information.
The basic mechanism of binding customizations involves two parts: identifying the
part of the schema where the mapping should be modified and specifying the binding
Listing 2.35 Continued
102 Chapter 2 XML Primer
modification. All the customizations in this example are simple type mappings to int
and double performed via the element jxb:javaType. The Java type to map to is speci-
fied via the name attribute. Two other attributes, parseMethod and printMethod, provide
the unmarshalling and marshalling operations. JAXB provides convenience methods in
the javax.xml.bind.DatatypeConverter class, which we use here.
Identifying the part of the schema to modify is more complicated. We need a mecha-
nism to point to a part of the schema document. This mechanism is a language called
XPath g. XPath is one the XML standards developed by W3C. Think about the direc-
tory structure of your computer: The file path mechanism (for example, C:\dev\
projects\jaxb) gives you a way to navigate that structure. Now think about the struc-
ture described by the DOM representation of XML documents: XPath lets you navigate
that structure quickly and efficiently. We dont have the space to get into XPath, but here
are a few examples taken from the binding customization file:
n
/xsd:schemaThe top-level element in the XML document called xsd:schema.
The / syntax defines a level in the document element hierarchy, beginning from
the current node. Initially, the current node is the root of the DOM.
n
//xsd:attribute[@name=quantity]An element called xsd:attribute
(occurring anywhere in the document) whose name attribute value is quantity.
The // syntax covers all descendants from the current node.
n
//xsd:complexType[@name=poType]/xsd:attribute[@name=id]An
xsd:complexType element called poType (anywhere in the document) that has an
xsd:attribute child with the name id. Note that we cant use the simpler XPath
expression //xsd:attribute[@name=id] because AddressType in the schema
also has an id attribute. The XPath expression would result in more than one
DOM node, and the schema compiler would be unsure where to apply the bind-
ing customization.
In the binding customization, these XPath expressions are used within the context of
nested jxb:bindings elements. The top-level element lets us change global binding
rules. It has two children: one for the PO schema and one for the invoice schema.
Within those, we modify each attribute with a numeric type. In the invoice schema, we
modify the binding of priceType, and that modification automatically applies to all uses
of that type in unitPrice, tax, and other attributes.
For xjc to take advantage of the binding customization, we need to modify the com-
mand line slightly to xjc b bindings.xjb invoice.xsd. The same number of files are
generated. This time, however, the BigInteger and BigDecimal types in Listing 2.34 are
replaced with int and double types.
JAXB Processing Model
Now that the schema compiler is generating the correct Java classes, its time to look at
the JAXB processing model. Working with JAXB involves one main Java package:
javax.xml.bind. This package provides abstract classes and interfaces for working with
103 Processing XML
the JAXB frameworks three operations: marshalling, unmarshalling, and validation.You
access these operations via the Marshaller, Unmarshaller, and Validator classes in the
package.
The JAXBContext class is the entry point into the JAXB framework. It provides sup-
port for multiple JAXB implementations, and it also manages the connection between
XML elements and the Java classes that represent them. The package also has a rich set
of exception classes for marshalling, unmarshalling, and validation events that make
working with JAXB much easier and more natural than working with SAX or DOM.
With this information in mind, building a class to check invoice totals becomes rela-
tively simple. The JAXB implementation of InvoiceChecker is shown in Listing 2.36.
Listing 2.36 JAXB-Based Invoice Checker (InvoiceCheckerJAXB.java)
package com.skatestown.invoice;
import com.skatestown.ns.invoice.InvoiceType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
/**
* InvoiceChecker implementation using JAXB
*/
public class InvoiceCheckerJAXB implements InvoiceChecker
{
/**
* Check invoice totals.
*
* @param invoiceXML Invoice XML document
* @exception Exception Any exception returned during checking
*/
public void checkInvoice(InputStream invoiceXML)
throws Exception
{
// Create JAXB context + point it to schema types
JAXBContext jc = JAXBContext.newInstance(
com.skatestown.ns.po:com.skatestown.ns.invoice);
// Create an unmarshaller
Unmarshaller u = jc.createUnmarshaller();
// Unmarshall the invoice document
InvoiceType inv = (InvoiceType)u.unmarshal(invoiceXML);
104 Chapter 2 XML Primer
double runningTotal = 0.0;
// Iterate over order items and update the running total
List items = inv.getOrder().getItem();
for( Iterator iter = items.iterator(); iter.hasNext(); ) {
com.skatestown.ns.invoice.ItemType item =
(com.skatestown.ns.invoice.ItemType)iter.next();
runningTotal += item.getQuantity() * item.getUnitPrice();
}
// Add tax and shipping and handling
runningTotal += inv.getShippingAndHandling();
runningTotal += inv.getTax();
// Get invoice total
double total = inv.getTotalCost();
// Use delta equality check to prevent cumulative
// binary arithmetic errors. In this case, the delta
// is one half of one cent
if (Math.abs(runningTotal - total) >= 0.005) {
throw new Exception(
Invoice error: total is + Double.toString(total) +
while our calculation shows a total of +
Double.toString(Math.round(runningTotal * 100) / 100.0));
}
}
}
InvoiceCheckerJAXB must implement the InvoiceChecker interface in order to pro-
vide the checkInvoice() functionality. Apart from this, its a standalone class. As with
DOM, note that the class has no member data, because theres no need to maintain pars-
ing context. The context is implicit in the hierarchy of the Java object tree that will be
generated during the unmarshalling process.
The JAXB context is initialized with the names of the Java packages we want to
work with in the JAXBContext.newInstance factory call. This prepares the JAXB
framework to deal with PO and invoice XML documents. The next factory pattern call
creates an unmarshaller object. Parsing and unmarshalling the invoice document takes
one call to unmarshal(). We have to cast to the top-level invoice type because the inter-
face of the Unmarshaller class is generic to JAXB.
The code to recalculate the invoice total is simpler and more Java-friendly than in the
SAX and DOM examples. There is no indication of any XML behind the scenes. This is
what JAXB does bestit allows you to separate the Java data structures you want to
work with from the XML representation of these structures. Just as in the SAX and
Listing 2.36 Continued
105 Processing XML
DOM examples, the code has to use a difference check instead of a direct equality check
to guard against inexact decimal-to-binary conversions.
That completes the JAXB implementation of the invoice checker. Of course, JAXB is
much more powerful (and complex), and we dont have space to dig into it here; but
youve gotten an idea of why its the preferred method of working with XML from Java.
Testing the Code
The code to test the three different invoice checker implementations is written using
JavaServer Pages (JSP) (Listing 2.37). JSP allows Java code to be mixed with HTML for
building Web applications. JSP builds on the Java Servlet standard for building Web com-
ponents. Java application servers compile JSPs down to servlets.
Listing 2.37 JSP Page for Checking Invoices
<%@ page import=java.io.*,bws.BookUtil,com.skatestown.invoice.* %>
<HTML>
<HEAD><TITLE>Invoice Checker</TITLE></HEAD>
<h1>Invoice Checker</h1>
<p>This example implements a web form driver for SkatesTowns invoice
checker. You can modify the invoice on the form if you wish (the
default one is from Chapter 2), select a DOM or SAX parser and perform
a check on the invoice total.</p>
<FORM action=index.jsp method=POST>
<%
String xml = request.getParameter(xml);
if (xml == null) {
xml = BookUtil.readResource(application,
/resources/sampleInvoice.xml);
}
%>
<TEXTAREA NAME=xml ROWS=20 COLS=90><%= xml%></TEXTAREA>
<P></P>
Select parser type:
<INPUT type=RADIO name=parserType value=SAX CHECKED> SAX
<INPUT type=RADIO name=parserType value=DOM> DOM
<INPUT type=RADIO name=parserType value=JAXB> JAXB
<P></P>
<INPUT type=SUBMIT value= Check Invoice >
</FORM>
<%
// Check for form submission
if (request.getParameter(xml) != null) {
out.println(<HR>);
106 Chapter 2 XML Primer
// Instantiate appropriate parser type
InvoiceChecker ic;
if (request.getParameter(parserType).equals(SAX)) {
out.print(Using SAX parser...<br>);
ic = new InvoiceCheckerSAX();
} else if (request.getParameter(parserType).equals(DOM)) {
out.print(Using DOM implementation...<br>);
ic = new InvoiceCheckerDOM();
} else {
out.print(Using JAXB implementation...<br>);
ic = new InvoiceCheckerJAXB();
}
// Check the invoice
try {
ic.checkInvoice(new StringBufferInputStream(xml));
out.print(Invoice checks OK.);
} catch(Exception e) {
out.print(e.getMessage());
}
}
%>
</BODY>
</HTML>
JSP uses the <%@ ... %> syntax for compile-time directives. The page import=...
directive accomplishes the equivalent of a Java import statement.
The HTML code sets up a Web form that posts back to the same page. The form
contains a text area with the name xml that contains the XML of the invoice to be
validated.
In JSP, you can use the construct <% ... %> to surround arbitrary Java code embed-
ded in the JSP page. The request object is an implicit object on the page associated with
the Web request. Implicit objects in JSP are set up by the JSP compiler. They can be used
without requiring any type of declaration or setup. One of the most useful methods of
the request object is getParameter(), which retrieves the value of a parameter passed
from the Web such as a form field, or returns null if this parameter didnt come with the
request. The code uses getParameter(xml) to check whether the form is being dis-
played (return is null) versus submitted (return is non-null). If the form is displayed for
the first time, the page loads the invoice XML from a sample file in
/resources/sampleInvoice.xml.
The rest of the Java code runs only if the form has been submitted. It uses the implic-
it out object to send output to the resulting Web page. It uses the value of the
parserType field in the Web page to determine whether to instantiate a SAX, DOM, or
Listing 2.37 Continued
107 Summary
JAXB implementation. It then checks the invoice by passing the value of the xml text
area on the page to the checkInvoice() method. If the call is successful, the invoice
checks out okay, and an appropriate message is displayed. If checkInvoice() throws an
exception, an invoice total discrepancy (or an XML processing error) has been detected,
which is output to the browser.
Figure 2.11 shows the Web test client for the invoice checker, ready for submission.
Figure 2.11 Invoice checker Web page
Summary
This chapter has focused on core features of XML and related technologies. The goal
was to prepare you for the Web servicerelated material in the rest of the book, which
relies heavily on the concepts presented here. We covered the following topics:
n
The origins of XML and the fundamental difference between document- and
data-centric XML applications. Web services are an extreme example of data-
centric XML use. The material in this chapter purposely ignored some aspects of
XML that are more document-oriented.
n
The syntax and rules governing the physical structure of XML documents: docu-
ment prologs, elements, attributes, character content, CDATA sections, and so on.
We omitted document-oriented features of XML such as entities and notations
108 Chapter 2 XML Primer
due to their infrequent use in the context of Web services. The SkatesTown PO
document format made its initial appearance.
n
XML Namespaces, the key tool for resolving the problems of name recognition
and name collision in XML applications. Namespaces are fundamental to mixing
information from multiple schemas into a single document, and all core Web serv-
ice technologies rely on them. SkatesTowns PO inside an XML message wrapper
is an example of a common pattern for XML use; well explore it in depth in the
next chapter. The namespace mechanism is simple; however, people often try to
read more into it than is there, as demonstrated by the debate over whether name-
space URIs should point to meaningful resources. One of the more complex
aspects of the specification is that multiple namespace defaulting mechanisms sim-
plify document markup while preserving namespace information.
n
XML Schema, the de facto standard for describing document structure and XML
datatypes for data-oriented applications. Although XML Schema is a recent stan-
dard, the XML community defined specifications based on draft versions for nearly
two years. The flexible content models, the large number of predefined datatypes,
and the powerful extensibility and reuse features make this one of the most impor-
tant developments in the XML space since XML 1.0. All Web service specifications
are described using schemas. Through the definition of SkatesTowns PO and
invoice schemas, this chapter introduced enough of the key capabilities of the
technology to prepare you for what is to come in the rest of the book.
n
The key mechanisms for creating and processing XML with software. Starting with
the basic syntax-oriented XML processing architecture, the chapter progressed to
define a data-oriented XML processing architecture together with the key con-
cepts of XML data mapping and XML parsing. In the context of SkatesTowns
desire to independently validate invoice totals sent to its customers, we used the
Java APIs for XML Processing (JAXP), the Simple APIs for XML (SAX), the XML
Document Object Model (DOM), and the Java Architecture for XML Binding
(JAXB) to build three separate implementations of an invoice checker. A Web-
based front end served as the test bed for the code.
This chapter didnt focus on other less relevant XML technologies such as
XPointer/XLink, Resource Definition Framework (RDF), XPath, Extensible Stylesheet
Language Transformations (XSLT), or XQuery. Theyre important in their own domains
but not commonly used in the context of Web services. Other more technical XML
specifications, such as XML Digital Signatures, will be introduced later in the book as
part of Web service usage scenarios.
You now know enough about XML to go deep into the world of Web services.
Chapter 3 introduces the core Web service messaging technology: SOAP.
109 Resources
Resources
n
DOM Level 1Document Object Model (DOM) Level 1 Specification (W3C,
October 1998), http://www.w3.org/TR/REC-DOM-Level-1
n
DOM Level 2 CoreDocument Object Model (DOM) Level 2 Core
Specification (W3C, November 2000), http://www.w3.org/TR/DOM-Level-2-
Core/
n
JAXBJava Technology and XML DownloadsJava Architecture for XML
Binding (Sun Microsystems, Inc., January 2003),
http://java.sun.com/xml/downloads/jaxb.html
n
JAXP 1.2Java API for XML Processing (JAXP) (Sun Microsystems, Inc.,
August 2003), http://java.sun.com/xml/xml_jaxp.html
n
JDOMhttp://www.jdom.org/docs/apidocs
n
JSP 1.2JavaServer Pages Technology (Sun Microsystems, Inc., April 2001),
http://java.sun.com/products/jsp
n
RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax (IETF, August
1998), http://www.ietf.org/rfc/rfc2396.txt
n
SAXSimple API for XML (SAX) 2.0.1 (January 2002), http://www.
saxproject.org/
n
UnicodeForms of Unicode (Mark Davis, September 1999), http://www-
106.ibm.com/developerworks/library/utfencodingforms/
n
XMLExtensible Markup Language (XML) 1.0 (Second Edition) (W3C,
October 2000), http://www.w3.org/TR/REC-xml
n
XML NamespacesNamespaces in XML (W3C, January 1999),
http://www.w3.org/TR/REC-xml-names/
n
XML SchemaXML Schema Part 0: Primer,
http://www.w3.org/TR/xmlschema-0/; XML Schema Part 1: Structures,
http://www.w3.org/TR/xmlschema-1/; XML Schema Part 2: Datatypes,
http://www.w3.org/TR/xmlschema-2/ (all W3C, May 2001)
3
The SOAP Protocol
THE WEB SERVICES ARCHITECTURE GROUP AT THE W3C has defined a Web service as
follows (italics added):
A Web service is a software system designed to support interoperable machine-to-
machine interaction over a network. It has an interface described in a machine-
processable format (specifically WSDL). Other systems interact with the Web
service in a manner prescribed by its description using SOAP-messages, typically
conveyed using HTTP with an XML serialization in conjunction with other Web-
related standards.
Although our definition (see Chapter 1, Web Services Overview and Service-Oriented
Architectures) may be a bit broader, its clear that SOAP gis at the core of any sur-
vey of Web service technology. So just what is SOAP, and why is it often considered the
harbinger of a new world of interoperable systems?
The trouble with SOAP is that its so simple and so flexible that it can be used in
many different ways to fit the needs of different Web service scenarios. This is both a
blessing and a curse. Its a blessing because chances are, SOAP can fit your needs. Its a
curse because you may not know how to make it do what you require. When youre
through with this chapter, youll know not only how to use SOAP straight out of the
box but also how to extend SOAP to support your diverse and changing needs.Youll
have also followed the development of a meaningful e-commerce Web service for our
favorite company, SkatesTown. Last but not least, youll be ready to handle the rest of the
book and climb higher toward the top of the Web services interoperability stack.
The chapter will cover the following topics:
n
The evolution of XML protocols and the history and motivation behind SOAPs
creation
n
The SOAP messaging framework: versioning, the extensibility framework, header-
based vertical extensibility, intermediary-based horizontal extensibility, error han-
dling, and bindings to multiple transport protocols
112 Chapter 3 The SOAP Protocol
n
The various mechanisms for packaging information in SOAP messages, including
SOAPs own data encoding rules and heuristics for putting just about any kind of
data in SOAP messages
n
The use of SOAP within multiple distributed system architectures such as RPC-
and messaging-based systems in all their flavors
n
A quick introduction to building and consuming Web services using the Java-based
Apache Axis Web services engine
So, why SOAP? As this chapter will show, SOAP is simple, flexible, and highly extensible.
Since its XML based, SOAP is programming-language, platform, and hardware neutral.
What better choice for the XML protocol thats the foundation of Web services? To
prove this point, lets start the chapter by looking at some of the earlier work that
inspired SOAP.
SOAP
Microsoft started thinking about XML-based distributed computing in 1997. The goal
was to enable applications to communicate via Remote Procedure Calls (RPCs) using a
simple network of standard data types on top of XML/HTTP. DevelopMentor (a long-
standing Microsoft ally) and Userland (a company that saw the Web as a great publishing
platform) joined the discussions. The name SOAP was coined in early 1998.
Things moved forward, but as the group tried to involve wider circles within
Microsoft, politics stepped in and the process stalled. The DCOM camp at the company
disliked the idea of SOAP and believed that Microsoft should use its dominant position
in the market to push the DCOM wire protocol via some form of HTTP tunneling
instead of pursuing XML. Some XML-focused folks at Microsoft believed that the
SOAP idea was good but had come too early. Perhaps they were looking for some of the
advanced facilities that could be provided by XML Schema and Namespaces. Frustrated
by the deadlock, Userland went public with a version of the spec published as XML-
RPC in the summer of 1998.
In 1999, as Microsoft was working on its version of XML Schema (XML Data) and
adding support for namespaces in its XML products, the idea of SOAP gained momen-
tum. It was still an XML-based RPC mechanism, however, which is why it met with
resistance from the BizTalk (http://www.biztalk.org) team; the BizTalk model was
based more on messaging than RPCs. SOAP 0.9 appeared for public review on
September 13, 1999. It was submitted to the IETF as an Internet public draft. With few
changes, in December 1999, SOAP 1.0 came to life.
Right before the XTech conference in March 2000, the W3C announced that it was
looking into starting an activity in the area of XML protocols. At the conference, there
was an exciting breakout session in which a number of industry visionaries argued the
finer points of what XML protocols should do and where they were goingbut this
conversation didnt result in one solid vision of the future.
113 SOAP
On May 8, 2000 SOAP 1.1 was submitted as a note to the W3C with IBM as a co-
author. IBMs support was an unexpected and refreshing change. In addition, the SOAP
1.1 spec was much more modular and extensible, eliminating some concerns that back-
ing SOAP implied backing a Microsoft proprietary technology. This, and the fact that
IBM immediately released a Java SOAP implementation that was subsequently donated
to the Apache XML Project (http://xml.apache.org) for open source development,
convinced even the greatest skeptics that SOAP was something to pay attention to. Sun
voiced support for SOAP and started work on integrating Web services into the J2EE
platform. Not long after, many vendors and open source projects began working on Web
service implementations.
In September 2000, the XML Protocol working group at the W3C was formed to
design the XML protocol that was to become the core of XML-based distributed com-
puting in the years to come. The group started with SOAP 1.1 as a foundation and pro-
duced the first working draft. After many months of changes, improvements, and difficult
decisions about what to include, SOAP 1.2 became a W3C recommendation almost two
years after that first draft, in June 2003.
What Is SOAP, Really?
Despite the hype that surrounds it, SOAP is of great importance because its the indus-
trys best effort to date to standardize on the infrastructure technology for cross-platform
XML distributed computing. Above all, SOAP is relatively simple. Historically, simplicity
is a key feature of most successful architectures that have achieved mass adoption.
At its heart, SOAP is a specification for a simple yet flexible second-generation XML
protocol. Because SOAP is focused on the common aspects of all distributed computing
scenarios, it provides the following (covered in greater detail later):
n
A mechanism for defining the unit of communicationIn SOAP, all information is pack-
aged in a clearly identifiable SOAP message g. This is done via a SOAP envelope
gthat encloses all other information. A message can have a body gin which
potentially arbitrary XML can be used. It can also have any number of headers g
that encapsulate information outside the body of the message.
n
A processing modelThis defines a well-known set of rules for dealing with SOAP
messages in software. SOAPs processing model is simple; but its the key to using
the protocol successfully, especially when extensions are in play.
n
A mechanism for error handlingUsing SOAP faults g, you can identify the source
and cause of an error and it allows for error diagnostic information to be
exchanged between participants of an interaction.
n
An extensibility modelThis uses SOAP headers to implement arbitrary extensions
on top of SOAP. Headers contain pieces of extensibility data which travel along
with a message and may be targeted at particular nodes along the message path.
114 Chapter 3 The SOAP Protocol
n
A flexible mechanism for data representationThis mechanism allows for the exchange
of data already serialized in some format (text, XML, and so on) as well as a con-
vention for representing abstract data structures such as programming language
datatypes in an XML format.
n
A convention for representing Remote Procedure Calls (RPCs) and responses as SOAP
messagesRPCs are a common type of distributed computing interaction, and
they map well to procedural programming language constructs.
n
A protocol binding frameworkThe framework defines an architecture for building
bindings to send and receive SOAP messages over arbitrary underlying transports.
This framework is used to supply a binding that moves SOAP messages across
HTTP connections, because HTTP is a ubiquitous communication protocol on
the Internet.
Before we dive deeper into the SOAP protocol and its specification, lets look at how
our example company, SkatesTown, is planning to use SOAP and Web services.
Doing Business with SkatesTown
When Al Rosen of Silver Bullet Consulting first began his engagement with
SkatesTown, he focused on understanding the e-commerce practices of the company and
its customers. After a series of conversations with SkatesTowns CTO, Dean Caroll, Al
concluded the following:
n
SkatesTowns manufacturing, inventory management, and supply chain automation
systems are in good order. These systems are easily accessible by SkatesTowns Web-
centric applications.
n
SkatesTown has a solid consumer-oriented online presence. Product and inventory
information is fed into an online catalog that is accessible to both direct consumers
and SkatesTowns reseller partners via two different sites.
n
Although SkatesTowns order-processing system is sophisticated, its poorly con-
nected to online applications. This is a pain point for the company because
SkatesTowns partners are demanding better integration with their supply chain
automation systems.
n
SkatesTowns internal purchase order system is solid. It accepts purchase orders in
XML format and uses XML Schemabased validation to guarantee their correct-
ness. Purchase order item SKUs and quantities are checked against the inventory
management system. If all items are available, an invoice is created. SkatesTown
charges a uniform 5% tax on purchases and the higher of 5% of purchases or $20
for shipping and handling.
Digging deeper into the order-processing part of the business, Al discovered that it uses a
low-tech approach that has a high labor cost and isnt suitable for automation. One area
that badly needs automation is the process of purchase order submission. Purchase orders
115 Doing Business with SkatesTown
are sent to SkatesTown by email. All emails arrive in a single managers account in opera-
tions. The manager manually distributes the orders to several subordinates. They have to
open the email, copy only the XML over to the purchase order system, and enter the
order there. The system writes an invoice file in XML format. This file has to be opened,
and the XML must be copied and pasted into a reply email message. Simple misspellings
of email addresses and cut-and-paste errors are common, and they cost SkatesTown and
its partners money and time.
Another area that needs automation is the inventory checking process. SkatesTowns
partners used to submit purchase orders without having a clear idea whether all the
items were in stock. This often caused problems having to do with delayed order pro-
cessing. Further, purchasing personnel from the partner companies would engage in long
email dialogs with operations people at SkatesTown. To improve the situation,
SkatesTown built a simple online application that communicates with the companys
inventory management system. Partners can log in, browse SkatesTowns products, and
check whether certain items are in stock, all via a standard web browser. This was a good
start, but now SkatesTowns partners are demanding the ability to have their purchasing
applications directly inquire about order availability.
Looking at the two areas that most needed to be improved, Al chose to focus first on
the inventory checking process because the business logic was already present. He just
had to enable better automation. To do this, he had to better understand how the appli-
cation worked.
The logic for interacting with the inventory system is simple. Looking through the
JSP pages that made up the online application, Al easily extracted the key business logic
operations. Given a SKU and a desired product quantity, an application needs to get an
instance of the SkatesTown product database and locate a product with a matching SKU.
If such a product is available and if the number of items in stock is greater than or equal
to the desired quantity, the inventory check succeeds. Since most of the example in this
chapter will talk to the inventory system, lets take a slightly deeper look at its imple-
mentation.
Note
A note of caution: this books example applications demonstrate uses of Java technology and Web services
to solve real business problems while at the same time remaining simple enough to fit in the books scope
and size limitations. To keep the code simple, we do as little data validation and error checking as possible
without allowing applications to break. We dont define custom exception types or produce long, readable
error messages. Also, to get away from the complexities of external system access, we use simple XML files
to store data.
SkatesTowns inventory is represented by a simple XML file stored in /resources/
products.xml. The inventory database XML format is as follows:
<?xml version=1.0 encoding=UTF-8?>
<products>
116 Chapter 3 The SOAP Protocol
<product>
<sku>947-TI</sku>
<name>Titanium Glider</name>
<type>skateboard</type>
<desc>Street-style titanium skateboard.</desc>
<price>129.00</price>
<inStock>36</inStock>
</product>
...
</products>
By modifying this file, you can change the behavior of the examples. The Java represen-
tation of products in SkatesTowns systems is the com.skatestown.data.Product class;
its a simple bean that has one property for every element under product.
SkatesTowns inventory system is accessible via the ProductDB (for product database)
class in package com.skatestown.backend. Listing 3.1 shows the key operations it sup-
ports. To construct an instance of the class, you pass an XML DOM Document object
representation of products.xml. After that, you can get a listing of all products or search
for a product by its SKU.
Listing 3.1 SkatesTowns Product Database Class
public class ProductDB
{
private Product[] products;
public ProductDB(Document doc) throws Exception
{
// Load product information
}
public Product getBySKU(String sku)
{
Product[] list = getProducts();
for ( int i = 0 ; i < list.length ; i++ )
if ( sku.equals( list[i].getSKU() ) ) return( list[i] );
return( null );
}
public Product[] getProducts()
{
return products;
}
}
This was all Al Rosen needed to know to move forward with the task of automating the
inventory checking process.
117 Inventory Check Web Service
Inventory Check Web Service
SkatesTowns inventory check Web service is simple. The interaction model is that of an
RPC. There are two input parameters: the product SKU (a string) and the quantity
desired (an integer). The result is a simple Boolean value thats true if more than the
desired quantity of the product is in stock and false otherwise.
Choosing a Web Service Engine
Al decided to host all of SkatesTowns Web services on the Apache Axis Web service
engine for a number of reasons:
n
The open source implementation guaranteed that SkatesTown wont experience
lock-in by a commercial vendor. Further, if any serious problems were discovered,
a programmer could look at the code to see what was going on or fix the issue.
n
Axis is one of the best Java-based Web services engines. Its better architected and
much faster than its Apache SOAP predecessor. The core Axis team includes Web
service gurus from companies such as Macromedia, IBM, Computer Associates,
and Sonic Software.
n
Axis is also one of the most extensible Web service engines. It can be tuned to
support new versions of SOAP as well as the many types of extensions that current
versions of SOAP allow for.
n
Axis can run on top of a simple servlet engine or a full-blown J2EE application
server. SkatesTown could keep its current J2EE application server without having
to switch.
SkatesTowns CTO, Dean, agreed to have all Web services developed on top of Axis. Al
spent some time on http://ws.apache.org/axis learning more about the technology
and its capabilities.
Service Provider View
To expose the inventory check Web service, Al had to do two things: implement the
service backend and deploy it into the Web service engine. Building the backend for the
inventory check Web service was simple because most of the logic was already available
in SkatesTowns JSP pages.You can see the service class in Listing 3.2.
Listing 3.2 Inventory Check Web Service Implementation
package com.skatestown.services;
import com.skatestown.data.Product;
import com.skatestown.backend.ProductDB;
import com.skatestown.STConstants;
118 Chapter 3 The SOAP Protocol
/**
* Inventory check Web service
*/
public class InventoryCheck implements STConstants {
/**
* Checks inventory availability given a product SKU and
* a desired product quantity.
*
* @param sku product SKU
* @param quantity quantity desired
* @return true|false based on product availability
* @exception Exception most likely a problem accessing the DB
*/
public static boolean doCheck(String sku, int quantity)
throws Exception
{
// Get the product database, which has been conveniently pre-placed
// in a well-known place (if you want to see how this works,
// check out the com.skatestown.GlobalHandler class!).
ProductDB db = ProductDB.getCurrentDB();
Product prod = db.getBySKU(sku);
return (prod != null && prod.getNumInStock() >= quantity);
}
}
The backend code for this service relies on the fact that some other piece of code has
already made the appropriate ProductDB available via a static accessor method on the
ProductDB class. Well unearth the provider of ProductDB in Chapter 5, Implementing
Web Services with Apache Axis.
Once we have the ProductDB, the rest of the service code is trivial; we check if the
quantity available for a given product is equal to or greater than the quantity requested,
and return true if so.
Deploying the Service
To deploy this initial service, Al chose to use the instant deployment feature of Axis:
Java Web service (JWS) files. In order to do so, he saved the InventoryCheck.java
file as InventoryCheck.jws underneath the Axis webapp, so its accessible at
http://skatestown.com/axis/InventoryCheck.jws.
The Client View
Once the service was deployed, Al wanted some of SkatesTowns partners to test it. To
test it himself, he built a simple client using Axis (see Listing 3.3).
Listing 3.2 Continued
119 Inventory Check Web Service
Listing 3.3 The InventoryCheck Client Class
package ch3.ex2;
import org.apache.axis.AxisEngine;
import org.apache.axis.client.Call;
import org.apache.axis.soap.SOAPConstants;
/*
* Inventory check Web service client
*/
public class InventoryCheckClient {
/** Service URL */
static String url =
http://localhost:8080/axis/InventoryCheck.jws;
/**
* Invoke the inventory check Web service
*/
public static boolean doCheck(String sku, int quantity)
throws Exception {
// Set up Call object
Call call = new Call(url);
// Use SOAP 1.2 (default is SOAP 1.1)
call.setSOAPVersion(SOAPConstants.SOAP12_CONSTANTS);
// Set up parameters for invocation
Object[] params = new Object[] { sku, new Integer(quantity) };
// Call it!
Boolean result = (Boolean)call.invoke(, doCheck, params);
return result.booleanValue();
}
public static void main(String[] args) throws Exception {
String sku = args[0];
int quantity = Integer.parseInt(args[1]);
System.out.println(Making SOAP call...);
boolean result = doCheck(sku, quantity);
if (result) {
System.out.println(
Confirmed - the desired quantity is available);
} else {
System.out.println(
Sorry, the desired quantity is not available.);
}
}
}
120 Chapter 3 The SOAP Protocol
The client uses Axiss Call class, which is the central client-side API. When Al constructs
the Call class, he passes in the URL of his deployed service so that the Call knows
where to send SOAP messages. The actual invocation is simple: He knows hes calling
the doCheck() method, so he passes the method name and an array of arguments
(obtained from the command line) to the invoke() method on the Call object. The
results come back as a Boolean object, and when the client is run, it looks like this:
% java InventoryCheckClient SKU-56 35
Making SOAP call...
Confirmed the desired quantity is available.
%
A Closer Look at SOAP
The current SOAP specification is version 1.2, which was released as a W3C recommen-
dation in June 2003. At the time of this writing (early 2004), toolkits are just starting to
offer complete support for the new version, and most of them still use SOAP 1.1 as a
baseline. Since this chapter is primarily about the SOAP protocol, well focus on SOAP
1.2the standard the industry will be using into the future. The 1.1 version is also criti-
cally important, so well also explain it and use sidebars to call out differences between
the versions as we go. (You can find an exhaustive list of differences between SOAP 1.1
and SOAP 1.2 in the SOAP 1.2 Primer: http://www.w3.org/TR/2003/REC-soap12-
part0-20030624/.) Most of the other examples in this book use SOAP 1.1, but we
want you to be a 1.2-ready developer.
The Structure of the Spec
The SOAP 1.2 specification is the ultimate reference to the SOAP protocol; the latest
version is at http://www.w3.org/TR/SOAP. The spec is divided into two parts:
n
Part 1, the Messaging FrameworkLays out the central foundation of SOAP, consist-
ing of the processing model, the extensibility model, and the message structure.
n
Part 2, AdjunctsImportant adjuncts to the core spec defined in Part 1. Although
theyre extensions (and therefore by definition optional), they serve two critical
purposes. First, they act as proofs-of-concept for the modular design of SOAP,
demonstrating that it isnt limited, for instance, to only being used over HTTP (a
common misconception). Second, the core of SOAP in Part 1 isnt enough to
build something usable for functional interoperable services. The extensions in part
2, in particular the HTTP binding, provide a baseline for implementers to use,
even though the marketplace may define other components beyond those in the
spec as well.
121 The SOAP Messaging Framework
Infosets
The SOAP 1.2 spec has been written in terms of the XML infoset, which is an abstract model of all the infor-
mation in an XML document or document fragment. When the spec talks about element information items
instead of just elements, it means that what is important is the structure of the information, not necessarily
the fact that its serialized with angle brackets. As youll see later, this becomes important when we talk
about bindings. The key thing to remember is that all the information items are really abstract ways of talk-
ing about things like elements and attributes that you see in everyday XML. So this XML
<elem attr=foo>
<childEl>text</childEl>
Other text
</elem>
would abstractly look like the structure in Figure 3.1 (rectangles are elements, rounded rectangles attributes,
and ovals text).
Figure 3.1 A simple XML infoset
The SOAP Messaging Framework
The first part of the SOAP specification is primarily concerned with defining how
SOAP messages are structured and the rules processors must abide by when producing
and consuming them. Lets look at a sample SOAP message, the inventory check request
described in our earlier example:
Note
All the wire examples in this book have been obtained by using the tcpmon tool, which is included in the
Axis distribution you can obtain with the example package from the Sams Web site. Tcpmon (short for TCP
monitor) allows you to record the traffic to and from a particular TCP port, typically HTTP requests and
responses. Well go into detail about this utility in Chapter 5.
elem elem
foo text
elem childE1 elem attr Other text
122 Chapter 3 The SOAP Protocol
POST /axis/InventoryCheck.jws HTTP/1.0
Content-Type: application/soap+xml; charset=utf-8
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<doCheck soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<arg0 xsi:type=soapenc:string
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>947-TI</arg0>
<arg1 xsi:type=soapenc:int
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>3</arg1>
</doCheck>
</soapenv:Body>
</soapenv:Envelope>
This is clearly an XML document (Chapter 2, XML Primer, covered XML in detail),
which has been sent via an HTTP POST. Weve removed a few of the nonrelevant
HTTP headers from the trace, but we left the content-type header, which indicates that
this POST contains a SOAP message (note that this content-type would be different for
SOAP 1.1see the sidebar for details). Well cover the HTTP-specific parts of SOAP
interactions further a bit later in the chapter.
The root element is soapenv:Envelope, in the http://www.w3.org/2003/05/
soap-envelope namespace, which surrounds a soapenv:Body containing application-
specific content that represents the central purpose of the message. In this case were ask-
ing for an inventory check, so the central purpose is the doCheck element. The
Envelope element has a few useful namespace declarations on it, for the SOAP envelope
namespace and the XML Schema data and instance namespaces.
SOAP 1.1 Difference: Identifying SOAP Content
The SOAP 1.1 envelope namespace is http://schemas.xmlsoap.org/soap/envelope/, where-
as for SOAP 1.2 it has changed to http://www.w3.org/2003/05/soap-envelope. This name-
space is used for defining the envelope elements and for versioning, which we will explain in more detail in
the Versioning in SOAP section.
The content-type used when sending SOAP messages across HTTP connections has changed as wellit was
text/xml for SOAP 1.1 but is now application/soap+xml for SOAP 1.2. This is a great improve-
ment, since text/xml is a generic indicator for any type of XML content. The content type was so generic
that machines had to use the presence of a custom HTTP header called SOAPAction: to tell that XML
traffic was, in fact, SOAP (see the section on the HTTP binding for more). Now the standard MIME infra-
structure handles this for us.
The doCheck element represents the remote procedure call to the inventory check serv-
ice. Well talk more about using SOAP for RPCs in a while; for now, notice that the
123 The SOAP Messaging Framework
name of the method were invoking is the name of the element directly inside the
soapenv:Body, and the arguments to the method (in this case, the SKU number and the
quantity desired) are encoded inside the method element as arg0 and arg1. The real
names for these parameters in Java are SKU and quantity; but due to the ad-hoc way
were calling this method, the client doesnt have any way of knowing that information,
so it uses the generated names arg0 and arg1.
The response to this message, which comes back across in the HTTP response, looks
like this:
Content-Type: application/soap+xml; charset=utf-8
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<doCheckResponse
soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<rpc:result xmlns:rpc=http://www.w3.org/2003/05/soap-rpc>return</rpc:result>
<return xsi:type=xsd:boolean>true</return>
</doCheckResponse>
</soapenv:Body>
</soapenv:Envelope>
The response is also a SOAP envelope, and it contains an encoded representation of the
result of the RPC call (in this case, the Boolean value true).
What good is having this envelope structure, when we could send our XML formats
directly over a transport like HTTP without a wrapper? Good question; as we answer it,
well examine some more details of the protocol.
Vertical Extensibility
Lets say you want your purchase order to be extensible. Perhaps you want to include
security in the document someday, or you might want to enable a notarization service to
associate a token with a particular purchase order, as a third-party guarantee that the PO
was sent and contained particular items. How might you make that happen?
You could drop extensibility elements directly into your document before sending it.
If we took the purchase order from the last chapter and added a notary token, it might
look something like this:
<po id=43871 submitted=2004-01-05 customerId=73852>
<notary:token xmlns:notary=http://notaries-r-us.com>
XQ34Z-4G5
</notary:token>
<billTo>
124 Chapter 3 The SOAP Protocol
<company>The Skateboard Warehouse</company>
...
</billTo>
...
</po>
To do things this way, and make it easy for your partners to use, youd need to do two
things. First, your schema would have to be explicitly extensible at any point in the
structure where you might want to add functionality later (this can be accomplished in a
number of ways, including the xsd:any/ schema construct); otherwise, documents con-
taining extension elements wouldnt validate. Second, you would need to agree on rules
by which those extensibility elements were to be processedwhich ones are optional,
which ones affect which parts of the document, and so on. Both of these requirements
present challenges. Not all schemas have been designed for extensibility, and you may
need to extend a document that follows a preexisting standard format that wasnt built
that way. Also, processing rules might vary from document type to document type, so it
would be challenging to have a uniform model with which to build a common proces-
sor. It would be nice to have a standardized framework for implementing arbitrary
extensibility in a way that everyone could agree on.
It turns out that the SOAP envelope, in addition to containing a body (which must
always be present), may also contain an optional Header elementand the SOAP
Header structure gives us just what we want in an XML extensibility system. Its a con-
venient and well-defined place in which to put our extensibility elements. Headers are
just XML elements that live inside the soapenv:Header/soapenv:Header tags in the
envelope. The soapenv:Header always appears, incidentally, before the soapenv:Body if
its present. (Note that in the SOAP 1.2 spec, the extensibility elements are known as
header blocks. However, the industryand the rest of this bookcolloquially refers to
them simply as headers.)
Lets look at the extensibility example recast as a SOAP message with a header:
<soapenv:Envelope
xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope>
<soapenv:Header>
<notary:token xmlns:notary=http://notaries-r-us.com>
XQ34Z-4G5
</notary:token>
</soapenv:Header>
<soapenv:Body>
<PO>
...normal purchase order here...
</PO>
</soapenv:Body>
</soapenv:Envelope>
Since the SOAP envelope wraps around whatever XML content you want to send in
the body (the PO, in this example), you can use the Header to insert extensions (the
125 The SOAP Messaging Framework
notary:token header) without modifying the central core of the message. This can be
compared to a situation in real life where you want to send a document and some auxil-
iary information, but you dont want to mark up the documentso you put the docu-
ment inside an envelope and then add another piece of paper or two describing your
extra information.
Each individual header represents one piece of extensibility information that travels
with your message. A lot of other protocols have this same basic conceptwere all
familiar with the email model of headers and body. HTTP also contains headers, and
both email and HTTP use the concept of extensible, user-defined headers. However, the
headers in protocols like these are simple strings; since SOAP uses XML, you can encode
much richer data structures for individual headers. Also, you can use XMLs structure to
make processing headers much more powerful and flexible than a basic string-based
model.
Headers can contain any sort of data imaginable, but typically theyre used for two
purposes:
n
Extending the messaging infrastructureInfrastructure headers are typically processed
by middleware. The application doesnt see the headers, just their effects. They
could be things like security credentials, correlation IDs for reliable messaging,
transaction context identifiers, routing controls, or anything else that provides serv-
ices to the application.
n
Defining orthogonal dataThe second category of headers is application defined.
These contain data that is orthogonal to the body of the message but is still des-
tined for the application on the receiving side. An example might be extra data to
accompany nonextensible schemasif you wanted to add more customer data
fields but couldnt change the billTo element, for instance.
Using headers to add functionality to messages is known as vertical extensibility, because
the headers build on top of the message. A little later well discuss horizontal extensibili-
ty as well.
Now that you know the basics, well consider some of the additional framework that
SOAP supplies for headers and how to use it. After that, well explain the SOAP process-
ing model, which is the key to SOAPs scalability and expressive power.
The mustUnderstand Flag
Some extensions might use headers to carry data thats nice to know but not critical to
the main purpose of the SOAP message. For instance, you might be invoking a buy
book operation on a stores Web service.You receive a header in the response confirma-
tion message that contains a list of other books the site thinks you might find interesting.
If you know how to process that extension, then you might offer a UI to access those
books. But if you dont, it doesnt matteryour original request was still processed suc-
cessfully. On the other hand, suppose the request message of that same buy book opera-
tion contained private information (such as a credit card number). The sender might
126 Chapter 3 The SOAP Protocol
want to encrypt the XML in the SOAP body to prevent snooping. To make sure the
other side knows what to do with the postencryption data inside the body, the sender
inserts a header that describes how to decrypt the message. That header is important, and
anyone trying to process the message without correctly processing the header and
decrypting the body is going to run into trouble.
This is why we have the mustUnderstand gattribute, which is always in the SOAP
envelope namespace. Heres what our notary header would look like with that attribute:
<notary:token xmlns:notary=http://notaries-r-us.com
soapenv:mustUnderstand=true>
XQ34Z-4G5
</notary:token>
By marking things mustUnderstand (when we refer to headers marked
mustUnderstand, we mean having the soapenv:mustUnderstand attribute set to true),
youre saying that the receiver must agree to all the terms of your extension specification
or they cant process the message. If the mustUnderstand attribute is set to false or is
missing, the header is defined as optionalin this case, processors not familiar with the
extension can still safely process the message and ignore the optional header.
SOAP 1.1 Difference: mustUnderstand
In SOAP 1.2, the mustUnderstand attribute may have the values 0/false (false) or 1/true (true). In SOAP
1.1, despite the fact that XML allows true and false for Boolean values, the only legal mustUnderstand
values are 0 and 1.
The mustUnderstand attribute is a key part of the SOAP processing model, since it
allows you to build extensions that fundamentally change how a given message is
processed in a way that is guaranteed to be interoperable. Interoperable here means that
you can always know how to gracefully fail in the face of extensions that arent under-
stood.
SOAP Modules
When you implement a semantic using SOAP headers, you typically want other parties
to use your extension, unless its purely for internal use. As such, you typically write a
specification that details all the constraints, rules, preconditions, and data formats of your
extension. These specifications are known as SOAP modules g. Modules are named
with URIs so they can be referenced, versioned, and reasoned about. Well talk more
about module specifications when we get to the SOAP binding framework a bit later.
SOAP Intermediaries
So far, weve addressed SOAP headers as a means for vertical extensibility within SOAP
messages. There is another related notion, however: horizontal extensibility. Whereas verti-
cal extensibility is about the ability to introduce new pieces of information within a
127 SOAP Intermediaries
SOAP message, horizontal extensibility is about targeting different parts of the same
SOAP message to different recipients. Horizontal extensibility is provided by SOAP
intermediaries g.
The Need for Intermediaries
SOAP intermediaries are applications that can process parts of a SOAP message as it
travels from its origination point to its final destination point. The route taken by a
SOAP message, including all intermediaries it passes through, is called the SOAP message
path g(see Figure 3.2).
Figure 3.2 The SOAP message path
Intermediaries can both accept and forward SOAP messages, and they usually do some
form of message processing as well. Three key use-cases define the need for SOAP inter-
mediaries: crossing trust domains, ensuring scalability, and providing value-added services
along the SOAP message path.
Crossing trust domains is a common issue faced while implementing security in dis-
tributed systems. Consider the relation between a corporate or departmental network
and the Internet. For small organizations, its likely that the IT department has put most
computers on the network within a single trusted security domain. Employees can see
their co-workers computers as well as the IT servers, and they can freely exchange
information between them without the need for separate logons. On the other hand, the
corporate network probably treats all computers on the Internet as part of a separate
security domain that isnt trusted. Before an Internet request reaches the network, it
needs to cross from its untrustworthy domain to the trusted domain of the internal net-
work. Corporate firewalls and virtual private network (VPN) gateways guard the net-
work: Their job is to let some requests cross the trust domain boundary and deny access
to others.
Another important need for intermediaries arises because of the scalability require-
ments of distributed systems. A simplistic view of distributed systems could identify two
types of entities: those that request work to be done (clients) and those that do the work
(servers). Clients send messages directly to the servers they want to communicate with.
Servers, in turn, get some work done and respond. In this nave universe, there is little
need for distributed computing infrastructure. However, we cant use this model to build
highly scalable distributed systems.
Requester Provider Intermediary Intermediary
128 Chapter 3 The SOAP Protocol
Take email as an example. When [email protected] sends an email message to
[email protected], its not the case that their email client locates the mail server
london.co.uk and sends the message to it. Instead, the client sends the message to its
email server at company.com. Based on the priority of the message and how busy the
mail server is, the message will leave either by itself or in a batch of other messages.
(Messages are often batched to improve performance.) The message will probably make a
few hops through different nodes on the Internet before it gets to the mail server in
London.
The lesson from this example is that highly scalable distributed systems (such as email)
require flexible buffering of messages and routing based both on message parameters
such as origin, destination, and priority, and on the state of the system considering fac-
tors such as the availability and load of its nodes as well as network traffic information.
Intermediaries hidden from the eyes of the originators and final recipients of messages
can perform this work behind the scenes.
Finally, we need intermediaries so that we can provide value-added services in a dis-
tributed system. The type of services can vary significantly, and some of them involve the
message sender being explicitly aware of the intermediary, unlike our previous examples.
Here are a couple of common scenarios:
n
Securing message exchanges, particularly through untrustworthy domainsYou could
secure SOAP messages by passing them through an intermediary that first encrypts
them and then digitally signs them. On the receiving side an intermediary would
perform the inverse operations: checking the digital signature and, if its valid,
decrypting the message.
n
Notarization/nonrepudiationwhen the sender or receiver (or both) desires a third
party to make a record of an interaction, a notarizing intermediary is a likely solu-
tion. Instead of sending the message directly to the receiver, the sender sends to
the intermediary, who makes a persistent copy of the request and then sends it to
the service provider. The response typically comes back via the intermediary as
well, and then both parties are usually given a token they can use to reference the
transaction record in the future.
n
Providing message tracing facilitiesTracing allows the message recipient to find out
the path the message followed, complete with detailed timings of arrivals and
departures to and from intermediaries. This information is indispensable for tasks
such as measuring quality of service (QoS), auditing systems, and identifying scala-
bility bottlenecks.
Transparent and Explicit Intermediaries
Message senders may or may not be aware of intermediaries in the message path. A trans-
parent intermediary is one the client knows nothing aboutthe client believes its sending
129 SOAP Intermediaries
messages to the actual service endpoint, and the fact that an intermediary is doing work
in the middle is incidental. An explicit intermediary, on the other hand, involves specific
knowledge on the part of the clientthe client knows the message will travel through
an intermediary before continuing to its ultimate destination.
The security intermediaries discussed earlier would likely be transparent; the organi-
zation providing the service would publish the outward-facing address of the intermedi-
ary as the service endpoint. The notarization service described earlier would be an
example of an explicit intermediarythe client would know that a notarization step was
going on.
Intermediaries in SOAP
SOAP is specifically designed with intermediaries in mind. It has simple yet flexible
facilities that address the three key aspects of an intermediary-enabled architecture:
n
How do you pass information to intermediaries?
n
How do you identify who should process what?
n
What happens to information that is processed by intermediaries?
All header elements can optionally have the soapenv:role attribute. The value of this
attribute is a URI that identifies who should handle the header entry. Essentially, that
URI is the name of the intermediary. This URI might mean a particular node (for
instance the Solaris machine on Johns desk), or it might refer to a class of nodes (as in,
any cache manager along the message path). (This latter case prompted the name
change from actor to role in SOAP 1.2.) Also, a given node can play multiple roles: the
Solaris machine on Johns desk might also be a cache manager, for instance, so it would
recognize either role URI.
The first step any node takes when processing a SOAP message is to collect all the
headers that are targeted at the nodethis means headers that have a role gattribute
matching any of the roles node is playing. It then looks through these nodes for headers
marked mustUnderstand and confirms that it recognizes each such header and is able to
process it in accordance with the rules associated with that SOAP header. If it finds a
mustUnderstand header that it doesnt recognize, it must immediately stop processing.
There are several special values for the role attribute:
n
http://www.w3.org/2003/05/soap-envelope/role/nextIndicates that the
header entrys recipient is the next SOAP node that processes the message. This is
useful for hop-by-hop processing required, for example, by message tracing.
n
http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiverRefers
to the final recipient of the SOAP message. Note that omitting the role attribute
or using an empty value () also implies that the final recipient of the SOAP
message should process the header entry. The final recipient of the SOAP message
is the same node that processes the body.
130 Chapter 3 The SOAP Protocol
n
http://www.w3.org/2003/05/soap-envelope/role/noneA special role that no
SOAP node should ever assume. That means that headers addressed to this role
should never be processed; and since no one will ever be in this role, the value of
the mustUnderstand attribute wont matter for such headers (remember that the
first thing a SOAP node does is pick out the headers it can see by virtue of play-
ing the right role, before looking at mustUnderstand). Also note that the relay
attribute (discussed later) never matters on a header addressed to the none role, for
the same reason. Even though your SOAP node cant act as the none role, it can
still look at the data inside headers marked as none. So, headers marked for the
none role can still be used to carry data. (Well give an example in a bit.)
SOAP 1.1 Difference: actor versus role
In SOAP 1.1, the attribute used to target headers is called actor, not role. Also, SOAP 1.1 only specifies
a special next actor URI (http://schemas.xmlsoap.org/soap/actor/next), not an actor for
none or ultimateRecipient.
Forwarding and Active Intermediaries
Some intermediaries, like the notarization example discussed earlier, only do processing
related to particular headers in the SOAP envelope before forwarding the message to the
next node in the message path. In other words, the work of the intermediary is defined
by the contents of the incoming messages. These are known as forwarding intermediaries.
Other intermediaries do processing and potentially modify the message in ways not
defined by the message contents. For instance, an intermediary at a company boundary
to the outside world might add a digital signature header to every outbound message to
ensure that receivers can check the integrity of all messages. No explicit markers in the
messages are used to trigger this behavior; the node simply does it. This type of interme-
diary is known as an active intermediary.
Either type of intermediary may do arbitrary work on the message (including the
body) based on its internal rules.
Rules for Intermediaries and Headers
By default, all headers targeted at a particular intermediary are removed from the mes-
sage when its forwarded on to the next node. This is because the specification tells us
that the contract implied by a given header is between the sender of that header and
the first node satisfying the role at which its targeted. Headers that arent targeted at a
particular intermediary should, in general, be forwarded through untouched (see
Figure 3.3).
An intermediary removes headers targeted at any role its playing, regardless of
whether theyre understood. In Figure 3.4, one header is processed and then removed;
another isnt understood, but because its targeted at our intermediary and not marked
mustUnderstand, its still removed.
131 SOAP Intermediaries
Figure 3.3 Intermediary header removal
Inbound SOAP message
<token envrole=hotary>
<notaryData/>
</token>
<cache envrole=cacheMgr>
<cacheData/>
</cache>
<cache envrole=cacheMgr>
<cacheData/>
</cache>
Header
<doSomethingCool>
<bodyData>
</doSomethingCool>
Body
Outbound SOAP message
Header
<doSomethingCool>
<bodyData/>
</doSomethingCool>
Body
token is processed and removed
cacheis forwarded untouched
Known roles:
notary
intermediary2
Known headers:
token
Intermediary
Figure 3.4 Removing optional headers targeted at an intermediary
There are two exceptions to the removal rules. First, the specification for a particular
extension may explicitly indicate that an identical copy of a given header from the
incoming message is supposed to be placed in the outgoing message. Such headers are
known as reinserted, and this has the effect of forwarding them through after processing.
An example might be a logging extension targeted at a logManager. Any log manager
receiving it along the message path would make a persistent copy of the message for log-
ging purposes and then reinsert the header so that other log managers later in the chain
could do the same.
The second exception is when you want to indicate to intermediaries that extensions
targeted at them, but not understood, should still be passed through. SOAP 1.2 intro-
duces the relay attribute for this purpose. If the relay attribute is present on a header
which is targeted at a given intermediary, and it has the value true, the intermediary
should forward the header regardless of whether it understands it. Figure 3.5 shows an
unknown header arriving at our notary intermediary. Since all nodes must recognize the
next role, the unknown header is targeted at the intermediary. Despite the fact that the
intermediary doesnt understand the header, its forwarded because the relay attribute is
true.
Inbound SOAP message
<token envrole=hotary>
<notaryData/>
</token>
<unknown envrole=notary>
<mysteriousData/>
</unknown>
Header
<doSomethingCool>
<bodyData>
</doSomethingCool>
Body
Outbound SOAP message
Header
<doSomethingCool>
<bodyData>
</doSomethingCool>
Body
token is processed and removed
unknownis simply removed
Known roles:
notary
intermediary2
Known headers:
token
Intermediary
132 Chapter 3 The SOAP Protocol
Figure 3.5 Forwarding headers with the relay attribute
The SOAP Body
The SOAP Body gelement immediately surrounds the information that is core to the
SOAP message. All immediate children of the Body element are body entries (typically
referred to as bodies). Bodies can contain arbitrary XML. Sometimes, based on the intent
of the SOAP message, certain conventions govern the format of the SOAP body (for
instance, we discuss the conventions for representing RPCs and communicating error
information later).
When a node that identifies itself as the ultimate recipient (the service provider in the
case of requests, or the client in the case of responses) receives a message, its required to
process the contents of the body and perform whatever actions are appropriate. The
body carries the core of the SOAP message.
The SOAP Processing Model
Now were ready to finish describing the SOAP 1.2 processing model. Here are the steps
a processor must perform when it receives a SOAP message, as described in the spec:
1. Determine the set of roles in which the node is to act. The contents of the SOAP
envelope, including any SOAP header blocks and the SOAP body, may be inspect-
ed in making such determination.
2. Identify all header blocks targeted at the node that are mandatory.
3. If one or more of the SOAP header blocks identified in step 2 arent understood
by the node, then generate a single SOAP fault with the value of Code set to
env:mustUnderstand. If such a fault is generated, any further processing must not
be done. Faults related to processing the contents of the SOAP body must not be
generated in this step.
Inbound SOAP message
<token envrole=hotary>
<notaryData/>
</token>
<unknown envrole=../next
envrelay=true>
<mysteriousData/>
</unknown>
Header
<doSomethingCool>
<bodyData>
</doSomethingCool>
Body
Outbound SOAP message
<unknown envrole=../next
envrelay=true>
<mysteriousData/>
</unknown>
Header
<doSomethingCool>
<bodyData>
</doSomethingCool>
Body
token is processed and removed
unknownis forwarded
due to the relay attribute
Known roles:
notary
intermediary2
Known headers:
token
Intermediary
133 Versioning in SOAP
4. Process all mandatory SOAP header blocks targeted at the node and, in the case of
an ultimate SOAP receiver, the SOAP body. A SOAP node may also choose to
process nonmandatory SOAP header blocks targeted at it.
5. In the case of a SOAP intermediary, and where the SOAP message exchange pat-
tern and results of processing (for example, no fault generated) require that the
SOAP message be sent further along the SOAP message path, relay the message.
The processing model has been designed to let you use mustUnderstand headers to do
anything you want. We could imagine a mustUnderstand header, for instance, that tells
the processor at the next hop to process all headers and ignore the role attribute.
Versioning in SOAP
One interesting note about SOAP is that the Envelope element doesnt expose any
explicit protocol version in the style of other protocols such as HTTP (HTTP/1.0 ver-
sus HTTP/1.1) or even XML (?xml version=1.0?). The designers of SOAP explicit-
ly made this choice because experience had shown simple number-based versioning to
be fragile. Further, across protocols, there were no consistent rules for determining what
changes in major versus minor version numbers mean.
Instead of going this way, SOAP leverages the capabilities of XML namespaces and
defines the protocol version to be the URI of the SOAP envelope namespace. As a
result, the only meaningful statement you can make about SOAP versions is that they are
the same or different. Its no longer possible to talk about compatible versus incompatible
changes to the protocol.
This approach gives Web service engines a choice of how to treat SOAP messages
that have a version other than the one the engine is best suited for processing. Because
an engine supporting a later version of SOAP will know all previous versions of the
specification, it has options based on the namespace of the incoming SOAP message:
n
If the message version is the same as any version the engine knows how to
process, it can process the message.
n
If the message version is recognized as older than any version the engine knows
how to process, or older than the preferred version, it should generate a
VersionMismatch fault and attempt to negotiate the protocol version with the
client by sending information regarding the versions it can accept. SOAP 1.1
didnt specify how such information might be encoded, but SOAP 1.2 introduces
the soapenv:Upgrade header for this purpose. (Well describe it in detail when we
cover faults.)
n
If the message version is newer than any version the engine knows how to process
(in other words, completely unrecognized), it must generate a VersionMismatch
fault.
The simple versioning based on the namespace URI results in fairly flexible and accom-
modating behavior of Web service engines.
134 Chapter 3 The SOAP Protocol
Processing Headers and Bodies
The SOAP spec has a specific meaning for the word process. Essentially, it means to fulfill
the contract indicated by a particular piece of a SOAP message (a header or body).
Processing a header means following the rules of that extension, and processing the body
means performing whatever operation is defined by the service.
SOAP says you dont have to process an element in order to look at it as a part of
other processing. So even though an intermediary might, for instance, encrypt the body
as a message passes through it, we dont consider this processing in the SOAP sense,
because encrypting the body isnt the same as doing what the body requests.
This gets back to the question of why you might use the none role. Imagine that
SkatesTown wants to extend its purchase order schema by adding additional customer
information. The company didnt design the schema for explicit extensibility, so adding
elements in the middle will cause any older systems receiving the new XML to fail vali-
dation. SkatesTown can continue to use the old schema in the body but add arbitrary
additional information in a SOAP header. That way, newer systems will notice the exten-
sions and use them, but older ones wont be confused. This header would be purely data,
without an associated SOAP module specification and processing rules, so it would make
sense for SkatesTown to target the header at the none role to make sure no one tries to
process it.
Faults: Error Handling in SOAP
When something goes wrong in Java, we expect someone to throw an exception; the
exception mechanism gives us a common framework with which to deal with problems.
The same is true in the SOAP world. When a problem occurs, the SOAP spec provides a
well-known way to indicate what has happened: the SOAP fault. Lets look at an exam-
ple fault message:
<env:Envelope xmlns:env=http://www.w3.org/2003/05/soap-envelope
xmlns:st=http://www.skatestown.com/ws>
<env:Header>
<st:PublicServiceAnnouncement>
Skatestowns Web services will be unavailable after 5PM today
for a two hour maintenance window.
</st:PublicServiceAnnouncement>
</env:Header>
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Sender</env:Value>
<env:Subcode>
<env:Value>st:InvalidPurchaseOrder</env:Value>
</env:Subcode>
</env:Code>
135 Faults: Error Handling in SOAP
<env:Reason>
<env:Text xml:lang=en-US>
Your purchase order did not validate!
</env:Text>
</env:Reason>
<env:Detail>
<st:LineNumber>9</st:LineNumber>
<st:ColumnNumber>24</st:ColumnNumber>
</env:Detail>
</env:Fault>
</env:Body>
</env:Envelope>
Structure of a Fault
A SOAP fault message is a normal SOAP message with a single, well-known element
inside the body: soapenv:Fault. The presence of that element acts as a signal to proces-
sors to indicate something has gone wrong. Of course, just knowing something is wrong
is rarely useful enough; you need a structure to help determine what happened so you
can either try again with a better idea of what might work or let the user know the
problem. SOAP faults have several components to help in this regard.
Fault Code
The fault code is the first place to look, since it tells you in a general sense what the
problem was. Fault codes are QNames, and SOAP defines the set of legal codes as fol-
lows (each item is the local part of the QNamethe namespace is always the SOAP
envelope namespace):
n
SenderThe problem was caused by incorrect or missing data from the sender.
For instance, if a service required a security header in order to do its work and it
was called without one, it would generate a Sender fault.You typically have to
make a change to your message before resending it if you hope to be successful.
n
ReceiverSomething went wrong on the receiver while processing the message,
but it wasnt directly attributable to the message contents. For example, a necessary
resource like a database was down, a thread wasnt available, and so on. A message
causing a Receiver fault might succeed if resent at a later time.
n
mustUnderstandThis fault code indicates that a header was received that was
targeted at the receiving node, marked mustUnderstand=true, and not under-
stood.
n
VersionMismatchThe VersionMismatch code is generated when the name-
space on the SOAP envelope that was received isnt compatible with the SOAP
version on the receiver. This is the way SOAP handles protocol versioning; well
talk about it in more detail later.
136 Chapter 3 The SOAP Protocol
The fault code resides inside the Code element in the fault, in a subelement called
Value. In the example code, you can see the Sender code, meaning something must
have been wrong with the request that caused this fault. We have the Value element
instead of putting the code qname directly inside the Code element so that we can
extend the expressive space of possible fault codes by adding more data inside another
element, Subcode.
Subcodes
SOAP 1.2 lets you specify an arbitrary hierarchy of fault subcodes, which provide further
detail about what went wrong. The syntax is a little verbose, but it works. Heres an
example:
<env:Code>
<env:Value>env:Sender</env:Value>
<env:Subcode>
<env:Value>st:InvalidPurchaseOrder</env:Value>
</env:Subcode>
</env:Code>
The Code element contains an optional Subcode element. Just as Code contains a manda-
tory Value, so too does each Subcodeand each Subcode may contain another
Subcode, to whatever level of nesting is desired. Generally the hierarchy wont go more
than about three levels deep. In our example, the subcode tells us that the problem was
an invalid purchase order.
Reason
The Reason element, also required, contains one or more human-readable descriptions
of the fault condition. Typically, the reason text might appear in a dialog box that alerts
the user of a problem, or it might be written into a log file. The Text element contains
the text and there can be one or more such messages. Why would you have more than
one? In the increasingly international environment of the Web, you might wish to send
the fault description in several languages, as in this example from the SOAP primer:
<env:Reason>
<env:Text xml:lang=en-US>Processing error</env:Text>
<env:Text xml:lang=cs>Chyba zpracovn</env:Text>
</env:Reason>
The spec states that if you have multiple Text elements, you should have a different
value for xml:lang in each oneotherwise you might confuse the software thats trying
to print out a single coherent message in a given language.
Node and Role
The optional Node element, not shown in our example, tells us which SOAP node (the
sender, an intermediary, or the ultimate destination) was processing the message at the
time the fault occurred. It contains a URI.
137 Faults: Error Handling in SOAP
The Role element tells which role the faulting node was playing when the fault
occurred. It contains a URI that has exactly the same semantics, and the same values, as
the role attribute we described when we were talking about headers. Note the differ-
ence between this element and NodeNode tells you which SOAP node generated the
fault, and Role tells what part that node was playing when it happened. The Role ele-
ment is also optional.
Fault Details
We have a custom fault code and a fault message, both of which can tell a user or soft-
ware something about the problem; but in many cases, we would also like to pass back
some more complex machine-readable data. For example, you might want to include a
stack trace while youre developing services to aid with debugging (though you likely
wouldnt do this in a production application, since stack traces can sometimes give away
information that might be useful to someone trying to compromise your system).
You can place anything you want inside the SOAP faults Detail element. In our
example at the beginning of the section, the line number and column number where the
validation error occurred are expressed, so that automated tools might be able to help
the user or developer to fix the structure of the transmitted message.
SOAP 1.1 Difference: Handling Faults
Faults in SOAP 1.2 got an overhaul from SOAP 1.1s version. All the subelements of the SOAP Fault ele-
ment in SOAP 1.1 are unqualified (in no namespace). The Fault subelements in SOAP 1.2 are in the
envelope namespace.
In SOAP 1.1, there is no Subcode, only a single faultcode element. The SOAP 1.1 fault code is a
QName, but its hierarchy is achieved through dots rather than explicit structurein other words, whereas in
SOAP 1.1 you might have seen
<faultcode>env:Sender.Authorization.BadPassword</faultcode>
in SOAP 1.2 you see something like:
<env:Code>
<env:Value>env:Sender</env:Value>
<env:Subcode>
<env:Value>myNS:Authorization</env:Value>
<env:Subcode>
<env:Value>myNS:BadPassword</env:Value>
</env:Subcode>
</env:Subcode>
</env:Code>
The env:Reason element in SOAP 1.2 is called faultstring in SOAP 1.1. Also, 1.1 only allows a sin-
gle string inside faultstring, whereas 1.2 allows different env:Text elements inside env:Reason
to account for different languages.
138 Chapter 3 The SOAP Protocol
The Client fault code from 1.1 is now Sender, which is less prone to interpretation. Similarly, 1.1s
Server fault code is now Receiver.
In SOAP 1.1, the detail element is used only for information pertaining to faults generated when pro-
cessing the SOAP body. If a fault is generated when processing a header, any machine-readable information
about the fault must travel in headers on the fault message. The reasoning for this went something like this:
Headers exist so that SOAP can support orthogonal extensibility; that means you want a given message to
be able to carry several extensions that might not have been designed by the same people and might have
no knowledge of each other. If problems occurred that caused each of these extensions to want to pass
back data, they might have to fight for the detail element. The problem with this logic is that the
detail element isnt a contended resource, in the same way the soapenv:Header isnt a contended
resource. If multiple extensions want to drop their own elements into detail, that works just as well
as putting their own headers into the envelope. So this restriction was dropped in SOAP 1.2, and
env:Detail can contain anything your application desiresbut the rule still must be followed for
SOAP 1.1.
SOAP 1.2 introduces the NotUnderstood header and the Upgrade header, both of which exist in order
to clarify what went wrong with particular faults (mustUnderstand and VersionMismatch) in a
standard way.
Using Headers in Faults
Since a fault is also a SOAP message, it can carry SOAP headers as well as the fault
structure. In our example at the beginning of this section, you can see that SkatesTown
has included a public service announcement header. This optional information lets any-
one who cares know that the Web services will be down for maintenance; and since it
isnt marked mustUnderstand, it doesnt affect the processing of the fault message in any
way. SOAP defines some headers specifically for use in faults.
The NotUnderstood Header
Youll recall that SOAP processors are forced to fault if they encounter a
mustUnderstand header that they should process but dont understand. Its great to
know something wasnt understood, but its more useful if you have an indication of
which header was the cause of the problem. That way you might be able to try again with
a different message if the situation warrants. For example, lets say a message was sent
with a routing header marked mustUnderstand=true. The purpose of the routing
header is to let the service know that after it finishes processing the message, its sup-
posed to send a copy to an endpoint whose address is in the contents of the header
(probably for logging purposes). If the receiver doesnt understand the header, it sends
back a mustUnderstand fault. The sender might then, for instance, ask the user if they
would still like to send the message, but without the carbon-copy functionality. If the
routing header is the only one in the envelope, then its easy to know which header the
mustUnderstand fault refers to. But what if there are multiple mustUnderstand headers?
139 Faults: Error Handling in SOAP
SOAP 1.2 introduced a NotUnderstood header to deal with this issue. When sending
back a mustUnderstand fault, SOAP endpoints should include a NotUnderstood header
for each header in the original message that was not understood. The NotUnderstood
header (in the SOAP envelope namespace) has a qname attribute containing the QName
of the header that wasnt understood. For example:
<env:Envelope xmlns:env=http://www.w3.org/2003/05/soap-envelope>
<env:Header>
<abc:Extension1
xmlns:abc=http://example.org/2001/06/ext
env:mustUnderstand=true/>
<def:Extension2
xmlns:def=http://example.com/stuff
env:mustUnderstand=true />
</env:Header>
<env:Body>
. . .
</env:Body>
</env:Envelope>
If a processor received this message and didnt understand Extension1 but did under-
stand Extension2, it would return a fault like this:
<env:Envelope
xmlns:env=http://www.w3.org/2003/05/soap-envelope
xmlns:xml=http://www.w3.org/XML/1998/namespace>
<env:Header>
<env:NotUnderstood qname=abc:Extension1
xmlns:abc=http://example.org/2001/06/ext />
</env:Header>
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:mustUnderstand</env:Value>
</env:Code>
<env:Reason>
<env:Text xml:lang=en>One or more mandatory
SOAP header blocks not understood
</env:Text>
</env:Reason>
</env:Fault>
</env:Body>
</env:Envelope>
This information is handy when youre trying to use the SOAP extensibility mechanism
to negotiate QoS or policy agreements between communicating parties.
140 Chapter 3 The SOAP Protocol
The Upgrade Header
Back in the section on versioning, we mentioned the Upgrade header, which SOAP 1.2
defines as a standard mechanism for indicating which versions of SOAP are supported by
a node generating a VersionMismatch fault. This section fully defines this header.
An Upgrade header (which actually is a misnomerit doesnt always imply an
upgrade in terms of using a more recent version of the protocol) looks like this in con-
text:
<?xml version=1.0 ?>
<env:Envelope
xmlns:env=http://www.w3.org/2003/05/soap-envelope
xmlns:xml=http://www.w3.org/XML/1998/namespace>
<env:Header>
<env:Upgrade>
<env:SupportedEnvelope qname=ns1:Envelope
xmlns:ns1=http://www.w3.org/2003/05/soap-envelope/>
<env:SupportedEnvelope qname=ns2:Envelope
xmlns:ns2=http://schemas.xmlsoap.org/soap/envelope//>
</env:Upgrade>
</env:Header>
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:VersionMismatch</env:Value>
</env:Code>
<env:Reason>
<env:Text xml:lang=en>Version Mismatch</env:Text>
</env:Reason>
</env:Fault>
</env:Body>
</env:Envelope>
This fault would be generated by a node that supports both SOAP 1.1 and SOAP 1.2, in
response to some envelope in another namespace. The Upgrade header, in the SOAP
envelope namespace, contains one or more SupportedEnvelope elements, each of which
indicates the QName of a supported envelope element. The SupportedEnvelope ele-
ments are ordered by preference, from most preferred to least. Therefore, the previous
fault indicates that although this node supports both SOAP 1.1 and 1.2, 1.2 is preferred.
All the VersionMismatch faults weve shown so far use SOAP 1.2. However, if a
SOAP 1.1 node doesnt understand SOAP 1.2, it wont be able to parse a SOAP 1.2
fault. As such, SOAP 1.2 specifies rules for responding to SOAP 1.1 messages from a
node that only supports SOAP 1.2. Its suggested that such nodes recognize the SOAP
1.1 namespace and respond with a SOAP 1.1 version mismatch fault containing an
Upgrade header as specified earlier. That way, nodes that have the capability to switch to
SOAP 1.2 will know to do so, and nodes that cant do so will still be able to understand
the fault as a versioning problem.
141 Objects in XML: The SOAP Data Model
Objects in XML: The SOAP Data Model
As you saw in Chapter 2, XML has an extremely rich structureand the possible con-
tents of an XML data model, which include mixed content, substitution groups, and
many other concepts, are a lot more complex than the data/objects in most modern
programming languages. This means that there isnt always an easy way to map any given
XML Schema into familiar structures such as classes in Java. The SOAP authors recog-
nized this problem, so (knowing that programmers would like to send Java/C++/VB
objects in SOAP envelopes) they introduced two concepts: the SOAP data model and the
SOAP encoding. The data model is an abstract representation of data structures such as
you might find in Java or C#, and the encoding is a set of rules to map that data model
into XML so you can send it in SOAP messages.
Object Graphs
The SOAP data model gis about representing graphs of nodes, each of which may be
connected via directional edges to other nodes. The nodes are values, and the edges are
labels. Figure 3.6 shows a simple example: the data model for a Product in SkatesTowns
database, which you saw earlier.
Figure 3.6 An example SOAP data model
947-TI
Street-style
titanium
skateboard
Titanium
Glider
skateboard
36
129
Product
sku
type
unit price description
name numInStock
142 Chapter 3 The SOAP Protocol
In Java, the object representing this structure might look like this:
class Product {
String description;
String sku;
double unitPrice;
String name;
String type;
int numInStock;
}
Nodes may have outgoing edges, in which case theyre known as compound values, or
only incoming edges, in which case theyre simple values. All the nodes around the edge
of the example are simple values. The one in the middle is a compound value.
When the edges coming out of a compound value node have names, we say the node
represents a structure. The edge names (also known as accessors) are the equivalent of field
names in Java, each one pointing to another node which contains the value of the field.
The node in the middle is our Product reference, and it has an outgoing edge for each
field of the structure.
When a node has outgoing edges that are only distinguished by position (the first
edge, the second edge, and so on), the node represents an array. A given compound value
node may represent either a structure or an array, but not both.
Sometimes its important for a data model to refer to the same value more than
oncein that case, youll see a node with more than one incoming edge (see Figure
3.7). These values are called multireference values, or multirefs g.
Figure 3.7 Multireference values
The model in this example shows that someone named Joe has a sister named Cheryl,
and they both share a pet named Fido. Because the two pet edges both point at the same
node, we know its exactly the same dog, not two different dogs who happen to share
the name Fido.
Fido
Cheryl
Joe
name
name
name
sister
pet
pet
143 Objects in XML: The SOAP Data Model
With this simple set of concepts, you can represent most common programming lan-
guage constructs in languages like C#, JavaScript, Perl, or Java. Of course, the data model
isnt very useful until you can read and write it in SOAP messages.
The SOAP Encoding
When you want to take a SOAP data model and write it out as XML (typically in a
SOAP message), you use the SOAP encoding g. Like most things in the Web services
world, the SOAP encoding has a URI to identify it, which for SOAP 1.2 is
http://www.w3.org/2003/05/soap-encoding. When serializing XML using the encod-
ing rules, its strongly recommended that processors use the special encodingStyle
attribute (in the SOAP envelope namespace) to indicate that SOAP encoding is in use,
by using this URI as the value for the attribute. This attribute can appear on headers or
their children, bodies or their children, and any child of the Detail element in a fault.
When a processor sees this attribute on an element, it knows that the element and all its
children follow the encoding rules.
SOAP 1.1 Difference: encodingStyle
In SOAP 1.1, the encodingStyle attribute could appear anywhere in the message, including on the
SOAP envelope elements (Body, Header, Envelope). In SOAP 1.2, it may only appear in the three places
mentioned in the text.
The encoding is straightforward: it says when writing out a data model, each outgoing
edge becomes an XML element, which contains either a text value (if the edge points to
a terminal node) or further subelements (if the edge points to a node which itself has
outgoing edges). The earlier product example would look something like this:
<product soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<sku>947-TI</sku>
<name>Titanium Glider</name>
<type>skateboard</type>
<desc>Street-style titanium skateboard.</desc>
<price>129.00</price>
<inStock>36</inStock>
</product>
If you want to encode a graph of objects that might contain multirefs, you cant write
the data in the straightforward way weve been using, since youll have one of two prob-
lems: Either youll lose the information that two or more encoded nodes are identical, or
(in the case of circular references) youll get into an infinite regress. Heres an example: If
the structure from Figure 3.7 included an edge called owner back from the pet to the
person, we might see a structure like the one in Figure 3.8.
If we tried to encode this with a nave system that simply followed edges and turned
them into elements, we might get something like this:
144 Chapter 3 The SOAP Protocol
<person soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<name>Joe</name>
<pet>
<name>Fido</name>
<owner>
<name>Joe</name>
<pet>
--uh oh! stack overflow on the way!--
Figure 3.8 An object graph with a loop
Luckily the SOAP encoding has a way to deal with this situation: multiref encoding. When
you encode an object that you want to refer to elsewhere, you use an ID attribute to
give it an anchor. Then, instead of directly encoding the data for a second reference to
that object, you can encode a reference to the already-serialized object using the ref
attribute. Heres the previous example using multirefs:
<person id=1 soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<name>Joe</name>
<pet id=2>
<name>Fido</name>
<owner ref=#1/> <!-- refer to the person -->
</pet>
</person>
Much nicer. Notice that in this example you see an id of 2 on Fido, even though noth-
ing in this serialization refers to him. This is a common pattern that saves time on
processors while they serialize object graphs. If they only put IDs on objects that were
referred to multiple times, they would need to walk the entire graph of objects before
writing any XML in order to figure that out. Instead, many serializers always put an ID
on any object (any nonsimple value) that might potentially be referenced later. If there is
no further reference, then youve serialized an extra few bytesno big deal. If there is,
you can notice that the object has been written before and write out a ref attribute
instead of reserializing it.
Fido
Joe
name
name
pet owner
145 Objects in XML: The SOAP Data Model
SOAP 1.1 Differences: Multirefs
The href attribute that was used to point to the data in SOAP 1.1 has changed to ref in SOAP 1.2.
Multirefs in SOAP 1.1 must be serialized as independent elements, which means as immediate children of
the SOAP:Body element. This means that when you receive a SOAP body, it may have multiref serializa-
tions either before or after the real body element (the one you care about). Heres an example:
<soap:Envelope xmlns:soap=http://schemas.xmlsoap.org/soap/envelope
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding>
<soap:Body>
<!-- Here is the multiref -->
<multiRef id=obj0 soapenc:root=0 xsi:type=myNS:Part
soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<sku>SJ-47</sku>
</multiRef>
<!-- Here is the method element -->
<myMultirefMethod soapenc:root=1
soapenv:encodingStyle=
http://www.w3.org/2003/05/soap-encoding>
<arg href=#obj0/>
</myMultirefMethod>
<!-- The multiref could also have appeared here -->
</soap:Body>
</soap:Envelope>
This is the reason for the SOAP 1.1 root attribute (which you can see in the example). Multiref serializa-
tions typically have the root attribute set to 0; the real body element has a root=1 attribute, mean-
ing its the root of the serialization tree of the SOAP data model. When serializing a SOAP message 1.1,
most processors place the multiref serializations after the main body element; this makes it much easier
for the serialization code to do its work. Each time they encounter a new object to serialize, they automati-
cally encode a forward reference instead (keeping track of which IDs go with which objects), just in case the
object was referred to again later in the serialization. Then, after the end of the main body element, they
write out all the object serializations in a row. This means that all objects are written as multirefs whenever
multirefs are enabled, which can be expensive (especially if there arent many multiple references). SOAP 1.2
fixes this problem by allowing inline multirefs. When serializing a data model, a SOAP 1.2 engine is allowed
to put an ID attribute on an inline serialization, like this:
<SOAP:Body>
<method>
<arg1 id=1 xsi:type=xsd:string>Foo</arg1>
<arg2 href=#1/>
</method>
</SOAP:Body>
Now, making a serialized object available for multireferencing is as easy as dropping an id attribute on it.
Also, this approach removes the need for the root attribute, which is no longer present in SOAP 1.2.
146 Chapter 3 The SOAP Protocol
Encoding Arrays
The XML encoding for an array in the SOAP object model looks like this:
<myArray soapenc:itemType=xsd:string
soapenc:arraySize=3>
<item>Huey</item>
<item>Duey</item>
<item>Louie</item>
</myArray>
This represents an array of three strings. The itemType attribute on the array element
tells us what kind of things are inside, and the arraySize attribute tells us how many of
them to expect. The name of the elements inside the array (item in this example)
doesnt matter to SOAP processors, since the items in an array are only distinguishable
by position. This means that the ordering of items in the XML encoding is important.
The arraySize attribute defaults to *, a special value indicating an unbounded
array (just like [] in Javaan int[] is an unbounded array of ints).
Multidimensional arrays are supported by listing each dimension in the arraySize
attribute, separated by spaces. So, a 2x2 array has an arraySize of 2 x 2. You can use
the special * value to make one dimension of a multidimensional array unbounded,
but it may only be the first dimension. In other words, arraySize=* 3 4 is OK, but
arraySize=3 * 4 isnt.
Multidimensional arrays are serialized as a single list of items, in row-major order
(across each row and then down). For this two-dimensional array of size 2x2
0 1
Northwest Northeast
Southwest Southeast
the serialization would look like this:
<myArray soapenc:itemType=xsd:string
soapenc:arraySize=2 2>
<item>Northwest</item>
<item>Northeast</item>
<item>Southwest</item>
<item>Southeast</item>
</myArray>
SOAP 1.1 Differences: Arrays
One big difference between the SOAP 1.1 and SOAP 1.2 array encodings is that in SOAP 1.1, the dimension-
ality and the type of the array are conflated into a single value (arrayType), which the processor needs
to parse into component pieces. Here are some 1.1 examples:
147 Objects in XML: The SOAP Data Model
arrayType Value Description
xsd:int[5] An array of five integers
xsd:int[][5] An array of five integer arrays
xsd:int[,][5] An array of five two-dimensional arrays of integers
p:Person[5] An array of five people
xsd:string[2,3] A 2x3, two-dimensional array of strings
In SOAP 1.2, the itemType attribute contains only the types of the array elements. The dimensions are
now in a separate arraySize attribute, and multidimensionality has been simplified.
SOAP 1.1 also supports sparse arrays (arrays with missing values, mostly used for certain kinds of database
updates) and partially transmitted arrays (arrays that are encoded starting at an offset from the beginning
of the array). To support sparse arrays, each item within an array encoding can optionally have a posi-
tion attribute, which indicates the items position in the array, counting from zero. Heres an example:
<myArray soapenc:arrayType=xsd:string[3]>
<item soapenc:position=[1]>Im the second element</item>
</myArray>
This would represent an array that has no first value, the passed string as the second element, and no third
element. The same value can be encoded as a partially transmitted array by using the offset attribute,
which indicates the index at which the encoded array begins:
<myArray soapenc:arrayType=xsd:string[3] soapenc:offset=[1]>
<item>Im the second element</item>
</myArray>
Due to several factors, including not much uptake in usage and interoperability problems when they were
used, these complex array encodings were removed from the SOAP 1.2 version.
Encoding-Specific Faults
SOAP 1.2 defines some fault codes specifically for encoding problems. If you use the
encoding (which you probably will if you use the RPC conventions, described in the
next section), you might run into the faults described in the following list. These all are
subcodes to the code env:Sender, since they all relate to problems with the senders data
serialization. These faults arent guaranteed to be senttheyre recommended, rather than
mandated. Since these faults typically indicate problems with the encoding system in a
SOAP toolkit, rather than with user code, you likely wont need to deal with them
directly unless youre building a SOAP implementation yourself:
n
MissingIDGenerated when a ref attribute in the received message doesnt cor-
respond to any of the id attributes in the message
n
DuplicateIDGenerated when more than one element in the message has the
same id attribute value
n
UntypedValueOptional; indicates that the type of node in the received message
couldnt be determined by the receiver
148 Chapter 3 The SOAP Protocol
The SOAP RPC Conventions
Once you have the SOAP data model, its quite natural to map remote procedure call
(RPC) interactions to SOAP by using a struct to represent a call. The best way to
describe this is with an example.
Lets say we have this method in Java:
public int addFive(int arg);
A request message representing a call to this method in SOAP would look something
like this:
<env:Envelope>
<env:Body>
<myNS:addFive xmlns:myNS=http://my-domain.com/
enc:encodingStyle=http://>
<arg xsi:type=xsd:int>33</arg>
</myNS:addFive>
</env:Body>
</env:Envelope>
Notice that the method name has been translated into XML (the rules by which a name
in a language like Java gets turned into an XML name, and vice versa, can be found in
part 2 of the SOAP 1.2 spec), and weve put it in a namespace that is specific to our
service (this is common practice, but not strictly necessary). The method invocation, as
we mentioned, is an encoded struct with one accessor for each argument, so the arg
element is inside the method element, and the argument contains the value were
passing: 33.
If we pass this message to a service, the response looks something like this:
<env:Envelope>
<env:Body>
<myNS:addFiveResponse xmlns:myNS=http://my-domain.com/
xmlns:rpc=http://www.w3.org/2003/05/soap-rpc
enc:encodingStyle=http://>
<rpc:result>ret</rpc:result>
<ret xsi:type=xsd:int>38</ret>
</myNS:addFive>
</env:Body>
</env:Envelope>
The RPC response is also modeled as a struct, and by convention the name of the
response struct is the name of the method with Response appended to the end. The struct
contains accessors for all inout and out parameters in the method call (see the next sec-
tion) as well as the return value.
The first accessor in the struct is interesting, and it brings to light another difference
between SOAP 1.1 and 1.2: In SOAP 1.1s RPC style, there was no way to tell which
accessor in the struct was the return value of the method and which were the out
parameters. This was a problem unless you had good meta-data, and even then the situa-
tion could be confusing. SOAP 1.2 resolves this issue by specifying that an RPC
149 The SOAP RPC Conventions
response structure containing a return value must contain an accessor named result in
the SOAP RPC namespace. The value of this field is a QName that names the accessor
containing the return value for the invocation.
out and inout Parameters
In some environments, programmers use out parameters gto enable returning multiple
values from a given RPC call. For instance, if we wanted to return not only a Boolean
yes/no value from our inventory check service but also the actual number of units in
stock, we might change our signature to something like this (using pseudocode):
boolean doCheck(String SKU,
int quantity,
out int numInStock)
The idea is that the numInStock value is filled in by the service response as well as
returning a Boolean true/false. Inout parameters gare similar, except that they also get
passed inso we could use an inout like this:
boolean doCheck(String SKU,
inout int quantity)
In this situation, wed pass a quantity value in, and then we expect the value of the
quantity variable to have been updated to the actual quantity available by the service.
Java developers arent used to the concept of inout or out parameters because, typi-
cally, in Java all objects are automatically passed by reference. When youre using RMI,
simple objects may be passed by value, but other objects are still passed by reference. In
this sense, any mutable objects (whose state can be modified) are automatically treated as
inout parameters. If a method changes them, the changes are seen automatically by any-
one else.
In Web services, the situation is different: All parameters are passed by value. SOAP
has no notion of passing parameters by reference. This design decision was made in order
to keep SOAP and its data encoding simple. Passing values by reference in a distributed
system requires distributed garbage collection and (potentially) a lot of network round-
trips. This not only complicates the design of the system but also imposes restrictions on
some possible system architectures and interaction patterns. For example, how can you
do distributed garbage collection when the requestor and the provider of a service can
both be offline at the same time?
Therefore, for Web services, the notion of inout and out parameters doesnt involve
passing objects by reference and letting the target backend modify their state; instead,
copies of the data are exchanged. Its then up to the service client code to create the
perception that the state of the object that has been passed in to the client method has
been modified. Well show you what we mean. Lets take the modified doCheck() you
saw earlier:
boolean doCheck(String SKU,
inout int quantity)
150 Chapter 3 The SOAP Protocol
When this method is called, the request message looks like this on the wire:
<soapenv:Envelope xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<doCheck soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<SKU>318-BP</SKU>
<quantity xsi:type=xsd:int>3</quantity>
</doCheck>
</soapenv:Body>
</soapenv:Envelope>
And heres the response message:
<soapenv:Envelope xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<doCheckResponse soapenv:encodingStyle=
http://www.w3.org/2003/05/soap-encoding>
<rpc:result xmlns:rpc=http://www.w3.org/2003/05/soap-rpc>return</rpc:result>
<return>true</return>
<quantity xsi:type=xsd:int>72</quantity>
</doCheckResponse>
</soapenv:Body>
</soapenv:Envelope>
This is a request to see if 3 items are available, and the response indicates that not only
are there 3 (the true response), but there are in fact 72 (the new quantity value). The
endpoint receiving this response should then update the appropriate programming-lan-
guage construct for the quantity parameter with the new value.
Finally, heres the example that adds an extra out parameter containing the number of
items in stock to our doCheck() method:
boolean doCheck(in sku, in quantity, out numInStock)
If we called this new method, the request would look identical to the one you saw earli-
er in this chapter, but the response would now look like this:
<soapenv:Envelope xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<doCheckResponse soapenv:encodingStyle=
http://www.w3.org/2003/05/soap-encoding>
<rpc:result xmlns:rpc=http://www.w3.org/2003/05/soap-rpc>return</rpc:result>
<return>true</return>
<numInStock>72</numInStock>
151 When to Use Which Style
</doCheckResponse>
</soapenv:Body>
</soapenv:Envelope>
Despite the fact that Java doesnt have a native concept of inout and out parameters, we
can still use them with toolkits like Axiswell explore how to do this in Chapter 5.
XML, Straight Up: Document-Style SOAP
Although the RPC pattern is a common use case for SOAP, there are no restrictions on
the contents of the SOAP body. Typically, sending nonencoded XML content in the
body is known as document-style SOAP, since it centers around the message as an XML
document rather than an abstract data model that happens to be encoded into XML.
Keep in mind that even if you dont explicitly use the SOAP RPC conventions as
described, you can still map your document-style XML to and from procedure calls (or
use whatever programming paradigm you like, since SOAP is about the structure of
messages on the wire).Various toolkits, including .NET and Axis, have been doing this
for some time now. When you publish a Visual Basic or C# class as a .NET Web service,
the default behavior is to use document-style SOAP to expose the methods. The mes-
sages still look similar to the RPC style, but they dont use the SOAP encoding. The
only problem with this approach is that there are no standard encodings for things like
arrays or structures, and you lose the referential integrity of objects that are referenced
multiple times.
When to Use Which Style
As a Java programmer, it might not always be clear which style of SOAP to use. Here are
a few rules of thumb you can use when thinking about it:
n
If youre starting from code in Java, C#, or another procedural/OO language, and
youre trying to expose methods as Web services, the RPC style is a natural fit.
n
If you have preexisting XML formats (schemas and so on) that you want to sup-
port, using the SOAP encoding would just get in the way. So, document style is
preferable.
n
If you need to transmit object graphs that must maintain referential integrity (cir-
cular lists, for instance, or complex graphs), RPC provides an interoperable way to
do it.
n
Validating document-style messages is straightforward, because they typically have
XML schemas.You cant easily write an XML schema that will correctly validate
an encoded SOAP data model, since the model allows for a nondeterministic set
of valid serializationsfor instance, multiref objects can be serialized in a variety of
places (either in-place at any of the references, or as an independent element at the
top level), and arrays are still valid regardless of the XML element name of the
items. So, if schema validation using standard tools is important, document style is
the way to go.
152 Chapter 3 The SOAP Protocol
As youll see in Chapter 13, Web Services Interoperability, the Web Service
Interoperability organization (WS-I) has come down hard against SOAP encoding, and
has in fact banned its use in their Basic Profile of SOAP 1.1 (we can only assume they
will continue to dislike it for SOAP 1.2).
The jury is definitely still out with respect to the value of the SOAP encoding. A lot
of companies seem to be moving away from it; but, on the other hand, its a developer-
friendly technology for people trying to expose preexisting classes and methods as Web
services. Despite WS-Is claims, there has been a lot of interoperability work to make sure
RPC style works between most implementations of SOAP 1.1 and SOAP 1.2.
Well talk a lot more about these styles in the next couple of chapters, when we
describe them in relationship to WSDL and Axis.
The Transport Binding Framework
The SOAP processing model talks about what a node should do when it processes a
SOAP message. As weve discussed, messages are described abstractly in terms of the
XML infoset. Now its time to look more closely at how those infosets are moved from
place to place.
A SOAP protocol binding gis a set of rules that describes a method of getting a
SOAP infoset from one node to another. For instance, you already know that HTTP is a
common way to transport SOAP messages. The purpose of the SOAP HTTP binding
(which youll find in part 2 of the spec) is to describe how to take a SOAP infoset at
one node and serialize it across an HTTP connection to another node.
Many bindings, including the standard HTTP one, specify that the XML 1.0 serializa-
tion for the infoset should be usedthat means if you look at the messages on the wire,
youll see angle brackets as in any regular XML document. But the bindings job is really
to move the infoset from node to node, and the way the infoset is represented on the
wire is up to the binding author. This means bindings have the freedom to specify cus-
tom serializations; they might do this to increase efficiency with compression or a binary
protocol, or to add security via encryption, among other reasons.
Since bindings determine the concrete structure of the bits on the wire and how
theyre processed into SOAP messages, its critical that communicating parties agree on
what binding to usethat agreement is just as important as the agreement of what data
to send. This is why bindings, like SOAP modules, are named with URIs. The standard
SOAP 1.2 HTTP binding, for instance, is identified by the URI http://www.w3.org/
2003/05/soap/bindings/HTTP/. Note that there can be many different SOAP bindings
for a given underlying protocolagreeing on a binding means not only agreeing to use
HTTP to send SOAP messages, but also to follow all the rules of the given binding
specification when doing so.
Bindings can be simple or complex, and they can provide a variety of different levels
of functionality. A raw UDP binding, for instance, might not provide any guarantee that
the messages sent arrive at the other side. On the other hand, a binding to a reliable
message queuing system such as the Java Message Service (JMS) might provide
153 The Transport Binding Framework
guaranteed (eventual) arrival, even in the face of crashes or network failures. We can also
imagine bindings to underlying protocols that provide security, such as HTTPS, or with
the ability to broadcast messages to a variety of recipients at once, like multicast IP.
Even when the underlying protocol doesnt provide desired functionality directly, its
still possible to write a binding that does fancy things. For instance, you could design a
custom HTTP binding specifying that, for security reasons, the XML forming each mes-
sage should be encrypted before being sent (and decrypted on the receiving side). Such
custom bindings are possible, but its often a better idea to use the extensibility built into
the envelope to implement this kind of functionality. Also, bindings only serve to pass
messages between adjacent nodes along a SOAP message path. If you need end-to-end
functionality in a situation where intermediaries are involved, its usually better to use in-
message extensibility with SOAP headers.
Features and Properties
Bindings can do a huge variety of different things for uscompare this variety to what
the SOAP header mechanism can do, and you might notice that the set of possible
semantics you can achieve is similar (almost anything, in fact). The designers of SOAP
1.2 noticed the similarity as well, and wanted to write a framework that might help
future extension authors describe their semantics in ways that were easy to reason about,
compare with each other, and refer to in machine-readable ways.
What was needed was a way to specify that a given semantic could be implemented
either by a binding or by using SOAP headers. Nodes might use this information to
decide whether to send particular headersif youre running on top a secure binding,
for instance, you might not need to add security headers to your messages, which can
save your application effort and time. Also, since a variety of modules might be available
to an engine at any given time, it would also be nice to be able to unambiguously indi-
cate that a particular module performed a certain set of desired semantics. To achieve
these goals, the first job was to have a standard way to name the semantics in question.
The SOAP authors came up with an extensibility framework that is described in sec-
tion 3 of part 1 of the SOAP 1.2 spec. The framework revolves around the notion of an
abstract feature g, which is essentially a semantic that has a name (a URI) and a specifi-
cation explaining what it means. Features can be things like reliability, security, transac-
tions, or anything you might imagine. A feature is typically specified by describing the
behavior of each node involved in the interaction, any data that needs to be known
before the feature does its thing, and any data that is transferred from node to node as a
result. Its generally convenient if the specification of the features behavior can be found
at the URI naming the featureso if a user sees a reference to it somewhere, they can
easily locate a description of what it does.
The state relevant to a feature is generally described in terms of named properties g.
Properties, in the SOAP 1.2 sense, are pieces of state, named with URIs, which affect the
operation of features. For instance, you might describe a favorite color feature that
involves transferring a hexadecimal color value from one node to another.You would
154 Chapter 3 The SOAP Protocol
pick a URI for the feature, describe the rules of operation, and name the color property
with another URI. Because a feature definition is abstract, you dont say anything about
how this color would move across the wire.
Features are expressed (turned into reality via some mechanism) by bindings and mod-
ules. Weve already described both of theserecall that a binding is a means for perform-
ing functions below the SOAP processing model, and a module is a means for perform-
ing functions using the SOAP processing model, via headers.
Heres a simple example of how a feature might be expressed by a binding in one
case and a module in another. Imagine a secure channel feature. The specification for
this feature indicates that it has the URI http://skatestown.com/secureChannel, and
the abstract feature describes a message traveling from node to node in an unsnoopable
fashion (to some reasonable level of security). We might then imagine a SOAP binding
to the HTTPS protocol, which would specify that it implements the
http://skatestown.com/secureChannel feature. Since HTTPS meets the security
requirements of the abstract feature, the feature would be satisfied by the binding with
no extra work, and the binding specification would indicate that it natively supports this
feature. We could also imagine a SOAP module (something like WS-Security, which we
discuss in Chapter 9, Securing Web Services) that provides encryption and signing of a
SOAP message across any binding. The module specification would also state that it
implements the secureChannel feature. With this information in hand, it would be pos-
sible, in a situation that required a secure channel, to decide to engage our SOAP mod-
ule in some situations (when using the HTTP binding, for instance) and to not bother
in other situations where the HTTPS binding is in use.
Another example of making features concrete could be the color feature discussed
earlier. The feature spec describes moving a color value from the sender to the receiver
in the abstract. A custom binding over email might define a special SMTP header to
carry this color information outside the SOAP envelope. Writing a SOAP module to
satisfy the feature would involve defining a SOAP header that carried the value in the
envelope.
Message Exchange Patterns
One common type of feature is a Message Exchange Pattern (MEP) g. An MEP specifies
how many messages move around in a given interaction, where they originate, and
where they end up. Each binding can (and must) support one or more message exchange
patterns. The SOAP 1.2 spec defines two standard MEPs: Request-Response and SOAP
Response. Without going into too much detail (you can find the full specifications for
these MEPs in the SOAP 1.2 spec, part 2), well give you a flavor of what these MEPs
are about.
The Request-Response MEP involves a requesting node and a responding node. As
you might expect, it has the following semantics: First, the requesting node sends a
SOAP message to the responding node. Then the responding node replies with a SOAP
message that returns to the requesting node. See Figure 3.9.
155 The Transport Binding Framework
Figure 3.9 The Request-Response MEP
You should note a couple of important things here. First, the response message is corre-
lated to the request messagein other words, the requesting node must be able to figure
out which response goes with which request. Since the MEP is abstract, though, it
doesnt specify how this correlation is achieved but leaves that up to implementations.
Second, if a fault is generated at the responding node, the fault is delivered in the
response message.
The SOAP Response MEP is similar to the Request-Response MEP, except for the
fact that the request message is explicitly not a SOAP message. In other words, the
request doesnt trigger the execution of the SOAP processing model on the receiving
node. The receiving node responds to the request with a SOAP message, as in the
Request-Response MEP (see Figure 3.10).
Requesting
SOAP
Node
Responding
SOAP
Node
SOAP
Request
SOAP
Response
(Could be fault)
Figure 3.10 The SOAP Response MEP
The primary reason for introducing this strange-seeming MEP is to support REST-style
interactions with SOAP (see the sidebar REST-Style versus Tunneled Web Services).
The SOAP Response MEP allows a request to be something as simple as an HTTP
GET; and since the responding node doesnt have to implement the SOAP processing
model, it can return a SOAP message in any way it deems appropriate. It might, for
instance, be an HTTP server with a variety of SOAP messages in its filesystem.
As youll see in a moment, the HTTP binding natively supports both of these MEPs.
Of course, MEPs, like other abstract features, can also be implemented via SOAP mod-
ules. Heres an example of how the Request-Response MEP might be implemented
using SOAP headers across a transport that only allows one-way messages:
<soapenv:Envelope
xmlns:soapenv=http://www.w3.org/2003/05/soap/envelope>
<soapenv:Header>
Requesting
SOAP
Node
Responding
SOAP
Node
Non-SOAP
Message
SOAP
Response
(Could be fault)
156 Chapter 3 The SOAP Protocol
<!--This header specifies the return address-->
<reqresp:ReplyTo soapenv:mustUnderstand=true
xmlns:reqresp=http://skatestown.com/requestResponse>
<destination>udp://me.com:6666</destination>
</reqresp:ReplyTo>
<!--This header specifies a correlation ID-->
<reqresp:correlationID soapenv:mustUnderstand=true
xmlns:reqresp=http://skatestown.com/requestResponse>
1234
</reqresp:correlationID>
</soapenv:Header>
<soapenv:Body>
<doSomethingCool/>
</soapenv:Body>
</soapenv:Envelope>
This message might have been sent in a UDP datagram, which is a one-way interaction.
The receiver would have to understand these headers (since theyre marked
mustUnderstand) in order to process the message; when it generated a reply, it would
follow the rules of the ReplyTo header and send the reply via UDP to port 6666 of host
me.com. The reply would contain the same correlationID sent in the request, so that
the receiver on me.com would be able to match the response to the pending request. The
WS-Addressing spec includes a mechanism a lot like this, which well cover in Chapter
8, Web Services and Stateful Resources.
If your interaction requires the request-response MEP, you might choose to use the
HTTP binding (or any binding that implements that MEP natively), or you might use a
SOAP module that specifies how to implement the MEP across other bindings. The
important goal of the framework is to enable abstractly defining what functionality you
needthen youre free to select appropriate implementations without tying yourself to a
particular way of doing things.
REST-Style versus Tunneled Web Services
REpresentational State Transfer (REST) grefers to an architectural style of building software that mirrors
what HTTP is built to do: transfer representations of the state of named resources from place to place with
a small set of methods (GET, POST, PUT, DELETE). The methods have various semantics associated with
themfor instance, a GET is a request for a resources representation, and by definition GETs should be safe
operations. Safe in this context means they should have no side effects. So, you do a GET on my bank
account URL in order to obtain your current balance, you can repeat that operation many times with no ill
effects. However if the GET withdrew funds from your account, , that would clearly be a serious side effect
(and therefore illegal in REST).
Another interesting thing to note about GET is that the results are often cacheable: If you do a GET on a
weather report URL, the results probably wont change much if you do it again in 10 minutes. As a result,
the infrastructure can cache the result, and if someone asks for the same GET again before the cache entry-
expires, you can return the cached data instead of going out over the Net to fetch the same weather again.
157 The Transport Binding Framework
The HTTP infrastructure uses this caching style to great effect; most large ISPs or companies have shared
caching proxies at the edge of the network that vastly reduce the network bandwidth outside the organiza-
tioneach time anyone asks for http://cnn.com, the cache checks if a local, fresh copy exists before
going out across the network.
We bring up REST because it shines light on an interesting controversy. REST advocates (sometimes known
as RESTifarians) noticed that SOAP 1.1s HTTP binding mandated using the POST method. Since POSTs are
not safe or cacheable in the same way GETs are, even simple SOAP requests to obtain data like stock quotes
or weather reports had to use a mechanism that prevented the HTTP infrastructure from doing its job. Even
though these operations might have been safe, SOAP was ignoring the HTTP semantic for safe operations
and losing out on valuable caching behavior as a consequence.
This issue has since been fixed in the SOAP 1.2 HTTP binding, which supports the WebMethod feature. In
other words, SOAP 1.2 is a lot more REST-friendly than SOAP 1.1 was, since it includes a specific technique
for utilizing HTTP GET, including all that goes along with it in terms of caching support and operation safety.
HTTP semantics are respected, and HTTP isnt always used as a tunnel for SOAP messages.
We believe that SOAPs flexible messaging and extensibility model (which supports both semantics provided
by the underlying protocol and also those provided by higher-level extensions in the SOAP envelope) offer
the right framework for supporting a large variety of architectural approaches. We hope this framework will
allow the protocol both good uptake and longevity in the development community.
Features and Properties Redux
The features and properties mechanism in SOAP 1.2 is a powerful and flexible extensi-
bility framework. By naming semantics, as well as the variables used to implement those
semantics, we enable referencing those concepts in an unambiguous way. Doing so is
useful for a number of reasonsas youll see in Chapter 4, Describing Web Services,
naming semantics lets you express capabilities and requirements of your services in
machine-readable form. This allows software to reason about whether a given service is
compatible with particular requirements. In addition to the machine-readability aspect,
naming features and properties allows multiple specifications to refer to the same
concepts when describing functionality. This encourages good composition between
extensions, and also allows future extensions to be written that implement identical (but
perhaps faster, or more flexible) semantics.
Now that you understand the framework and its components, well go into more
detail about the SOAP HTTP binding.
The HTTP Binding
You already know the HTTP binding is identified with the URI http://www.w3.org/
2003/05/soap/bindings/HTTP/ and that it supports both the Request-Response and
the SOAP Response MEPs. Before we examine the other interesting facets of this bind-
ing, lets discuss how those MEPs are realized.
158 Chapter 3 The SOAP Protocol
Since HTTP is natively a request/response protocol, when using the Request-
Response MEP with the HTTP binding, the request/response semantics map directly to
the equivalent HTTP messages. In other words, the SOAP request message travels in the
HTTP request, and the SOAP response message is in the HTTP response. This is a great
example of utilizing the native capabilities of an underlying protocol to implement an
abstract feature.
The SOAP Response MEP is also implemented natively by the HTTP binding and is
typically used with the GET Web method to retrieve representations of Web resources
expressed as SOAP messages, or to make idempotent (safe) queries. When using this
MEP this way, the non-SOAP request is the HTTP GET request, and the SOAP
response message (or fault) comes back, as in the Request/Response case, in the HTTP
response.
The HTTP binding also specifies how faults during the processing of a message map
to particular HTTP status codes and how the semantics of HTTP status codes map to
Web service invocations.
SOAP 1.1 Differences: Status Codes
In SOAP 1.1, the HTTP binding specified that if a fault was returned in an HTTP response, the status code
had to be 500 (server error). This worked, but it didnt allow systems to use the inherent meaning in the
HTTP status codes400 means a problem with the sender, 500 means a problem with the receiver, and so
on. SOAP 1.2 resolves this issue by specifying a richer set of fault codes; for example, if a
soapenv:Sender fault is generated, indicating a problem with the request message, the engine should
use the 400 HTTP status code when returning the fault. Other faults generate a 500, as in SOAP 1.1.
The HTTP binding also implements two abstract features, which are described next.
The SOAP Action Feature
In the SOAP 1.1 HTTP binding, you were required to send a custom HTTP header
called SOAPAction along with SOAP messages. The purpose of this header was twofold:
to let any system receiving the message know that the contents were SOAP and to con-
vey the intent of the message via a URI. In SOAP 1.1, the first purpose was necessary
because the media type was text/xml; since that media type could carry any XML doc-
ument, processors needed to look inside the XML (which might involve an expensive
parse) to check if it was a SOAP envelope. The SOAPAction header allowed them to
realize this was a SOAP message and perhaps make decisions about routing or logging
based on that knowledge. The presence of the header was enough to indicate that it was
SOAP, but the URI could also convey more specific meaning, abstractly describing the
intent of the message. Many implementations use this URI for dispatching to a particu-
lar piece of code on the backend, especially when using document-style SOAP. For
example, you might send a purchase order as the body contents to two different meth-
odsone called submitPO() and the other called validatePO(). Since the XML is the
same in both cases, you could use the value of the SOAPAction URI to differentiate.
159 The Transport Binding Framework
SOAP 1.2 uses the application/soap+xml media type, and the SOAPAction header
is no longer needed to differentiate SOAP traffic from other XML documents moving
across HTTP connections. But a lot of people still want a standard way to indicate a
messages purpose outside of the SOAP envelope. The features and properties mechanism
described earlier seemed like a perfect fit for an abstract feature.
The SOAPAction feature can be made available for any binding that uses the
application/soap+xml media type to send its messages. The definition of that media
type (which you can find in the glossary) specifies an optional action parameter, which
is used in SOAP 1.2 instead of an HTTP-specific header to carry the SOAPAction URI.
The feature has a single property, http://www.w3.org/2003/05/soap/features/
action/Action. When this property is given a URI value, the spec indicates that the
binding that implements the feature must place the value into the action parameter. So,
if the property had the value http://example.com/myAction at the sender, the message
would start like this (assuming the HTTP binding):
POST /axis/SomeService.jws
Content-Type: application/soap+xml; charset=utf-8;
action=http://example.com/myAction
...rest of message...
On the receiving side, a node receiving a message with an action parameter over a
binding that supports the SOAP Action feature is required to make the value of the
action parameter available in the http://www.w3.org/2003/05/soap/features/
action/Action property.
The Web Method Feature
Normal SOAP exchanges across HTTP use the POST HTTP verb. However, sometimes
other HTTP methods are more appropriate for a given situation. For instance, when you
use the SOAP Response MEP, youre often making a state query, which in HTTP is
modeled with the GET verb. If you only allowed POST, you would be forcing HTTP
into a particular limited set of uses (purely as a transport). Therefore, in an effort to allow
developers to better integrate the semantics of SOAP with the semantics of HTTP, the
Web Method Feature (defined in part 2 of the spec) was born.
This feature defines a single property, http://www.w3.org/2003/05/soap/
features/web-method/Method, which contains one of the words GET, POST, PUT, or
DELETE (although other values may be supported later). When sending a message, the
HTTP binding will use the verb specified in this property instead of the default POST.
This is most often used in concert with the SOAP Response MEP to implement REST
semantics.
Right now this feature is only relevant to HTTP; but if other bindings are developed
to underlying protocols with REST-like semantics, it would be available for them as
well.
160 Chapter 3 The SOAP Protocol
Using SOAP to Send Binary Data
Our example messages to date have been fairly small, but we can easily imagine wanting
to use SOAP to send large binary blobs of data. For example, consider an automated
insurance claim registryremote agents might use SOAP-enabled software to submit
new claims to a central server, and part of the data associated with a claim might be dig-
ital images recording damages or the environment around an accident. Since XML cant
directly encode true 8-bit binary data at present, a simple way to do this kind of thing
might be to use the XML Schema type base64binary and encode your images as
base64 text inside the XML:
<soap:Envelope
xmlns:soap=http://www.w3.org/2003/05/soap-envelope
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soap:Body>
<submitClaim>
<accountNumber>5XJ45-3B2</accountNumber>
<eventType>accident</eventType>
<image imageType=jpg xsi:type=base64binary>
4f3e9b0...(rest of encoded image)
</image>
</submitClaim>
</soap:Body>
</soap:Envelope>
This technique works, but its not particularly efficient in terms of bandwidth, and it
takes processing time to encode and decode bytes to and from base64. Email has been
using the Multipurpose Internet Mail Extensions (MIME) standard for some time now
to do this job, and MIME allows the encoding of 8-bit binary. MIME is also the basis for
some of the data encoding used in HTTP; since HTTP software can usually deal with
MIME, it might be nice if there were a way to integrate the SOAP protocol with this
standard and a more efficient way of sending binary data.
SOAP with Attachments and DIME
In late 2000, HP and Microsoft released a specification called SOAP Messages with
Attachments.The spec describes a simple way to use the multiref encoding in SOAP
1.1 to reference MIME-encoded attachment parts. We wont go into much detail here; if
you want to read the spec, you can find it at http://www.w3.org/TR/2000/NOTE-SOAP-
attachments-20001211.
The basic idea behind SOAP with Attachments (SwA) gis that you use the same
HREF trick you saw in the section Object Graphs to insert a reference to the data in
the SOAP message instead of directly encoding it. In the SwA case, however, you use the
content-id (cid) of the MIME part containing the data youre interested in as the refer-
ence instead of the ID of some XML. So, the message encoded earlier would look some-
thing like this:
161 Using SOAP to Send Binary Data
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary;
type=application/soap+xml;start=<[email protected]>
--MIME_boundary
Content-Type: application/soap+xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <[email protected]>
<soap:Envelope
xmlns:soap=http://www.w3.org/2003/05/soap-envelope>
<soap:Body>
<submitClaim>
<accountNumber>5XJ45-3B2</accountNumber>
<eventType>accident</eventType>
<image href=cid:[email protected]/>
</submitClaim>
</soap:Body>
</soap:Envelope>
--MIME_boundary
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-ID: <[email protected]>
...binary JPG image...
--MIME_boundary--
Another technology called Direct Internet Message Encapsulation (DIME) g, from
Microsoft and IBM, used a similar technique, except that the on-the-wire encoding was
smaller and more efficient than MIME. DIME was submitted to the IETF in 2002 but
has since lost the support of even its original authors.
SwA and DIME are great technologies, and they get the job done, but there are a few
problems. The main issue is that both SwA and DIME introduce a data structure that is
explicitly outside the realm of the XML data model. In other words, if an intermediary
received the earlier MIME message and wanted to digitally sign or encrypt the SOAP
body, it would need rules that told it how the content in the MIME attachment was
related to the SOAP envelope. Those rules werent formalized for SwA/DIME.
Therefore, tools and software that work with the XML data model need to be modified
in order to understand the SwA/DIME packaging structure and have a way to access the
data embedded in the MIME attachments.
Various XML and Web service visionaries began discussing the general issue of merg-
ing binary content with the XML data model in earnest. As a result, several proposals are
now evolving to solve this problem in an architecturally cleaner fashion.
162 Chapter 3 The SOAP Protocol
PASWA, MTOM, and XOP
In April 2003, the Proposed Infoset Addendum to SOAP With Attachments (PASWA) g
document was released by several companies including Microsoft, AT&T, and SAP.
PASWA introduced a logical model for including binary content directly into a SOAP
infoset. Physically, the messages that PASWA deals with look almost identical to our two
earlier examples (the image encoded first as base64 inline with the XML and then as a
MIME attachment)the difference is in how we think about the attachments. Instead of
thinking of the MIME-encoded image as a separate entity that is explicitly referred to in
the SOAP envelope, we logically think of it as if it were still inline with the XML. In
other words, the MIME packaging is an optimization, and implementations need to
ensure that processors looking at the SOAP data model for purposes of encryption or
signing still see the actual data as if it were base64-encoded in the XML.
In July 2003, after a long series of conversations between the XML Protocol Group
and the PASWA supporters, the Message Transmission Optimization Mechanism (MTOM)
gwas born, owned by the XMLP group. It reframed the ideas in PASWA into an
abstract feature to better sync with the SOAP 1.2 extensibility model, and then offered
an implementation of that feature over HTTP. The serialization mechanism is called
XML-Binary Optimized Packaging (XOP) g; it was factored into a separate spec so that
it could also be used in non-SOAP contexts.
As an example, we slightly modified the earlier insurance claim by augmenting the
XML with a content-type attribute (from the XOP spec) that tells us what MIME con-
tent type to use when serializing this infoset using XOP. Heres the new version:
<soap:Envelope
xmlns:soap=http://www.w3.org/2003/05/soap-envelope
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xop-mime=http://www.w3.org/2003/12/xop/mime>
<soap:Body>
<submitClaim>
<accountNumber>5XJ45-3B2</accountNumber>
<eventType>accident</eventType>
<image xop-mime:content-type=image/jpeg
xsi:type=base64binary>
4f3e9b0...(rest of encoded image)
</image>
</submitClaim>
</soap:Body>
</soap:Envelope>
An MTOM/XOP version of our modified insurance claim looks like this:
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary;
type=application/soap+xml;start=<[email protected]>
163 Small-Scale SOAP, Big-Time SOAP
--MIME_boundary
Content-Type: application/soap+xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <[email protected]>
<soap:Envelope
xmlns:soap=http://www.w3.org/2003/05/soap-envelope
xmlns:xop=http://www.w3.org/2003/12/xop/include
xmlns:xop-mime=http://www.w3.org/2003/12/xop/mime>
<soap:Body>
<submitClaim>
<accountNumber>5XJ45-3B2</accountNumber>
<eventType>accident</eventType>
<image xop-mime:content-type=image/jpeg>
<xop:Include href=cid:[email protected]/>
</image>
</submitClaim>
</soap:Body>
</soap:Envelope>
--MIME_boundary
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-ID: <[email protected]>
...binary JPG image...
--MIME_boundary--
Essentially, its the same on the wire as the SwA version, but it uses the xop:Include>
element instead of just the href attribute. The real difference is architectural, since we
imagine tools and APIs will manipulate this message exactly as if it were an XML data
model.
MTOM and XOP are on their way to being released by the XML Protocol Working
Group some time in 2004, and it remains to be seen how well they will be accepted by
the broader user community. Early feedback has been very positive, however, and the
authors of this book are behind the idea of a unified data model for XML and binary
content.
Small-Scale SOAP, Big-Time SOAP
To finish our coverage of the SOAP protocol in this chapter, well briefly examine how
the simple rules of SOAPs design allow a lot of flexibility for implementations. Lets
consider three different styles of SOAP processors and look at how this one baseline pro-
tocol can scale from the very small to the very large.
164 Chapter 3 The SOAP Protocol
Imagine that a maker of scientific equipment has just released a new digital ther-
mometer for the home. Being a forward-looking company, it decides to add a SOAP-
over-HTTP interface so that other devices plugged into the home network can query
the temperature. This thermometer has exactly one method, getTemperature(), which
returns the current temperature in degrees Celsius. Since its so simple, the vendor has
built a single-purpose SOAP engine into the hardware. This engine doesnt include a full
XML processor but instead uses simple pattern-matching with regular expressions to do
three simple things:
n
Check that the envelope looks right, and return a VersionMismatch fault if neces-
sary.
n
If any header blocks are targeted for the ultimate destination that are marked
mustUnderstand=true, return a mustUnderstand fault.
n
Confirm the soap:Body contains exactly one element with the getTemperature
QName.
Despite its simplicity and extremely limited processing capability, our thermometer is
perfectly SOAP 1.2 compliant. That means any other software that speaks SOAP can talk
to it.
Our second example is a single-purpose application built to talk to a particular kind
of Web service. SkatesTown might have built something like this in order to automate
finding the lowest price on parts from its various suppliers (all of whom implement a
standard RPC-style price checking service) each day. This sort of system typically has
real XML parsing and uses shared libraries to manage the SOAP processing model, so
that the developer doesnt have to rewrite the same mustUnderstand-checking logic
again and again. It might also support one or two built-in extensions, such as a com-
monly supported security module. However, the capabilities of this system are deter-
mined when its constructed, and it cant be dynamically extended.
Finally, lets consider a general-purpose middleware SOAP solution as you might
implement it with a toolkit like Axis. Axis was designed to be a flexible framework that
provides as much generic processing capability as possible while letting you build appli-
cations and extensions that easily plug in to the framework. It uses full-scale XML pars-
ing, supports schema validation, and has a mechanism for building handlerspieces of
extension code that process SOAP messages in order to implement either local function-
ality (logging, management) or extension semantics (security, correlation).
The system in this example might be for a Grid computing system (see Chapter 8 for
more) that needs to be able to talk to an unbounded variety of other services. Our
SOAP engine has a variety of extensions for popular security, transactionality, reliability,
and management protocols on top of SOAP. When conversations begin, an initial negoti-
ation phase uses SOAP headers to determine which encryption protocols to use,
whether the parties are legally allowed to transact business across the network, and what
kind of notarization service will be used. Essentially, we use mustUnderstand headers to
ask for the maximum level of functionality; then, if we receive errors, we back off to
165 Resources
lower levels if appropriate. Once the business-level messages start to flow, they will be
safely encrypted and contain SOAP headers to manage transactional context, handle
routing through intermediaries, and ensure nonrepudiation. The system is also extensible
by dropping in new JAR files and tweaking a few deployment descriptors.
So weve gone from a completely static, dumb system like a thermometer all the way
to a richly functional B2B fabricand SOAPs model of simple, well-layered abstractions
has been the foundation throughout. Although its true that the secure Grid client wont
be able to make the thermometer talk at its level, the inherent capabilities of the SOAP
processing model make it possible for the more functional client to find that out and to
scale back (if appropriate) in order to talk to the simpler service.
Of course, wouldnt it be nice if there were standard ways for communicating parties
to automatically find out about each others capabilities before any messages even flow
over the wire? There is such a way: Its called WSDL, and its the subject of the next
chapter.
Summary
We hope this chapter has demystified what SOAP is about and given you a good
grounding in the essentials of the protocol and its behavior.Youve seen how vertical
(SOAP headers) and horizontal (intermediaries) extensibility, plus the binding frame-
work, can be used to stretch the capabilities of the core in controlled ways. Well return
again and again to these extensibility concepts as we move through the examples in the
rest of the book.
Of course, unless you plan to implement a SOAP engine on your own, youll be
using a toolkit to perform SOAP invocations. As such, weve also talked about Apache
Axis, the open source SOAP engine that SkatesTown and SBC are using, and youve
seen how the HTTP-based purchase order system can be converted into a SOAP Web
service.Youll learn a lot more about Axis in Chapter 5, but before that well look at the
other major pillar that makes up the foundation of Web services: the Web Service
Description Language, or WSDL.
Resources
n
XML Protocol Working Group (home of all SOAP 1.2 information)
http://www.w3.org/2000/xp/Group
n
SOAP 1.2 spec (latest version)http://www.w3.org/TR/SOAP
n
SOAP 1.2 Primerhttp://www.w3.org/TR/2003/REC-soap12-part0-20030624/
n
SOAP 1.1 spechttp://www.w3.org/TR/2000/NOTE-SOAP-20000508/
n
SOAP with Attachmentshttp://www.w3.org/TR/SOAP-attachments
n
MTOMhttp://www.w3.org/TR/soap12-mtom/
n
XOPhttp://www.w3.org/TR/xop10/
4
Describing Web Services
TO THIS POINT, WEVE DESCRIBED XML and positioned it as the underpinning of the
SOAP messaging protocol. Given that XML is a self-describing, human-readable repre-
sentation of data, isnt that enough to make a SOAP message also self-describing? If so,
then why do we need an approach for service descriptions? Well answer these questions
in this chapter.
Why Service Descriptions?
Lets look at this problem from the service requestors perspective. A customer of
SkatesTown wants to invoke the POSubmission service. How does the customer know
what kind of message to send? Sure, they probably know to use SOAP, but SOAP is just
the format of the envelope; SOAP itself doesnt indicate what message format to put into
the envelope. The customer needs to understand what XML to put into the body of the
SOAP envelope, whether a particular security SOAP header is required, what format the
response message might come in, and whether a response is to be expected at all. The
customer also needs to know what messaging protocol to use and where, exactly, to send
the message. The customer may even determine that the service supports alternatives to
SOAP; perhaps just straight HTTP POST of an XML message is all that is needed to
submit the PO.
One way for the customer to determine what message to send is to examine a textual
description of the service published on the SkatesTown Web site. Although doing so is
simple, it could cause problems. The textual description may not be precise and can
allow the customers developers to misinterpret the specification. This results in too
much trial and error on the customers part to get the message format right.
Another approach is to include sample message formats in the documentation. The
developer can then observe the samples and modify them to suit their companys needs.
This approach is slightly better in that the developer can probably get the message for-
mat correct with fewer iterations, but the customer still must do too much hand-crafted
code development. If each Web service required analysis, design, and coding to invoke,
the Web services approach would quickly pass into the dustbin of technology history.
168 Chapter 4 Describing Web Services
To make the job of invoking Web services easier for the service requestor, we need a
formal mechanism to describe the service. This formal approach provides unambiguous
specification of what the service requestor needs to do in order to invoke the Web serv-
ice. As a result of the formality of the service description, software tool developers can
provide tooling to automate the development of code to invoke Web services. Software
tools help developers build Web services quickly from shared, industry-accepted Web
service descriptions.
Role of Service Description in a Service-
Oriented Architecture
Service description is key within a service-oriented architecture (SOA). A service
description is involved in each of the three operations of SOA: publish, find, and bind.
Recall the SOA approach, depicted in Figure 4.1. The service provider publishes a
service description to one or more service registries. The description of the service is
published in service registries, not the actual code for the Web service itself. The service
provider uses a service description to tell the service requestor everything it needs to
know in order to properly understand how to invoke the Web service.
Service
Registry
Service
Requestor
Service
Provider
Bind
Find Publish
Figure 4.1 Service-oriented architecture
Similarly, the service description is central to the find operation. The service requestor
uses aspects of the service description; for example, you might be looking for a Web
service that implements a particular purchase order standard, as the basis for the find
query to a service registry. In Chapter 6, Discovering Web Services, we discuss service
registries at length and describe the find and publish operations in more detail. The result
of the find operation is ultimately a service description made available to the service
requestor.
169 Well-Defined Service
Why does the service provider publish a service description? To communicate to
service requestors how to invoke a Web service. Why does a service requestor want to
get hold of a service description? Because it describes exactly what needs to happen at
the bind operation. Service description is key to the bind operation, describing exactly
what message format needs to be delivered to what network address in order to invoke a
Web service.
Well-Defined Service
What makes a good service description? What aspects of the Web service must be
described in order for the Web service to be considered sufficiently defined?
A service description has two major components: its functional description and its
nonfunctional description. The functional description defines details of how the Web service
is invoked, where its invoked, and so on. This description is focused on details of the
syntax of the message and how to configure the network protocols to deliver the mes-
sage. The nonfunctional description provides other details that are secondary to the message
(such as security policy) but instruct the requestors runtime environment to include
additional SOAP headers (for example) or may influence whether a service requestor
would choose to invoke the Web service. An example of the latter may be a privacy poli-
cy statement.
Functional Description
The functional description of a Web service describes what operations are available on
the Web service and the syntax of the message required to invoke them; it defines the
Interface Definition Language (IDL) gfor the Web service description. The IDL for Web
services serves the same function as IDLs in other distributed computing approaches (see
the section History of IDLs). Fundamentally, its the functional description of the Web
service that determines what the service requestor needs to do in order to invoke a Web
service.
Like most things in Web services, XML is at the basis of service description. XML is
the type system in Web services. As you saw in Chapter 3, The SOAP Protocol, XML
describes the datatypes for the elements that flow within the SOAP message and, in par-
ticular, within the SOAP payload. This XML needs to be formatted by the service
requestor and interpreted by the service provider. Much of the effort within a Web serv-
ices infrastructure (like Axis, IBMs WebSphere, or Microsofts .NET) goes into properly
encoding and decoding XML elements to/from native programming language objects.
The functional description can be decomposed into two major pieces: the service
implementation definition gand the service interface definition g. Both use the Web
Services Description Language (WSDL) standard. Well describe the WSDL language in
more detail later in this chapter in the Web Services Description Language (WSDL)
section.
170 Chapter 4 Describing Web Services
The service implementation definition describes where the service is located or, more
precisely, to which network address messages must be sent in order to invoke the Web
service. The service interface definition describes exactly what messages need to be sent
and how to use the various messaging protocols and encoding schemes in order to for-
mat messages in a manner acceptable to the service provider.
Nonfunctional Description
The functional description is important, but there is more that should be described
about a Web service. Basically, a nonfunctional description can be characterized in con-
trast to the functional description. Whereas the functional description describes where to
send the message, how the core application-specific message syntax needs to appear, and
how to use the network transport protocols and message encoding schemes, the non-
functional description addresses several other things including why a service requestor
should invoke the Web servicefor example, what business function the Web service
addresses and how it fits into a broader business process. A nonfunctional description
may also give more details about who the service provider is. For example, does the serv-
ice provider provide auditing and ensure privacy? A nonfunctional description may also
augment the how aspect of the service description, adding details about security and so
on that arent part of the domain-specific or business-process-specific nature of the mes-
sage.
At the moment, the most widely adopted approach to describing nonfunctional
requirements is the combination of two specifications: WS-Policy and WS-
PolicyAttachment. Characteristics of the hosting environment could include the security
policy in place at the service providers endpoint, what levels of quality of service (QoS)
are available to support Web service invocation, what kind of privacy policy is observed
by the service provider, and so on. We go into more detail about these specifications later
in the chapter. At one level, you can think of the nonfunctional description as aspects of
the Web service that dont affect the shape of the application-specific parts of the mes-
sage. Nonfunctional descriptions might have some influence on the syntax of the mes-
sage (for example, adding security-related SOAP headers to the message), but the core,
application-specific content of the payload of the message is derived from the functional
description.
As well examine in Chapter 6, the UDDI service registry also has an impact on cer-
tain nonfunctional aspects of the service description. In particular, the taxonomy scheme
supported by UDDI is another mechanism by which a service provider can describe
what kind of service is being provided, what business function it supports, and what kind
of business is providing the service.
Description Summary
Currently, most of the work in the standards community has been devoted to establish-
ing and evolving the functional description of the service. The WSDL approach is well
established, is the basis for standardization work in the W3C, is the basis for Web services
171 History of Interface Definition Languages (IDLs)
runtime and tooling support from multiple vendors, and is used by the majority of Web
services developers.
The standards related to the nonfunctional description are still being developed. WS-
Policy and WS-PolicyAttachment exist, and work is under way to describe how these
general frameworks can be used to describe discipline-specific policies such as security,
QoS, and so on.
To summarize, a Web service is described using a combination of techniques. A Web
services description is used to unambiguously answer several questions about the Web
service. Table 4.1 summarizes these questions and how theyre addressed.
Table 4.1 Roles of Each Layer of the Service Description Stack
Question Where Addressed
Who Nonfunctional description
What Service interface
Where Service implementation
Why Nonfunctional description
How Service interface and nonfunctional description
For most of the remainder of this chapter, lets focus on the use of the WSDL 1.1 stan-
dard as the functional description of a Web service. Toward the end of the chapter, in the
WS-Policy section, well briefly revisit the nonfunctional layers of the service descrip-
tion. Well also examine the evolution of the WSDL 2.0 standardization activity within
the W3C.
History of Interface Definition Languages (IDLs)
Before we dive into the WSDL discussion, a little background might be helpful. Every
distributed computing approach has a mechanism for describing interfaces to compo-
nents. Lets examine a brief history of IDLs.
IDLs have a long history in distributed computing. The major use of IDL came as
part of the Open Software Foundations Distributed Computing Environment (DCE) in
its specification of RPC in 1994. DCE IDL was a breakthrough concept that quickly
spread to other distributed computing initiatives, such as Object Management Groups
(OMG) CORBA IDL and Microsofts COM IDL and COM ODL (Object Definition
Language). As with most such technologies, the various flavors of IDL are slightly differ-
ent and, therefore, are more or less incompatible. WSDL brings unity to the Web services
brand of distributed computing.
Most people used to developing simple software say, Why bother defining the inter-
faces of any software operations? Just get a pointer/reference to an object or a function
and make the call.The reason is that if a software system has even the slightest amount
of heterogeneity, this simple approach wont work.
172 Chapter 4 Describing Web Services
The best way to approach these problems is to agree on a bridging strategy. This strat-
egy establishes common ground in the middle (the bridge) without worrying about how
the roads at the endpoints are constructed. In distributed computing, a bridging strategy
involves two parts:
1. Agreeing on how to make an invocation: the mechanics of naming, activation, data
encoding, error handling, and so on. This is what distributed computing standards
such as DCE, CORBA, and COM do.
2. Specifying what to call: the operation names, their signatures, return types, and any
exceptions that they might generate. This is the job of IDL.
In a typical distributed computing architecture, a tool called an IDL compiler combines
the information in an IDL file together with the conventions on how to make invoca-
tions to code-generate the pieces that make the bridge work. The client that wants to
invoke operations uses a client proxy g(sometimes called a client stub). The proxy has
the same interface as the operation provider. It can be used as a local object on the
client. The proxy implementation knows how to encode and marshal the invocation data
to the operation provider and how to capture the operation result and return it to the
client. The operation provider wraps its implementation inside a skeleton g(sometimes
called a server stub). The skeleton knows how to capture the data sent by the proxy and
pass it to the implementation. It also knows how to package the result of operations and
send it back to the client. Proxies and skeletons are helped by sophisticated distributed
computing middleware. A key part of the story is that proxies and skeletons need not be
generated by the same IDL compilers, as long as these compilers are following the same
distributed computing conventions. This is the power of IDLit describes everything
necessary to make invocation possible in a distributed environment.
DCE IDL specified flat function interfaces. There was no notion of object instance
contexts when making calls. CORBA IDL changed that by adding many important
extensions to IDL. CORBAs IDL is the de facto IDL standard on non-Microsoft envi-
ronments. Its also standardized internationally as ISO/IEC 14750.
CORBAs IDL is purely declarative; it provides no implementation details. It defines a
remote object API concisely (the spec is less than 40 pages long) and covers key issues
such as naming, complex type definition, in/out/in-out parameters, and exceptions. The
syntax is reminiscent of C++ with some keywords to cover additional concepts. This
IDL supports the notion of inheritance, which makes it convenient to describe object-
oriented distributed systems. CORBA IDL even supports the notion of multiple inter-
face inheritance, as in MyPetTurtle deriving from both Pet and Animal.
In a CORBA-enabled environment, the IDL can be used to invoke the CORBA
object via dynamic invocation from a scripting language, generate proxies for client
access to the object, generate implementation skeletons to be plugged into the CORBA
middleware, and store information about the implementation in an interface repository g
(a central store of meta-data about CORBA components interfaces)all without being
tied to a specific implementation language.
173 Web Services Description Language (WSDL)
Microsofts ODL isnt syntax-compatible, or even concept-compatible, with CORBA
IDL. However, apart from syntactical differences, COM objects can be used in a fashion
similar to CORBA objects.
Programmers working with modern languages, such as Java, C#, and any other .NET
Common Language Runtime (CLR) languages can typically engage in distributed com-
puting applications without having to worry much about IDL. Has IDL become irrele-
vant in these cases? Not at all! IDL isnt present on the surface, but IDL concepts are
working behind the scenes.
Both Java and the CLR languages are fully introspectable. This means a compiled lan-
guage component carries complete meta-data about itself, such as information about its
parent class, properties, methods, and any supported interfaces. This information is suffi-
cient to replace the need for an explicit IDL description of the component. That is why,
for example, Java developers can invoke the RMI compiler directly on their object with-
out having to generate IDL first.
However, in the cases where these languages need to interoperate with components
built using other programming languages, there is no substitute for IDL. Separating
interfaces from implementations is the only guaranteed mechanism for ensuring the
potential for interoperability across programming languages, platforms, machines, address
spaces, memory models, and object versions. On the Web, where heterogeneity is the
rule, this is more important than ever.
Web Services Description Language (WSDL)
WSDL is used to describe the message syntax associated with the invocation and
response of a Web service. WSDL version 1.1 was submitted to the W3C for standardiza-
tion by IBM, Microsoft, and a number of other companies in March 2001. The current
version of the specification is available at http://www.w3.org/TR/wsdl. WSDL 1.1
forms the basis for the current standards work in this area, WSDL version 2.0. Well dis-
cuss WSDL 2.0 later in this chapter.
A WSDL service description is an XML document conformant to the WSDL schema
definition. A WSDL 1.1 document, without any extensions, isnt a complete service
description, but rather it covers the functional description of the service. WSDL is the
IDL for Web services. Essentially, a WSDL description describes three fundamental prop-
erties of a Web service:
n
What a service doesThe operations (methods) the service provides, and the data
(arguments and returns) needed to invoke them
n
How a service is accessedDetails of the data formats and protocols necessary to
access the services operations
n
Where a service is locatedDetails of the protocol-specific network address, such
as a URL
174 Chapter 4 Describing Web Services
WSDL Information Model
The WSDL information model takes full advantage of the separation between abstract
specifications and concrete implementations of these specifications. This reflects the split
between service interface definition (abstract interface) and service implementation defi-
nition (concrete implementation at a network endpoint).
Like all good applications of XML, the WSDL schema defines several high-level or
major elements in the language. Lets look at Web service description in terms of the
major elements in WSDL:
n
portTypeA Web services abstract interface definition (think Java interface defi-
nition) where each child operation element defines an abstract method signature.
n
messageDefines the format of the message, or a set of parameters, referred to by
the method signatures or operations. A message can be further decomposed into
parts (think detailed method parameter format definitions).
n
typesDefines the collection of all the datatypes used in the Web service as refer-
enced by various message part elements (think base datatypes; think XML).
n
bindingContains details of how the elements in an abstract interface
(portType) are converted into a concrete representation in a particular combina-
tion of data formats and protocols (think encoding schemes; think SOAP over
HTTP).
n
portExpresses how a binding is deployed at a particular network endpoint
(think details about a particular server on a particular network location; think place
where you specify HTTP URL).
n
serviceA poorly named element. A named collection of ports (think arbitrary
bag of Web services endpoints).
So the portType (with details from the message and type elements) describes the what
of the Web service. The binding describes the how, and WSDL port and service
describe the where of the Web service.
Figure 4.2 shows one perspective on the relationships between the elements in
WSDL.
The figure shows one possible view of the organization of the WSDL information
model.You can see a clear relationship between the abstract and concrete notions of
message and operation as contained in the portType and binding. The words in bold on
the diagram signify the terms from the WSDL specification. The element names used in
WSDL are somewhat confusing because no consistent naming convention allows you to
distinguish between abstract and concrete concepts. For example, portType could have
been called AbstractInterface and binding called ConcreteInterface.You have to
memorize which elements represent abstract concepts and which elements represent
concrete concepts.
At first glance, WSDL seems complicated. Dont worry; well explain the components
in more detail. Part of this appearance is due to the factoring chosen by the WSDL
175 Web Services Description Language (WSDL)
authors. This complexity feels a lot like the complexity observed with a highly
normalized relational data model. WSDL 2.0 reduces some of this complexity (but adds
different complexity of its own). Although the same information might be more suc-
cinctly expressed, the flexibility that results from the factoring in WSDL is occasionally
necessary. So, just as you might have learned to understand highly normalized relational
models, practice with reading WSDL documents will help you to focus on the important
aspects of a Web service description.
(abstract)
operation
(abstract)
operation
type part
(abstract)
operation
abstract interface
portType
(abstract)
message
(abstract)
message
(abstract)
message
(abstract)
operation
(abstract)
operation
(concrete)
operation
concrete
implementation
binding
(abstract)
message
(abstract)
message
(concrete)
message
service
concrete endpoint
port
made
concrete by
contains zero
or more
Figure 4.2 The WSDL information model
Origin of WSDL
WSDL wasnt the first IDL language for Web services. IBM developed a language called Network Accessible
Service Specification Language (NASSL) to further early internal adoption of SOAP. Meanwhile, Microsoft
developed SOAP Contract Language (SCL), which was an evolution of Microsofts earlier attempt at a SOAP
IDL called Service Definition Language.
IBM and Microsoft realized that having competing IDLs would hinder rapid adoption of Web services. WSDL
is the result of hard work and compromise in merging NASSL and SCL. As a result of this merger, a single
ubiquitous mechanism describes the interface definition of Web services: WSDL.
176 Chapter 4 Describing Web Services
Parts of the WSDL Language
Lets take a closer look at the parts of a WSDL description. Although we examine these
elements in detail, you dont have to become an expert in WSDL; the software industry
continues to churn out tools to generate WSDL from existing IT assets like COM
objects and Enterprise JavaBeans (EJBs) and to generate client-side stubs or proxies
(access mechanism helper classes) from WSDL to ease the burden of invoking Web serv-
ices by the service requestor. Later in this chapter, well sketch how WSDL translates to
Java; and in Chapter 5, Implementing Web Services with Apache Axis, well examine
the WSDL tooling available in Axis. However, by reviewing WSDL, you will get a good
background in case the tools dont generate exactly the WSDL or Java to fit your partic-
ular needs. In certain circumstances, WSDL descriptions must be hand crafted, and client
and Web service implementation code must be manually developed.
Lets discuss the WSDL language in the context of a WSDL example. Well start with
a simple use of WSDL: SkatesTowns priceCheck service. Later, well examine more
advanced WSDL concepts by reviewing SkatesTowns StockAvailableNotification
service and the POSubmission service. When we review SkatesTowns use of WSDL,
well comment on various best practices with WSDL design. In particular, well com-
ment on various points raised by Web Service Interoperability (WS-I) Organization that
clarify how to build interoperable WSDL descriptions of Web services. We cover WS-I in
more detail in Chapter 13, Web Services Interoperability.
This chapter will help get you started understanding WSDL. The best way to learn
WSDL is to examine a collection of WSDL documents.You can find an excellent reg-
istry of WSDL documents at the SalCentral Web services brokerage (http://www.
salcentral.com) or the XMethods Web site (http://www.xmethods.net). After a little
practice, reading a WSDL document will become as familiar as reading Java code.
Simple WSDL
The priceCheck service was built in response to growing demand from customers to
extend the inventoryCheck Web service to include price information as well as avail-
ability. The priceCheck Web service is an extension of the inventoryCheck Web service,
allowing a requestor to determine the price of one of SkatesTowns products. Its invoked
with a product SKU number, and the response is a price and the number of units of that
item currently available from inventory. The entire priceCheck WSDL document is
shown in Listing 4.1.
Listing 4.1 The priceCheck WSDL Document
<?xml version=1.0?>
<definitions name=PriceCheck
targetNamespace=http://www.skatestown.com/services/PriceCheck
xmlns:pc=http://www.skatestown.com/services/PriceCheck
xmlns:avail=http://www.skatestown.com/ns/availability
xmlns:wsi=http://ws-i.org/schemas/conformanceClaim/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
177 Web Services Description Language (WSDL)
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns=http://schemas.xmlsoap.org/wsdl/>
<!-- Type definitions -->
<types>
<xsd:schema
targetNamespace=http://www.skatestown.com/ns/availability >
<xsd:element name=sku type=xsd:string />
<xsd:complexType name=availabilityType>
<xsd:sequence>
<xsd:element ref=avail:sku/>
<xsd:element name=price type=xsd:double/>
<xsd:element name=quantityAvailable type=xsd:integer/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name=StockAvailability
type=avail:availabilityType />
</xsd:schema>
</types>
<!-- Message definitions -->
<!-- A PriceCheckRequest is simply an item code (sku) -->
<message name=PriceCheckRequest>
<part name=sku element=avail:sku/>
</message>
<!-- A PriceCheckResponse consists of an availability structure, -->
<!-- defined above in the types element. -->
<message name=PriceCheckResponse>
<part name=result element=avail:StockAvailability/>
</message>
<!-- Port type definitions -->
<portType name=PriceCheckPortType>
<operation name=checkPrice>
<input message=pc:PriceCheckRequest/>
<output message=pc:PriceCheckResponse/>
</operation>
</portType>
<!-- Binding definitions -->
<binding name=PriceCheckSOAPBinding type=pc:PriceCheckPortType>
Listing 4.1 Continued
178 Chapter 4 Describing Web Services
<soap:binding
style=document transport=http://schemas.xmlsoap.org/soap/http />
<operation name=checkPrice>
<soap:operation
soapAction=http://www.skatestown.com/services/PriceCheck/checkPrice />
<input>
<soap:body use=literal />
</input>
<output>
<soap:body use=literal />
</output>
</operation>
</binding>
<service name=PriceCheck>
<port name=PriceCheck binding=pc:PriceCheckSOAPBinding>
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</port>
</service>
</definitions>
Structure of a WSDL Document: Definitions
The root element of a WSDL document is a definitions element. A WSDL defini-
tions contains all the other WSDL elements (well examine each element in its own
section). A definitions element can contain the following (in recommended order of
appearance):
n
Zero or more documentation elements
n
Zero or more import elements
n
Optionally, one types element
n
Zero or more message elements
n
Zero or more portType elements (usually just one)
n
Zero or more binding elements (usually just one, for the portType element)
n
Zero or more service elements (again, usually just one)
A WSDL document must conform to the XML Schema defined for the WSDL language
and available at http://schemas.xmlsoap.org/wsdl/2003-02-11.xsd.
The definitions element contains a name attribute (the name usually corresponds to
the name of the Web service), which is purely for purposes of documentation. The def-
initions element also defines the targetNamespace attribute (a namespace URI) for
Listing 4.1 Continued
179 Web Services Description Language (WSDL)
the entire WSDL file. This targetNamespace attribute is critical to understanding how
to form QNames of portTypes, bindings, and so on, and how to combine WSDL
descriptions that span multiple files. Of course, the usual XML namespace (xmlns:) dec-
larations also typically appear in the definitions element.
The following is the definitions element from the priceCheck WSDL:
<?xml version=1.0?>
<definitions name=PriceCheck
targetNamespace=http://www.skatestown.com/services/PriceCheck
xmlns:pc=http://www.skatestown.com/services/PriceCheck
xmlns:avail=http://www.skatestown.com/ns/availability
xmlns:wsi=http://ws-i.org/schemas/conformanceClaim/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns=http://schemas.xmlsoap.org/wsdl/>
. . .
</definitions>
Except for the value of the name attribute targetNamespace attribute and the various
xmlns: declarations, youll see this pattern at the beginning of every WSDL document.
PortType
The best starting point to understanding a WSDL document is the portType element.
The portType describes the interface to a Web service. Just as a Java interface definition
is comprised mainly of method signatures, the interesting part of a portType definition is
the collection of operation elements it contains.
This is the most succinct description of what the service does; understand the
portType, and you understand what the Web service does. The rest of the elements in
the WSDL definition are essentially details that the portType depends on; well examine
them in later sections.
The portType in the priceCheck service description looks like this:
<!-- Port type definitions -->
<portType name=PriceCheckPortType>
<operation name=checkPrice>
<input message=pc:PriceCheckRequest/>
<output message=pc:PriceCheckResponse/>
</operation>
</portType>
The priceCheck portType is about as simple as portTypes can get. It defines one oper-
ation: checkPrice. Well study the checkPrice operation in more detail in the next sec-
tion, but first, a few more thoughts about portTypes.
A WSDL document can contain zero or more portType definitions. Typically, WSDL
documents contain a single portType. Using this convention separates different Web
service interface definitions into different documents. This granularity of separation is
180 Chapter 4 Describing Web Services
good for reasons of reuse; when we discuss how UDDI can be used to register WSDL
documents (in Chapter 6), it will become apparent why this is a best practice.
A portType has a single name attribute. In our case, the priceCheck Web service con-
tains a portType of name PriceCheckPortType. Often, youll see the name of the
portType follow this pattern: nameOfWebServicePortType. If a WSDL file contains mul-
tiple portTypes, each portType must have a different name. The name of the portType
together with the namespace of the WSDL document (refer to the discussion of the
definitions element) define a unique name for the portType. This unique name, or
QName, is the way the portType is referenced by other components of WSDL.
Simple, well-factored Web services often result in simple looking portTypes. Much of
the detail is in the rest of the elements in the WSDL definition; and as far as the
portType element is concerned, the detail is in the collection of operation child
elements.
Operation
An operation in WSDL is the equivalent of a method signature in Java. An operation
defines a method on a Web service, including the name of the method and the input
parameters and the output or return type of the method.
The PriceCheck portType describes one operation, named (cleverly) checkPrice.
The checkPrice operation defines an input message and output message. If we invoke
the checkPrice operation with a priceCheckRequestMessage (youll see what these
messages look like in the next section), the Web service returns a
priceCheckResponseMessage. Note that the input elements and output elements are
associated with a message and use the QName style of referencing. As it turns out, mes-
sages also have QNames.
Note
When youre designing WSDL portTypes, you should be careful with the names of the portTypes
operations: They should all be different.
The operation elements define a combination of input, output, and fault messages. The
different combinations are the subject of advanced WSDL concepts covered later in this
chapter.
Message
A message is a simple concept. Its the construct that describes the abstract form of an
input, output, or fault message. The message is modeled either as an XML document
(document-centric) or as the parameters in a method call (RPC-style messaging).
A WSDL document can contain zero or more message elements. Each message has a
name, which must be unique within the WSDL document, and contains a collection of
part elements.
181 Web Services Description Language (WSDL)
Lets look at the messages defined in the priceCheck WSDL:
<!-- Message definitions -->
<!-- A PriceCheckRequest is simply an item code (sku) -->
<message name=PriceCheckRequest>
<part name=sku element=avail:sku/>
</message>
<!-- A PriceCheckResponse consists of an availability structure, -->
<!-- defined above in the types element. -->
<message name=PriceCheckResponse>
<part name=result element=avail:StockAvailability/>
</message>
The first message, PriceCheckRequest, is a simple message element. Recall that
PriceCheckRequest is used as the input message to the checkPrice operation. The
PriceCheckRequest message defines one part element named sku.
Part
Message elements arent terribly interesting; theyre just collections of parts. However,
parts are quite interesting. The parts mechanism in WSDL allows a message to be
decomposed into smaller units or parts. Each part can be manifested in different ways
depending on the network protocol chosen for the Web service. This mapping between
parts and protocol-specific components is described in the binding element. Some
parts can appear as SOAP header elements, some can be mapped to HTTP headers,
and some can be used as individual parameters in an RPC message or to form the body
of a document-centric message.You cant understand the true use of a part until you
look at how the abstract notion of the part is mapped into a concrete data representa-
tion. This mapping is the responsibility of the binding element. Well examine how dif-
ferent parts are modeled as different components of a message later in this chapter.
A part element is made up of two properties: the name of the part and the kind of
the part. The name property is represented by the name attribute, which must be unique
among all the part child elements of the message element. The kind property of the
part is defined as either a type attribute (a simpleType or complexType from the XSD
schema type system) or an element attribute (also defined by referring to an element
defined in XML schema).You choose one of these attributes to represent the kind of
part being described.You cant use both an element attribute and a type attribute to
define the same part. (Well examine XML Schema elements and types in more detail in
the next section.) Often, the name of the part says it all, and you need not dive into the
details of how the types associated with the part are modeled. As youll see later, the sku
part will turn into a method parameter in the service invocation.
The choice of using element or type when defining the part is very important.
When you use the element attribute, youre specifying that the payload of the message
on the wire should be precisely that XML element. For example, the
182 Chapter 4 Describing Web Services
PriceCheckRequest example (listed earlier) specifies that the XML element named sku
from the http://www.skatestown.com/ns/availability namespace must be used as
the body of the message. Heres an example of what a PriceCheckRequest message
would look like on the wire:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<sku xmlns=http://www.skatestown.com/ns/availability>123</sku>
</soapenv:Body>
</soapenv:Envelope>
Note how the body contains exactly the specified sku element. The element attribute
should be used to define the type of the part when the Web service is meant to be doc-
ument oriented. (Well discuss how to specify a document-oriented Web service later in
this chapter when we review the WSDL binding element.) Whenever the element
attribute is used, its value must refer to a global XML element declared (or imported) in
an XML schema in the types section.
The only other message element defined in the priceCheck WSDL is
PriceCheckResponse, which describes the format of the output message of the
checkPrice operation. Like its companion message, PriceCheckResponse defines a sin-
gle part. Heres what a PriceCheckResponse message looks like on the wire:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<StockAvailability xmlns=http://www.skatestown.com/ns/availability>
<sku>123</sku>
<price xmlns=>100.0</price>
<quantityAvailable xmlns=>12</quantityAvailable>
</StockAvailability>
</soapenv:Body>
</soapenv:Envelope>
Note how the response message contains exactly what the WSDL message said it would,
an element named StockAvailability from the http://www.skatestown.com/ns/
availability namespace.
The PriceCheckRequest example could have been built differently, using a part
defined with the type attribute, like this:
<!-- Message definitions -->
<!-- A PriceCheckRequest is simply an item code (sku) -->
183 Web Services Description Language (WSDL)
<message name=PriceCheckRequest>
<part name=sku type=xsd:string/>
</message>
In this case, we dont explicitly indicate that a particular XML element is required to be
the sku in the PriceCheckRequest message on the wire. Rather, the element must have
xsd:string as its type. As youll see later, the element is determined by the binding.
Heres an example of a valid PriceCheckRequest request message, using the RPC-style
suggested by this form of the PriceCheckRequest message:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<ns1:checkPrice soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xmlns:ns1=http://www.skatestown.com/services/PriceCheck>
<sku xsi:type=xsd:string>123</sku>
</ns1:checkPrice>
</soapenv:Body>
</soapenv:Envelope>
The PriceCheckResponse example could also have been built using a part defined with
the type attribute. This form of the message would look like this:
<!-- A PriceCheckResponse consists of an availability structure, -->
<!-- defined above. -->
<message name=PriceCheckResponse>
<part name=result type=avail:availabilityType/>
</message>
The result part is slightly more interesting because its type is any element whose
complex type is the availabilityType from the namespace corresponding to the
avail: prefix. Heres an example of a valid response message:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<ns1:checkPriceResponse
soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xmlns:ns1=http://www.skatestown.com/services/PriceCheck>
<result href=#id0/>
</ns1:checkPriceResponse>
<multiRef id=id0
soapenc:root=0
184 Chapter 4 Describing Web Services
soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xsi:type=ns2:availabilityType
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/
xmlns:ns2=http://www.skatestown.com/ns/availability>
<sku xsi:type=xsd:string>123</sku>
<price xsi:type=xsd:double>100.0</price>
<quantityAvailable xsi:type=xsd:integer>12</quantityAvailable>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
In this case, an element named result and the contents of this element are defined
using an href to the actual contents.
You can see that the separation of messages from the operation is well factored, but
its overkill for our simple SkatesTown examples. Many WSDL documents dont require
the full power achieved by factoring messages from operations and decomposing mes-
sages into parts. Some uses of WSDL (for example, those that describe multipart
MIME messages) exploit the flexibility of the WSDL part mechanism.
Types
Youve seen the message element in WSDL and its parts. However, some of the types
used in these example WSDL documents need further discussion.
The default type system in WSDL is XML Schema (XSD). To build interoperable
Web services, the WSDL should only use datatypes defined using XML Schema. XML
Schema can be used to describe all the types used in the messages to invoke a Web
service.
The types element in the priceCheck WSDL is typical of the use of this element:
<!-- Type definitions -->
<types>
<xsd:schema
targetNamespace=http://www.skatestown.com/ns/availability >
<xsd:element name=sku type=xsd:string />
<xsd:complexType name=availabilityType>
<xsd:sequence>
<xsd:element ref=avail:sku/>
<xsd:element name=price type=xsd:double/>
<xsd:element name=quantityAvailable type=xsd:integer/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name=StockAvailability
type=avail:availabilityType />
</xsd:schema>
</types>
185 Web Services Description Language (WSDL)
The types element is essentially a place for the WSDL document to define user-defined
XML types and elements for later use in the parts elements. A WSDL document can
have at most one types element. When a types element appears in a WSDL document,
it typically contains a single schema definition element, as you saw with the priceCheck
WSDL. However, its legal (but not recommended) to have more than one schema ele-
ment contained by a types element.
Note
Many organizations have modeled their data objects as XML schemas. Because WSDL types is defined to
contain schema definition element(s), WSDL can import these definitions, and you dont have to duplicate
work already done in XML schema. Business objects already modeled in XML can be imported and then used
to define parts of messages and, therefore, used to define the input and output messages for the Web
services operations. When you want to use existing XML types and elements in your WSDL, use an XML
schema import element within the types element.
Many WSDL documents dont include a types element with much more than an
import statement. Consider the following types element from a POSubmission Web
service:
<!-- Type definitions -->
<types>
<xsd:schema>
<!-- rest of invoice schema definition from chapter 2
assumes XSD file is in same directory -->
<xsd:import namespace=http://www.skatestown.com/ns/invoice
schemaLocation=http://www.skatestown.com/schema/invoice.xsd/>
<!-- rest of purchaseOrder schema definition from chapter 2
assumes XSD file is in same directory -->
<xsd:import namespace=http://www.skatestown.com/ns/po
schemaLocation=http://www.skatestown.com/schema/po.xsd/>
</xsd:schema>
</types>
All this schema defines is two import statements. Now any of the datatypes and elements
defined in the invoice.xsd and po.xsd XML Schema files can be used to define parts
in this WSDL file. The schemaLocation attribute indicates that these files can be found
in the same directory as the WSDL file. Note that unlike the types element from the
priceCheck WSDL, this WSDL doesnt define a targetNamespace as part of its schema
element. This is good practice only when the schema element contains just import ele-
ment children. If you use the schema element in the types part of your WSDL to define
XML datatypes or elements, put a targetNamespace attribute on it. Many WSDL
designers use the same targetNamespace for their schema element as they do for the
entire WSDL definitions element.
186 Chapter 4 Describing Web Services
For the priceCheck WSDL, the availability type is defined by a schema element
within a WSDL types element. Recall that the availability type was used as part of
the PriceCheckResponse message. This is all standard XML Schema work that we cov-
ered in Chapter 2, XML Primer.You can use any XML Schema construct within the
schema element defined in a WSDL types element.
WSDL is quite flexible in the type system used. Although XML Schema is the pre-
dominant type system used, the types element allows you the flexibility to describe a
completely different type system. WSDL types lets you describe another type system, say
the Java type system, and define all the messages in terms of this type system. The use of
this flexibility is theoretical; its rare in practice. Consider that deviating from the XML
schema type system increases the chances that more service requestors will be unable to
invoke your Web service.
Note
There are several variants of XML schema; schema has been under development by the W3C for several
years. The XML Schema version referenced in the listings is http://www.w3.org/2001/
XMLSchema. You might also encounter other versions used in types elements such as
http://www.w3.org/2000/10/XMLSchema and http://www.w3.org/1999/XMLSchema.
These declarations have subtle implications on some sophisticated use of the XML schema language. Consult
advanced resources on XML schema for more detail. As time moves on, uses of these older versions of
schema become rarer. To make sure your Web service is maximally interoperable, use the http://www.
w3.org/2001/XMLSchema version.
Binding
Youve seen that a portType, like the PriceCheckPortType, defines one or more
operations, and you have some idea about the sorts of XML types and elements these
operations need as input and produce for output. However, you still dont know how to
format the message to invoke these operations. We havent seen anything in the WSDL
description that relates to SOAP headers, SOAP bodies, SOAP encoding, and so on. The
portType, message, and type elements define the abstract or reusable portion of the
WSDL definition. Is this service invoked by a SOAP message or a simple HTTP POST
of an XML payload? Is this an RPC invocation or a document-centric message invoca-
tion? These details are given by one or more binding elements associated with the
portType.
The binding element in WSDL tells the service requestor how to format the mes-
sage in a protocol-specific manner. Each portType can have one or more binding ele-
ments associated with it. For a given portType, a binding element can describe how to
invoke operations using a particular messaging/transport protocol, like SOAP over
HTTP, SOAP over SMTP, a simple HTTP POST operation, or any other valid combi-
nation of networking and messaging protocol standards.
187 Web Services Description Language (WSDL)
Lets look at the binding element in the priceCheck WSDL:
<binding name=PriceCheckSOAPBinding type=pc:PriceCheckPortType>
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/http />
<operation name=checkPrice>
<soap:operation
soapAction=
http://www.skatestown.com/services/PriceCheck/checkPrice />
<input>
<soap:body use=literal />
</input>
<output>
<soap:body use=literal />
</output>
</operation>
</binding>
The name of this binding element is PriceCheckSOAPBinding. The name must be
unique among all the binding elements defined in the WSDL document.
Conventionally, the name of the binding combines the portType name with the
name(s) of the protocol(s) to which the binding maps.You often see the word binding
appended to the name. The type attribute identifies which portType this binding
describes. Because WSDL uses a QName reference to link the binding element to a
portType, you can see why portType name uniqueness is so important.Youll see the
same is true for binding name uniqueness when we discuss how a port references a
binding.
Typically, most WSDL documents contain only a single binding. The reason is similar
to why conventional WSDL documents contain a single portType element: convenience
of document reuse.
Now, to which protocol is this binding mapping the priceCheck portType? We
need to look for clues inside the binding element (besides the naming convention, of
course). The first clue is the XML namespace of the first child element (the prefix of the
element is soap:). This is a strong hint that this binding is related to the SOAP messag-
ing protocol. So, the PriceCheckSOAPBinding element describes how the priceCheck
portType (remember, priceCheck is an abstract service interface definition) is expressed
using SOAP. Most Web services you see in practice have at least a SOAP binding
defined for them.You can define other bindings in addition to the SOAP/HTTP
binding.
WSDL defines a clever extensibility convention that allows the binding to be
extended with elements from different XML namespaces to describe bindings to any
number of messaging and transport protocols. Pick a messaging/transport protocol set,
find the WSDL convention that corresponds to that pair, and fill in the details. The
WSDL spec defines three standard binding extensions for SOAP/HTTP, HTTP
GET/POST, and SOAP with MIME attachments.
188 Chapter 4 Describing Web Services
The PriceCheckSOAPBinding element shown earlier decorates the priceCheck
portType in four ways (invocation style, SOAPAction, input message appearance, and
output message appearance), as explained in the following sections. This is a straightfor-
ward use of the SOAP binding extension convention defined in the WSDL specification.
Well examine more advanced features of the binding element later in this chapter.
Invocation Style
The first use of the SOAP binding extension indicates the style of invocation:
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/http/>
This declaration applies to the entire binding. It indicates that all operations for the
priceCheck portType are defined in this binding as SOAP messages. Further, the style
attribute indicates that operations will follow a document-centric approach, meaning the
body of the SOAP message is to be interpreted as straight XML; this is contrasted with
the other alternative value for the style attribute (rpc), which indicates that binding
uses the Remote Procedure Call (RPC) conventions for the SOAP body as defined in
the SOAP specification. The default style for the binding can be explicitly overridden
by a style declaration in any child operation element. However, its strongly recom-
mended that you dont override the style value. Practice has shown that overriding the
style of the binding leads to interoperability problems.
With the document style comes some restrictions.You should only use a document
style binding for certain kinds of portTypesthose that reference messages that con-
tain parts using the element attribute. If a portType references a message whose parts
use the type attribute, you should define only RPC bindings for it. Note that this is an
example where the separation between abstract elements and concrete elements in
WSDL isnt clean.
PriceCheckRequest and PriceCheckResponse messages only use the element attrib-
ute in their parts; this allows SkatesTown to define a document-style binding.
The transport attribute tells you that the requestor must send the SOAP message
using HTTP. Other possible values for this attribute could include http://
schemas.xmlsoap.org/soap/SMTP/, http://schemas.xmlsoap.org/soap/ftp/, or any
other URI. For interoperability reasons, each portType should have one SOAP/HTTP
binding defined. In the Web services world, almost all clients can participate in a proto-
col defined by SOAP using HTTP.
SOAPAction
The second use of the SOAP binding extension is
<operation name=checkPrice>
<soap:operation
soapAction=
http://www.skatestown.com/services/PriceCheck/checkPrice />
189 Web Services Description Language (WSDL)
This declaration indicates the value that should be placed in the SOAPAction HTTP
header as part of the HTTP message carrying the priceCheck service invocation mes-
sage. As we discussed in Chapter 3, the purpose of the SOAPAction header is to describe
the intent of the message. In the case of the checkPrice operation, the WSDL tells the
service requestor to put exactly the http://www.skatestown.com/services/
PriceCheck/checkPrice string as the value for the SOAPAction header. Heres an exam-
ple HTTP header for a priceCheckRequest message:
POST /pc/services/PriceCheck HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.0
Host: localhost:9080
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: http://www.skatestown.com/services/PriceCheck/checkPrice
Content-Length: 334
Note that a SOAPAction header whose value is an empty string indicates that the URI
of the request is the place where the intent of the message is to be found. If the
SOAPAction value is empty, no intent of the message is to be found. Better conventions
with SOAP would require the requestor to put an interesting value as the SOAPAction
to help SOAP routers dispatch the message to the appropriate Web service. When bind-
ing different operations in the same portType, you can assign different SOAPAction
headers for each operation.
Input Message Appearance
The third use of the SOAP binding extension is to describe what the input message
should look like, including specifying the SOAP body and SOAP header components of
the message. The priceCheck WSDL document describes how the input message to the
checkPrice appears in the parts of the SOAP message:
<input>
<soap:body use=literal />
</input>
The entire input message, PriceCheckRequest (from the portType declaration for the
checkPrice operation), is declared to have exactly one component (a SOAP body); and
the content of that SOAP body is literally a single XML element (there is only one part
defined for the PriceCheckRequest message) named sku, from the namespace
http://www.skatestown.com/ns/availability. Heres an example
PriceCheckRequest message, illustrating this SOAP body:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
190 Chapter 4 Describing Web Services
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<sku xmlns=http://www.skatestown.com/ns/availability>123</sku>
</soapenv:Body>
</soapenv:Envelope>
WSDL bindings whose style is document should contain only soap:body elements with
a use attribute of literal. WSDL message elements intended for document-style
bindings must contain parts that only use the element attribute to define the type of
the part. If a type attribute appears on the part, it isnt meant for a document-style
binding.
Output Message Appearance
The fourth use of the SOAP binding extension in our example describes how the out-
put message of checkPrice should appear. Nothing new is introduced with this exam-
ple. For completeness, heres an example response that corresponds to the pattern
described in the binding element for the output of the checkPrice operation:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<StockAvailability xmlns=http://www.skatestown.com/ns/availability>
<sku xsi:type=xsd:string>123</sku>
<price xmlns=>100.0</price>
<quantityAvailable xmlns=>12</quantityAvailable>
</StockAvailability>
</soapenv:Body>
</soapenv:Envelope>
</SOAP-ENV:Envelope>
Just like the PriceCheckRequest message, the output message of the checkPrice
operation (PriceCheckResponse) is literal. The contents of the SOAP body is an ele-
ment corresponding to an XML global element declaration named StockAvailability
in the namespace identified by http://www.skatestown.com/ns/availability.
The only missing piece is the network address: To what URL do we send the mes-
sage? These details are given in the WSDL port and service.
Port
The port in WSDL is simple. Its only purpose is to specify the network address of the
endpoint hosting the Web service. More precisely, the port associates a single protocol-
specific address to an individual binding. Port elements are named, and the name must
191 Web Services Description Language (WSDL)
be unique among all the ports within a WSDL document. The port element for the
priceCheckSOAPBinding is:
<port name=PriceCheck binding=pc:PriceCheckSOAPBinding>
...
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</port>
This port indicates the URL to which messages should be sent in order to invoke the
priceCheck operations over SOAP. Note the soap:address element; this is another
aspect of the SOAP extension to WSDL. Most of the extension is in the binding, and
this is the only part of the extension outside the binding.
The port doesnt stand alone; port elements are children of a WSDL service.
Service
The purpose of a WSDL service is to contain a set of related portsnothing more.
Although a WSDL document can contain a collection of service elements, convention-
ally a WSDL document contains a single service. Each service is named, and each
name must be unique among all the services in the WSDL document. The following
shows the entire service for the priceCheck Web service:
<service name=PriceCheck>
<port name=Pricecheck binding=pc:PriceCheckSOAPBinding>
...
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</port>
</service>
It seems like a lot of bother to waste all these elements to express a group of ports. Why
would you group several ports together into a service?
One reason is to group the ports that are related to the same service interface
(portType) but expressed by different protocols (bindings). For example, if the
priceCheck portType was implemented in two ways, one using SOAP over HTTP and
another using SOAP over SMTP, a single service could contain the port describing
the URL for the SOAP/HTTP network endpoint and the port describing the email
address for the SOAP/SMTP network endpoint. Its a good convention to have a differ-
ent address for each port in a service.
Another reason might be to group related but different portTypes together. This,
however, isnt very interoperable and, as youll see in our examination of WSDL 2.0,
doesnt move forward into the upcoming version of the language.
However, no standard describes or documents the meaning of any aggregation of
ports within a service. Largely for this reason, most services contain only one port.
That is the bulk of the important elements in a WSDL definition. Well now examine
a few other elements that appear in a WSDL document.
192 Chapter 4 Describing Web Services
Documentation
The documentation element is used to provide useful, human-readable information
about the Web service description. One conventional use is to declare that the WSDL
file is an interoperable description (compliant with the WS-I basic profile, as discussed in
Chapter 13). This use of the documentation element appears in the priceCheck serv-
ice element:
<service name=PriceCheck>
<port name=Pricecheck binding=pc:PriceCheckSOAPBinding>
<documentation>
<wsi:Claim
conformsTo=http://ws-i.org/profiles/basic/1.0 />
</documentation>
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</port>
</service>
Any other WSDL element can contain a documentation element, usually as the first
child element.
Import
WSDL defines an import element that allows WSDL documents to be linked together.
The import element lets you reuse WSDL documents, by allowing WSDL elements in
one file to reference WSDL elements defined in a different file. Really, an import ele-
ment binds a network location to an XML namespace. This is similar to the import ele-
ment in XML Schema; however, dont mix the two. Use WSDL import to import
WSDL definitions, and use XML Schema import (within a schema element in the
types section) to import other XML Schema type and element definitions.
The following line shows the definition of an import element in WSDL:
<import namespace=uri location=uri/>
Just like similar constructs such as the schemaLocation attribute on the import element
in XSD, the location attribute is a hint to the location of the WSDL file. This value
should be a URL. The namespace value should be the same as the WSDL definitions file
found wherever the location attribute points.
Conventional Use of the Import Element
Many developers split their WSDL designs into two parts, a service interface definition
and a service implementation definition, each placed in a separate document. The service
interface definition, containing the types, message, portType, and binding elements,
appears in one file; it encapsulates the reusable components of a service description.You
can then place this file, for example, on a well-known Web site (in an e-marketplace, for
example) for everyone to view. Each organization that wants to implement a Web service
conformant to that well-known service interface definition would describe a service
193 Web Services Description Language (WSDL)
implementation definition containing the port and service elements, describing how
that common, reusable, service interface definition was, in fact, implemented at the net-
work endpoint hosted by that organization. (Youll see in Chapter 6 that this split is very
important for registering WSDL Web service definitions in UDDI.)
When the WSDL definition is split among multiple files, the naming restrictions (for
example, portType names must be unique) apply across files. Technically, names like
portType, and so on, must be unique among all files that include a definitions ele-
ment that has the same targetNamespace.
The designers at SkatesTown used multiple files for the poSubmission WSDL, which
is a service description for the poSubmission SOAP service you saw in Chapter 3. It
uses schema definitions from Chapter 2. The service interface definition for the
poSubmission service interface definition file is shown in Listing 4.2.
Listing 4.2 poSubmission Service Interface Definition
<?xml version=1.0 ?>
<definitions name=poSubmission
targetNamespace=
http://www.skatestown.com/services/interfaces/poSubmission.wsdl
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:po=http://www.skatestown.com/ns/po
xmlns:pos=http://www.skatestown.com/services/interfaces/poSubmission.wsdl
xmlns:inv=http://www.skatestown.com/ns/invoice
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns=http://schemas.xmlsoap.org/wsdl/>
<!-- Type definitions -->
<types>
<xsd:schema>
<!-- rest of invoice schema definition from chapter 2
assumes XSD file is in same directory -->
<xsd:import namespace=http://www.skatestown.com/ns/invoice
schemaLocation=http://www.skatestown.com/schema/invoice.xsd/>
<!-- rest of purchaseOrder schema definition from chapter 2
assumes XSD file is in same directory -->
<xsd:import namespace=http://www.skatestown.com/ns/po
schemaLocation= http://www.skatestown.com/schema//po.xsd/>
</xsd:schema>
</types>
<!-- Message definitions -->
<message name=poSubmissionRequest>
<part name=purchaseOrder element=po:po/>
</message>
194 Chapter 4 Describing Web Services
<message name=poSubmissionResponse>
<part name=invoice element=inv:invoice/>
</message>
<!-- Port type definitions -->
<portType name=poSubmissionPortType>
<operation name=doSubmission>
<input message=pos:poSubmissionRequest/>
<output message=pos:poSubmissionResponse/>
</operation>
</portType>
<!-- Binding definitions -->
<binding name=poSubmissionSOAPBinding
type=pos:poSubmissionPortType>
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/http/>
<operation name=doSubmission>
<soap:operation soapAction=
http://www.skatestown.com/services/poSubmission/submitPO/>
<input>
<soap:body parts=purchaseOrder use=literal/>
</input>
<output>
<soap:body parts=invoice use=literal/>
</output>
</operation>
</binding>
</definitions>
Note the use of the XML Schema import element, importing the elements defined in
the po and invoice schema definitions.
This WSDL file is a typical pattern for a simple document-centric SOAP service. The
messages are document instances; the SOAP binding indicates the use of literal encod-
ingno deserialization of the XML message into programming languagespecific
objects will occur.
This service interface definition can be reused by many organizations. This is especial-
ly true if the data formats (the purchase order and invoice schemas) are industry stan-
dard.
The information specific to how SkatesTown implements the poSubmission service
interface is contained in the poSubmissionService service implementation definition
file (Listing 4.3).
Listing 4.2 Continued
195 Web Services Description Language (WSDL)
Listing 4.3 poSubmissionService Service Interface Definition
<?xml version=1.0 ?>
<definitions name=poSubmissionService
targetNamespace=
http://www.skatestown.com/services/POSubmissionService.wsdl
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:pop=http://www.skatestown.com/services/interfaces/poSubmission.wsdl
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns=http://schemas.xmlsoap.org/wsdl/>
<import namespace=
http://www.skatestown.com/services/interfaces/poSubmission.wsdl
location=./poSubmission.wsdl/>
<!-- assumes interface file in same directory -->
<!-- Service definition -->
<service name=poSubmissionService>
<port name=poSubmissionSOAPPort binding=pop:poSubmissionSOAPBinding>
<soap:address
location=http://www.skatestown.com/services/submitPO/>
</port>
</service>
</definitions>
This technique is a nice separation of concerns. The service implementation document is
succinct and contains information that is specific to the implementation of this type of
service by SkatesTown.
Another convention, sometimes followed by WSDL designers, is to separate the
binding from the service interface definition. It remains controversial that the service
interface definition includes, by convention, the binding element. After all, the binding
element, when used with the SOAP extensions to WSDL, includes the SOAPAction
attribute in the operation element. This has more to do with implementation than
reusable description. As youll see in Chapter 6, part of the argument to keep the bind-
ing element with the other reusable elements was due to the convention of registering
WSDL documents within UDDI.
Exploring More WSDL Features
Lets explore some more sophisticated features of WSDL, including richer details on
operation definition and bindings, by examining the StockAvailableNotification
WSDL (Listing 4.4). SkatesTown provides the StockAvailableNotification Web serv-
ice to support product ordering. The RegistrationRequest schema defines the
datatypes. In particular, this Web service is used when a customer places an order with
SkatesTown but one or more of the items arent currently available from SkatesTowns
196 Chapter 4 Describing Web Services
inventory. The purpose of this service is to allow customers to register to be notified
when all the products in their order are once again available for sale from inventory.
Listing 4.4 The StockAvailableNotification WSDL Document
<?xml version=1.0 ?>
<definitions name=StockAvailableNotification
targetNamespace=
http://www.skatestown.com/services/StockAvailableNotification
xmlns:tns=http://www.skatestown.com/services/StockAvailableNotification
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:reg=http://www.skatestown.com/ns/registrationRequest
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/
xmlns:wsdl=http://schemas.xmlsoap.org/wsdl/
xmlns=http://schemas.xmlsoap.org/wsdl/>
<!-- Type definitions from the registration schema-->
<types>
<xsd:schema
targetNamespace=http://www.skatestown.com/ns/registrationRequest >
<xsd:import namespace=http://schemas.xmlsoap.org/soap/encoding/
schemaLocation=http://schemas.xmlsoap.org/soap/encoding//>
<xsd:complexType name=ArrayOfItem
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
<xsd:complexContent>
<xsd:restriction base=soapenc:Array>
<xsd:attribute ref=soapenc:arrayType
wsdl:arrayType=xsd:string[]/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name=registrationRequest>
<xsd:sequence>
<xsd:element name=items type=reg:ArrayOfItem />
<xsd:element name=address type=xsd:string/>
<xsd:element name=transport
default=smtp minOccurs=0 >
<xsd:simpleType>
<xsd:restriction base=xsd:string>
<xsd:enumeration value=http/>
<xsd:enumeration value=smtp/>
197 Web Services Description Language (WSDL)
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name=clientArg type=xsd:string minOccurs=0/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name=correlationID>
<xsd:restriction base=xsd:string>
<!-- some appropriate restriction -->
</xsd:restriction>
</xsd:simpleType>
<xsd:element name=Expiration type=xsd:dateTime />
<xsd:element name=ErrorString type=xsd:string />
</xsd:schema>
</types>
<!-- Message definitions -->
<message name=StockAvailableRegistrationRequest>
<part name=registration type=reg:registrationRequest/>
<part name=expiration element=reg:Expiration/>
</message>
<message name=StockAvailableRegistrationResponse>
<part name=correlationID type=reg:correlationID/>
</message>
<message name=StockAvailableRegistrationError>
<part name=errorString element=reg:ErrorString/>
</message>
<message name=StockAvailableExpirationError>
<part name=errorString element=reg:ErrorString/>
</message>
<message name=StockAvailableNotification>
<part name=timeStamp type=xsd:dateTime/>
<part name=correlationID type=reg:correlationID/>
<part name=items type=reg:ArrayOfItem/>
<part name=clientArg type=xsd:string/>
</message>
<message name=StockAvailableExpirationNotification>
<part name=timeStamp type=xsd:dateTime/>
Listing 4.4 Continued
198 Chapter 4 Describing Web Services
<part name=correlationID type=reg:correlationID/>
<part name=items type=reg:ArrayOfItem/>
<part name=clientArg type=xsd:string/>
</message>
<message name=StockAvailableCancellation>
<part name=correlationID type=reg:correlationID/>
</message>
<!-- Port type definitions -->
<portType name=StockAvailableNotificationPortType>
<!--Registration Operation -->
<!-- Note: the requestor must invoke the registration operation first. -->
<operation name=registration>
<input message=tns:StockAvailableRegistrationRequest/>
<output message=tns:StockAvailableRegistrationResponse/>
<fault message=tns:StockAvailableRegistrationError
name=StockAvailableNotificationErrorMessage/>
<fault message=tns:StockAvailableExpirationError
name=StockAvailableExpirationError/>
</operation>
<!--Notification Operation -->
<operation name=notification>
<output message=tns:StockAvailableNotification/>
</operation>
<!--Expiration Notification Operation -->
<operation name=expirationNotification>
<output message=tns:StockAvailableExpirationNotification/>
</operation>
<!--Cancellation Operation -->
<operation name=cancellation>
<input message=tns:StockAvailableCancellation/>
</operation>
</portType>
<!-- Binding definitions -->
<binding name=StockAvailableNotificationSOAPBinding
type=tns:StockAvailableNotificationPortType>
<soap:binding style=rpc
transport=http://schemas.xmlsoap.org/soap/http/>
<!-- Note: the requestor must invoke the registration operation first. -->
<operation name=registration>
Listing 4.4 Continued
199 Web Services Description Language (WSDL)
<soap:operation
soapAction=
http://www.skatesTown.com/StockAvailableNotification/registration />
<input>
<soap:header message=tns:StockAvailableRegistrationRequest
part=expiration use=literal >
<soap:headerfault message=tns:StockAvailableExpirationError
part=errorString use=literal />
</soap:header>
<soap:body parts=registration use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/ />
</input>
<output>
<soap:body use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</output>
<fault name=StockAvailableNotificationErrorMessage>
<soap:fault name=StockAvailableNotificationErrorMessage
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</fault>
</operation>
<operation name=notification>
<output>
<soap:body use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</output>
</operation>
<operation name=expirationNotification>
<output>
<soap:body use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</output>
</operation>
<operation name=cancellation>
<soap:operation
soapAction=
Listing 4.4 Continued
200 Chapter 4 Describing Web Services
http://www.skatesTown.com/StockAvailableNotification/cancellation />
<input>
<soap:body use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</input>
</operation>
</binding>
<!-- Service definition -->
<service name=StockAvailableNotification>
<port name=StockAvailableNotification
binding=tns:StockAvailableNotificationSOAPBinding>
<soap:address
location=
http://www.skatestown.com/services/StockAvailableNotification/>
</port>
</service>
</definitions>
This Web service has four operations. The first operation allows the customer to regis-
ter for a notification. This is a request/response operation. The customer invokes this
service, passing in a collection of item numbers (for out of stock product numbers), a
network address (default is an email address), an optional transport type (valid values are
http and smtp), and a client argument token of type string. The client argument
token is opaque to the service; its a requestor-specific correlation identifier. The client
argument is returned by the notification operation.
This operation also includes an expiration time to be included in the message (as a
SOAP header, as youll discover). This is used to indicate what time in the future this
service will cease issuing notifications.
The normal response of this operation is a provider-side correlation id g(a string).
The possible fault messages include
n
Invalid product number (one of the product numbers doesnt correspond to a
product in SkatesTowns product catalog)
n
Invalid transport (some value other than smtp or http was specified)
n
Invalid expiration (this appears as a soap:headerfault gif the original expira-
tion time header was invalid in some way; well describe soap:headerfault ele-
ments in more detail later in this chapter)
The second operation, notification, uses the notification transmission primitive g
in WSDL (well talk about transmission primitives in more detail later). This message is
Listing 4.4 Continued
201 Web Services Description Language (WSDL)
sent from SkatesTown to the requestors address indicated in the registration opera-
tion. This message indicates a timestamp, the provider-side correlation ID established in
the registration message, the item numbers from the registration message, and the
requestor-specific correlation ID. (Note that this is a simple notification mechanism and
specific to SkatesTown. Standardized Web services notification mechanisms are just
beginning to appear; we discuss one such specification in Chapter 8, Web Services and
Stateful Resources.)
The third operation, expirationNotification, also uses the notification transmis-
sion primitive in WSDL. This message is sent from SkatesTown to the requestors address
when the expiration period indicated on the original registration operation elapses.
This functionality is similar to the soft-state mechanism used in the Open Grid Services
Infrastructure (OGSI; see Chapter 8). This message indicates a timestamp, the provider-
side correlation ID established in the registration message, the item numbers from the
registration message, and the requestor-specific correlation ID.
The fourth operation is a one-way operation for cancellation of the notification. It
allows the requestor to abandon its interest in the notification. The cancellation message
is the provider-side correlation ID.
Transmission Primitives
The WSDL specification defines four different combinations of input, output, and fault
messages. WSDL 1.1 uses the term transmission primitive to describe these combinations.
WSDL 1.1 defines four transmission primitives: request-response, one-way, notification,
and solicit-response. As youll see later in the chapter, this concept has been generalized
in WSDL 2.0 as message exchange pattern g. Well now visit each of these transmission
primitives in more detail.
Request-Response Operations
The request-response style is the most common form of operation. This kind style
operation defines an input message (the request) followed by an output message (the
response), and an optional collection of fault messages. Because many Web services are
deployed using SOAP over HTTP, request-response is the most common form of oper-
ation found in WSDL documents.
The checkPrice operation from the priceCheckPortType is a request-response
operation, as is registration from the StockAvailableNotification service.
Request-response messages can retrieve information about some object represented by a
Web service (like the checkPrice operation); they can also change the state of the serv-
ice provider and include information about the new state in the response (like the reg-
istration operation).
Although checkPrice doesnt use it, the request-response transmission primitive
allows the service provider to list the possible fault messages that can appear in response
to a Web service invocation. The fault element is used in the
StockAvailableNotificationPortType as part of the definition of the registration
202 Chapter 4 Describing Web Services
operation:
<!-- Port type definitions -->
<portType name=StockAvailableNotificationPortType>
<!--Registration Operation -->
<operation name=registration>
<input message=tns:StockAvailableRegistrationRequest/>
<output message=tns:StockAvailableRegistrationResponse/>
<fault name=StockAvailableNotificationErrorMessage
message=tns:StockAvailableRegistrationError />
<fault name=StockAvailableExpirationError
message=tns:StockAvailableExpirationError />
</operation>
Fault elements must be named, and the name must be unique among all the fault ele-
ments defined for the operation. Like the input and output elements, the fault element
refers to a message which describes the data contents of the fault.
One-Way Operations
A one-way operation doesnt have an output element (or a fault element); it has no
response message going back to the requestor. A one-way operation is like a data sink.
You might use it to change the state of the service provider.
The cancellation operation from the StockAvailableNotification service is a
one-way operation:
<!--Cancellation Operation -->
<operation name=cancellation>
<input message=tns:StockAvailableCancellation/>
</operation>
Because many Web services are accessed through SOAP over HTTP, many one-way
messages end up being request-response messages at the network transport level, with
the response being a simple HTTP-level acknowledgment of the message. This is an
interesting point to note: The operation description in WSDL doesnt necessarily model
the network transport-level message flow. More precisely, for a one-way operation, the
HTTP response must not contain a SOAP envelopeclients wont expect it to be there,
and most clients will ignore it if it does appear. If you want an application-level semantic
to be transmitted in response to the Web service invocation, you should model the oper-
ation using a request-response operation style, not a one-way style.
Notification Operations
A notification operation is like a one-way push from the service provider. Output
messages are pushed to the service requestor as the result of an event on the service
provider side, such as a time-out or operation completion. The notification operation in
the StockAvailableNotification Web service is a notification type of operation;
203 Web Services Description Language (WSDL)
SkatesTown pushes a message to the requestor when a particular item is once again in
stock:
<!--Notification Operation -->
<operation name=notification>
<output message=tns:StockAvailableNotification/>
</operation>
The notification style of interaction is commonly used in systems built around asynchro-
nous messaging. Although systems with asynchronous messaging might be a little harder
to conceptualize and implement, theyre more loosely coupled and, therefore, are easier
to maintain; often this flexibility adds robustness to the system.
Given that notification is a one-way push message, how does SkatesTown know
where to push the output messages? Nothing in the WSDL specification describes this
directly. This correlation semantic must be described by other means. One mechanism
used to address this problem is to have a network address (a URL or email address) as a
parameter in another message. Another might be to use a transport binding that takes
care of this for you, such as you might find in Message-Oriented Middleware (MOM)
solutionsthe service address in this case would be something the client would listen to,
rather than a place to send messages. This sort of coordination problem, and the vague-
ness of the WSDL specification of a one-way push message, has limited its use by WSDL
designers and has limited support by tooling and middleware vendors.
In the case of the StockAvailableNotification Web service, SkatesTown has creat-
ed the registration operation to determine where to send a notification message.
The service requestor must invoke the registration operation first, and then the
notification operation can send a message to the requestor. The ordering require-
ment of these operationsthat is, the fact that the registration operation must be
invoked in order to receive notification messagescant be described in WSDL without
extensions. At this point, SkatesTown must describe this semantic using prose. The fol-
lowing comment appears with this portType:
<!--Registration Operation -->
<!-- Note: the requestor must invoke the registration operation first. -->
The semantics of notification style operations havent been agreed on in the industry.
Therefore, to ensure interoperability of Web services, you shouldnt use this style of
operation.
Solicit-Response Operations
A solicit-response operation models a push operation similar to a notification opera-
tion. However, unlike the notification style of operation, the solicit-response opera-
tion expects an input (response) from the service requestor as the response in a solicit-
response exchange. A solicit-response operation could look like this:
<portType name=someName>
<operation name=exampleSolicitResponse>
204 Chapter 4 Describing Web Services
<output message=tns:pushThis/>
<input message=tns:responseToPush/>
<fault name=someFaultName message=tns:faultPushedToRequestor/>
</operation>
</portType>
Output and fault flows are pushed to the service requestor as the result of some event
on the service provider side, such as a time out or operation completion. The solicit-
response style of operation has the same problem as the notification style of operation:
It isnt clear how to determine where to push the output/fault messages. Again, one
solution is the same as we discussed with the notification style of operation.
You can tell the difference between a request-response operation and a solicit-
response operation by looking at the ordering of the input and output elements. In
request-response, the input child element comes first. In solicit-response, the output
child element comes first.
Solicit-response also has uneven interpretation by tooling and middleware vendors,
limiting its use by WSDL designers. In fact, the vagueness of the WSDL 1.1 specification
in this area motivated the WSDL 2.0 specification team to define the message exchange
pattern work. And, as noted with notification style operations, interoperable Web serv-
ices shouldnt use the solicit-response style of operation.
Rounding Out WSDL Operations
Lets consider a couple of final details related to the operation element. The WSDL
specification allows the input, output, and fault messages to have a name attribute.
Typically, the designer doesnt bother to come up with a name for input and output
elements. This detail is often too much clutter in the WSDL document. And besides,
WSDL provides default values for these names based on the operation name. For
example, we repeat our checkPrice example, this time filling in the default values
WSDL would have supplied:
<!-- Port type definitions -->
<portType name=PriceCheckPortType>
<operation name=checkPrice>
<input name=checkPriceRequest message=pc:PriceCheckRequest/>
<output name=checkPriceResponse message=pc:PriceCheckResponse/>
</operation>
</portType>
Typically, the name attribute of the input and output elements doesnt add much.
Further, many WSDL designers encode this information into the names of the message
elements they define. If you do add a name to an input or output element, it must be
unique among all the input and output elements within the portType. Table 4.2
describes the default names for input and output elements, for an operation
named XXX.
205 Web Services Description Language (WSDL)
Table 4.2 Defaults for Input and Output Elements for Operation XXX
Input Output
Request-response XXXRequest XXXResponse
One-way XXX Not applicable
Solicit-response XXXSolicit XXXResponse
Notification Not applicable XXX
Fault elements require a name, because several fault elements can be associated with
any operation and the fault name is used to distinguish among the set of possible faults.
This is particularly important in the binding, which describes the mapping between the
fault element and the way the fault is presented in a protocol-specific fashion. Its good
design to include as many faults as are known when the portType is designed.
However, this list of faults isnt exhaustive, and other faults could appear at runtime
for any Web service implementing the portType.
WSDL also lets you specify a parameterOrder attribute on an operation, which is
used only for RPC-style operations. This is a bit of a layering violation in the specifica-
tion, because the portType is abstract, and its nature as an RPC or document-centric
message is revealed in the binding. Further, this attribute is informational only and is
completely optional, even for operations described as RPC within the binding. This
attribute has two purposes: It provides a mechanism to describe the original parameter
ordering of the RPC function as a list of part names separated by spaces, and it disam-
biguates which of the parameters contain a return value.
Rounding Out WSDL Bindings
The SOAP binding style also specifies the way SOAP headers, SOAP faults, and SOAP
header faults should be formatted. These additional aspects of the SOAP binding con-
vention are illustrated in the StockAvailableNotificationSOAPBinding.
Binding and RPC
We can explore more sophisticated options with bindings and at the same time discuss
the other major style of binding: RPC. With an RPC-style binding, the way the SOAP
body format is specified is quite different. The binding for the
StockAvailableNotification portType demonstrates a typical RPC style of binding:
<binding name=StockAvailableNotificationSOAPBinding
type=tns:StockAvailableNotificationPortType>
<soap:binding style=rpc
transport=http://schemas.xmlsoap.org/soap/http/>
...
In addition, this binding illustrates how the abstract input and output messages specified
by the portType can correspond to SOAP body and SOAP header elements on the
wire. Lets look at how the binding specifies a SOAP body. (Well examine how this
206 Chapter 4 Describing Web Services
binding specifies SOAP headers in the next section.) The binding for the registration
operation illustrates an RPC operation:
<!-- Note: the requestor must invoke the registration operation first. -->
<operation name=registration>
<soap:operation
soapAction=
http://www.skatesTown.com/StockAvailableNotification/registration />
<input>
...
<soap:body parts=registration use=encoded
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/ />
</input>
Recall from the StockAvailableNotificationPortType that the input message to the
registration operation has two parts: registration and expiration. The previous
binding specifies that just the registration part goes in the body and, furthermore,
that it uses SOAP encoding. The rule here is that the binding must map all the parts of
the message to a SOAP body or a SOAP header. The encodingStyle causes the bind-
ing to specify a SOAP body like the following to appear on the wire:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Header>
<ns1:Expiration xsi:type=xsd:dateTime
xmlns:ns1=http://www.skatestown.com/ns/registrationRequest>
2004-01-30T05:00:00.000Z
</ns1:Expiration>
</soapenv:Header>
<soapenv:Body>
<ns2:registration
soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xmlns:ns2=http://www.skatestown.com/ns/registrationRequest>
<registration href=#id0/>
</ns2:registration>
<multiRef id=id0
soapenc:root=0
soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xsi:type=ns3:registrationRequest
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/
xmlns:ns3=http://www.skatestown.com/ns/registrationRequest>
<items xsi:type=soapenc:Array soapenc:arrayType=xsd:string[2]>
<item>Skateboard</item>
<item>Wheels</item>
207 Web Services Description Language (WSDL)
</items>
<address xsi:type=xsd:string>[email protected]</address>
<transport href=#id1/>
<clientArg xsi:type=xsd:string>RFQ8</clientArg>
</multiRef>
<multiRef id=id1
soapenc:root=0
soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
smtp
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
The parameter of the message, registration, appears as its own element in the body of
the SOAP message.
Note the child element of the SOAP body:
<soapenv:Body>
<ns2:registration
With RPC-encoded bindings, the name of child element of the SOAP body is derived
from the operation name (in this case, the operation is named registration).
You dont see the namespace or the encodingStyle attribute used with document-
literal bindings, just RPC-encoded bindings. Because special encodings are tricky to
get right between requestors and providers, they arent considered a form of interopera-
ble binding. A recommended approach is to use RPC with the value of the use attrib-
ute as literal rather than encoded.
SOAP Header and Header Fault Formatting
Now we can examine the other part in the input message to the registration opera-
tion. This Web service has been designed to allow the expiration to be carried in the
input message as a SOAP header. Heres the part of the input binding for the registra-
tion operation that deals with the expiration header:
<soap:header message=tns:StockAvailableRegistrationRequest
part=expiration use=literal >
<soap:headerfault message=tns:StockAvailableExpirationError
part=errorString use=literal />
</soap:header>
Note that the soap:header extension to the binding can specify a part from messages
other than the one associated with the operations input or output message; this is what
the message attribute is used for. The message attribute and the part attribute together
identify the header element. Note that best practice is to declare soap:header elements
to have the value literal for the use attribute; this is better for interoperability.
208 Chapter 4 Describing Web Services
This binding would cause the input message to the registration request to include
a SOAP header like the following:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Header>
<ns1:Expiration xsi:type=xsd:dateTime
xmlns:ns1=http://www.skatestown.com/ns/registrationRequest>
2004-01-30T05:00:00.000Z
</ns1:Expiration>
</soapenv:Header>
<soapenv:Body>
<ns2:registration ...
...
Otherwise, the specification for a SOAP header in a binding is similar to that of a SOAP
body. Note one interesting distinction: There can be many SOAP headers, and each of
these can reference only a single part, whereas with the SOAP body there can be only
one, but it can reference multiple parts.
SOAP Fault and Header Fault Formatting
The soap:fault extension is an additional facility described by the soap:binding
extension that appears in the StockAvailableNotification WSDL. The soap:fault
extension describes how a fault element (like the one described in the registration
operation in the StockAvailableNotificationPortType) is mapped in SOAP. A use of
this extension is shown here:
<operation name=registration>
. . .
<fault name=StockAvailableNotificationErrorMessage>
<soap:fault name=StockAvailableNotificationErrorMessage
namespace=http://www.skatestown.com/ns/registrationRequest
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</fault>
The soap:fault extension has attributes similar to those of the soap:body extension.
The fault message must have a single part.
SOAP requires that the details of any errors generated by the SOAP engine when
processing a SOAP header (rather than the body) must be communicated back to the
requestor in the form of SOAP headers, not in the details element of a fault message.
MustUnderstand faults are communicated this way, for example. The SOAP extension
in WSDL defines the soap:headerfault element for this purpose. A soap:headerfault
element is used to describe how the StockAvailableExpirationError is expressed in
209 Web Services Description Language (WSDL)
SOAP, as a fault header that could potentially flow to communicate errors related to
how the requestor formats the expiration header. The following example shows how
WSDL models this situation:
<operation name=registration>
...
<input>
<soap:header message=tns:StockAvailableRegistrationRequest
part=expiration use=literal >
<soap:headerfault message=tns:StockAvailableExpirationError
part=errorString use=literal />
</soap:header>
. . .
The soap:headerfault element is associated with the soap:header definition that
might be in error, not as part of a fault or output message part of the operation.
Example SMTPBinding
As a slight variant on the SOAP using HTTP binding theme, consider the possibility of
sending a priceCheck request using email. SkatesTown could provide an additional
SOAP over SMTP binding to the priceCheck portType, allowing a customer to email
a priceCheck request and receive as a reply email the priceCheck response message.
Not much changes with this new binding. Of course, the priceCheck portType and
the messages and types it references dont change. What does change is additional bind-
ing, port, and service elements. The biggest changes include use of a different URI for
the transport attribute of the soap:binding element to indicate SMTP is the transport
mechanism:
<!-- Binding definitions -->
<binding name=PriceCheckSMTPBinding type=pc:PriceCheckPortType>
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/smtp/>
<operation name=checkPrice>
<input>
<soap:body use=literal/>
</input>
<output>
<soap:body use=literal/>
</output>
</operation>
</binding>
So, we now know that the only format supported for the priceCheck and
StockAvailableNotification services is SOAP, and we know how the abstract types
should be formatted into a concrete message. We now have (almost) all the details
210 Chapter 4 Describing Web Services
needed to invoke these Web services. At this point, we must use a SOAP message some-
thing like the following to invoke the checkPrice operation:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<sku xmlns=http://www.skatestown.com/ns/availability>123</sku>
</soapenv:Body>
</soapenv:Envelope>
and the response message is formatted as follows:
<?xml version=1.0 encoding=UTF-8?>
<soapenv:Envelope
xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<soapenv:Body>
<StockAvailability xmlns=http://www.skatestown.com/ns/availability>
<sku>123</sku>
<price>100.0</price>
<quantityAvailable>12</quantityAvailable>
</StockAvailability>
</soapenv:Body>
</soapenv:Envelope>
WSDL Extension Mechanism
The WSDL language allows most of the WSDL elements to be extended with elements
from other namespaces. The language specification further defines standard extensions for
SOAP, HTTP GET/POST operations, and MIME attachments.Youve seen the use of
the SOAP extension extensively in the previous sections of this chapter. Well briefly
describe the other two extension frameworks here.
WSDL Descriptions of HTTP GET/POST Web Services
Imagine a variant on the priceCheck Web service that was tuned to support Web
browsers. It would use URL encoding to include the item number as part of the service
request using HTTP GET. An invocation of this service could look like an HTTP GET
message sent to the following URL:
http://www.skatestown.com/checkPrice?item=xxx1234.
211 Web Services Description Language (WSDL)
The priceCheck WSDL definition would be extended to include a new binding:
<!-- Binding definitions -->
. . .
<binding name=PriceCheckHTTPGetBinding type=pc:PriceCheckPortType>
<http:binding verb=GET/>
<operation name=checkPrice>
<http:operation location=checkPrice/>
<input>
<http:urlEncoded/>
</input>
<output>
<mime:content type=text/xml/>
</output>
</operation>
</binding>
The first HTTP extension is shown as the first child of the binding. This element does
two things: It tells us that this binding is an HTTP binding, and it indicates that the
GET verb is used (the other option was the POST verb).
The second HTTP extension is shown as the first child of the operation. This ele-
ment indicates that the service is to be invoked at a relative URI location. This is to be
with the absolute URI location indicated in the port element.
The third HTTP extension is shown as the first child of the input. This element
indicates that the parts of the input message are encoded in the request URI as
name/value pairs, where the HTTP GET parameter names correspond to the WSDL
part names. Recall that the input message to the checkPrice operation is the
PriceCheckRequest message, and it has only one part: a string named sku. This means
that the value of the input will appear in the URI, following the string ?sku=.
The fourth HTTP extension is shown as the first child of the output element. This
element indicates that the priceCheckResponse message will appear as XML text in the
HTTP response.
The last thing required is to update the service of the priceCheck WSDL to
include a port describing the http:address of the priceCheckHTTPGetBinding. This
update is as follows:
<!-- Service definition -->
<service name=PriceCheckService>
. . .
<port name=PriceCheckBrowserPort binding=pc:PriceCheckHTTPGetBinding>
<http:address location=http://www.skatestown.com/services//>
</port>
. . .
Here the URL of the priceCheck service is given using the http:address WSDL
extension. Just like the SOAP extension, most of the HTTP extension is in the binding
212 Chapter 4 Describing Web Services
(where you would expect it), and the only remaining piece is an extension to the port
expressing the endpoint network address in a protocol-specific manner.
The HTTP extension also specifies how to express the input message as HTTP POST
using FORM-POST and how to express the input using urlReplacement. Refer to the
WSDL specification for more detail.
WSDL Descriptions of Web Services Incorporating MIME
WSDL also supports a standard extension to describe message parts as MIME. We cov-
ered SOAP with MIME attachments in Chapter 3. This extension would be used if the
designers at SkatesTown decided to include (in addition to the normal SOAP response)
a GIF or JPEG image of the part queried in a priceCheck service invocation. To support
this addition, the following changes would be necessary in the priceCheck WSDL. First,
the response message would be updated to include the new part:
<message name=PriceCheckResponse>
<part name=result type=avail:availability/>
<part name=picture type=xsd:binary/>
</message>
This change doesnt exercise the MIME extension standard, because the MIME exten-
sions are only within the binding. However, the only change necessary in the binding
is to indicate that the output is modeled as multipart MIME, with the result part
appearing as one MIME part, the SOAP body; the picture appears in another MIME
part as GIF or JPEG. The following listing shows the binding element with these
changes:
<!-- Binding definitions -->
<binding name=PriceCheckSOAPBinding type=PriceCheckPortType>
<soap:binding style=rpc
transport=http://schemas.xmlsoap.org/soap/http/>
<operation name=checkPrice>
<soap:operation SOAPAction=/>
<input>
<soap:body use=encoded
namespace=http://www.skatestown.com/ns/availability
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</input>
<output>
<mime:multipartRelated>
<mime:part>
<soap:body use=encoded
namespace=http://www.skatestown.com/services/PriceCheck
encodingStyle=http://schemas.xmlsoap.org/soap/encoding//>
</mime:part>
<mime:part>
<mime:content part=picture type=image/gif/>
213 A Sketch of How WSDL Maps to Java
<mime:content part=picture type=image/jpeg/>
</mime:part>
</mime:multipartRelated>
</output>
</operation>
</binding>
The only thing that has changed is within the output element (added definitions are in
bold). Note the duplicate mime:content elements with the part named picture. When
you see them, you are to interpret them as alternative formats, one of which might
appear.
A Sketch of How WSDL Maps to Java
Weve examined the WSDL standard for service description. Now lets see how it
addresses automating the invocation of Web services by the service requestor.
WSDL maps naturally to Java. Many tools automate this mapping, for both the
requestor and the service provider. One tool in particular, WSDL2Java (provided by Axis),
is examined in more detail in Chapter 5. For now, well sketch the mapping from a
WSDL definition to Java. There are many possible approaches to mapping Java to WSDL;
well use the pattern that the Axis WSDL2Java tool uses.
Just as with the examination of WSDL, lets start by looking at the portType. The
portType most naturally maps into a Java interface. The name of the interface typically
takes on the name of the portType. So, the portType named PriceCheckPortType gen-
erates a Java interface named PriceCheckPortType. This file is declared in a package
named from the targetNamespace URI of the WSDL definitions element containing
the portType. Heres a piece of the Java interface generated for the PriceCheck
portType:
package com.skatestown.www.services.PriceCheck;
public interface PriceCheckPortType extends java.rmi.Remote {
...
}
For each of the portTypes operations, a public method is declared as a part of the
interface. The signature of the method is built from the name of the operation; the
input and output signatures as defined in the operation and any fault elements associ-
ated with the operation are included as exceptions thrown by the method. Heres the
method signature generated (as part of the PriceCheckPortType interface) from the
checkPrice operation:
public com.skatestown.www.ns.availability.AvailabilityType
checkPrice(java.lang.String sku) throws java.rmi.RemoteException;
If the checkPrice operation had a fault element, it would have appeared as an excep-
tion thrown by the checkPrice method.
214 Chapter 4 Describing Web Services
For those messages that are referenced by input, output, and fault elements, a separate
class is generated for complexTypes (and all faults, regardless of whether theyre simple or
complexTypes) referenced by the parts of those messages. These type-based classes are
used as part of the mechanism to deserialize and serialize XML to and from Java. The
name of the class is taken from the name of the type or element. The package for the
class is taken from the targetNamespace URI of the XML schema that defines the type
or element. The class to serialize and deserialize the AvailabilityType element is an
example and is used as part of the signature for the checkPrice method.
Each binding also generates a separate stub class, again using the name of the binding
to name the class and the targetNamespace of the definitions element to define the
package name. This class is a proxy to the service, in that it encapsulates all the imple-
mentation details associated with how a given portType is made concrete by the bind-
ing. This class implements the interface defined by the portType. Think of the binding
class as a protocol and transport-specific implementation of the interface defined by the
portType. For convenience, you can also use the binding to define a corresponding
server-side skeleton to help insulate all the protocol- and transport-specific details from
the actual implementation of the business logic associated with the Web service.
Finally, the WSDL service also generates an interface and class, which encapsulate
details of invoking the service from the client application.
Nonfunctional Descriptions in WSDL
Weve described how WSDL is used to describe the functional characteristics of a Web
service, but what about the nonfunctional characteristics of a Web service? How can you
describe security requirements and transactional capabilities of a Web service? How can a
requestor determine whether a Web service invocation will be logged or audited by the
service provider? How can the requestor know if their privacy will be respected?
These sorts of nonfunctional characteristics of a Web service can be described using
WS-Policy and related specifications. In this section, well give an overview of how you
can use WS-Policy to describe the requirements, capabilities, and restrictions associated
with your Web services and the endpoints in which they execute.
Policies
There are several reasons why you might want to augment your WSDL functional
descriptions with policy-based nonfunctional descriptions. First, its important that a
requestor know everything needed in order to invoke a Web service. The WSDL
describes the sort of parameters to put into a request message to invoke business logic,
but no part of WSDL natively describes things like security headers, reliable messaging
capabilities, and so on. To complete a well-defined service, you augment WSDL with
extra information.
Another reason to associate a WSDL with this extra information is to help the
requestor make an appropriate choice of service. For example, all things being equal, a
215 Nonfunctional Descriptions in WSDL
requestor will prefer to interact with a service that provides a statement of a privacy pol-
icy over one that doesnt. So, sometimes, the nonfunctional decorations of a service
description have no bearing on the format of the messages going into and out from the
Web service and only serve the purpose to help the requestor choose to invoke the Web
service.
If nonfunctional information is added to the WSDL in a standard and well-known
way, then there is a better chance that client- and server-side tooling will be available to
act on these nonfunctional statements about a Web service. WS-Policy is one common
way to express policy information in a service description.
The WS-Policy family of specifications has three major components: the framework,
the assertions, and the attachment. Lets review how these pieces fit together and then
get into details.
The basic component of the policy framework is a policy assertion g: a concrete
statement about the requirement, preference, capability, or other characteristic of a Web
service or its operating environment. Policy assertions describe certain qualities of service
such as reliability of messaging:
<wsrm:DeliveryAssurance Value=wsrm:ExactlyOnce/>
(This is described in more detail in Chapter 10, Web Services Reliable Messaging.)
Youll also see policy assertions in security. For example, the requestor must use digital
signatures when invoking a Web service, described as follows:
<wsse:Integrity wsp:Usage=wsp:Required>
<wsse:Algorithm Type=wsse:AlgCanonicalization
URI=http://www.w3.org/Signature/Drafts/xml-exc-c14n/>
<wsse:Algorithm Type=wsse:AlgSignature
URI= http://www.w3.org/2000/09/xmldsig#rsa-sha1/>
<MessageParts
Dialect=http://schemas.xmlsoap.org/2002/12/wsse#soap>
S:Body
</MessageParts>
</wsse:Integrity>
<wsse:SecurityToken>
<wsse:TokenType>wsse:X509v3</wsse:TokenType>
</wsse:SecurityToken>
</wsse:Integrity>
An entire specification, WS-SecurityPolicy, describes security-related policy assertions.
We go into more detail about security policies in Chapter 9, Securing Web Services.
Sometimes a policy assertion is a simple statement of fact, such as this Web service
uses a certain privacy policy or this service uses a certain dialect of SOAP. Sometimes
the policy assertion is a complicated statement, indicating possible sets of requestor-
specifiable parameters, various optional solutions to a requirement, and so on.
Policy assertions come from various realms, such as security and reliable messaging.
WS-Policy itself doesnt define any policy assertions (except a few general-purpose ones
216 Chapter 4 Describing Web Services
well describe later, which are defined by a related specification called WS-
PolicyAssertions).
Policy assertions are grouped together to form a policy g.You can think of policy
assertions as building blocks, or basic components that are combined in various ways to
form an actual policy. The WS-Policy specification defines the associated framework,
including a container, grouping elements, and standard attributes.
A policy forms a named collection of policy assertions that can be referenced, using
standard XML mechanisms, by other XML and Web services components such as a
WSDL definition. One common form of reference mechanism is the way by which a
policy is associated (attached) to a policy subject g. A policy subject can be a Web serv-
ice, a component of a Web service description, a part of the Web services operating
environment, or various other entities related to a Web service. The specification called
WS-PolicyAttachments describes how policies are associated with policy subjects.
WS-Policy
WS-Policy was originally published in December 2002 by BEA, IBM, Microsoft, and
SAP.Version 1.1 was published in May 2003, to improve on version 1.0.
WS-Policy defines how to group policy assertions into a named collection that can
be referenced by other components. WS-Policy defines the fundamental framework for
policies in a Web services world. This framework has three pieces:
n
An XML element to act as a container for one or more policy assertions
n
A set of XML elements that describe how the policy assertions grouped by the
container are to be combined
n
A set of standard XML attributes that may be associated with policy assertions
WS-Policy has a frustratingly schizophrenic mechanism to name policies. There is not
one, but two standard mechanisms by which a policy can be named: by XML QName or
by URI.You should choose one form and stick with it for all your policy work. If youre
using WS-Policy mostly in conjunction with WSDL, stay with the QName approach
used throughout WSDL. (In our opinion, its unfortunate that the WS-Policy authors
couldnt settle on one form.) Throughout the rest of the WS-Policy specification and
related specifications, various optional elements and attributes describe how to refer to
policies by QName or by URI.Youll need to cope with both forms, since either might
be associated with Web services defined by your business partners.
The general form of the policy container element appears as follows:
<wsp:Policy ((Name= TargetNamespace= ? )| Id= )
<policy specific assertion> *
<policy-specific security>?
</wsp:Policy>
You need to name a policy using the Name and TargetNamespace attributes (forming
the QName of the policy) or using an Id, which is a local name and is combined with
217 Nonfunctional Descriptions in WSDL
the XML base of the document containing the policy element to form a URI to the
policy. Note that the TargetNamespace can also be derived from the context of the pol-
icy element (for example, the namespace in which the element is defined, such as
defined by an XML schema element or a WSDL definitions element).
The container also provides an option to specify security policy assertions specific to
this policy element. This is an interesting example of the specification using itself to
define itself.
Policy assertions are normally added to the policy container as independent entities.
Unless there is some discipline-specific rule about how individual policy assertions inter-
act when theyre placed within the same policy container element, we assume theyre
completely independent. But what are the semantics of this group of individual policy
assertions? How should a requestor interpret how the individual assertions are meant to
be combined? This is the role of the second component of the WS-Policy framework:
the combinatorial elements or operators.
Policy Operators
Four operators are defined by WS-Policy to describe different combinations of policy
assertions: All, ExactlyOne, OneOrMore, and the basic policy element (which has the
same semantic as the All operator).
A policy such as
<wsp:Policy
name=PolicyExample1
TargetNamespace=http://www.skatestown.com/policies >
<wsp:All>
<Assertion:A />
<Assertion:B />
<Assertion:C />
</wsp:All>
</wsp:Policy>
defines a policy named PolicyExample1 in the http://www.skatestown.com/policies
namespace. This policy states that all of the assertions A, B, and C are in effect. Of course,
what it means for a policy assertion to be in effect is entirely dependent on the domain of
each policy assertion and the policy subject to which the policy is attached. Some policy
assertions, like the assertion of a privacy policy, are in effect because they state an inten-
tion of the Web service. Other policy assertions are in effect because they make a state-
ment on required components the requestor needs to include in a message to invoke the
Web service.
The ExactlyOne operation defines a choice between the component policy asser-
tions, so a policy such as:
<wsp:Policy
name=PolicyExample2
TargetNamespace=http://www.skatestown.com/policies >
218 Chapter 4 Describing Web Services
<wsp:ExactlyOne>
<Assertion:A />
<Assertion:B />
<Assertion:C />
</wsp:ExactlyOne>
</wsp:Policy>
defines a policy named PolicyExample2 in the http://www.skatestown.com/policies
namespace in which only one of the assertions A, B, and C is in effect.
Finally, the operator named OneOrMore is a variation of this combination, where some
subset of the policy assertions listed as child elements is in effect.
Note that operators can nest. For example, in the previous examples, any of the
Assertion elements can be replaced by an operator, allowing you to form complex,
nested policy expressions that combine policy assertions in arbitrary ways.
Policy Usage and Preference
The third component of WS-Policy is a pair of global XML attributes: Usage and
Preference.You can add these attributes to the various policy assertion child elements
of the policy. The Usage attribute describes how the policy assertion is to be interpreted
in the context of the policy; it can take any of the following values:
n
RequiredThe assertion must apply, or an error occurs. As an example, this Usage
would be used on a digital signature policy assertion if the Web service provider
wanted all input messages to be digitally signed.
n
RejectedThe assertion must not apply, or an error occurs. For example, if you
explicitly dont want to receive encrypted messages, and you return a fault message
if one is received, a policy assertion using this Usage should be used.
n
OptionalThe assertion may apply, but it doesnt have to apply. This Usage
would be used if youve built a Web service that could accept digitally signed input
messages but is also willing to accept input messages that arent signed.
n
ObservedThis Usage is informational in nature; it lets the requestor know that a
particular assertion will be applied. A privacy policy would typically use this
Usage.
n
IgnoredThis Usage is also informational, telling the requestor that if something
happens to cause the policy assertion to be in effect, no error message will be
emitted, nor will some other action be taken by the Web service.
The other global XML attribute defined by WS-Policy is the Preference attribute. This
attribute is usually used in conjunction with the ExactlyOnce operator. If there is a
choice between a set of policy assertions, this attribute can act as a hint to the requestor.
The value of the Preference attribute is an integer value; the higher the number, the
stronger the preference. So, a policy such as
<wsp:Policy
name=PolicyExample3
219 Nonfunctional Descriptions in WSDL
TargetNamespace=http://www.skatestown.com/policies >
<wsp:ExactlyOne>
<Assertion:A wsp:Preference=100 />
<Assertion:B wsp:Preference=50 />
<Assertion:C wsp:Preference=1 />
</wsp:ExactlyOne>
</wsp:Policy>
suggests to the requestor that it has a choice of assertion A, B, or C, and that the service
provider would much prefer the requestor to choose assertion A.
Referencing Policies
The last part of the WS-Policy specification well review is the policy referencing capa-
bility. WS-Policy defines an element called PolicyReference that allows you to include
the contents of one policy into another. The PolicyReference element can appear any-
where a policy assertion can, and it refers (by QName or URI) to another policy. The
meaning of this include function is that the contents of the included policy element are
wrapped with an All operator element and inserted in place of the PolicyReference
element. For example, combined with the previous examples, the following policy
statement
<wsp:Policy
name=PolicyExample4
TargetNamespace=http://www.skatestown.com/policies
xmlns:tns=http://www.skatestown.com/policies>
<wsp:ExactlyOne>
<wsp:PolicyReference Ref=tns:PolicyExample2 />
<Assertion:D />
<Assertion:E />
</wsp:ExactlyOne>
</wsp:Policy>
is equivalent to
<wsp:Policy
name=PolicyExample4
TargetNamespace=http://www.skatestown.com/policies >
<wsp:ExactlyOne>
<wsp:All>
<wsp:ExactlyOne>
<Assertion:A />
<Assertion:B />
<Assertion:C />
</wsp:ExactlyOne>
</wsp:All>
<Assertion:D />
<Assertion:E />
</wsp:ExactlyOne>
</wsp:Policy>
220 Chapter 4 Describing Web Services
The WS-Policy framework is simple and powerful. But most of the art of policy design
is in the way policy assertions are specified and standardized and then used by Web serv-
ice designers.
Policy Assertions
As we mentioned earlier, policy assertions are the building blocks for policies. Policy
assertions are discipline-specific, such as security policy assertions, reliability policy asser-
tions, and so on.You choose from these standardized policy assertions, configure them,
and combine them into a policy document.
WS-Policy includes a separate document, WS-PolicyAssertions, that defines four stan-
dard policy assertions describing generic policy statements you should make about a Web
service. These assertions are as follows:
n
Text EncodingThis policy assertion lets you declare which character set is used
for text that appears in Web services messages. The possible values are the ones
typically associated with XML documents (such as ISO-8859-5) and are defined in
the XML Schema documents.
n
Language AssertionThis policy assertion allows you to declare which human lan-
guage is expected in messages. These values are the ones that are typically associat-
ed with the xml:lang attribute in XML.
n
Spec AssertionThis policy lets you declare which versions of a particular technical
specification a Web service is compliant with. For example, you can use this policy
assertion to declare exactly which version of a security specification the Web serv-
ice has been built to accommodate.
n
Message PredicateThis policy assertion is pretty sophisticated. It lets you be
detailed about the exact contents of a message going into or coming out of a Web
service. The contents of the message are described using a pattern, typically using
the XPath language. Although the WSDL language does a much clearer job of
describing the content of Web service messages, this policy assertion can be more
detailed in the description of the message. To make the job of specifying the con-
tents of a Web service message easier, WS-PolicyAssertions also defines a function
library of XPath functions to reference various parts of a SOAP message.
Policy Attachments
So far, weve discussed policies and policy assertions, but we havent been clear about
how a policy is associated with a policy subject like a WSDL portType or a WSDL mes-
sage.You can even attach policies to UDDI elements. This is the job of the WS-
PolicyAttachment specification.
A policy can be associated with a policy subject in one of two ways: as part of the
subjects definition (for example, within a WSDL document) or external to the subjects
definition. WS-PolicyAttachment standardizes a pair of XML global attribute definitions
221 Nonfunctional Descriptions in WSDL
that can be added to any XML element either explicitly in the elements schema defini-
tion or implicitly, if the element is defined with open attribute content (it allows attrib-
utes in addition to those declared explicitly in its definition). A pair of global attributes is
defined because WS-Policy can be named by URI or by QName. For example, the fol-
lowing policy expression could be included to declare that the language expected with
SkatesTowns services can be English, Spanish, or French (to serve the entire North
American marketplace for skateboards). The policy itself would be declared as follows:
<wsp:Policy name=SkatestownLanguages
TargetNamespace=http://www.skatestown.com/policies >
<wsp:OneOrMore>
<wsp:Language Language=en />
<wsp:Language Language=es />
<wsp:Language Language=fr />
</wsp:OneOrMore>
</wsp:Policy>
The policy could then be referenced from within the PriceCheck service declaration,
using a WS-PolicyAttachment attribute:
<service name=PriceCheck
wsp:PolicyRefs=stp:SkatestownLanguages
xmlns:wsp=http://schemas.xmlsoap.org/ws/2002/12/policy
xmlns:stp=http://www.skatestown.com/policies >
<port name=PriceCheck binding=pc:PriceCheckSOAPBinding>
<documentation>
<wsi:Claim
conformsTo=http://ws-i.org/profiles/basic/1.0 />
</documentation>
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</port>
</service>
The PolicyRefs attribute takes a list of QNames as its value, allowing you to associate a
collection of policies to any policy subject. The alternative approach, using PolicyURIs,
has a similar function, but it uses the URI mechanism of naming policies. (Dont you
wish the designers of WS-Policy had agreed on one naming mechanism rather than
two?)
Its worth noting that policies can be inherited with a collection of WSDL elements.
WS-PolicyAttachment exploits a natural inheritance within WSDL. For example, you
would expect that a policy attached to a portType would be inherited by its input,
output, and fault child elements. The term effective policy gdefines the policy associ-
ated with a WSDL element. This can be either a policy that is inherited or a policy that
is directly attached to a WSDL element, either explicitly, as part of the WSDL document,
or using the external policy attachment mechanism well discuss shortly. The inheritance
of effective policy in WSDL is described in Table 4.3.
222 Chapter 4 Describing Web Services
Table 4.3 Policy Inheritance in WSDL
WSDL Element Effective Policy
message Policy associated with the message
message/part Policy associated with the part, merged with the effec-
tive policy of the parts message parent
portType Policy associated with the portType
portType/operation Policy associated with the operation merged with the
effective policy of the operations portType parent
portType/operation/input Policy associated with the input merged with the effec-
tive policy of the inputs operation parent and merged
with the effective policy of the message associated with
the input element
portType/operation/output Similar to input
portType/operation/fault Similar to fault
binding Policy associated with the binding merged with the
effective policy of the associated portType
binding/operation Policy associated with the operation merged with the
effective policy of the operations binding parent and
merged with the effective policy of the corresponding
portType/operation
binding/operation/input Policy associated with the input merged with the effec-
tive policy of the inputs operation parent and merged
with the effective policy of the corresponding
portType/operation/input
binding/operation/output Similar to input
binding/operation/fault Similar to fault
service Policy associated with the service
service/port Policy associated with the port merged with the effective
policy of the ports service parent
Now consider the following problem: what if you dont have the ability to modify a
WSDL used to describe one of your Web services? For example, perhaps the portType
being used is defined by an industry standards body, and you cant go in and add a
PolicyRefs attribute to the WSDL definition of the portType. This is an example of
why the WS-PolicyAttachment specification defines an alternate or external means of
attaching policy to a policy subject.
The PolicyAttachment element is defined to allow you to express the attachment of
one or more policies to a policy subject. The policy subject is identified by a domain-
specific expression. For example, the PolicyAttachment can be expressed to attach a
policy to any Server given a range of IP addresses.
Thats it for our discussion of WS-Policy. Now, lets look ahead to see how the WSDL
language is changing.
223 Standardizing WSDL: W3C and WSDL 2.0
Standardizing WSDL: W3C and WSDL 2.0
As we mentioned earlier, WSDL is being standardized within the W3C. This work has
been underway since the spring of 2001, and its continuing at the time this book is
being written. This section will use the November 2003 version of the draft specifica-
tions that define WSDL 2.0. Well also sketch out how some of SkatesTowns WSDLs
might look using WSDL 2.0.
Whats New in WSDL 2.0
WSDL 2.0 represents a formal standardization of WSDL. Although the starting point was
WSDL 1.1, the W3C working group has made quite a few changes to the WSDL lan-
guage. There are so many changes, in fact, that the work that began with the name
WSDL 1.2 has been renamed WSDL 2.0.
The Web services community did all this work to standardize WSDL 2.0 for several
reasons:
n
Remove ambiguity in WSDL 1.1For example, no one could agree on one standard
definition for solicit-response style of message exchange (transmission primitive).
n
Align and/or simplify language semanticsFor example, it was confusing to some
XML users that the WSDL 1.1 import element wasnt the same semantically as
XML Schemas import element.
n
ConsistencyFor example, all WSDL elements allow open content (elements from
other XML namespaces can be inserted as children).
n
Better namingSeveral elements have new names; for example port and portType
have been renamed to the more resonant terms endpoint and interface.
n
SimplificationCertain complexities that many found confusing (such as WSDL
message and WSDL part) have been removed and replaced with straightforward
XML constructs.
n
New functionalitySeveral important new mechanisms have been introduced into
the language, such as interface extension.
n
Removal of functionalitySome things have been removed. For example, WSDL 1.1
allowed operation overloading (multiple operations with the same name but differ-
ent input or output messages), but WSDL 2.0 doesnt.
The WSDL 2.0 language is defined by three related specifications:
n
Part I, the Core Language
n
Part II, Message Patterns
n
Part III, Bindings
The language used within the specifications has also been greatly formalized. Although
this makes the documents harder to read and understand, it removes ambiguity. Happily,
224 Chapter 4 Describing Web Services
there will be a primer, which will help people understand how to use WSDL 2.0. Of
course, books like this one will also be of great aid to the Web service designer.
Overview of WSDL 2.0
Lets examine the contents of WSDL 2.0 in roughly the same order we examined the
contents of WSDL 1.1. For each component of WSDL, well examine what is new,
sketch out how SkatesTown might use WSDL 2.0, and summarize the changes in a table,
using the following notational convention:
+ Feature added
- Feature removed
Feature changed
interface
What we knew as a portType in WSDL 1.1, well know as an interface in WSDL 2.0.
This represents more than just a name change, and an intuitive one at that. The inter-
face element introduces an important new concept: interface extension g. It lets you
build a Web service interface by combining the operations defined in other
interfaces. For example, the StockAvailableNotification interface could be refac-
tored to define a notification interface as follows:
<interface name=Notification ...>
<operation name=registration ...>
...
<operation name=notification ...>
...
<operation name=expirationNotification ...>
...
<operation name=cancellation ...>
...
</interface>
The StockAvailableNotification interface could extend it as follows:
<interface name=StockAvailableNotification
extends=sa:NotificationPortType ...>
<operation name=xyz ...>
...
</interface>
In this way, Notification is separated out as an interface that can be utilized by other
business functions besides stock availability. By using the extends attribute, the
StockAvailableNotification interface includes the operations defined in the
Notification interface. In fact, the extends attribute can have a list of QNames;
thereby an interface can extend multiple interfaces. This is similar to interface inher-
itance in Java.
225 Standardizing WSDL: W3C and WSDL 2.0
WSDL 2.0 is also much more consistent with respect to extensibility. All elements,
including interface, can have child elements from other namespaces. Lack of open
content in the WSDL 1.1 portType was constraining; for example, it wasnt legal to add
elements to describe nonfunctional characteristics of a portType. Another extensibility
mechanism, called features and properties, can be used, but well examine that feature
later.
Another thing added to the interface component is a styleDefault attribute. This
optional attribute lets you describe a style of message-exchange pattern that applies to all
the child operations of the interface. WSDL 2.0 defines several styles, including
RPC style and two attribute-related styles (Get-attribute and Set-attribute) that desig-
nate certain operations as getter and setter operations (like the JavaBeans pattern). Well
examine message exchange patterns later, when we look in more detail at WSDL 2.0
operation elements.
WSDL 2.0 includes a new concept called features and properties. This concept is used
to describe abstract functionality and associated it with various components of a WSDL
2.0 description. This concept is similar to the corresponding feature and module concept
in SOAP 1.2. The notion is that a particular feature name and set of corresponding
properties is standardized by a discipline. For example, the security community might
standardize authentication, authorization, and other features and corresponding proper-
ties. Designers would then include these features in their interface definition (and
other WSDL 2.0 elements) to specify that their Web service uses the feature and how it
constrains the properties associated with that feature. Clearly, the features and properties
concept is useful to describe the nonfunctional characteristics of a Web service.
Unfortunately, we already have a mechanism called WS-Policy to do this for us.Youre
advised to examine how the Web services community figures out whether to use WS-
Policy or WSDL 2.0 features and properties to describe things like security, reliability,
transactionality, and so on. At the moment, specifications such as WS-Security use WS-
Policy, not features and properties.
Heres a summary of the changes to the interface:
Name changed to interface from portType
+ Interface extension (aggregation of operations from multiple interfaces)
+ Open content
+ styleDefault attribute
+ Features and properties
operation
The biggest change to operation is the clarification of the transmission primitives.
Whereas WSDL 1.1 defined four (in, in-out, notification, and solicit-response) and only
the first two were ever properly understood and implemented, WSDL 2.0 defines the
concept of a message exchange pattern that allows myriad different patterns to be stan-
dardized and therefore allows interoperability between implementations. Message exchange
226 Chapter 4 Describing Web Services
patterns gare described in a separate specification for WSDL 2.0 (Part II). They
describe abstract templates for a set of messages between senders and receivers. The num-
ber (cardinality) of messages, the sequence of messages, the direction in which the mes-
sages are sent (relative to the provider of the service being described by the WSDL), and
the possible placement of fault messages are codified by a message exchange pattern.
WSDL 2.0 Part II replaces the four transmission primitives defined in WSDL 1.1 with
nine standard patterns (of which only two are likely to ever be used with any regularity:
in-only and in-out).
For any given operation, you must specify which message exchange pattern is used.
For this, WSDL 2.0 defines the style attribute, which takes the URI name of the mes-
sage exchange pattern as its value. Although there can be many different types of message
exchange patterns, you really only have to know two of them: in-out (http://
www.w3.org/2003/11/wsdl/in-out) and in-only (http://www.w3.org/2003/11/
wsdl/in-only). The PriceCheck interface from SkatesTown demonstrates this:
<interface name=PriceCheck>
<operation name=checkPrice
style=http://www.w3.org/2003/11/wsdl/in-out>
...
</operation>
</interface>
A major improvement to WSDL is the elimination of the WSDL message and part ele-
ments.You now use the names of XML element declarations to specify the format of
input, output, and fault messages. The checkPrice operation has a much simpler and
direct-looking signature:
<interface name=PriceCheck>
<operation name=checkPrice>
style=http://www.w3.org/2003/11/wsdl/in-out>
<input message=avail:sku/>
<output message=avail:StockAvailability/>
</operation>
</interface>
There is no more indirection to look up, from the input element, to the message ele-
ment, to the part element, to the actual XML element or type declaration. There is, of
course, an additional, optional messageReference attribute, which may appear when the
message exchange pattern being used may define multiple message components associat-
ed with the input or output roles. However, if you stick with the simple message
exchange patterns, you dont have to worry about naming your messages.
One aspect of WSDL 2.0 that reduces the flexibility of operations is forbidding oper-
ation overloading. If two operations have the same QName, then they must be equiva-
lent in structure (same input, output, and fault signature). To minimize the chances of
this error, when youre combining interfaces using the interface extension feature, make
227 Standardizing WSDL: W3C and WSDL 2.0
sure each operation defined by any interface in a WSDL namespace has a name that
is unique among all the operations in that namespace.
WSDL 2.0 also clarifies the role of faults. In conjunction with the formalization of
message exchange patterns, the WSDL 1.1 fault element has been divided into two ele-
ments: infault and outfault. Other than this clarification and the removal of WSDL
message and part, faults are much the same as in WSDL 1.1.
WSDL 2.0 operations may also include feature and properties elements.
Heres a summary of changes to operation (child of interface):
WSDL 1.1 transmission primitives clarified as message exchange patterns
+ style attribute
Abstract message format specified using XML types and elements; WSDL 1.1
message and part elements removed
- Overloading of operations
fault element becomes inFault and outFault
+ Features and properties
types
The major change to the types element is that its content has been clarified. Whereas in
WSDL 1.1 it was only by convention and profiling that you learned to put XML
schema elements under a types element, WSDL 2.0 clarifies that the content should
only be an XML schema element or an XML import element. Note that WSDL 2.0,
like WSDL 1.1, lets you use other type systems besides XML schema.
Heres a summary of changes to types:
Clarifies contents as either an XML schema element or XML import element
binding
The WSDL 2.0 binding is similar to the WSDL 1.1 binding. The changes are due to
the structural changes within interface, particularly the removal of the WSDL message
and part.
The binding is also used to bind features and properties that are specified for an
interface. For example, if an interface specifies that an authentication feature is being
used, a binding can specify that this feature is realized by some form of Kerberos ticket
that must be presented as proof of identity to authenticate the requestor.
The other major change is in the set of binding types standardized by WSDL 2.0.
WSDL 1.1 standardized a SOAP 1.1, an HTTP, and a MIME binding type. WSDL 2.0
defines an HTTP and a MIME binding, but it defines a SOAP 1.2 binding type instead
of a SOAP 1.1 binding type. The details of the SOAP 1.2 binding are similar to the
SOAP 1.1 binding; the changes are due to the restructuring of the way messages are
defined in operations at the interface level, and additional elements were added, such
as a module specification element and a property constraint element, to reflect new
228 Chapter 4 Describing Web Services
concepts introduced by SOAP 1.2. Heres an example binding of SkatesTowns
PriceCheck interface:
xmlns:soap=http://www.w3.org/2003/11/wsdl/soap12
...
<binding name=PriceCheckSOAPBinding type=pc:PriceCheck>
<soap:binding
protocol=http://www.w3.org/2003/05/soap/bindings/HTTP/ />
</binding>
If you dont need to specify SOAP headers or binding-specific properties and features,
youre done. Note that a lot of the complexity, like document versus RPC and encoded
versus literal, has gone away. Message exchange patterns have addressed document versus
RPC (this is now specified using the style attribute).
Heres a summary of changes to binding:
Binding of operations changed due to changes in operations at the interface
level
+ Features and properties
- encodingStyle and use attributes
SOAP 1.2 binding replaces SOAP 1.1 binding
endpoint
The endpoint element is a renaming of the port element from WSDL 1.1. Otherwise,
its the same.
The summary of changes to endpoint is simple:
Change of name from port to endpoint
service
Service has been changed slightly to clarify that the constituent endpoint elements
represent alternative implementations of the same interface. An interface attribute
was added to specify which interface is implemented by the service. Heres what the
PriceCheck service looks like in WSDL 2.0:
<service name=PriceCheck interface=pc:PriceCheck>
<endpoint name=PriceCheck binding=pc:PriceCheckSOAPBinding>
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</endpoint>
</service>
As you see, not much has changed in this area that affects SkatesTown.
The summary of changes to service is as follows:
+ Added the notion that a service implements a single interface
All endpoints (was port) child elements must refer (via binding) to the same
interface
229 Standardizing WSDL: W3C and WSDL 2.0
definitions
The only change to the definitions element in WSDL 2.0 is the removal of the name
attribute. It was at best documentation in WSDL 1.1, and by removing it, WSDL 2.0 is
slightly simpler with no loss of function.
Heres a summary of changes to definitions:
- The name attribute
import and include
The way that WSDL documents are linked together has changed in WSDL 2.0. WSDL
1.1 used a single element, import, to link any WSDL document, regardless of namespace.
WSDL 1.1 also was never clear about which types of documents could legally be
imported. WSDL 2.0 clarifies this area. First, it introduces an additional element,
include, which divides the work between import and include. The import element is
used to import WSDL definitions from other WSDL namespaces. The include element
is used to include elements defined in different documents, but with the same name-
space. This semantic is analogous to the way import and include work in XML schema.
The summary of changes to WSDL document linking is as follows:
Clarified the content of the import element to refer to WSDL documents only
+ include element, aligning WSDL document linking with XML schema docu-
ment linking
A Complete WSDL 2.0 Description
Although we havent exhaustively reviewed all the details of WSDL 2.0, we have given
you an idea of the scope of the changes. We end this section with a sketch of what
Skatestowns PriceCheck service looks like, in its entirety, in WSDL 2.0 (see Listing 4.5).
Listing 4.5 PriceCheck Service described in WSDL 2.0
<?xml version=1.0 encoding=UTF-8?>
<definitions
targetNamespace=http://www.skatestown.com/services/PriceCheck
xmlns:pc=http://www.skatestown.com/services/PriceCheck
xmlns:avail=http://www.skatestown.com/ns/availability
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:soap=http://www.w3.org/2003/11/wsdl/soap12
xmlns:wsdl2=http://www.w3.org/2003/11/wsdl
xmlns=http://www.w3.org/2003/11/wsdl>
<!-- Type definitions -->
<types>
<xsd:schema
targetNamespace=http://www.skatestown.com/ns/availability
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
230 Chapter 4 Describing Web Services
<xsd:element name=sku type=xsd:string />
<xsd:complexType name=availabilityType>
<xsd:sequence>
<xsd:element ref=avail:sku/>
<xsd:element name=price type=xsd:double/>
<xsd:element name=quantityAvailable type=xsd:integer/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name=StockAvailability
type=avail:availabilityType />
</xsd:schema>
</types>
<interface name=PriceCheck>
<operation name=checkPrice>
style=http://www.w3.org/2003/11/wsdl/in-out>
<input message=avail:sku/>
<output message=avail:StockAvailability/>
</operation>
</interface>
<binding name=PriceCheckSOAPBinding type=pc:PriceCheck>
<soap:binding
protocol=http://www.w3.org/2003/05/soap/bindings/HTTP/ />
</binding>
<service name=PriceCheck interface=pc:PriceCheck>
<endpoint name=PriceCheck binding=pc:PriceCheckSOAPBinding>
<soap:address location=http://www.skatestown.com/services/PriceCheck/>
</endpoint>
</service>
</definitions>
Summary
We began this chapter with a question: How does the requestor know what message for-
mat should be used to invoke a Web service? We motivated the role of service descrip-
tion within a service-oriented architecture and explained how service description was
the basis for the publish, find, and bind operations. We reviewed the characteristics of a
well-defined service. The basis of a well-defined service is an IDL-level description of its
Listing 4.5 Continued
231 Resources
interface, described using version 1.1 of the Web Services Description Language
(WSDL) together with the nonfunctional characteristics of the service described using
WS-Policy. We reviewed the WSDL 1.1 language in great detail, using Web service
descriptions for several of SkatesTowns services. We also sketched how WSDL relates to
Java programming language artifacts, using the mapping implemented by the Axis tooling
as an example.
We also discussed nonfunctional descriptions of Web services, focusing on the use of
WS-Policy. We concluded this chapter by discussing the current direction of WSDL, the
WSDL 2.0 work within the W3C.
In the next chapter, well describe the Axis project and how it supports Web services,
including how it uses WSDL to generate Java code for the client and the server.
Resources
n
WSDL 1.1Web Services Description Language (WSDL) 1.1, (W3C Note 15
March 2001), http://www.w3.org/TR/wsdl
n
WSDL 2.0Web Services Description Language (WSDL) Version 2.0 Part 1:
Core Language (W3C Editors copy $Date: 2004/03/25 17:03:13 $),
http://www.w3.org/2002/ws/desc/wsdl20
n
WS-PolicyWeb Services Policy Framework (WSPolicy) (28 May 2003),
http://www-106.ibm.com/developerworks/library/ws-polfram/
n
WS-PolicyAttachmentWeb Services Policy Attachment (WSPolicyAttachment) (28
May 2003), http://www-106.ibm.com/developerworks/library/ws-polatt/
5
Implementing Web
Services with Apache Axis
IN THIS CHAPTER, WERE GOING TO DIVE into the Apache Axis package, which we
briefly introduced in Chapter 3, The SOAP Protocol.To whet your appetite before we
do, heres a high-level list of what Axis provides, and therefore what well be talking
about:
n
A set of client-side APIs for dynamically invoking SOAP Web services (with or
without WSDL descriptions)
n
Tools to translate WSDL documents into easy-to-use Java frameworks for either
consuming or supplying Web services
n
Mechanisms for hosting your Web services either within a servlet container (such
as Tomcat, or any J2EE application server) or via a standalone server
n
A framework that lets you create and compose message processing components
(called handlers) into flexible and powerful processing chains
n
A set of APIs for manipulating SOAP envelopes, bodies, and headers, and using
them inside Message objects, which can also contain attachments (pieces of binary
data outside the SOAP envelope)
n
A transport framework that allows pluggable usage of a variety of underlying trans-
port mechanisms (such as JMS, email, or anything youre inspired to write compo-
nents for)
n
Data binding, which enables mapping Java classes into XML schemas and vice
versa
Theres a lot to cover, and we dont mean for this chapter to be a complete reference to
all the features of Axis (otherwise it might take up half the book!). However, by the end
of the chapter, you should be comfortable enough using most of this functionality to
build and consume basic Web services on your own, and youll have a solid base from
234 Chapter 5 Implementing Web Services with Apache Axis
which to do more advanced work with the help of the Axis resources available through
Apache and elsewhere on the Net. Before we get into the architecture and APIs, lets
quickly discuss where Axis came from.
A Brief History of Axis
IBM contributed an early implementation of the SOAP protocol to Apache in 1999,
which became known as Apache SOAP. This implementation, based on earlier work
called SOAP4J, was a very functional library for processing SOAP messages, but it was
written in a monolithic styleone code path pretty much did everything. This didnt
jibe with the flexibility inherent in the SOAP extensibility model; so the Apache SOAP
community decided in 2000 that some major rearchitecting would help, both in terms of
extensibility and in hopes of increasing performance. As such, around the time of Apache
SOAP version 2.1, the development team started soliciting input on Apache SOAP 3.0, a
major refactoring/redesign of the codebase.
There was a lot of interest in the new project. Several proposals, all evincing similar
ideas, were submitted to the development list amidst active discussion. A face-to-face
design meeting took place at XML2000, including about 15 participants. That meeting
in many ways was the real birthplace of Axis.
Axis is short for Apache eXtensible Interaction System. This name was chosen instead
of Apache SOAP 3.0 (the original plan) because at that time, the XML Protocol work-
ing group at the W3C was just getting underway, and they believed the protocol was
going to be called XP rather than SOAPas such, having SOAP in the name seemed
retro. As it turned out, SOAP 1.2 is still SOAP, but the Axis name stuck, and it does a
good job of signaling the extensibility that was achieved with the new architecture.
The main architectural idea behind Axis is that of chains of message-processing components
that can be developed separately and assembled at deployment time. These components,
called handlers g, can each process portions of the message or do other custom work
and can be combined to generate powerful and flexible systems. Axis also made the
decision to switch from Apache SOAPs DOM-based XML processing to a faster (but
somewhat more complex) SAX system. Well get into more detail about these and other
elements of the architecture in the rest of the chapter.
JAX-RPC, JAXM/SAAJ, and JAXB
During the Axis development cycle, the Java Community Process (Suns consortium for
defining Java community standards) was working on a couple of JSRs (Java Specification
Requests) related to Web services. In particular, JAXM (XML messaging for Java) g
and JAX-RPC (XML based RPC for Java) gwere created, and JAXB (Java APIs for
XML data-binding) gwas also in process.
JAX-RPCs charter was to define standard APIs for implementing RPC-style Web
services in Java. Several Axis developers were in the JAX-RPC expert group and helped
to introduce a handler concept into the JAX-RPC spec (although the JAX-RPC
version is different than Axiss handlers, as youll see later).
235 A Brief History of Axis
JAXM was originally the JSR that defined the javax.xml.soap package, which con-
tained the APIs for manipulating the SOAP data structures (envelopes, bodies, and so
on), messages, and attachments. Once JAX-RPC realized a need for these same APIs, the
JAXM group refactored the SOAP APIs into a separate specification called SAAJ (Soap
with Attachments API for Java) g. Axis contains SOAP message classes that implement
the SAAJ specification, as well as functionality beyond what SAAJ specifies.
Axis 1.2 (the latest version) implements the current JAX-RPC and SAAJ specs, and
Axis developers sit on the JAX-RPC 2.0 expert group, who are in the midst of revamp-
ing the JAX-RPC specification for greater functionality and ease of use. Later in the
chapter well point out a few of the differences between what JAX-RPC currently
defines and what Axis provides beyond that.
Current State of the Project
The Axis team, which includes several of the authors of this book, brought the project to
a 1.0 release in October 2002.Version 1.1, with many improvements, followed in 2003.
As this book is going to press, Axis version 1.2 is on its way to release, so the APIs we
cover are from the 1.2 beta version. Although there was a lot of API churn during the
first few versions of Axis, things have stabilized greatly, and changes are now much slower
and more managed. This means you can count on the APIs we cover here to be the way
to do things for a while.
While the Axis team is happy with 1.2 and the progress weve made toward making a
usable and interoperable package, there is a lot of room for improvement. At the end of
this chapter, well discuss some of the changes and new features we expect to see in
future versions of Axis.
Installing Axis
You can obtain the latest version of Axis by going to http://ws.apache.org/axis and clicking on
the Downloads link on the left side of the page. Instructions are included with the package in the docs/
directory.
Installing Axis on most servlet engines is as easy as dropping a Web application into your deployment direc-
tory. In the Axis installation, youll find a webapps directory, underneath which is an axis/ directory.
Copy that directory into your servlet engines deployed webapps directory (for Apache Tomcat, this would
be tomcat/webapps), and you should be done. Some other servlet engines may have more complex pro-
cedures for installing webapps; please read the documentation for your particular system (as well as the
installation guide from Axis) to make sure you follow the correct steps.
To check that the installation is running correctly, try to access the app with a Web browser. If youre using
Tomcat, this will mean pointing the browser at http://localhost:8080/axis/ (adjust as appro-
priate for other servlet enginesrefer to the documentation for the servlet engine). On the start page, you
should see a link to verify your installations configuration; click it to make sure Axis is finding all the com-
ponents it needs (see the section Development/Debugging Tools for more details on the happyAxis page).
236 Chapter 5 Implementing Web Services with Apache Axis
To use the Axis client software well be describing, youll also need to make sure the JARs in the Axis distri-
butions lib/ directory are all on your classpath. These include
n
axis.jar
n
commons-discovery.jar
n
commons-logging.jar
n
log4j-1.2.8.jar
n
wsdl4j.jar
n
jaxrpc.jar
n
saaj.jar
Axis Architecture
Axis was designed to be usable in a wide variety of environments by users of varying
skill levels and interests. As such, the system can be considered in a number of chunks,
each of which can be used without needing to know much about the others. This sec-
tion will cover the essential architecture of Axis. Well give you a 20,000-foot view, and
then in the following sections cover each area in more detail.
Handlers and Chains: Concepts
Axis is all about chains of message processing components that work together to handle
(receive, process, and produce) SOAP messages. These components are called handlers, and
they are all Java classes based around a simple interface:
void invoke(MessageContext context) throws AxisFault;
This method, from the org.apache.axis.Handler interface, is the central thing that
handlers need to implement. When invoked, each handler does whatever it has been
built to do. That might involve reading or writing pieces of a SOAP message, logging
information to a database, checking a users authentication credentials, or anything else
you might imagine. Axis comes with prebuilt handlers for common tasks such as author-
ization and session management, and end-users and third parties are building more all
the time.
The MessageContext g(see Figure 5.1) is even more central to Axis than handlers.
This class represents all the information relevant to a particular SOAP interactionin
particular the request message, the response message, and a bag of properties that allow
viewing and controlling the behavior of the system. There are also a few other special
fields outside the bag. A given MessageContext is passed from handler to handler to
process a particular interaction (Web service invocation).
Handlers can be combined into chains gwhich, as it turns out, look just like han-
dlers to the system. In other words, the chain classes that group handlers together also
implement the Handler interface and can themselves be invoked. This is a classic exam-
ple of a composable architecture: When a chains invoke() method is called, it calls
237 Axis Architecture
invoke() on each of its constituent handlers, which themselves might be chains. This
method of grouping and composition allows you to build sets of handlers that work
together to accomplish a task, and then use the whole set as a pluggable unit.
Response Message
Request Message
Properties
Other Fields
Other Fields
Other Fields
MessageContext
Handler1 Handler2
Simple Chain
Handler3
Figure 5.1 The Axis MessageContext
Axis uses two types of chains: simple chains gand targeted chains g. A simple chain is
a list of handlers that should be invoked in order (see Figure 5.2). A targeted chain (see
Figure 5.3) is a little different; instead of a linear list of handlers, a targeted chain has
exactly three handlers it cares about: the request handler, the pivot ghandler, and the
response handler.
Figure 5.2 A simple chain
Targeted chains are built with the idea that the pivot handler is the place where the real
work happens. For instance, deployed services in Axis are targeted chains, and the pivot
handler calls the Java class youre exposing as a Web service. Before the pivot, the request
handler (which is usually a chainas such, well often refer to the request chain and the
response chain) does preprocessing work. After the pivot, the response handler does post-
processing work. On the server side, this means the request chain generally operates on
the incoming request message, and the response chain generally operates on the outgo-
ing response message generated by your service.
238 Chapter 5 Implementing Web Services with Apache Axis
Figure 5.3 A targeted chain
Server-Side Message Processing
Now lets talk about how Axis processes messages. First well walk through the message
flow on the server side, and then well turn our attention to the client.
Look at Figure 5.4, which is a graphical representation of the server-side message
flow through Axis. When you use Axis as a SOAP server, the first thing that happens is
that a transport listener receives a message. Transport listener gis a fancy term for any
software that can take input and turn it into something that Axis understands. For exam-
ple, this might be a servlet, a JMS (Java Message Service) listener, or a class youve writ-
ten to poll a directory for new files in which you expect to find SOAP messages. The
transport listener is the primary piece responsible for implementing the rules of a partic-
ular SOAP binding, as described in Chapter 3.
Request
Handler
Response
Handler
Targeted Chain
Pivot
Handler
Entry
Point
= Handler
AxisServer
Transport
Chain
(http)
Global
Chain
Service
Message
Context
HTTP
Listener
(Axis Servlet)
1
9
8
2
3
4 5
6 7
Figure 5.4 Axis server-side message processing
239 Axis Architecture
The listener were most often interested in is the built-in HTTP listener, which is imple-
mented as a servlet (class org.apache.axis.transport.http.AxisServlet). This class
accepts an HTTP request (step 1 in the diagram), wraps it in an Axis Message class, and
then puts that Message into a MessageContext and hands it to an AxisServer g(step
2), which is the main server-side processing class.
The transport listener, in addition to the message itself, has loaded up the
MessageContext with various properties that it thinks might be useful for the engine to
know. These properties might include generic things like the time the message was
received, and also transport-specific details such as a reference to the HTTP headers in a
format that the servlet understands.
One of the special properties the listener must set is the transport name (the
MessageContext has a specific accessor for this field, instead of carrying it in the proper-
ty bag). The listener might have a transport name compiled into its code, or it might get
the name from a configuration parameter. The transport name allows you to configure
different kinds of functionality for different endpoint addresses.
Transport-Specific Message Processing
The first thing the AxisServer does when it starts processing the message is to look for
a transport chain (a targeted chain) whose name matches the transport name in the
MessageContext. If it finds one, it hands the MessageContext to the request handler of
that targeted chain before doing anything else. This allows the server to implement
transport-specific processing.
Transport-specific processing consists of any work that closely relates to the transport
over which a message was received. Examples include anything that deals with HTTP
headers for an HTTP transport. The general goal of the transport-specific handlers when
receiving a message is to take protocol-specific data and make the appropriate parts of
that data available in a more general way; ideally, the handlers after the transport-specific
chain shouldnt need to access the transport-specific portions of the MessageContext.
Heres an example that might make this easier to understand. HTTP has a built-in
authentication mechanism that lets you pass a username and password via an HTTP
header. Other transport protocols have the same concept of username/password, but they
pass that information in different ways. Its also possible to pass a username/password in a
SOAP header, which wouldnt be transport-specific. So, because we dont want to have
to write three or four different versions of code that checks username/password combi-
nations, we arrange for each transport-specific set of credentials to be turned into a stan-
dard username/password pair by handlers specific to that transport. Later handlers can
look for the generic version and wont need to worry about the transport-specific one.
Global Message Processing
After the transport-specific request processing completes without error, the server then
passes the MessageContext on to the global request chain (step 4). This chain contains
handlers that process every message that comes into the system, no matter the transport.
You might use the global chain gto implement sitewide security policies, for instance,
240 Chapter 5 Implementing Web Services with Apache Axis
or provide all deployed services with the ability to process a certain set of SOAP exten-
sions in a consistent fashion. Another typical global handler usage is for logging/manage-
ment.
Service Message Processing
After the global request chain is finished, the server needs to call a service handler that
does the real work of processing the message (step 5). Because Axis strives to be flexible,
there are a variety of ways the engine might figure out which service to call to process a
given message, which might include looking at the endpoint URL or content inside the
message. For now, lets assume that we know which service is relevant. (The service, like
the transport name, is also one of the special fields in the MessageContext that exist
outside the bag of properties.)
The service handler is a special kind of wrapper handler called a SOAPService g
(org.apache.axis.handlers.soap.SOAPService). This class is itself a targeted chain,
with a request and response chain inside it. This allows you to insert pre- and postpro-
cessing handlers into the flow that are specific to your service, which you might do to
implement service-specific extensions or management policies.
Inside the SOAPService is a special handler known as the provider gthat is respon-
sible for doing the real work of your Web service, including calling your service class.
Note that in order to make a successful Java call to a typical back-end Web service, Axis
has to use its type mapping gsystem to translate the incoming XML data into Java
objects and then again to translate the Java objects in the response back into XML.
The Provider
The provider is the pivot handler for the SOAPService, since its the point at which you
finish processing the request message, turn back toward the other direction, and continue
processing with the response message. So the MessageContext, perhaps laden with a
response message from your service, passes back out through the service-specific response
chain, then the global response chain (step 6), and finally through the transport-specific
response chain (step 7). All of these response handlers therefore get a chance to look at
or alter the response message as its on the way out of the system. This is the place you
might insert session-management headers, for instance, or encrypt the outgoing body if
appropriate.
Once the message comes back out of the AxisServer, the HTTP listener (the
servlet) takes the response message out of the MessageContext (step 8) and sends it back
to the client as an HTTP response (step 9).
Client-Side Message Processing
The AxisClient g(org.apache.axis.client.AxisClient) is the client-side equiva-
lent of the AxisServer class, and it handles the message flow through the various com-
ponents on the client. The Call gobject (org.apache.axis.client.Call), however,
is the main client-side entry point to Axis. Just as on the server side there is always a
transport listener that invokes the AxisServer, on the client side you always invoke the
241 Axis Architecture
AxisClient by using a Call, or, as youll see later, by using a custom-built stub that can
insulate you from some of the details of using the Call object. In other words, even
though the AxisClient does the work of moving the MessageContext through the
processing flow, you never call it directly.
Inside the AxisClient (Figure 5.5), the message flow looks similar to that of the
server, but the order of the transport/global/service processing flow is reversed. If you
think about it a moment, youll see why this makes sense: On the client, youre making a
request to a remote service via a transport such as HTTP. That means the last thing that
happens to an outgoing message before it gets sent is the transport-specific stuff. Before
that, any global request handlers that have been configured for your client get a chance
to examine or alter the request message; and before that, any service-specific request
handlers get to do the same.
Entry
Point
= Handler
AxisClient
Service
Global
Chain
Transport
invoke()
Transport
sender
Call
Object
1 2 3
4 5
Transport
protocol
request
Transport
protocol
response
Figure 5.5 Client-side message processing
The transport chain on the client side is a little different than the one on the server. On
the server, we only care about the request and response chains inside the transport, and
there isnt a specific pivot handler that the engine uses (the same is true of the global
handlers, by the wayonly the request and response parts are used, not the pivot). On
the client, however, the transport targeted chain does have a pivot handler, which is the
transport sender g. The sender is responsible for taking the request message out of the
MessageContext and sending it across the wire in a protocol-specific way. Then it
retrieves any response message from the other side and, just like the provider on the
server side, turns the processing around and lets the response bubble back through the
response chainsfirst transport, then global, then service. At that point, the Call object
regains control and takes responsibility for getting the results back to your application
code.
242 Chapter 5 Implementing Web Services with Apache Axis
The MessageContext and Its Many Uses
The MessageContext, as youve seen, is a central piece of the Axis architecture. The
main reason the MessageContext is so important is that it encourages a loosely coupled
architecture for message-processing components, which is both powerful and flexible. To
show you what we mean, lets consider an example.
Imagine that two handlers want to communicate with each other while processing a
message. In particular, lets posit a SOAP header that contains a username/password com-
bination. The first handler wants to authenticate that the user checks out against the local
user database, with the correct password. It then wants to tell the second handler that the
user is OK, after which the second one will (later, on the response chain) include a spe-
cial header in the response message containing a promotional offer for current customers
(see Figure 5.6).
Handler1
(on request chain)
class Handler1 {
invoke (MessageContext)
{ see below}
}
process SOAP header
from request msg
if (authenticationOK(user)) {
// Tell handler2 directly
handler2.setUserIsOK(true);
}
Handler2
(on response chain)
class Handler2 {
boolean userIsOK;
setUserIsOK(boolean ok)
{
userIsOK = ok;
}
invoke (MessageContext)
{see below}
}
Boolean userIsOK =
mc.getProperty("UserIsOK");
code which uses property
Boolean userIsOK =
mc.getProperty("UserIsOK");
code which uses property
MessageContext
Property
UserIsOK
Value
true
Handler1 Handler3
(logging)
Handler2
Figure 5.7 Handler collaboration via the MessageContext
This is nothing groundbreakingits a lot like the blackboard style of software design
from the 1970s, and also somewhat reminiscent of tuple spaces. But its a simple and
potent way to build message-processing systems. Many Web service engines are using or
moving toward an architecture like this.Youll see more examples later in the chapter
that demonstrate the benefits of this architecture.
The Message APIs and SAAJ
Weve talked about handlers processing messages, but we havent yet shown you what
messages look like in Java. Lets remedy that with a brief spin through the Axis message
APIs and their parents, the standard SAAJ APIs.
244 Chapter 5 Implementing Web Services with Apache Axis
A Message by Any Other Name
If you ask the MessageContext for the request or the response message, you get back an
object of type org.apache.axis.Message (this class extends and makes concrete the
abstract SOAPMessage class from SAAJ). The main purpose of the Message is as a con-
tainer for two things: a SOAPPart gand zero or more AttachmentParts g. These
are called parts in order to mirror the WSDL 1.1 specificiation (see Chapter 4,
Describing Web Services).
Messages conforming to the simple SOAP HTTP binding will have only a SOAPPart
and no attachments. If the Message uses either SOAP with Attachments or DIME, how-
ever, then we expect to find both a SOAPPart and some number of AttachmentParts.
The SOAPPart gives you access to the SOAP envelope associated with the message, the
form of which well discuss more in the next section. The AttachmentParts each let
you access an abstract attachment represented by the Java Activation Frameworks
DataHandler object.
Dealing with attachments in detail is beyond the scope of this overview chapter, so
we refer you to the Axis documentation if youd like to learn more about it. Well con-
tinue here with the SOAP message APIs.
Accessing the SOAP Envelope, Bodies, and Headers
The SOAPPart allows you to get the SOAPEnvelope; and for handlers, especially the ones
dealing with headers, this is the object that matters. Some important classes involving the
SOAP envelope are laid out in Figure 5.8 and defined here:
MessageElement
SOAPEnvelope
SOAPHeader SOAPBody
SOAPBodyElement
SOAPFault
SOAPHeaderElement
Contains
Extends
Figure 5.8 The SOAP message classes
n
MessageElementAll the SOAP element classes in Axis inherit from
org.apache.axis.message.MessageElement as a common ancestor.
MessageElement implements the SAAJ SOAPElement interface but also lets you do
a number of interesting Axis-specific tricks. For instance, MessageElement allows
245 The Message APIs and SAAJ
you to access the contents of the element in XML-speak by asking for child ele-
ments, attribute values, and the likebut it also lets you ask for a Java representa-
tion of the deserialized element content. Well cover this in detail in Using the
MessageElement XML/Object APIs.
n
SOAPEnvelopeThis class represents the SOAP envelope. It directly contains
exactly one SOAPBody and zero or one SOAPHeaders. The Axis version (although
not the SAAJ one) has convenience methods that avoid having to deal with the
SOAPBody and SOAPHeader elements directly, so you can add a body element
directly to a SOAPEnvelope with envelope.addBodyElement(bodyEl), for
instance.
n
SOAPBodyThis class represents the <soap:Body> element and acts as a container
for SOAPBodyElements. SAAJ requires using this class explicitly, whereas Axis has
usually preferred to let the <soap:Body> and <soap:Header> elements be man-
aged entirely by the infrastructure.
n
SOAPBodyElementThese elements live in a SOAP body. They represent RPC
calls, SOAP faults, or anything else that goes directly inside <soap:Body>.
n
SOAPHeaderThis class represents the <soap:Header> element and acts as a con-
tainer for SOAPHeaderElements.
n
SOAPHeaderElementThis is a SOAP header (in other words, an element living
inside the <soap:Header> wrapper). A SOAPHeaderElement has all the APIs you
would expect in order to read and write the SOAP header attributes such as
mustUnderstand and actor/role. Each header also has a processed flag, which
can be manipulated with the setProcessed(boolean) API. This flag determines
whether the header has been understood (in the SOAP sense) by the endpoint; so
if your handlers process headers, they should remember to call
setProcessed(true) on them to avoid spurious MustUnderstand faults (see
Chapter 3).
n
SOAPFaultThis class extends SOAPBodyElement and represents a SOAP fault.
Accessors are available for all the common fault fields such as faultString,
faultCode, and so on.
To demonstrate the usage of the message APIs, well show you a piece of code that you
might put in a handler. Its purpose is to print to the console how many headers are in
the message, whether a particular header is present, and whether the SOAP body con-
tains a fault:
void invoke(MessageContext context) throws AxisFault {
// Get the SOAP envelope from the request message in the context
Message requestMsg = context.getRequestMessage();
SOAPEnvelope env = requestMsg.getSOAPEnvelope();
// Count the headers
246 Chapter 5 Implementing Web Services with Apache Axis
Vector headers = env.getHeaders();
System.out.println(There are + headers.size() + headers.);
// Check if a particular header is present.
SOAPHeaderElement header = env.getHeaderByName(http://example,
trigger);
if (header != null) {
System.out.println(Trigger header found.);
}
// Does the body contain a fault?
SOAPBodyElement body = env.getFirstBody();
if (body instanceof SOAPFault) {
System.out.println(First body element is a fault, code = +
((SOAPFault)body).getFaultCode().toString());
}
}
One of the other interesting things to note about these classes is that they all implement
(as of SAAJ 1.2) the DOM Element APIs as well as their special SOAP-aware APIs. As
such, you can take a SOAPEnvelope object and feed it into a standard DOM-based tool
such as an XSLT engine to get at the data that way.
The Axis Client APIs
The client APIs can broadly be split into two categories: dynamic invocation g, where
you use only preexisting Java classes to do your work, and stub generation, where a tool
generates code for you from WSDL descriptions. Both are handy, so well start with the
dynamic invocation interface (DII), which involves using the Axis low-level client
objects directly.
The Service Object
Although the Call object, which youve seen in previous chapters, is the main entry
point for invoking services, there is a Service gobject
(org.apache.axis.client.Service) that deserves some attention as well.
The Service acts as a factory for Call objects, and it also stores useful meta-data
about the servicefor instance, the Service object is where the AxisClient instance
that processes your invocations lives, and its also where the type mappings for
XMLJava binding are stored. This meta-data lives on the Service and not the Call
itself, because each Call object represents a single invocation of a service. The Service
object can generate many Call objects, and since all those Call objects will talk to the
same Web service, it makes sense to keep the common meta-data in a single place instead
of repeating it for each Call.
A Service may or may not be associated with a WSDL description. If it is, you can
not only request generic Call objects but also Calls that have been preconfigured with
247 The Axis Client APIs
all the meta-data gleaned from the WSDL; in other words, you can get a Call for an
operation that knows all the datatypes for the parameters and return values for that oper-
ation.
JAX-RPC expects Service objects to come from somewhere in the system, usually
JNDI, preconfigured with the WSDL. As such, the Service API as defined by JAX-RPC
has no direct means to access WSDL documents for dynamic invocation. The Axis
Service object has a couple of constructors that let you do this association in a
simple way:
n
Service(URL wsdlLocation, QName serviceName)This constructor builds
you a Service object with meta-data initialized from the WSDL at the specified
URL. The second argument is the service QName from the WSDL, as described
in Chapter 4.
n
Service(String wsdlLocation, QName serviceName)This is just like the
previous constructor, except wsdlLocation is a String that may be a URL or
may also be a filename on the local filesystem, relative to the current directory.
The Axis Service object also has a no-argument constructor for use without a WSDL.
Using the Call Object for Dynamic Invocation
The Service object we just described is the standard JAX-RPC way to get Call
objects, using the createCall() method:
import org.apache.axis.client.Service;
import javax.xml.rpc.Call;
...
Service service = new Service();
Call call = service.createCall();
call.setTargetEndpointAddress(url);
Here you see the no-argument Service constructor that we mentioned earlier, which
creates a blank Service. Then the createCall() factory method, in this case with no
arguments (well cover other versions in a bit), creates a generic JAX-RPC Call object.
CreateCall returns a javax.xml.rpc.Call, but since were using Axis, we know that
this is really an org.apache.axis.client.Callif you need to use any of the Axis-
specific methods on the Call, you can downcast it to the Axis type.
We called setTargetEndpointAddress() after getting the Call; this allows us to pass
the endpoint URL of the target Web service to the newly created Call object. Axis also
provides a couple of direct constructors for the Call object that allow you to create a
Call pointing to a particular Web service endpoint:
Call(String endpoint)
Call(URL endpoint)
These constructors exist as convenience methods to avoid the need to use a Service
object directly.
248 Chapter 5 Implementing Web Services with Apache Axis
What can you do with a Call once you have one? You can explore the full Call API by
looking at the JavaDoc included with Axis, but well cover a few important pieces here
in our overview.
Invoking a Web Service
The main thing you want to do with every Call object is, at some point, invoke a Web
service. This is the purpose of the invoke() method, which has several different forms.
The data for any given invocation is typically handed to invoke() as an array of Java
objects. For RPC-style services, these objects are parameters for a remote method call,
and each one maps to an XML element inside a wrapper method call element on the
wire. For document-style services, there is generally only a single object in the array you
hand to invoke(), and it maps to the entire SOAP body for your operation.
Lets review an example of an RPC invocation a lot like the one from Chapter 3. It
calls the same doCheck() method on the InventoryCheck service, but with hard-coded
arguments for simplicity:
import org.apache.axis.AxisEngine;
import org.apache.axis.client.Call;
import org.apache.axis.soap.SOAPConstants;
/*
* Inventory check Web service client
*/
public class InventoryCheckClient {
/** Service URL */
static String url =
http://localhost:8080/axis/InventoryCheck.jws;
public static void main(String args[]) {
// Set up Call object using convenience constructor
Call call = new Call(url);
// Use SOAP 1.2 (default is SOAP 1.1)
call.setSOAPVersion(SOAPConstants.SOAP12_CONSTANTS);
// Set up parameters for invocation
Object[] params = new Object[] { SKU-56, new Integer(25) };
// Call it!
Boolean result = (Boolean)call.invoke(, doCheck, params);
// ...do something with the result
}
}
This code will end up producing a SOAP message that looks like this:
<soapenv:Envelope xmlns:soapenv=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
249 The Axis Client APIs
<soapenv:Body>
<doCheck soapenv:encodingStyle=http://www.w3.org/2003/05/soap-encoding>
<arg0 xsi:type=soapenc:string
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>947-TI</arg0>
<arg1 xsi:type=soapenc:int
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>3</arg1>
</doCheck>
</soapenv:Body>
</soapenv:Envelope>
Notice that its a SOAP 1.2 message, because we set the SOAP version using the
setSOAPVersion() API (the default is 1.1). It uses the SOAP RPC conventions, includ-
ing the SOAP encoding, because RPC style is the default for dynamic invocation. The
method name matches the one we passed in, and the parameters have the values we
passed in.
When you use this method of invocation, the arguments are called arg0, arg1, and so
on, in the generated SOAP message, because we havent given the engine a reason to call
them anything else. Most of the time, this isnt adequate, because the services youre call-
ing will require the parameters to have certain XML signatures (Axis is a notable excep-
tion in that it allows RPC parameters to be referenced by position and not just by
name). As such, youll need a way to control the names and format of the XML corre-
sponding to your parameters.
Taking Control of Parameters
If you want your parameters to serialize in particular ways instead of the defaults, you
can use the addParameter() APIs on the Call object to do so. This method also lets
you control the type of the parameter and the parameter mode (in, out, inout). The first
time you call addParameter(), you set all this information for the first parameter in the
arguments array that you pass to invoke(). The second time, you set it for the second
parameter, and so forth.
There are a number of different signatures for addParameter(), but the basic one is
addParameter(String paramName, QName xmlType, ParameterMode paramMode)
The arguments to this method are as follows:
n
paramNameThe name that should be used when serializing the parameter. A
quick note here: Then youre using SOAPs RPC conventions, parameters should
not have namespaces. Therefore this version of addParameter() is used. Another
version of addParameter() takes a QName instead of a String as the first argu-
mentthats the version to use if youre using document-style services and need
to control the namespaces of parameter elements.
n
xmlTypeA QName that represents the XML Schema type of the parameter. This
type is used by the type mapping system so you know how to correctly write and
read XML for this service.
250 Chapter 5 Implementing Web Services with Apache Axis
n
paramModeThe parameter mode, which is in, out, or inout (discussed in
Chapter 3). JAX-RPC, and therefore Axis, has a ParameterMode class
(javax.xml.rpc.ParameterMode) that contains constants for each of the three
modes.
Suppose our example changes to this:
// Set up Call object
Call call = new Call(url);
// Use SOAP 1.2 (default is SOAP 1.1)
call.setSOAPVersion(SOAPConstants.SOAP12_CONSTANTS);
addParameter(SKU,
org.apache.axis.Constants.XSD_STRING,
ParameterMode.IN);
addParameter(quantity,
org.apache.axis.Constants.XSD_INT,
ParameterMode.IN);
// Set up parameters for invocation
Object[] params = new Object[] { SKU-56, new Integer(35) };
// Call it!
Boolean result = (Boolean)call.invoke(, doCheck, params);
Then our SOAP message will change to look like this:
<soapenv:Body>
<doCheck>
<SKU xsi:type=xsd:string>SKU-56</SKU>
<quantity xsi:type=xsd:int>35</quantity>
</doCheck>
</soapenv:Body>
The XML has changed to match the parameter names passed to the Call.
Just as you can use addParameter() to set the types for outgoing XML, you can use
the setReturnType() API to tell the Call object what XML type to expect as the
return value from a Web service invocation. Doing so allows the correct deserialization
of XML that lacks xsi:type attributes. The setReturnType() method looks like this:
call.setReturnType(qname)
The qname argument is an XML type QName.You can find constants for most common
schema type QNames in the org.apache.axis.Constants class. For instance,
Constants.XSD_STRING is a prebuilt QName object with the namespace
http://www.w3.org/2001/XMLSchema and the localPart string. Axis natively sup-
ports all the basic XML schema types, and you can see what other types are supported
out of the box later in the type mapping section.
251 The Axis Client APIs
NOTE
JAX-RPC is much more stringent than Axis originally was about meta-data requirements for dynamic invo-
cation. In particular, JAX-RPC requires you to either specify all meta-data (both setReturnType() and
addParameter() for each parameter) or none. So, if you want to add parameter meta-data but not
specify a return type, youre out of luck. The same is true for setting the return type but not adding parame-
ter meta-data. The Axis team didnt agree with this restriction (the team thought the engine should be able
to work with as much meta-data as it had, since it could generally make good guesses) but needed to
implement it to pass the JAX-RPC compatibility tests.
Other invoke()s
The Call object supports a few other invoke() styles, which you can see by investigat-
ing the Javadoc or the source. One of note is SOAPEnvelope invoke(SOAPEnvelope
env), which allows you to construct your own custom SOAPEnvelope using the message
APIs, send it using the Axis framework to a given destination, and then access the SOAP
envelope returned from the service (after processing by the appropriate handlers, of
course).
Using the Call Object with a WSDL-Enabled Service
When you use one of the WSDL-aware Service() constructors, the Axis framework
extracts the same kind of Call meta-data we just spoke about (parameters, endpoint,
return types, and so on) from the WSDL description. So if you create Call objects and
specify which WSDL ports and operations they correspond to, you dont have to set the
meta-data yourself, which can be handy and is less prone to coding mistakes. The APIs
for creating Calls this way look like this:
n
service.createCall(QName portName)This accessor returns a Call that has
been initialized with the endpoint address referred to by the named port in the
WSDL. No other meta-data is set.
n
service.createCall(QName portName, QName operation)This version not
only sets the endpoint address but also preconfigures all the parameters and return
types for the specified operation. Note that if you try to call addParameter(),
setReturnType(), and so on, on the resulting Call object, youll get an
Exception because the configuration is already set.
n
service.createCall(QName portName, String operation)This is just like
the previous version, but it accepts the unqualified operation name from the
WSDL rather than a full QName.
Type Mapping Using the Call
Axis, as youve seen, has the ability to map XML to Java and vice versa. Well discuss the
type-mapping system in detail later; to control these mappings on the client side, you
need to know the following: A type mapping consists of an XML type (a QName), a
252 Chapter 5 Implementing Web Services with Apache Axis
Java type (a class), a serializer (to write Java to XML), and a deserializer (to write Java
from XML). To map types on the client side, you can use the Call objects
registerTypeMapping API (this API is Axis-specific, it isnt part of JAX-RPC):
call.registerTypeMapping(Class javaType,
QName xmlType,
Class serializerFactoryClass,
Class deserializerFactoryClass)
If you have a JavaBean class that represents a Product (with a name, sku, price, and so
on), you can tell Axis how to serialize that type as XML using the default Bean serializer
and deserializer that come with Axis:
call.registerTypeMapping(Product.class,
new QName(http://skatestown.com, Product),
BeanSerializerFactory.class,
BeanDeserializerFactory.class)
Setting Properties on the Call
The Call class has a setProperty() API that looks like this:
void setProperty(String name, Object value)
This method allows you to set properties on the Call object, just like you can set prop-
erties on the MessageContext. All the properties you set on the Call will be available to
every MessageContext that is created as a result of using that Call. This is an important
point, which well bring up again when we talk about the server and property scoping.
For now, just remember that you can use a Call object to make multiple invocations to
a given service (although only one at a time)each time a new MessageContext will be
created, and all handlers involved in the invocation will have access not only to the
MessageContext properties for that invocation, but also to the ones you set on the Call
that persist across invocations.
Using Sessions
As a client, sometimes youre calling services that perform the same functions the same
way regardless of who calls them (think getStockQuote()). At other times, though, its
important that the server remember who you are when youre making multiple service
callsfor instance, a shopping cart service needs to keep track of the items in your cart
and be able to sell them to you after you finish shopping. To do this, it needs some kind
of session gthat it uses to associate a set of data with your client.
Axis has some simple APIs for session support. The goal of these APIs is to support
session maintenance in a pluggable fashion, allowing you to use underlying session
mechanisms supplied by application servers to custom sessions implemented as SOAP
extensions.
In Axis terms, session maintenance is primarily a server-side concept. The server, by
handing the client back some kind of cookie, can then accept the same cookie from the
253 The Axis Client APIs
client on subsequent requests and know that the new requests are associated with the
same client that made the earlier one. This process enables the server to maintain state on
behalf of the client, which is often desirable for interactions that span multiple invoca-
tions.
Session Implementations
Axis has two built-in ways to maintain sessions across Web service connections. The first
method uses the standard HTTP session mechanism that is built in to any conforming
servlet engine; this mechanism uses HTTP cookies to store session state, and the actual
session data is kept by the servlet framework. The second method uses a custom imple-
mentation to store session data and passes session identifiers via protocol-independent
SOAP headers.
To support the first method, you need to use the HTTP transport (the default), and
you must call setMaintainSession(true) on either the Call or the Service (either
will work fine).
To use the second method, you must deploy the SimpleSessionHandler
(org.apache.axis.handlers.SimpleSessionHandler) that is included with Axis. Well
talk about how to deploy handlers using Web service deployment descriptors (WSDDs)
in a little while, but youll need to deploy this handler on both the global request and
response flows of the client in order for it to work.
Well also cover some other ways of implementing stateful Web services in Chapter 8,
Web Services and Stateful Resources.
Using Stubs and WSDL2Java
As you saw in Chapter 4, WSDL is a great machine-readable way to describe Web serv-
ices and the operations they can perform for you. To the Java developer, this is a boon
because tools can automatically do the kind of work weve been doing by hand with the
Call object and type mappings. Axis comes with such tools:
n
WSDL2Java pulls in WSDL descriptions and generates Java code. It can generate
both client stubs, which help call the service, and server-side implementation scaf-
folding, which makes it easy to build your own implementation of the service.
n
Java2WSDL is a command-line tool for taking Java interfaces and generating
WSDL.
n
At runtime, the Axis engine can automatically generate WSDL for deployed
services.
Stubs
A stub gis a Java class with a Java-friendly API that closely matches the Web service
interface defined in a given WSDL document. In other words, instead of using a generic
method like call.invoke() and having to typecast return values, youll see methods like
float getClosingPriceOnDay(String symbol, Date day). This is both easier to
254 Chapter 5 Implementing Web Services with Apache Axis
program and safer, since the compiler can check the types of your arguments and return
values instead of using Object and casts everywhere. Stubs are built using WSDL2Java.
Heres an example: Lets build a stub from the WSDL for the InventoryCheck service
from Chapter 3. We can obtain the WSDL by accessing http://localhost:8080/axis/
InventoryCheck.jws?wsdl (Figure 5.9 shows what this looks like in a browser). Now
that we know where the WSDL comes from, we can easily generate a Java client frame-
work to make calling the service extremely simple:
Figure 5.9 Obtaining the WSDL for the InventoryCheck service
% java org.apache.axis.wsdl.WSDL2Java
http://localhost:8080/axis/InventoryCheck.jws?wsdl
WSDL2Java will fetch the WSDL, parse it, and generate a Java stub for calling the serv-
ice in a type-safe, convenient fashion. Lets look at the classes WSDL2Java generates
for us:
n
InventoryCheckThis is the service interface that corresponds to the portType
in the WSDL. In JAX-RPC terms, this is known as the Service Endpoint Interface
(SEI) g.
n
InventoryCheckServiceThis interface is generated as per the JAX-RPC speci-
fication and allows type-safe access to the SEI from the locator class below. This
interface includes two important methods:
n
InventoryCheck getInventoryCheck()
n
InventoryCheck getInventoryCheck(URL url)
255 The Axis Client APIs
The first one gets an implementation of the InventoryCheck interface that will
call the exact endpoint specified in the WSDL. The second one gets an
InventoryCheck stub that uses the same WSDL interface and binding but points
at a different endpoint. This is especially handy when youre using proxies, inter-
mediaries, or test tools.
n
InventoryCheckServiceLocatorThis locator gclass implements the
InventoryCheckService interface and acts as the factory for stub instances. In a
full J2EE environment (as when youre calling Web services from EJBs or servlets),
the locator might look in a JNDI repository for pooled stub objects; but the
default behavior for Axis is to create stubs as needed.
n
InventoryCheckSoapBindingStubThis is the real core of our client, the class
that implements the InventoryCheck interface.
All these classes are in the Java package localhost.axis.InventoryCheck_jws because
the WSDL targetNamespace in this case is the endpoint URL http://localhost:
8080/axis/InventoryCheck.jws. WSDL2Java maps each XML namespace encountered
in a WSDL description into a Java package name (you can even control this mapping
with command-line options or a configuration file). The default mapping of a namespace
URL like http://www.skatestown.com/services would be to the package
com.skatestown.www.servicesin other words, you invert the domain name compo-
nents as you would for software developed by that company (com.skatestown.www) and
then add each succeeding component of the URL, changing slashes to dots
(.services).
WSDL2Java doesnt expect you to edit any of the generated classes yourself, so be
careful if you doif you run the program again on the same WSDL, it will regenerate
the Java source files, overwriting anything that might already be there. (There is one
exception, when you have WSDL2Java create a service implementation as described
later; since those classes are meant for editing, Axis wont overwrite them.)
Using the Generated Stub
Now that we have our stub class, lets test it:
import localhost.*;
class Tester {
public static void main(String args[]) throws Exception {
// Collect arguments from command line - SKU and quantity
String sku = args[0];
int quantity = Integer.parseInt(args[1]);
// Get our stub from the locator object
InventoryCheckLocator locator = new InventoryCheckLocator();
InventoryCheck stub = locator.getInventoryCheck();
256 Chapter 5 Implementing Web Services with Apache Axis
// Call the Web service
boolean result = stub.doCheck(sku, quantity);
if (result) {
System.out.println(
Confirmed - the desired quantity is available.);
} else {
System.out.println(
Sorry, the desired quantity is not available.);
}
}
}
When we try it, the result is as follows:
% java Tester SKU-56 66
Sorry, the desired quantity is not available.
This is nice compared to the dynamic invocation example you saw earlierinstead of
having to type the operation name into a string argument of a generic invoke() call, we
have a doCheck() method right on the stub. Likewise, instead of having to pass a generic
array of Objects as arguments, doCheck() takes exactly what we need: a String and
an int.
Generating Test Cases
WSDL2Java can also build a test client for your services, which can be handy especially
during the development process. By specifying the -t option to the tool, WSDL2Java
will generate, in addition to the stub, a file called <name>TestCase.java, where <name>
is the name of the service. This file is a JUnit gtest case skeleton (for more about
JUnit, see http://junit.org).Youll notice that the generated test templates exercise
each of the Web services operations, but the values that are sent are all zeros and nulls
and there is no logic for checking the semantics of the service. As the comments in the
generated source indicate, that functionality is up to you to add, since the WSDL doesnt
describe the behavior of the servicejust the data formats that are accepted and returned.
Holders: Mapping inout/out Parameters to Java
SOAP and WSDL both support the idea of out and inout parameters for RPCsvalues
that come back from a service request in addition to the actual return value. Languages
with native support for these concepts allow you to pass a variable to a method; then,
when the method returns, the variable might have a different value. To support this idea
in Java, we need to build something on top of the basic language: a holder gclass.
A holder class is just what it sounds like: an Object that holds a value of a particular
type. So an IntHolder looks like this:
class IntHolder {
private int value;
257 The Axis Client APIs
public int getValue() { return val; }
public void setValue(int v) { val = v; }
}
You can pass one of these objects to a method, and the method code can update the int
value inside the holder so that the caller can use the new value:
void myMethod(IntHolder inOutParam) {
// Add five to the passed value
inOutParam.setValue(inOutParam.getValue() + 5);
}
When WSDL2Java notices out or inout parameters in operation descriptions, it auto-
matically generates signatures that include holder classes.
Automatic Type Mapping with WSDL2Java
Most services arent as simplistic as the examples youve seen so far. In particular, as
youve learned in the past few chapters, complex XML datatypes are an important part
of most Web services, and these types are typically described using XML schema embed-
ded within, or referenced from, WSDL descriptions. This is why WSDL2Java has been
built to help you turn WSDL services into Java stubs and also turn all the XML
datatypes referenced by those services into easy-to-use Java classes.
Lets look at an example that uses a slightly modified version of PriceCheck.wsdl
from Chapter 4. The only change is to replace the http://www.skatestown.com/
services/PriceCheck endpoint with http://localhost:8080/axis/services/
PriceCheck so we can access the locally installed service on our machine. Notice that
this service uses an AvailabilityType complex type as its return value. Pointing
WSDL2Java at that file as follows:
% java org.apache.axis.wsdl.WSDL2Java PriceCheck2.wsdl
results in the following set of files in the package
com.skatestown.www.services.PriceCheck:
n
PriceCheckPortType.javaThe actual service interface
n
PriceCheck.javaThe locator interface
n
PriceCheckSOAPBindingStubThe actual stub class
n
PriceCheckLocatorThe locator implementation
Heres the new part: Notice that there is another package,
com.skatestown.www.ns.availability (because the namespace was
http://www.skatestown.com/ns/availability), in which we find
AvailabilityType.java, the Java type corresponding to the XML type
availabilityType. Taking a look at the new class, we find a JavaBean with data mem-
bers corresponding to the XML elements inside the complexType:
258 Chapter 5 Implementing Web Services with Apache Axis
public class AvailabilityType implements java.io.Serializable {
private java.lang.String sku;
private double price;
private java.math.BigInteger quantityAvailable;
...
Most of the class methods are getters/setters for these fields. There are also prebuilt
implementations of the Java equals() and hashCode() methods, and the class also con-
tains built-in meta-data (a static method called getTypeDesc()) for ensuring that the
generated Java will always correctly map to the original XML.
Taking a look at the PriceCheckPortType interface, you can see the checkPrice()
method:
public interface PriceCheckPortType extends java.rmi.Remote {
public com.skatestown.www.ns.availability.AvailabilityType
checkPrice(java.lang.String sku) throws java.rmi.RemoteException;
}
Since the WSDL indicates that checkPrice() returns an availabilityType in the
http://www.skatestown.com/ns/availability namespace, the Java method therefore
returns our com.skatestown.www.ns.availability.AvailabilityType class.
Of course, the engine still needs to know how to map the XML type to the Java
typethat is accomplished by some generated code in the Stub class that ends up call-
ing the Call.registerTypeMapping() API we spoke about earlier. Its a lot better than
doing all that work manually.
Web Service Deployment Descriptor (WSDD)
How does the engine know where all its handlers come from, in what order to call
them, and how to configure them? Not to mention other things like its own configura-
tion options and type mappings? Thats where WSDD comes in.
NOTE
All WSDD elements are in the WSDD namespace, http://xml.apache.org/axis/wsdd/. When
discussing these elements in this chapter, we always assume that the default namespace is the WSDD
namespace; so when we say <deployment>, its the same as <wsdd:deployment
xmlns:wsdd=http://xml.apache.org/axis/wsdd/>.
WSDD is an XML format that Axis uses to store its configuration and deployment
information. The Axis server keeps its configuration in a file called server-
config.wsdd, and the Axis client has an equivalent client-config.wsdd. These files
both have default versions that are stored in the axis.jar itselfthis means if there isnt
a WSDD file in the appropriate directory, there will always be some reasonable configu-
ration.
259 Web Service Deployment Descriptor (WSDD)
When using Axis in a web application, the server looks for a server-config.wsdd in
the WEB-INF/ directory of your Web application, and then it looks in the classpath. When
using the Axis client code, the client looks for any necessary client configuration in a
client-config.wsdd file in the directory from which youre running the client and
then in the classpath.
The root element of these WSDD files is <deployment>. Inside the top-level element
is a <globalConfiguration> element, which contains options for the Axis engine
(client or server) as a whole, plus definitions for the global request and response chains.
Heres an example:
<globalConfiguration>
<parameter name=defaultSOAPVersion value=1.2/>
<requestFlow>
<handler type=java:org.apache.axis.handlers.LogHandler/>
</requestFlow>
<responseFlow>
</responseFlow>
</globalConfiguration>
The <parameter> declarations inside <globalConfiguration> are for setting options on
the AxisEngine gobject, which is either an AxisServer or an AxisClient. Particular
options allow you to control the default SOAP version (as in our example), whether the
engine should default to sending xsi:type attributes, and a variety of other things. (See
the Axis documentation for more.) Also note that you can set custom options as well as
the ones Axis defines.
The <requestFlow> and <responseFlow> elements define the request and response
global chains, as we described earlier. They can contain either <handler> elements or
<chain> elements (both of which are described more fully later). The components inside
<requestFlow> and <responseFlow> are invoked during the processing of a message in
exactly the order that they are declared in the XML file. In the previous example, the
LogHandler will be invoked on the global request chain, and no handlers are defined on
the global response chain (we could therefore have omitted the empty <responseFlow>
element).
Handler Declarations
Handler declarations tell Axis that a given Java class is a handler, configure it with a set of
options, and optionally name that configuration so you can easily refer to it later. A han-
dler declaration looks like this:
<handler [name=name] type=type>
<parameter name=name value=value/>
</handler>
Handler declarations can appear as immediate children of <deployment>, or inside a
<chain>, <requestFlow>, or <responseFlow> (all of which youll see in a moment).
260 Chapter 5 Implementing Web Services with Apache Axis
The type attribute tells you what kind of handler this is. The value for this attribute
is either a QName in a special Java namespace or the name of a previously defined han-
dler/chain.
Axis has a special namespace for Java types, which is usually defined with the prefix
java, like this:
xmlns:java=http://xml.apache.org/axis/wsdd/providers/java
If you use a type value with this namespace, youre declaring a handler based on a Java
class. For instance:
<handler type=java:org.apache.axis.handlers.LogHandler/>
Sometimes, especially if youll be using several instances of the same class in a WSDD
file, its nice to have shorthand to avoid typing the whole class name in each handler
declaration. If the optional name attribute is specified, youre labeling a particular handler
configuration that can be used again later by using the name as the value of the type
attribute in another <handler> element.
When you define handlers at the top level (underneath <deployment>), youre always
doing so for the purpose of referring to those handlers later. Declaring a handler with-
out a name attribute at the top level, while technically legal, is useless, since the engine
would never invoke such a handler.
You may specify any number of optional <parameter> elements inside a handler dec-
laration. These are used to set options for the handler to control its behavior. For
instance, if we wanted to specify a particular log file for our LogHandler to write to, we
would do so like this:
<handler name=log type=java:org.apache.axis.handlers.LogHandler>
<parameter name=logFile value=myLog.txt/>
</handler>
Note that we also named this handler log. That means in other places in our WSDD, we
can refer to it like this, inheriting both the Java class and the option settings with much
less typing:
<handler type=log/>
The set of options that a given handler supports should be explained in the documenta-
tion for that handler. The handler interface includes getOption() and setOption() APIs
that are used to obtain and modify the values of these parameters. To make sure that
code never changes the value of your deployed option, you can also specify a
locked=true attribute on the parameter element. Doing so will prevent any
modification to the option value at runtime.
Chain Definitions
You can put a series of handlers together into a chain and give the chain a name. That
name can then be used as a handler type just like any other. Heres an example:
261 Web Service Deployment Descriptor (WSDD)
<chain name=logAndNotify>
<!-- This chain logs the messages, then also sends an email -->
<!-- to a specified address. -->
<handler type=java:org.apache.axis.handlers.LogHandler/>
<handler type=java:myPackage.NotificationHandler>
<parameter name=email [email protected]/>
</handler>
</chain>
Now this chain can be used like any other handler. For instance, we could add it to a
request flow:
<requestFlow>
<handler type=logAndNotify/>
...more handler declarations...
</requestFlow>
Also note that you can parameterize handlers inside chains, as demonstrated by our con-
figuring the NotificationHandler with an email address to which notifications should
be sent.
Transports
Transport declarations define a named, targeted chain, which has a requestFlow, a
responseFlow, and, on the client, a pivot handler. Remember that on the client, the
transport is an outgoing concept and the pivot is the sender of the message. On the server,
no pivot handler is needed (nor should one be specified). Heres an example of a client
transport, from the default client-config.wsdd file:
<transport name=http
pivot=java:org.apache.axis.transport.http.HTTPSender/>
You define a pivot handler for a transport with the pivot attribute. In this example,
there are no other options or request/response handlers.
On the server, the standard HTTP transport declaration looks like this:
<handler type=java:org.apache.axis.handlers.http.URLMapper
name=URLMapper/>
<transport name=http>
<requestFlow>
<handler type=URLMapper/>
<handler type=java:org.apache.axis.handlers.http.HTTPAuthHandler/>
</requestFlow>
</transport>
Weve included the declaration of the URLMapper handler here so you can see another
example of referring to a named handler. This transport is named http, and it contains
two transport-specific handlers in the request chain. The first is the URLMapper, which
sets the Axis service name in the MessageContext based on the HTTP URL; this is the
262 Chapter 5 Implementing Web Services with Apache Axis
default dispatch mechanism, so that when you access http://host:8080/axis/
services/POSubmission, Axis will end up looking for a deployed service named
POSubmission. The other handler is the HTTP authentication handler, which takes the
username and password out of an HTTP Basic authentication header and puts them in
the MessageContext in a transport-agnostic form.
Type Mappings
Type mappings control how the mapping between Java classes and XML structures
works.You can tell the engine to map particular Java classes to particular XML types, and
even customize the serializer and deserializer classes that do the translation. Type map-
pings can be scoped globally (for the whole engine) or per service.
A type mapping in WSDD looks like this:
<typeMapping qname=typeQName
type=java:classname
serializer=Serializer
deserializer=DeserializerFactory
encodingSytle=uri/>
A type mapping contains the same information you saw earlier in the Call API
registerTypeMapping(): the Java type (class name), the XML schema type (QName), a
serializer class, and a deserializer class. The encodingStyle attribute also lets you
optionally specify a particular encoding style (see Chapter 3) within which you want
your type mapping to work. Note that if you fail to specify the encoding style, or you
specify (the empty string), the type mapping will work for all encoding styles (includ-
ing literal, or unencoded, use).
Notice the Java type is specified with an attribute called type, whose value is a
QName in the special WSDD Java namespace (http://xml.apache.org/axis/wsdd/
providers/java, here mapped to the prefix java). This namespace indicates a Java type,
and the local part is the class name. Why a QName instead of just a class name? WSDD
is a language-neural deployment mechanism, and doing it this way makes it easier to use
the same format for both Java and C++ implementations. This is the same reason we use
the java:prefix for handler types as well.
The <beanMapping> tag is shorthand for a <typeMapping> which uses the
BeanSerializer/BeanDeserializer classes (in package org.apache.axis.encoding.
ser) to do Axiss default data-mapping algorithms. In many cases you wont need custom
serialization for your JavaBeans, and therefore beanMappings are convenient and less clut-
tered to look at. BeanMappings look like this:
<beanMapping qname=type QName
languageSpecificType=java:classname
encodingStyle=url/>
Well talk more about type mapping after we go over how to write and deploy your
own services.
263 Building Services
Using Axis with SOAP 1.2
By default, Axis will communicate using SOAP 1.1. However, full support for SOAP 1.2 is built in, and Axis
will eventually default to 1.2 in a future release.
To switch an entire Axis engine to use SOAP 1.2, specify the defaultSOAPVersion option on the
engines WSDD configuration:
<globalConfiguration>
<option name=defaultSOAPVersion value=1.2/>
</globalConfiguration>
On the client side, you can also do this dynamically at runtime using the SOAPConstants object to set
the SOAP version as follows:
SOAPConstants constants = SOAPConstants.SOAP12_CONSTANTS;
call.setSOAPVersion(constants);
At present, there is no standard WSDL 1.1 binding for SOAP 1.2. The SOAPBuilders group, a grassroots coali-
tion of SOAP developers, has one that a few packages use, but it isnt widely accepted as yet. Axis therefore
allows you to build stubs/skeletons from WSDL 1.1 and then use the techniques weve described to switch
to SOAP 1.2 after the fact. This issue will be remedied when the community settles on a WSDL 1.1 / SOAP
1.2 binding; the problem will disappear when WSDL 2.0 becomes a W3C Recommendation, because it will
have SOAP 1.2 support built in.
Building Services
Weve discussed how Axis is architected, how to use the client-side APIs to call services,
and how to use WSDD to deploy components like handlers and transports. Now its
time to explore deploying your own Web services using the Axis framework.
Instant Deployment: JWS
You saw how to deploy a Java Web Service (JWS) gin Chapter 3, and theres not much
else to tell here. JWS services dont require an explicit deployment step aside from copy-
ing a .jws source file into your Axis-enabled Web application. When a SOAP message
arrives destined for a URL ending in .jws, Axis automatically notices and compiles the
class if necessary before invoking the service.
When you deploy a JWS service, there are a couple of things to note. First, all public
methods in the class will be exposed as service operations. Second, the only datatypes
Axis will know about for your JWS services are the global onesin other words, if you
use Java types in your JWS code that arent part of the standard type mapping Axis sup-
ports, you must declare global type mappings to support those types in your server
before the JWS service will function.
264 Chapter 5 Implementing Web Services with Apache Axis
WSDD for Services
When you want to deploy a service that uses custom handlers, service-specific type map-
pings, or any other kind of fine-grained configuration control, youll discover that JWS
isnt adequate because it lacks the ability to use service-specific meta-data. Most of the
enterprise-level services youre likely to deal with will use WSDD deployment instead.
(This situation may change in the future, due to some soon-to-be-standard ways of
embedding the same kind of meta-data you find in WSDD directly into the Java source
code of a JWS file.)
The AxisServers entire configuration is contained in a file usually called server-
config.wsdd. Each deployed service in the servers world has a <service> element in
that WSDD file that looks like this (the asterisks mean that element may appear zero or
more times, and as usual square brackets around an attribute mean its optional):
<service name=name [style=rpc|wrapped|document|message]
[use=literal|encoded] [provider=provider]>
<operation>* (see below)
<typeMapping>* / <beanMapping>*
<namespace>uri</namespace>*
<wsdlFile>absolute-filename</wsdlFile>
<endpointURL>uri</endpointURL>
<handlerInfoChain>
<parameter name=name value=value/>
</service>
The name attribute is the services name, which generally appears as the last portion of
the endpoint URL to access the service. For a standard installation using HTTP, if a
service is called submitPO, the URL to access it will be http://localhost:8080/
axis/services/submitPO, where localhost can be replaced with your machines DNS
name. Service names must be unique.
The style attribute lets you specify one of several different ways that Axis can map
SOAP messages to and from Java method calls: rpc, document, wrapped, or message. If
you specify a style, you dont need to specify the use or provider attribute, since they
will default based on the style you choose. The default style, if not specified, is rpc. Dont
worry, well fully explain these styles in a later section.
The provider attribute allows you to specify a QName that represents the particular
provider (see The Provider in the Axis Architecture section for more about
providers) to use for this service. The providers Axis knows about by default are as fol-
lows:
n
java:RPCThe standard RPC provider (org.apache.axis.providers.java.
RPCProvider), which is used for rpc, document, and wrapped styles. Despite the
fact that it also deals with doc/literal services, the class name RPCProvider is
left over from a time when Axis was primarily an RPC toolkit. Note that if you
specify rpc, document, or wrapped for the style attribute, this provider will be
automatically selected.
265 Building Services
When using the RPC provider (and its likely that most of your services will), you
have to convey two important pieces of information to the provider: which class
youre exposing and which methods are allowed.You do this using service parame-
ters, as follows:
<parameter name=className value=class name/>
This parameter value should be the fully qualified class name of your Web service.
<parameter name=allowedMethods value=methods/>
The value for this parameter is a space-separated list of the methods you want
accessible as Web service operations. If you use the special value *, it means that all
public methods on your service class will be available.
n
java:MSGThe message provider (org.apache.axis.providers.java.
MsgProvider), which handles dispatching raw XML to your service. Note that if
you specify the java:MSG provider, the service style will automatically be set to
message, and vice versa.
n
java:EJBThe EJB provider (org.apache.axis.providers.java.
EJBProvider), which allows you to use an Enterprise JavaBean as a Web service.
See the Axis documentation for more on this.
n
HandlerThis one is interesting, since it lets you specify your own handler class
as the provider for a particular service. For instance, there is a simple handler called
org.apache.axis.handlers.EchoHandler, which places a copy of the request
message into the response message.You could declare an echo service using the
handler like this:
<service name=echo provider=Handler>
<parameter name=handlerClass
value=org.apache.axis.handlers.EchoHandler/>
</service>
As shown, the handler provider uses the value of the handlerClass variable as the
class name of the pivot handler.
Axis also includes providers that bridge to BSF (Bean Scripting Framework), COM
(Microsofts Common Object Model), CORBA, and standard Java RMI. See the Axis
documentation for more on these.
The use attribute lets you specify encoded or literal use. This setting will be reflected
in WSDL generated from this service (we discuss generating WSDL later) and to control
whether the SOAP encoding is used when the service serializes and deserializes XML.
You generally wont need to set this yourself, since it defaults correctly based on the
style attribute.
266 Chapter 5 Implementing Web Services with Apache Axis
If <typeMapping> or <beanMapping> elements are inside your service deployment,
then those XML/Java mappings will hold only for that service. This enables multiple
services to map the same types in different ways.
You may have zero or more <namespace> elements inside your <service>. If present,
the first one is the default namespace of the service. This namespace is primarily useful
for RPC services, and it will determine the namespace of the body elements for the
service and the targetNamespace of the generated WSDL. Any of the specified name-
spaces can also be used by the axisEngine to dispatch messages to the servicein other
words, the engine can automatically find the correct service if an element in one of the
services namespaces is found in the <soap:Body>.
The <wsdlFile> element allows you to specify a custom WSDL file that the engine
will return when asked about the WSDL for this service. Normally, when someone
accesses your service URL plus ?wsdl, the engine will autogenerate a WSDL description
based on your running service. If you want to change this behavior (for example, to add
customizations not supported by the autogeneration), you can use this optional WSDD
technique to point at a file that will be returned instead.
JAX-RPC Handlers
The <handlerInfoChain> element is used to enable deploying JAX-RPC style handlers,
which are a little different than Axis handlers.You declare JAX-RPC handlers as follows:
<handlerInfoChain>
<handlerInfo class=className>
<parameter name= value=/>
<header qname=qname/>*
<role soapActorName=uri/>*
</handlerInfo>*
</handlerInfoChain>
JAX-RPC handlers in this chain will run after the global chain but before the normal
requestFlow for your service. At that time, each handler in the chain will have its
handleRequest() method invoked. Then, after the service responseFlow has completed,
the handlers on the JAX-RPC chain will have their handleResponse() method
invokedbut this time the chain will be reversed. This is the main difference: JAX-RPC
style handlers have two methods to handle the request and the response separately,
whereas Axis handlers use a single invoke() method and check the MessageContext, if
necessary, to figure out whether the current message is the request or the response. Each
<handlerInfo> defines a single JAX-RPC handler.
The <header> and <role> elements allow you to specify which header elements
should be processed by the handler and which SOAP roles that particular handler is
playing. At this time, they arent implemented in Axis.
The <operation> Element
A service can contain zero or more <operation> elements. The <operation> element is
used when you want fine-grained control of the options for a particular operation. In
267 Building Services
particular, it handles the mapping from arbitrary XML QNames in the SOAP body to
arbitrary Java methods, controlling how parameters to those methods map to XML ele-
ments and also how the Exceptions that are thrown by the Java methods map to and
from SOAP faults. Note that in all but the simplest cases, you wont be writing these
yourselfinstead they will be generated for you when you use WSDL2Java to create
service frameworks from WSDL documents. The element looks like this:
<operation name=name [qname=qname] [returnQName=qname]
[returnType=qname] [returnHeader=true|false]>
<parameter [qname= | name=] [mode=in|out|inout]
type=qname
inHeader=true|false outHeader=true|false/>*
<fault name=name qname=qname class=classname type=qname/>*
</operation>
The operation name is the name of the Java method this Web service operation will
invoke. The qname is the QName of the XML element that will map to this operation
for RPC services, there is never a need to specify this because it will always be derived
from the operation name.
Inside the operation element are zero or more parameter elements, each of which
represents a parameter of the operation. The information in the WSDD parameter ele-
ment is essentially the same information that would go in the addParameter() call on
the client side, with a couple of additions: If the inHeader or outHeader attribute is
specified, then the serialization of the parameter in question will be in the SOAP header
instead of in the body.
There are also zero or more fault elements inside the operation. These allow partic-
ular fault classes to be associated with particular element QNames inside the SOAP
faults <detail> element. In other words, if the faults specified in the <fault> mapping
are thrown by your service, Axis will serialize the data inside the fault class as a <detail>
element with the specified QName and XML type.
Deploying Services and the AdminClient
You have two choices of how you get the WSDD related to your service (or in fact, any
WSDD deployment) into a servers configuration. Either you can directly edit the
server-config.wsdd file the server is using (typically in the WEB-INF/ directory of your
web application), or you can use the AdminClient gtool that comes with Axis.
The client is the class org.apache.axis.client.AdminClient, and it works some-
thing like this:
java org.apache.axis.client.AdminClient [u{username}] [w{password}]
[-p{port}] [l{service-url}] {wsdd-file}
When you run this from the command line, it reads the specified WSDD file and
attempts to deploy it to the Axis engine running at the given URL. The default URL is
http://localhost:8080/axis/services/AdminService. If authentication is necessary,
268 Chapter 5 Implementing Web Services with Apache Axis
the username and password arguments may be used to send a set of credentials to the
deployment service.
If the WSDD has <deployment> as its root element, all the components in the
WSDD are deployed into the target server. Remember that you need to make sure all
the classes referred to in the WSDD (the service class, any data classes, handlers, and so
on) are available on the servers classpath before doing the deployment.
If the WSDD has <undeployment> as its root, all the referenced components will be
removed from the running server. When undeploying, you should only put the names of
the components you want undeployed in the WSDD, not the full configuration. This file
would remove a handler and a service:
<undeployment xmlns=http://xml.apache.org/axis/wsdd/>
<handler name=MyHandler/>
<service name=MyService/>
</undeployment>
NOTE
By default, the AdminService only allows deployment from clients on the local machine, for security
reasons. If you want to allow remote deployment, you can do so by switching off an option in the servers
WSDD. We recommend extreme caution when doing this; please make sure the AdminService has been
secured to prevent unauthorized users from gaining access to your machine. Remember that deploying a
service can make a Java class available for remote accessimagine if someone deployed the
java.lang.System class, and then called the exit() method! Bye-bye app server.
The option for remote deployment, if you wish to enable it, looks like this:
<service name=AdminService provider=java:MSG>
<parameter name=className value=org.apache.axis.util.Admin/>
<parameter name=allowedMethods value=*/>
<parameter name=enableRemoteAdmin value=true/>
</service>
Getting at the MessageContext
When youre building servlet applications, sometimes youll want to insulate your appli-
cation code from the servlet framework, and other times youll need to use the servlet
APIs. In the latter case, youll put the meat of your functionality into the servlet process-
ing method (doGet(), doPost()) and utilize the servlet request and response classes
directlyyour code knows its running as a servlet, and can use the functionality of the
servlet APIs. For the former case, your servlet is really an I/O wrapper for more abstract
application code that has no servlet dependence.
The situation is similar in Axis.You can build service methods that perform
application logic with no Axis-dependence (if youre exposing prebuilt Java classes as
Web services, this will certainly be the case). On the other hand, you can also build
service methods that are Axis-aware and thus have access to some of the features of the
269 Building Services
framework. The main thing such services want access to is the MessageContext, since it
contains references to everything you might need regarding the state of Axis: the request
and response messages, the type-mapping registry, the engine, the current set of proper-
ties, and so on. The only question is how to get at it from your service code.
You can access the current MessageContext from anywhere (including your service
code) by using the static method MessageContext.getCurrentContext(). This method
uses thread-local storage to make sure each call gets the right context, even if many
threads are all calling into your service method at the same time.
Once you have access to the MessageContext, you can get and set properties, exam-
ine the messages, find out what transport is in use, and so on. For example, you can easily
make your service behave differently based on whether a handler has put, for instance, an
authenticated user property into the MessageContext:
// Our service method
float getPrice() {
// Get the regular price
float result = getNormalPrice();
// Check if the user has been authenticated by looking in the MC
// for a User object in a well-known place.
MessageContext mc = MessageContext.getCurrentContext();
User user = (User)mc.getProperty(myUserProperty);
// If an earlier handler dropped a User object in myUserProperty,
// check if they have priviledges
if (user != null && user.isGoldMember() == true) {
// The authenticated user is a member of our
// Gold Club give them a discount!
result = result * 0.85f;
}
return result;
}
Property Scoping at Runtime
A useful and interesting pattern occurs when you ask for a property value from the
MessageContext. Not only will the MessageContext provide you with values that have
been directly inserted into its own property bag, but it also makes available options that
were configured into the active service and the engine. This allows you to set a configu-
ration option on the axisEngine, which can then be overridden by a particular service,
which can be overridden again by handlers in a particular message exchange if
appropriate.Lets look at an example. In this example, we use the value SEND_MULTIREFS
as a String constant that we assume is defined elsewhere. An Axis server might be con-
figured (by setting the SEND_MULTIREFS option to Boolean.False) to avoid sending
multirefs by default, in order to speed up serialization of SOAP 1.1 messages. Active
270 Chapter 5 Implementing Web Services with Apache Axis
MessageContexts for that AxisServer that didnt have a value themselves for the prop-
erty SEND_MULTIREFS would return false if someone called getProperty(SEND_
MULTIREFS). A specific service deployed in that engine could set the same parameter to
the value true, and for invocations of that service alone, the new value would override
the default (see Figure 5.10).
AxisServer
Name Value
Options
SEND_MULTIREFS Boolean.FALSE
myService
Name Value
Options
SEND_MULTIREFS Boolean.TRUE
MessageContext
Name Value
Properties
msgContext.service
myService.axisEngine
MyProperty 56
msgContext.getProperty(SEND_MULTIREFS)
will return Boolean.TRUE
Figure 5.10 Property scoping in Axis
Service Lifecycle and Scopes
When you deploy a service using Axis, you know there is generally a backend Java
object that implements the application logic. How is that object created? How many
such objects are there? Can they be shared across multiple threads at once? The answers
to these questions can be important when deciding how to build your services.
As it turns out, the service deployer has a number of lifecycle choices for service
objects, and theyre controlled by the scope option on the service in WSDD. Heres an
example:
<service name=MySingleton>
...
<parameter name=scope value=application/>
</service>
271 Building Services
The valid values for this option are as follows:
n
Application scope means there will only be a single instance of the service class for
the entire AxisEngine. This means that you, the developer of the service class,
must make sure all your methods are thread-safe, since there might be many active
requests threading their way through the same code in parallel. Any state you keep
in your service object will be shared across all invocations for the lifetime of the
engine.
n
Request scope is the opposite of application scopeservices configured with request
scope will cause a new service object to be created for every SOAP request. If you
use request scope, you should try to make sure your service objects dont have
slow or expensive initialization code in their constructors, since theyll be created
and deleted a lot. Request scope is the default for services without the option
specified.
n
Session scope is somewhere between application and request. Session-scoped service
objects are created once per client session (see Sessions on the Server Side); once
a session has been established with the axisEngine, a given client will use the
same service object instance for every request until the session terminates.You can
use data fields in your service objects to hold state on a per-session basis. Note that
to make session-scoped objects work, your clients must be able to support sessions
in a way compatible to the way your server does itsee the next section for more
on this.
When a service object is created or destroyed by the Axis runtime, the engine checks to
see if that object implements an interface from JAX-RPC called javax.xml.rpc.
server.ServiceLifecycle. This interface contains two methods:
void init(Object context) throws ServiceException;
void destroy();
By implementing these methods, you can do any initialization or cleanup that your
service object requires. When the engine creates a new service object, init() is called
with a context object that lets the service get at the MessageContext and various
servlet-related properties. The object is of type javax.xml.rpc.server.
ServletEndpointContext, but we dont recommend using it, since it ties your service
object to servlet-specific concepts.You can use the
MessageContext.getCurrentContext() API described earlier to get at whatever you
need from the environment (this is how the getMessageContext() method on the
ServletEndpointContext is implemented).
The Axis engine calls destroy() on your ServiceLifecycle objects when it frees
them for garbage collectioneither at shutdown (for all objects), when a session expires
(for session-scoped objects), or at the end of a request (for request-scoped objects).
272 Chapter 5 Implementing Web Services with Apache Axis
Sessions on the Server Side
Sessions, as we mentioned earlier, are about storing data on the server sidedata associ-
ated with a particular client. Axis has an abstraction for this concept in the
org.apache.axis.session package.
A given interaction can optionally be associated with a session, and thus the
MessageContext has a slot in it for the currently active session.You can use the
MessageContext.getSession() API to get a reference to the active session, if any.
A Session object acts much like a Map/Hashtable, in that it lets you store values in a
library indexed by String keys. The power of the Session lies in the fact that you (that
is, handlers or your service object) can put data in the Session during one interaction,
and then that data will be available again on the next interaction from that same client.
This pattern is familiar to anyone who has done web/servlet programming; it lets you do
things like remember a users name, authentication credentials, preferences, and so on.
As with the client, there are two built-in ways of accessing sessions on the Axis server:
The first uses the servlet HttpSession as its underlying datastore, and the second uses
the SimpleSession. When using the servlet version, the servlet engine will handle tim-
ing out the session for you; when using the SOAP version, the SimpleSessionHandler
will periodically reap expired sessions.You can set the timeout on a session with
session.setTimeout(int); if the number of seconds elapses with no activity on a given
session (activity is defined as any interaction that uses that session), it expires and all data
within it is lost.
The session implementations that Axis includes arent generally persistentin other
words, the data you store in the session will be lost in case of a server crash/restart.
Nothing would prevent you (or a third party) from developing a persistent session
implementation, though, and if the HttpSession is acting as the storage, its up to your
servlet engine whether to implement persistence.
Using WSDL2Java to Generate Services
Sometimes, instead of starting from Java classes, its desirable to take a WSDL description
of a Web service and create a skeleton implementation of the service described by the
WSDL. By specifying the -s option to WSDL2Java, you can have the tool generate, in
addition to the client and data classes we discussed previously, a framework implementa-
tion of the service and associated WSDD deploy and undeploy files.
Lets see what happens with the example from the Stubs section:
% WSDL2Java -s http://localhost:8080/axis/InventoryCheck.jws?wsdl
Now, in addition to the classes we saw previously, well find these:
n
InventoryCheckSOAPBindingImpl.javaThe framework implementation of the
service
n
deploy.wsddA prebuilt deployment file ready for use by the AdminClient
n
undeploy.wsddA prebuilt undeployment file
273 Building Services
If you look inside the Impl class, youll see a method like this:
public class InventoryCheckSoapBindingImpl implements
localhost.InventoryCheck_jws.InventoryCheck {
public boolean doCheck(java.lang.String sku, int quantity)
throws java.rmi.RemoteException {
return false;
}
}
This is where youd fill in the logic for your service method and return the real result.
Since the framework knows youll be editing the implementation class, if you run
WSDL2Java again in the same directory, it will not overwrite whats there (otherwise you
might inadvertently lose your work). If you want to make a new version, you should
delete or move the Impl file before rerunning WSDL2Java.
The steps for deploying a service based on a WSDL are as follows:
1. Use WSDL2Java with the -s option to build a Java framework for your service.
2. Open the generated Impl class in your favorite editor and actually implement the
logic of the methods.
3. Compile all the generated classes and put the .class files onto your servers class-
path.
4. Either edit the server-config.wsdd file for your server and add everything in the
generated deploy.wsdd, or use the AdminClient tool to deploy the generated
deploy.wsdd file to the server.
5. The generated undeploy.wsdd file can be used as input to the AdminClient and
will undeploy the service.
Generating WSDL for Your Services
You know from our earlier discussions that WSDL is a central part of making Web serv-
ices easy to use. Now that youre deploying Web services with Axis, how do you get the
WSDL to give to your prospective users or to publish in directories such as UDDI (dis-
cussed in Chapter 6)? Axis provides two ways to generate WSDL from your Java Web
services: Java2WSDL and ?wsdl.
Using Java2WSDL
Axis comes with a mirror-image of the WSDL2Java tool called Java2WSDL. Java2WSDL
is run from the command line; Axis also includes a custom ant task for using
Java2WSDL in Ant builds. Java2WSDL takes a Java class (or interface) and generates a
WSDL description of that class as a Web service, including schemas for all the necessary
datatypes utilized in the classs methods. Java2WSDL has a lot of options; if you want the
lowdown, check out the Axis documentation.
274 Chapter 5 Implementing Web Services with Apache Axis
Heres a basic example:
java org.apache.axis.wsdl.Java2WSDL o priceCheck.wsdl
lhttp://localhost:8080/axis/services/priceCheck n
http://skatestown.com/com.skatestown.services.PriceCheck
The o option specifies the output WSDL file location. The l option specifies the end-
point URL that will be reflected in the emitted WSDL file. The n option defines the
targetNamespace for the WSDL. Finally, the class name is the Java class that the WSDL
emitter will use to generate the WSDL operations.
Java2WSDL is a good tool, but it also has some problems. In particular, it doesnt use
the type-mapping configuration for the engine when generating schema types from the
Java types in your services. This means that although it will generate schemas from your
Java types, it will always use the default mappings, and not necessarily the ones your
service uses (as configured in the WSDD for the service). Someday Java2WSDL may be
able to read a WSDD file in order to use custom mappings; but for now, if you care
about this issue, you might want to consider letting the engine do your WSDL
generation for you.
Letting the Engine Do the Work: ?wsdl
The second WSDL-generation technique involves using the Axis server to dynamically
generate WSDL from a running deployed service. This technique has a couple of advan-
tages over Java2WSDL. First, its simpler; there are no command-line tools to learnyou
just need to know how to tack ?wsdl onto your normal service URL. As youve already
seen, you can get the WSDL for the InventoryCheck service at the URL
http://localhost:8080/axis/InventoryCheck.jws?wsdl. For regular services, its
http://host:port/yourwebapp/services/serviceName?wsdl.
The second advantage of the dynamic ?wsdl technique is that because the WSDL is
being generated by a running engine, you can use all the deployment information in the
engine. So, you can automatically take advantage of all the active type mappings to gen-
erate the right schemas, and you can allow the deployed handler chains to affect the
WSDL to insert extensions like SOAP headers.
Handler Framework for Generating WSDL
Each handler has, in addition to the normal invoke API, a method on it that looks like
this:
void generateWSDL(MessageContext context)
Since the provider is the meat of your service, its the component that is responsible for
generating most of the WSDL description. When WSDL is asked for, however, all the
handlers that would normally be invoked for a service interaction get to participate by
adding extensibility elements or documentation or changing things that were generated
by the provider. Note that right now, handlers that run before the provider cant affect the
WSDL muchthe provider generates it and then places it (as a DOM document) in a
275 A Guide to Web Service Styles
property called WSDL in the MessageContext. After the provider, any handler on any of
the response chains can then modify the DOM in that property.
We expect this mechanism to change in the future, so that it will be easier for han-
dlers/extensions to contribute to a WSDL model no matter where they are in the
deployment order. Also, dont forget that if for some reason the engine isnt doing what
you need it to when generating WSDL, you can override the automatic generation and
supply a custom WSDL file yourself. We explained how to do this in the WSDD for
Services section.
SimpleAxisServer: A Lightweight Container for Services
Axis comes with a simple multithreaded HTTP server that can be used to host Web services without the
overhead of a servlet engine. The class is called SimpleAxisServer, and its in
org.apache.axis.transport.http.SimpleAxisServer. It supports accessing JWS- and
WSDD-deployed services, including handling HTTP GETs to automatically retrieve WSDL.
To start it, execute the class:
java org.apache.axis.transport.http.SimpleAxisServer [-p port]
The optional port argument lets you select which port the SimpleAxisServer will listen on: the
default is 8080.
SimpleAxisServer looks for a server-config.wsdd file in the current directory and, if it finds
one, uses that file as its configuration. Note that you must have all the JAR files Axis needs (see the earlier
sidebar Installing Axis) and all the service and data classes that will be used by your deployed services in
your classpath.
Although SimpleAxisServer isnt suited for serious high-availability usage, its a handy tool to have in
your toolkit.
A Guide to Web Service Styles
Axis supports four different styles gof services, which are really just different ways to
map Java invocations to Web service invocations. These styles affect things on both the
client and the server; in this section well explain what they mean, for future reference in
the rest of the chapter.
RPC Style
RPC style uses the SOAP RPC conventions and the SOAP data model. In WSDL, this
maps directly to the rpc/encoded style we discussed in the last chapter.
On the client, this means that we typically use the invoke(methodName, arguments)
API, and that each argument in the passed array turns into a SOAP RPC parameter. On
the server, since the RPC conventions explicitly state that the first element in the SOAP
body will be the method name, the Axis server can use the QName of that element as a
key to dispatch to the correct operation for RPC style services.
276 Chapter 5 Implementing Web Services with Apache Axis
Wrapped Style
Wrapped style is a lot like RPC style, except in WSDL terms the messages are
document/literal (no SOAP encoding), and they dont use the SOAP RPC conven-
tions. Instead, a wrapper element is defined directly in the schema representing the
method name for each operation, and then a series of elements appears inside that, one
for each parameter. Once again the engine can use the QName of the body element to
figure out which operation to call.
The WSDL for a wrapped operation would look something like this (with details like
the binding and the definitions element removed for simplicity):
<types>
<schema targetNamespace=http://skatestown.com/
xmlns=http://www.w3.org/2001/XMLSchema>
<element name=doCheck>
<complexType>
<sequence>
<element name=sku type=xsd:string/>
<element name=quantity type=xsd:int/>
</sequence>
</complexType>
</element>
<element name=doCheckResponse>
<complexType>
<sequence>
<element name=result type=xsd:boolean/>
</sequence>
</complexType>
</element>
</schema>
</types>
<message name=doCheckRequest>
<part name=part1 element=st:doCheck
xmlns:st=http://skatestown.com/>
</message>
<message name=doCheckResponse>
<part name=part1 element=st:doCheckResponse
xmlns:st=http://skatestown.com/>
</message>
<interface name=InventoryCheck>
<operation name=doCheck>
<input message=doCheckRequest/>
<output message=doCheckResponse/>
</operation>
</interface>
277 A Guide to Web Service Styles
Notice that weve bolded the operation name and the matching element name to indi-
cate that they match. This is how the WSDL processor (and you) can tell that an opera-
tion is wrapped.
This pattern of using a wrapper element to represent the method name and the inner
elements to represent parameters is ad hoc (several packages support it, but it isnt stan-
dard). The downside is that no rules are written down that describe what is allowable
and what isnt in terms of message structure, and if such services allow anything that
schema allows, we might find ourselves in situations where the XML doesnt cleanly
map to Java invocations. The WSDL 2.0 group is remedying this situation by writing
clear rules for RPCs specified with schema elementsin other words, theyre codifying
wrapped operations in a standard and interoperable way.
Document Style
Document style moves further toward generic XML processing but still uses a Java data
structure to represent the entire message. In document style, the QName of the first
body element in the SOAP message can still be used for dispatching to the right opera-
tion on the server, but its no longer necessarily named the same as the operation. In
other words, its possible to have an operation defined like this in WSDL (again, some
details are elided from this example):
<types>
<schema targetNamespace= xmlns=http://www.w3.org/2001/XMLSchema>
<element name=theRequest>
<complexType>
<sequence>
<element name=inner type=string/>
</sequence>
</complexType>
</element>
</schema>
</types>
<message name=docRequest>
<part name=part1 element=theRequest/>
</message>
<interface name=docInterface>
<operation name=operation1>
<input message=docRequest/>
</operation>
</interface>
Since there is no explicit relationship between the operation name in the WSDL and the
element name on the wire, its possible for a WSDL to define two different operations
that use the exact same wire signature. Like many other Web service engines, Axis relies
on unique wire signatures to correctly determine which operation to invoke. As long as
278 Chapter 5 Implementing Web Services with Apache Axis
no other operation in the previous WSDL uses <theRequest> as its body element, we
can safely map any message that arrives with that element to the operation operation1.
If another operation were using <theRequest> as its input, however, dispatch would have
to work some other way; this is why the WS-I interoperability group (discussed in
Chapter 13, Web Services Interoperability) restricts document-style operations to hav-
ing unique element QNames.
To understand the difference between document and wrapped style in terms of Java
bindings, lets consider this SOAP message (namespace declarations elided for simplicity):
<soap:Envelope>
<soap:Body>
<setStockAlert>
<price xsi:type=xsd:float>50</price>
<symbol xsi:type=xsd:string>MSFT</symbol>
</setStockAlert>
</soap:Body>
</soap:Envelope>
If this were dispatched to a document-style service, the appropriate Java service method
would be
void operation(SetStockAlert body)
where operation is the operation name. SetStockAlert is a JavaBean that represents
the entire <setStockAlert> element in the SOAP body, something like this:
class SetStockAlert {
float price;
String symbol;
...
}
If, on the other hand, this were a wrapped-style service, we would expect the service
method like this instead:
void setStockAlert(float price, String symbol)
Here the method name is the same as the name of the XML wrapper element, and the
parameters have been unwrapped into individual arguments to the method.
The three styles youve seen so far have one thing in common: the Axis infrastructure
handles converting the XML into Java data structures and vice versa.
Message Style
Message style, the fourth option, is used in cases where you want to process the XML
yourself, with no data binding. In a message-style service, you dont necessarily have one
Java operation per Web service operationinstead its possible to have, for instance, a sin-
gle Java method processing any XML that arrives at the service.Youll see how this
works later when we discuss deploying services.
279 From XML to Java and Back Again: The Axis Type-Mapping System
Since there are several ways in which you might want to deal with XML, Axis pro-
vides several possible signatures for your message-style operations:
void method(SOAPEnvelope request, SOAPEnvelope response)
Element [] method(Element [] request)
SOAPBodyElement [] method(SOAPBodyElement [] request)
Document method(Document request)
If your service method wants to explicitly look at the whole SOAP envelope, including
headers, you probably should use the first method. Note also that the response
SOAPEnvelope is passed in to your method, not generated by itthis means there may
already be headers in the response that were placed there by earlier handlers (its up to
your code to decide what to do with them, if anything; generally you should leave them
alone if possible).
The next two options let you get at all the subelements of <soapenv:Body> (they are
arrays, since technically the SOAP spec allows more than one element inside the Body)
and return your own collection of elements to go into the response Body. They are iden-
tical except for your preference of DOM Elements or SOAPBodyElements.
The last option uses a DOM Document to hold the contents of the <soapenv:Body>
and returns a DOM Document for the response body. Note that since a Document can
only have a single root element, this method will only give you the first element inside
the body; if you expect multiple body elements, use one of the other options.
From XML to Java and Back Again: The Axis
Type-Mapping System
Weve touched on type mapping a number of times in the chapter; and you already
know that you can associate particular Java types with XML Schema types, both by using
client-side APIs (manually or as a result of using WSDL2Java) and with server-side meta-
data (WSDD). This section will duck briefly behind the scenes to describe the type
mapping architecture in Axis/JAX-RPC.
Registering Mappings
To make type mapping work, we need a registry of mappings, so that when the engine is
reading a particular piece of XML and comes across a given element of a given schema
type, it can locate an appropriate deserializer in order to convert the XML into Java. The
same goes for the other direction, when writing out Java objects as XML. This registry is
known as the TypeMappingRegistry. Although you might expect the
TypeMappingRegistry to contain the mappings themselves, JAX-RPC decided to intro-
duce another layer called a TypeMappingso the TypeMappingRegistry (TMR)
contains TypeMappings, and then the TypeMappings enable you to map XML and Java
types.
For certain encoding styles, the type mappings might be differentin particular, the
SOAP encoding defines its own types that wont be understood outside the context of
280 Chapter 5 Implementing Web Services with Apache Axis
encoded services. As such, the TypeMappingRegistry is first indexed by encodingStyle.
At any given time, there is a TypeMapping object for each encodingStyle.
The AxisEngine has its own TypeMappingRegistry to keep track of global type
mappings, and each SOAPService (on both the client and the server) has its own registry
as well, to contain service-specific mappings. The service-specific mapping delegates to
the engine global mapping; when its looking for a particular type from the service-
specific mapping, it automatically searches the global mapping if the type isnt found at
the service layer. Note that the encoded TypeMappings will also search the default
no-encodingStyle TypeMapping if they dont find a given type. This is good because
you dont have to store all the default mappings in every TypeMapping instance.
An example is shown in Figure 5.11. If you need to deserialize an xsd:long with the
encoded TypeMapping, it will first scan its own table of mappings, notice that the type
QName isnt there, and then delegate to the default mapping. There you find a mapping
to a Java long using the SimpleDeserializer (the default deserializer for most simple
types).
Default TypeMapping
encodingStyle =
apache:Map Map
xsd:string String
xsd:long long
MapDeserializer
SimpleDeserializer
SimpleDeserializer
SOAP Encoded TypeMapping
encodingStyle = http://www.w3.org/2003/05/soap-encoding
soapenc:int Integer
soapenc:string String
soapenc:Array Objects{[]
SimplerDeserializer
SimpleDeserializer
ArrayDeserializer
XML type Java type Deserializer
Figure 5.11 An example type mapping hierarchy
The MessageContext has a special slot for the active TypeMappingRegistry, which can
be accessed with the messageContext.getTypeMappingRegistry() API. The current
TypeMapping (which also takes into account the active encodingStyle) can be obtained
with messageContext.getTypeMapping().
281 From XML to Java and Back Again: The Axis Type-Mapping System
When you register a type in WSDD at the global level, the mapping goes into the
axisEngines (either AxisClient or AxisServer) TypeMappingRegistry. Likewise, reg-
istering a typeMapping in the <service> will put it into the service-specific
TypeMappingRegistry, making it accessible only to that particular service. When you
use the registerTypeMapping() method on the Call object on the client side, youre
registering types in the AxisClients global TypeMappingRegistry.
Default Type Mappings
Table 5.1 lists most of the default type mappings supported by Axis. These are the type
mappings that are built-in, and if your services use only these types, you wont need to
do any custom mappings. The types with the soapenc: prefix are from the SOAP
encoding namespace and are described later in this section; their mappings are available
only when youre using the SOAP encoding style.
Table 5.1 Default Axis type mappings
Schema Type Java Type
xsd:int int
xsd:integer java.math.BigInteger
xsd:long long
xsd:byte byte
xsd:float float
xsd:double double
xsd:hexBinary byte[]
xsd:base64Binary byte[]
xsd:Boolean boolean
xsd:date java.util.Date
xsd:decimal java.math.BigDecimal
xsd:dateTime java.util.Calendar
xsd:QName javax.xml.namespace.QName
xsd:string String
soapenc:int java.lang.Integer
soapenc:long java.lang.Long
soapenc:float java.lang.Float
soapenc:Boolean java.lang.Boolean
soapenc:short java.lang.Short
soapenc:double java.lang.Double
soapenc:integer java.math.BigInteger
soapenc:decimal java.math.BigDecimal
xmlsoap:Map Map (see the next section)
282 Chapter 5 Implementing Web Services with Apache Axis
The Apache Map Type
One of the problems with XML Schema and SOAP is that no standard type exists for
representing the equivalent of a Java Map (a simple key/value associative array). As such,
the Apache SOAP team came up with a simple serialization that has been adopted by
several other toolkits (including Systinets WASP, WebMethods GLUE, and Perls
SOAP::Lite). The schema for the Map type looks like this:
<schema xmlns=http://www.w3.org/2001/XMLSchema
xmlns:tns=http://xml.apache.org/xml-soap
targetNamespace=http://xml.apache.org/xml-soap>
<complexType name=Map>
<sequence>
<element name=item type=tns:mapItem
minOccurs=0 maxOccurs=unbounded>
</sequence>
</complexType>
<complexType name=mapItem>
<sequence>
<element name=key type=anyType />
<element name=value type=anyType />
</sequence>
</complexType>
</schema>
We hope a future version of XML Schema will include built-in support for commonly
used types like maps and tabular data sets. But for now the interoperability of these types
is somewhat limited due to lack of concerted effort from the greater Web services com-
munity.
The SOAP Encoding Datatypes
The SOAP encoding schema, in addition to what we discussed in Chapter 3, also intro-
duced a set of datatypes that mirror most of the XML Schema simple types. These new
types enable the ID and href attributes used for encoding multiref values. In Java, these
SOAP encoding types (as you can see in Table 5.1) map to the Java language wrapper
types like Integer, instead of the simple types like int.
Mapping Java Collections to and from XML
Collections are a ubiquitous programming language concept, from simple arrays (a basic
type in most languages) to more complex collections like sets. Java supports both basic
arrays (int[]) and a rich set of collection classes. In XML, collections can be represented
in several ways.
When using the SOAP encoding, Java collections and arrays will map to SOAP
arrays, with schemas that look like this in WSDL (for a Java String[]):
<complexType name=ArrayOfString>
<complexContent>
283 From XML to Java and Back Again: The Axis Type-Mapping System
<restriction base=soapenc:Array>
<attribute ref=soapend:ArrayType
wsdlArrayType=xsd:string[]>
</restriction>
</complexContent>
</complexType>
With typed arrays like int[], String[], and so on, its easy for the Axis engine to gen-
erate specific types like the ArrayOfString example. If you use the Collection classes
(ArrayList, Vector, and so forth) in your signatures, the WSDL mapping will be a plain
SOAP Array, since you can put any Object in it.
When using literal mode (no encoding), the common XML usage pattern for collec-
tions is to use the maxOccurs attribute on a schema element declaration:
<complexType name=BagOfThings>
<sequence>
<element name=things type=xsd:int
minOccurs=0 maxOccurs=unbounded/>
</sequence>
</complexType>
If we map the BagOfThings type to a Java type using the standard mappings, it will look
like this:
class BagOfThings {
int [] things;
...accessors...
}
When elements with maxOccurs greater than 1 are fields in a type, they are converted to
arrays. The same thing occurs with wrapped mode parameters that have maxOccurs
greater than 1. Well give you an example, but well do it the other way around this
timeif you deploy this Java method as a wrapped service operation:
int getAverage(int [] values)
youll see this schema for the operation wrapper element in the generated WSDL:
<element name=getAverage>
<complexType>
<element name=values minOccurs=0 maxOccurs=unbounded/>
</complexType>
</element>
Default Type Mapping and XML/Java Naming
BeanSerializer and BeanDeserializer are the default classes for reading and writing
284 Chapter 5 Implementing Web Services with Apache Axis
Java beans as XML complex types (this is why we have the <beanMapping> shorthand
element in WSDD). They use the standard JAX-RPC mapping rules, which means that
each field in a bean maps to an element or attribute in an XML complex type, and vice
versa.
Each class can have meta-data associated with it that tells these default serializers any-
thing special they need to know about how to map the Java fields into XML.You can
see examples of this meta-data in the getTypeDesc() method of any data class generated
by WSDL2Java.
The meta-data mostly serves to map the Java names to XML names, since there are
many occasions when an XML name doesnt neatly (and uniquely) map to a Java name.
For example, consider this schema snippet for a complex type:
<complexType name=NameClash>
<sequence>
<element name=name type=xsd:string/>
</sequence>
<attribute name=name type=xsd:int/>
</complexType>
Both the element and the attribute associated with this type are called name, and there-
fore they would each individually map to a Java field called nameexcept this time we
cant do that because we cant have two identically named fields. So WSDL2Java would
map this class as follows:
class NameClash {
String name; // element
int name2; // attribute
...
}
Later in the class declaration we would see meta-data that mapped the name field to an
element called name and the name2 field to an attribute called name. The same kind of
mapping would need to occur if the XML name of an element or attribute were some-
thing like int (a Java reserved word) or 456 (an illegal Java identifier).
Since the mapping between the XML data space and the Java data space isnt perfect,
without this kind of meta-data to control how individual fields are mapped, we would
run into many unworkable situations.
Custom Serializers and Deserializers
As youve already seen, each JavaXML mapping is associated with particular serializer
and deserializer classes; BeanSerializer/BeanDeserializer, combined with meta-data,
take care of most of the complex types you might encounter.You can also build custom
serializers and deserializers for tighter control over how your Java classes map to XML or
to improve performance over the standard mechanisms. Well give you a quick overview
of doing this here, but we wont go too deeply into the APIs.
285 From XML to Java and Back Again: The Axis Type-Mapping System
Serializers
Just as handlers have the invoke() method, serializers have the serialize() method as
their main focus:
void serialize(QName name, Attributes attributes,
Object value, context)
The point of a serializer is to take the Java object passed in the value argument and
write it, as XML, to the passed SerializationContext. The SerializationContext
object is the main way Axis writes XML. A SerializationContext keeps track of
things like which namespace prefix mappings are already in scope, and it can do things
like automatically write QNames for you with correct prefixes (including registering
new mappings if necessary). It also remembers which Java objects have already been
written, in order to correctly implement multiref serialization. If you look at some of the
serializer classes in the org.apache.axis.encoding.ser package, youll see a lot of uses
of this class.
The code inside serializers is the main arbiter of how particular Java types are turned
into XML serializations. So, the knowledge of how to describe what the XML looks like
for a particular serializer should also reside in the serializer. As such, the Serializer
interface has one more method on it, whose purpose is to allow the serializer to gener-
ate a schema description of the XML it will write:
Element writeSchema(Class javaType, Types types) throws Exception
The Axis engine will call this method during WSDL generation when it needs the
schema for a Java type that has been mapped to your custom serializer. When imple-
menting this method, your serializer should return a DOM element representing a
<simpleType> or a <complexType> schema element that describes the XML that will
be generated when serializing the Java type in the javaType argument. The Types refer-
ence that is also passed to the method allows your code access to the types that have
already been written and a few other convenience APIs for situations when your type
definition needs to reference other types.
Lets review:You have a Java datatype and wish to write it to XML in some custom
way. So, you do the following:
1. Write a Serializer implementation that takes an object of your Java type and
writes the XML the way you like it using a SerializationContext.
2. Make sure the serializer writes a correct schema for the generated XML.
3. Write a simple factory implementation so that the engine can create instances of
your serializer. Look at any of the serializer factories in the
org.apache.axis.encoding.ser package for examples. The SerializerFactory
you build will be the class you refer to as the serializer component in WSDD type
mappings and in the Call.registerTypeMapping() client APIs.
286 Chapter 5 Implementing Web Services with Apache Axis
Deserializers
The main purpose of a deserializer is to process XML and then end up with a Java
object as its value. Since Axis is a SAX-based system, the deserialization framework
works by processing SAX events. Deserializers (classes that implement the
org.apache.axis.encoding.Deserializer interface) have several SAX-like methods,
but all of them have an extra argument beyond that supplied by SAX in order to pass
the Axis-specific deserialization context. Heres an example:
public void startElement(String namespace, String localName,
String qName, Attributes attributes,
DeserializationContext context)
throws SAXException;
The DeserializationContext in a way mirrors the SerializationContext we dis-
cussed earlier. It enables the deserializer code to get at the current MessageContext, the
current TypeMappingRegistry, the mapping of multiref objects (which IDs map to
which objects), and other state for the active deserialization.
All Axis deserializers extend the class org.apache.axis.encoding.
DeserializerImpl, which contains a lot of boilerplate code to handle common tasks. As
with serializers, in order to register deserializers, you also need a factory class, which
should implement org.apache.axis.encoding.DeserializerFactory.
Look at the Axis source for many examples of writing custom serializers and
deserializers.
Using the MessageElement XML/Object APIs
The MessageElement class in Axis and all its subclasses (which we covered in The
Message APIs and SAAJ) provide some nice extras over the standard JAX-RPC
SOAPElement class. One of these features is the ability to use the MessageElement to
easily convert XML to Java and vice versa using the type-mapping infrastructure.
Suppose you have a header like this:
<soap:Header>
<ns:RetryCount xmlns:ns=myNS xsi:type=xsd:int>5</ns:RetryCount>
</soap:Header>
You can ask for its value (assuming you already have a reference to the enclosing
SOAPEnvelope) by doing the following:
SOAPHeaderElement retryHeader =
envelope.getHeaderByName(myNS, RetryCount);
Integer retryCount = (Integer)retryHeader.getObjectValue();
The engine knows how to deserialize this particular header as an Integer because it has
an xsi:type attribute that refers to a type thats understood. If the xsi:type were not
present, getObjectValue() as demonstrated here wouldnt work (youd get an error).
287 From XML to Java and Back Again: The Axis Type-Mapping System
Does this mean you cant rely on this deserialization mechanism if there isnt type data
on every element? Not at all.
You can use two other similar APIs to give the engine the necessary hint about what
type to use if it doesnt know from the XML. The first is getObjectValue() again, but
you can call it and also pass a Class object that is the Java class into which youd like the
element to deserialize:
Integer val = (Integer)retryHeader.getObjectValue(Integer.class);
If there is an active type mapping in scope for that Java class, the engine will try to dese-
rialize the XML with the appropriate deserializer. Note also that if you use this form of
the API and there is an xsi:type on the MessageElement, Axis will make sure there is a
valid mapping from that particular xsi:type to the Java class you passedif not, youll
get a fault.
You can also use the getValueAsType() API, which looks like this:
element.getValueAsType(Constants.QNAME_XSD_STRING)
element.getValueAsType(xmlType, javaType)
This is basically the same idea as getObjectValue(Class) except that instead of passing
the Java class, you tell Axis the XML type (a QName). The first version makes Axis
behave as if the QName you passed was present in the xsi:type attribute of the XML
element. The second one does the same but also suggests a particular Java class to target
for deserialization.
Creating MessageElements from Java Objects
Just as you can easily deserialize Java objects from MessageElements you receive, you can
also build MessageElements from Java objects when youre writing messages. Consider a
handler whose job is to insert a SOAP header that maps to a complex Java data object.
You could write the code to generate the Header XML manually, but that would be
essentially writing a serializer. Instead, you can allow the serialization framework to do
the work for you, like so:
SOAPHeaderElement myHeader = new SOAPHeaderElement(myQName, myObject);
SOAPHeaderElement borrows this constructor from MessageElement, which has the
same form. So we could imagine mixing the two, adding a header like this:
<myHeader>
<myObj>
<!-- serialized object data here -->
</myObj>
</myHeader>
with the following API call:
myHeader = new SOAPHeaderElement(myHeaderQName);
MessageElement myEl = new MessageElement(myObjQName, myObject);
288 Chapter 5 Implementing Web Services with Apache Axis
This technique can be handy when youre writing both message-style services and
handlers.
When Things Go Wrong: Faults and Exceptions
If your service throws an exception, Axis will automatically turn that exception into a
SOAP fault on the wire. If you wish to have more direct control over the SOAP fault
data (such as the faultCode and the faultRole, for instance), you can choose to throw
an AxisFault or any exception derived from AxisFault, instead.
You learned in Chapter 3 that when SOAP errors occur, they are serialized as SOAP
faults. In the Java world, when things go wrong, Exceptions are thrown. What seems
logical, then, would be if our Java SOAP framework would translate SOAP faults we
receive on the client into thrown Exceptions, and Exceptions that get thrown on the
server into SOAP faults on the wire. As you might surmise, this is exactly what Axis
does.
The AxisFault Class
AxisFault is a base exception class that includes explicit fields to carry the structural
items in a SOAP fault (discussed in the last chapter). Heres what some of the interface
looks like:
class AxisFault {
// Fault code accessors
QName getFaultCode();
void setFaultCode(QName code);
// Fault string accessors
String getFaultString();
void setFaultString(String faultString);
// Support adding custom headers to a fault message
ArrayList getHeaders();
void addHeader(SOAPHeaderElement header);
...
}
On the server side, if your class throws an AxisFault, the resulting wire-level SOAP
fault will reflect the values set in the AxisFault class. For instance, if a handler did this:
AxisFault fault = new AxisFault();
fault.setFaultCode(new QName(, Server.NoProduct));
fault.setFaultString(No such product);
fault.addHeader(new SOAPHeaderElement(, PhoneNumber, 867-5309));
throw fault;
289 When Things Go Wrong: Faults and Exceptions
it would turn into this on the wire:
<soap:Envelope xmlns:soap=http://www.w3.org/2003/05/soap-envelope
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
<soap:Header>
<PhoneNumber xsi:type=xsd:string>867-5309</PhoneNumber>
</soap:Header>
<soap:Body>
<soap:Fault>
<faultcode>Server.NoProduct</faultcode>
<faultstring>No such product</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Notice that because we set a header on the thrown fault object, it turned into a
PhoneNumber header in the serialized SOAP fault, as expected.
Using Typed Exceptions
AxisFaults work great, but what if you want to include more fault-specific informa-
tion? You can get at the details with an AxisFaults getFaultDetails() method, which
returns DOM elements, but that isnt very programmer friendly. Wed like to use Javas
structured exception-handling system for returning specific exception typesso that
when the server throws, for instance, a BadPartNumber fault, we can get that same
exception on the client, including any data that the server included in the thrown
exception. We can do this by using the WSDL fault construct.
Faults and WSDL
A fault in WSDL 1.1, as you saw in the last chapter, is defined with a name and an XML
structure that usually ends up in the <detail> element of a SOAP fault. Heres an
example. Lets imagine the WSDL for a modified version of our inventory checker that
throws a specific fault if the client sends a bad SKU. Well just show some of the changes
here, not reproduce the whole WSDL (we assume the prefix sf is mapped to
http://skatestown.com/faults):
<types>
<schema targetNamespace=http://skatestown.com/faults>
<element name=BadPart>
<complexType>
<sequence>
<element name=partNumber type=xsd:int/>
</sequence>
</complexType>
</element>
290 Chapter 5 Implementing Web Services with Apache Axis
</schema>
</types>
<message name=BadPartMsg>
<part name=thePart element=sf:BadPart/>
</message>
...
<portType name=InventoryCheck>
<operation name=doCheck>
<input name=doCheckRequest message=tns:doCheckRequest/>
<output name=doCheckResponse message=tns:doCheckResponse/>
<fault name=BadPartFault message=sf:BadPartMsg/>
</operation>
</portType>
This would cause WSDL2Java to generate the following class for the fault:
class BadPartNumberFault extends AxisFault {
int partNumber;
...
}
The element complexType, including all its data (the part number), has been mapped to
a Java type that extends AxisFault.
The generated Java interface for the InventoryCheck portType would now contain a
method that looked like this:
public boolean doCheck(java.lang.String sku, int quantity)
throws java.rmi.RemoteException, BadPartNumberFault
As expected, the method throws the generated fault type. On the wire, a BadPartFault
would look like this:
<soapenv:Fault>
<soapenv:Code>
<soapenv:Value>soapenv:Sender</Value>
</soapenv:Code>
<soapenv:detail>
<sf:BadPart>
<partNumber>0</partNumber>
</sf:BadPart>
</soapenv:detail>
</soapenv:Fault>
If the Axis client received a message containing this fault in the SOAP body, it would
deserialize the XML to a BadPartNumberFault. Then, the stub would throw that excep-
tion.
Note that Axis does this work both ways: If your Java services throw particular faults,
and youve mapped them with the correct type mappings and WSDD <fault> declara-
tions, Axis will generate WSDL containing the correct fault descriptions. Naturally, if you
291 Axis as an Intermediary
build your services with WSDL2Java, all the faults will be correctly generated and
mapped in the WSDD automatically.
Axis as an Intermediary
In this section, well talk about how to use Axis as a SOAP intermediary. Intermediaries,
as explained in Chapter 3, can be useful for a variety of reasons. Axis has been built to
take advantage of the SOAP infrastructure for processing messages based on roles: URIs
that represent classes of processing that a SOAP node might fulfill. By default, every Axis
client and server application always fulfills the next and ultimateDestination roles.You
can also configure other roles using WSDD as follows:
<role>http://skatestown.com/roles/manufacturer</role>
The <role> element (of which there can be zero or more) can be used either inside the
<globalConfiguration>, in which case the roles become active for the entire
axisEngine, or inside a <service> as in our example, which activates those roles only
for that particular service.
Reasons for Roles
There are two primary purposes to keeping track of what roles you play. First, doing so
enables easy access to all the headers targeted at the current node. The Axis message APIs
we discussed earlier automatically filter the headers based on the active roles for the cur-
rent AxisEngine service, so when your handler calls soapEnvelope.getHeadersByName(
namespace, localPart), you will by default see only headers targeted at one of the
active roles. This allows you to avoid doing this common logic yourself. If you really
need to access all the headers when searching for a particular one, you can either do the
search yourself by calling envelope.getHeaders() (which returns the entire unfiltered
list) or use the secondary form of getHeader(s)ByName(), which includes an extra
Boolean argument that indicates whether to search the entire headers list without role
filtering (envelope.getHeaderByName(namespace, localPart, searchAllHeaders)).
The second purpose to keeping track of roles is MustUnderstand checking. Before
the SOAPService calls the provider to execute the back-end Web service, a
MustUnderstandChecker runs to make sure all mandatory headers that were targeted at
the current node have been marked as processed. If a MustUnderstand header targeted at
the current node is found and it isnt marked processed, the engine will automatically
generate a MustUnderstand fault as per the SOAP spec, and your service method will
never be called. So its critical to remember to call setProcessed(true) on headers that
your handlers process.
Aside from dealing with roles, using Axis as an intermediary is fairly simple.You have
your service objects use the Axis client APIs to send the request message on to its next
destination after processing and then, if appropriate, collect the response and run
response processing on it before it goes back to the original client.
292 Chapter 5 Implementing Web Services with Apache Axis
In the future, Axis may have more explicit support built in for intermediary design
patterns. For instance, it may be able to specify an intermediary service in WSDD with a
standard place to configure a forwarding address for the next hop.
How to Write a Handler
The main thing you need to do when writing a handler class is to implement the
invoke() method, which is passed a MessageContext. Handlers are defined by the
org.apache.axis.Handler interface, which in addition to the invoke() method con-
tains methods for managing handler options and obtaining meta-data about the handlers
deployment options, as well as a hook for participating in the generation of WSDL.
Weve talked about most of this already, and also how to deploy handlers with WSDD; in
this section, well present a couple of examples of handler design and show you a few
APIs that can come in handy.
The SkatesTown EmailHandler
As SkatesTowns business has continued to flourish, it now finds that it wants more visi-
bility into all the Web service calls customers are making to the companys services. In
particular, SkatesTown is interested in finding out how often customers are checking for
more inventory than the warehouse currently holds; if this is happening a lot, the com-
pany might want to start keeping more of particular items in stock.
Al Rosen decided to use the Axis extensibility model to add this functionality to the
InventoryCheck service without any modifications to the existing code. To do so, he
created a handler that will sit on the response chain of the InventoryCheck service
(keep in mind this is the WSDD version of the service, not the JWS oneJWS services
cant have service-specific handlers). The handler is shown in Listing 5.1.
Listing 5.1 The EmailHandler
package com.skatestown.handlers;
import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.RPCElement;
import org.apache.axis.message.RPCParam;
import org.xml.sax.SAXException;
import java.util.Vector;
public class EmailHandler extends BasicHandler {
public EmailHandler() {
}
293 How to Write a Handler
public void invoke(MessageContext context) throws AxisFault {
try {
// Get the SOAPEnvelope
SOAPEnvelope env;
env = context.getRequestMessage().getSOAPEnvelope();
// The first body element will be an RPCElement
RPCElement rpcEl = (RPCElement)env.getFirstBody();
// Fetch the parameters
Vector params = rpcEl.getParams();
// First one is the SKU
RPCParam param = (RPCParam)params.get(0);
String sku = (String)(param.getValue());
// Second is quantity requested
param = (RPCParam)params.get(1);
Integer quantity = (Integer)(param.getValue());
// Now check the response for failures
env = context.getResponseMessage().getSOAPEnvelope();
RPCElement resEl = (RPCElement)env.getFirstBody();
param = resEl.getParam(doCheckReturn);
Boolean result = (Boolean)(param.getValue());
// Did we fail?
if (result.booleanValue() == false) {
// YES - email the configured address
String address = (String)getOption(emailAddress);
// Safety first - use default if no address configured
if (address == null)
address = [email protected];
String content = A request for + quantity + items ;
content += of type + sku + failed.;
sendEmail(address, content);
}
// The request succeeded, so just return
} catch (SAXException e) {
// Catch SAXExceptions and rethrow as AxisFaults
throw AxisFault.makeFault(e);
}
}
Listing 5.1 Continued
294 Chapter 5 Implementing Web Services with Apache Axis
public void sendEmail(String address, String content) {
// Fake email sending routine for demonstration. This would be
// where your real emailing code would go.
System.out.println(EmailHandler sending mail...);
System.out.println(EmailHandler : TO + address);
System.out.println(EmailHandler : Begin Message);
System.out.println(content);
System.out.println(EmailHandler : End Message);
}
}
You should notice a few things. First, Al wanted the code to be easily configurable with
new email addresses without any code changes. As such, he made the email address a
handler parameter and used the getOption() API to get the value. This means each time
this handler is deployed, the email address for that instance can be controlled like this (in
WSDD):
<handler type=java:com.skatestown.handlers.EmailHandler>
<parameter name=emailAddress [email protected]/>
</handler>
If no emailAddress parameter is configured in the WSDD, a default address is hard-
coded.
Second, the RPCElement and RPCParam classes are used here. RPCElement is a subclass
of SOAPBodyElement that represents an Axis method invocation of an operation with
parameters. Its used for RPC, document, and wrapped style services, despite the name (a
holdover from Axiss original RPC focus). The primary purpose of the class is to make it
easy to map an operation invocation and its XML representation, and to get at the indi-
vidual parameters of such an invocation. As you see here, we can get a Vector of the
parameters for a given RPCElement and then query the value of each one. We know in
the case of the request message that the first parameter is the sku and the second is the
quantity, so we get them by position. For the response message, we know the return
value will be called doCheckReturn (Axis always names return elements
{methodName}Return by default), so we ask for that RPCParam by name.
Finally, notice that the code is surrounded with a try/catch for SAXExceptions. We
do this because accessing the parameters of an RPCElement might require XML parsing
in some situations, and so methods like RPCElement.getParam() throw SAXExceptions.
Since the invoke() method only throws AxisFaults, we need to wrap any thrown
SAXExceptions as AxisFaults for this code to work. We do this with the static
AxisFault.makeFault() API, which takes any Exception and wraps an AxisFault
around it.
Listing 5.1 Continued
295 How to Write a Handler
The SkatesTown GlobalHandler
Our second example will demonstrate how the InventoryCheck service we introduced
in Chapter 3 obtains its reference to the Product database (ProductDB). Listing 5.2 shows
you the GlobalHandler, which is deployed on the global request chain of the
SkatesTown AxisServer. Its purpose is to store a ProductDB reference in the active
MessageContext, as the property PRODUCT_DB (a String constant defined in the inter-
face STConstants so that it can be easily shared).
Listing 5.2 The GlobalHandler
package com.skatestown.handlers;
// imports elided
public class GlobalHandler extends BasicHandler implements STConstants {
/**
* Set up commonly used MessageContext properties
*/
public void invoke(MessageContext msgContext) throws AxisFault {
try {
// First get the AxisEngines application scope session.
// This allows us to store things persistently throughout the
// lifetime of the engine.
AxisEngine engine = msgContext.getAxisEngine();
Session appContext = engine.getApplicationSession();
// Now check if weve already stored the product database in
// here.
ProductDB productDB = (ProductDB)appContext.get(PRODUCT_DB);
if (productDB == null) {
// No cached productDB - so well have to read the XML file
// and parse it. Then we store it away in the application
// session so we only have to do this once. NOTE : in the
// real world wed be watching a database for changes
// and reloading the cache appropriately, but in this example
// were not concerning ourselves with that.
String path = msgContext.getStrProp(Constants.MC_HOME_DIR);
path += /resources/products.xml;
InputStream inStream =
new BufferedInputStream(new FileInputStream(path));
Document doc = XMLUtils.newDocument(inStream);
productDB = new ProductDB(doc);
appContext.set(PRODUCT_DB, productDB);
296 Chapter 5 Implementing Web Services with Apache Axis
}
msgContext.setProperty(PRODUCT_DB, productDB);
} catch (Exception e) {
// If anything goes wrong above, wrap the exception in an AxisFault
// and throw it.
throw AxisFault.makeFault(e);
}
}
}
Notice the getApplicationSession()API on the AxisServer: This call gets us a
Session object that is global to the entire server. Essentially, this is a persistent Map that
we can use to store state as long as the AxisServer is running. In this case, we keep a
reference to this Session as appContext.
First we check to see if there is already a ProductDB instance in appContext under
the key PRODUCT_DB. If there is, we put it in the MesssageContext property of the same
name and return. If not, however, we go and initialize the ProductDB object using a
DOM Document that we obtain by reading the file product.xml from the resources/
directory underneath the AxisServers home directory. Two handy things here: Axis
always keeps the servers home directory in the MessageContext property MC_HOME_DIR
(a constant from org.apache.axis.Constants); and you can use the XMLUtils class
(org.apache.axis.utils.XMLUtils) to easily parse XML documents.
Back in Chapter 3, you saw that the ProductDB was obtained by a static call to
ProductDB.getCurrentDB(). To finish this example, well show you the implementation
of that method:
class ProductDB implements STConstants {
static ProductDB.getCurrentDB() {
MessageContext mc = MessageContext.getCurrentContext();
return mc.getProperty(PRODUCT_DB);
}
... rest of class ...
}
This method, which serves to insulate code like our InventoryCheck class from the
details of Axis, gets the value of the PRODUCT_DB property from the current
MessageContext. Since the GlobalHandler will have already run by the time we call
this method in the service, well find the ProductDB waiting there for us, ready to use.
Now that youve seen a couple of handler examples, well finish the chapter with a
few more important areas you might want to explore about Axis, starting with security.
Listing 5.2 Contiued
297 Built-in Security
Built-in Security
Axis includes a few basic security features, and the extensibility mechanism allows you to
add more. Well give you a quick overview of the authentication and authorization
mechanisms in this section, but again we refer you to the Axis documentation for more
details.
Using the Authentication/Authorization Handlers
To control access to services by username or role using the standard Axis infrastructure,
you must deploy two handlers into your system, either in the global request chain or the
service-specific request chain:
n
SimpleAuthenticationHandler serves to confirm at runtime that there is an
authenticated user associated with the current request.
n
SimpleAuthorizationHandler does the work of managing access control lists for
particular services.
Both of these handlers are in the package org.apache.axis.handlers.
Once youve deployed these two handlers, you can control which users are allowed to
use a given service by specifying the allowedRoles parameter on the service like so:
<service name=MySecureService>
...
<parameter name=allowedRoles value=manager,jim/>
</service>
The value of the parameter is a comma-separated list of the roles that are allowed to
access this service. Roles may be exact usernames (such as jim) or groups (manager).
The SimpleAuthenticationHandler relies on a SecurityProvider
(org.apache.axis.security.SecurityProvider), which is an interface that insulates
Axis from all the details of a given security system. Axis provides two implementations:
n
SimpleSecurityProvider uses a simple text file to store usernames and passwords.
The SimpleSecurityProvider is provided for demonstration only; it wouldnt be
used in any situation where security was a real concern.
n
ServletSecurityProvider uses the built-in security of the servlet engine into
which Axis is deployed.
To use the ServletSecurityProvider, you have to tell the AxisServlet to enable it.
You do so via a servlet init-parameter (this goes inside the <servlet> definition for the
AxisServlet in your web.xml):
<init-param>
<param-name>use-servlet-security</param-name>
<param-value>true</param-value>
</init-param>
298 Chapter 5 Implementing Web Services with Apache Axis
This parameter causes the AxisServlet to automatically set up a
ServletSecurityProvider in the MessageContext, which handlers such as the
SimpleAuthenticationHandler can utilize without caring about the specific imple-
mentation.
Of course, you can define your own SecurityProvider subclasses, as you might do
to enable other transport-specific security mechanisms. As youve seen, Axis has a flexible
notion of underlying transports, which well cover a bit more in the next section.
Understanding Axis Transports
Axis has been designed to be a transport-independent SOAP engine. As such, despite the
fact that most messages that enter and leave the system do so via HTTP, the HTTP-ness
is split into HTTP-specific components. In this section, well describe the other trans-
ports included with Axis and touch on how you can build your own.
Client Transports
On the client side, a new transport requires a class that inherits from the
org.apache.axis.client.Transport class.You can make sure your calls use the desired
transport in one of two ways. First you can directly call setTransport() on the Call
object:
call.setTransport(myTransportObject);
Second, you can register a Transport as the default handler for particular URL pro-
tocols, so if you want to register a Transport to handle all mail: URLs, you can do that.
(See the Axis docs for information.)
The Transport object is the client-side equivalent of the transport listener on the
server side. Its responsible for setting the transport name in the MessageContext and
setting up any MessageContext properties that should be ready to go before the engine
(the AxisClient in this case) begins its processing. Just before each outgoing invocation
is passed to the AxisClient, the Call object calls the setupMessageContext() method
on the current Transport object. This lets the Transport object set any specific
MessageContext properties it desires.
When the engine returns, the Transport object gets another chance to do some
work before the Call object returns control to the caller. The
processReturnedMessageContext() method is invoked to enable any transport-specific
processing that might be necessary on the response. Since the Transport object survives
across invocations, this can be used in order to hold transport-specific values (the
HTTPTransport uses this technique to hold cookie values so they can be returned on
subsequent invocations).You can see examples of these methods in the source code for
the various transports well describe.
The Transport object is the portion of the client transport that is outside the
AxisClientin other words, it runs before (on the request) and after (on the response)
the engine has done its magic with the MessageContext. There is also a transport
299 Understanding Axis Transports
component inside the AxisClient, which is the named targeted chain in the WSDD
that we spoke about early in this chapter, and the associated transport handlers.
Server Transports
There are two interlinked pieces to transports on the server as well. First is the transport
listener, which is the piece of code responsible for pulling a transport-specific message
from somewhere and invoking Axis. The second piece is the handlers associated with the
transport name passed in by the listener.
Since the listener is, by definition, outside the Axis engine, you need a framework to
run it. For HTTP, a servlet engine does this for you. For other transports, the listener
might be anything from a simple class with a main() that polls for messages, to a multi-
transport server framework. They all take in messages in some form, generate
MessageContexts, and deliver them to an AxisServer with the transport name field set
in a way that enables the server to find the correct targeted chain of handlers to perform
transport-specific processing.
Transports Included with Axis
The following transports are included with the Axis distribution.
JMS
Axis includes a JMS (Java Message Service) transport
(org.apache.axis.transport.jms), which was originally designed and implemented by
contributors from Sonic Software, a leading JMS vendor. This transport lets you send
messages over the reliable JMS infrastructure and control JMS behavior in convenient
ways. See the JMS sample in samples.jms for details.
Mail
The mail transport (org.apache.axis.transport.mail) lets Axis communicate using
SOAP over email connections. Typically you wouldnt use email as a transport for any
kind of time-critical interaction, but the email infrastructure does have some nice fea-
tures. Once your outgoing mail has been accepted by an SMTP server, the server will
handle retrying the send in case of any connectivity problems to the destination. On the
inbound side, the mail transport uses POP3 to pull SOAP emails from a particular
mailbox.
The mail transport requires the Jakarta commons.net package, which is available at
apache.org.
Local
The local transport (org.apache.axis.transport.local) is an in-process transport
designed mostly for testing. It essentially instantiates an AxisServer inside the transport
itself and uses that server to handle the request.You can see many examples of its use in
the Axis tests (for example, test.wsdd.TestAdminService or test.GenericLocalTest).
300 Chapter 5 Implementing Web Services with Apache Axis
Java
This transport (org.apache.axis.transport.java) lets you invoke Java methods
directly without deploying service classes.You can access a URL like
java:mypackage.MyClass, and the transport will call methods of a MyClass object as if
it had been deployed as an RPC service.
Custom Transports
Youll find a couple of examples of building custom server-side transports in
samples/transport in the Axis distribution. There is a custom TCP transport, and also a
file-based transport that monitors a given directory for new XML files containing SOAP
requests. Since these are transport listeners, they are, in a sense, outside of Axis proper. Its
their responsibility to accept incoming messages and then call the Axis engine. As such,
the TCPListener class has a main() method and can be run from the command line
much like SimpleAxisServer. The FileReader class doesnt have a main(); but its a
Thread subclass, so the FileTest handles spinning off the listener.
To make any of the optional transports we discussed work, you typically need to
make sure the appropriate classes/JAR files are in your classpath. The next section covers
some of the other software Axis collaborates with in order to better do its job.
No API Is an Island: Axis and Its Environment
Axis, like many kinds of toolkit software, has some fairly deep relationships with its envi-
ronment. Depending on the tools available in the environment, Axis can utilize plug-in
functionality that people have built as options, and Axis also tries not to reinvent the
wheel where possibleit utilizes other libraries for common functions. Well touch on
some of these in this section.
Commons-Discovery and Obtaining Resources
The framework Axis uses to get several of its pluggable resources is called the commons-
discovery library. This library lets you search for configurable implementations of par-
ticular interfaces. For instance, you could ask the discovery library to find an appropriate
JMS implementation. It would check various configuration options, including system
properties and local properties (such as properties of an AxisEngine), to find a concrete
class implementing the desired interface.
Logging Infrastructure
Axis uses another Jakarta commons library for its logging functionality. The commons-
logging library was developed because the Java world had at least three commonly used
logging systems: log4j, jlog, and the native logging in JDK 1.4. A need was felt for a thin
infrastructure layer that insulated the developer from the details of these systems in order
to provide a unified API.
301 Development/Debugging Tools
Security Providers
A variety of secure socket factory classes are available in the org.apache.axis.
components.net package.You can select them for use by setting the system property
org.apache.axis.components.net.SecureSocketFactory to the desired
SecureSocketFactory class; doing so engages the discovery mechanism described
earlier.
Compilers
When Axis needs to compile a JWS file, it uses either the standard javac compiler (the
default) or another compiler such as Jikes (a Java compiler from IBM). Again, discovery is
used to find a component implementing the org.apache.axis.components.Compiler
interface.
Especially with all these optional dependencies on other libraries/classes, it can some-
times be a challenge to make sure everything is working correctly when trying to build
and deploy your Web service requesters and providers. The next section covers some of
the tools Axis includes to help you make sure the system is working the way you want it
to, both at deployment time and at runtime.
Development/Debugging Tools
Axis comes with several aids to developing and debugging the installation and your indi-
vidual clients and services.
The happyAxis Page
As we discussed in the previous section, Axis has dependencies on other libraries, which
is the usual case in well-componentized software. Some of those libraries, like the JMS
transport or the Castor XML/Java binding system, are optionalAxis runs fine without
them. Other pieces, though, need to be present and installed/configured correctly in
order for Axis to work. The commons-discovery library and an XML parser are both
examples.
When the first few versions of Axis came out, it was tricky to figure out what was
wrong if your installation wasnt working. As a result, Axis provides a page called
happyAxis.jsp, whose purpose is to confirm that all the necessary componentry is
available and to give clear and user-readable errors if the environment isnt set
up correctly.
The happyAxis page looks like Figure 5.12 in a browser when things are set up right.
The message The core Axis libraries are present appears if everything looks good.
Configuring Logging
Axis uses the Jakarta commons-logging package, as mentioned earlier. By default, the
commons-logging package looks for a log4j installation and uses that as its concrete
logger, although you can configure it to use other loggers as well. Logging is a common
302 Chapter 5 Implementing Web Services with Apache Axis
way to diagnose and debug problems, so you might want to learn a bit about using log4j
and commons-logging. Again, we wont cover the details here, but well tell you the
basics.
Figure 5.12 The happyAxis page
Commons-logging and log4j both define several levels of severity for each event that
might be logged. These include DEBUG (only interesting if youre really debugging, tend-
ing toward verbosity), INFO (informative messages that can be nice to see but arent criti-
cal), ERROR (problem tracking), and FATAL (serious problems). These events are logged
with loggers that correspond to particular areas of the systemfor instance, there is gen-
erally a logger associated with each class.You can configure various log destinations like
the console and log files, and then control which events from which loggers go to which
destinations.
Log4j looks for a file called log4j.properties in one of the directories on your
classpath.Youll find a default version of this configuration file in the Axis distribution,
which defines a couple of appenders (log4js word for destinations): one for the console
and one for a log file called axis.log. By default, all messages of INFO or higher priority
are sent to the console logger as a result of this line:
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
The root category is the base from which all the loggers in the system inherit.You
can change this to reduce the verbosity (fewer messages) by changing INFO to either
ERROR or FATAL, or get more detailed messages by changing INFO to DEBUG.You can opt
to log to a file instead of the console by changing CONSOLE to LOGFILE.
303 Development/Debugging Tools
Often you dont want to get all the messages that setting the root category to DEBUG
produces, but you do want to debug a portion of the system, like the AxisServlet. This
can be done with a line like the following:
log4j.logger.org.apache.axis.transport.http.AxisServlet=DEBUG, CONSOLE
This line sets the AxisServlet logger to print DEBUG messages, leaving the rest of the
system alone.
Using tcpmon and SOAPMonitor
When youre trying to deploy and use successful Web services, its often critical to be
able to see the SOAP messages on the wire in order to understand what is being pro-
duced and consumed by clients and servers. Axis includes two ways to do this: the
tcpmon application and the SOAPMonitor servlet.
tcpmon
You can start tcpmon as follows:
java org.apache.axis.utils.tcpmon <localPort> <remoteHost> <remotePort>
Specifying a local port, a remote host, and a remote port (either none or all three must
be used) tells the TCP monitor which local port to listen on and where messages
received by that port should be forwarded.
If you dont use the command-line options, youll see a screen like that shown in
Figure 5.13. This UI allows you to start new monitor windows, each of which will listen
on the given local port and forward messages to and from the desired remote host/port.
Figure 5.13 The tcpmon UI
304 Chapter 5 Implementing Web Services with Apache Axis
If you select Proxy, then tcpmon will expect to be used as an HTTP proxy, which means
clients will send special HTTP headers to tell tcpmon where to forward each message. If
you select Listener, then as far as the client is concerned, tcpmon is the endpoint. As
such, youll need to configure in the real endpoints hostname and port number.
While tcpmon is running, it forwards messages back and forth to the real endpoint
and displays the message contents so that you can see whats going on. This is an invalu-
able tool for developing and debugging Web services, and you can also use it to monitor
regular non-SOAP HTTP traffic.
SOAPMonitor
SOAPMonitor provides essentially the same functionality as tcpmon, except that it works
within the Axis server. Instead of running a separate application, to use SOAPMonitor you
deploy a handler in both the request and response flows (either the global ones or for
each service you wish to monitor).
The default server-config.wsdd that comes with Axis includes commented-out
deployments of the SOAPMonitor servlet, so all you need to do is uncomment them to
use it. The handler watches the request and response messages as they go by and sends
copies of them to an applet, so that you can watch the traffic in a Web browser by
accessing http://hostname/axis/SOAPMonitor. See the Axis user guide for more details
on using SOAPMonitor.
Axis Futures: A Quick Tour
To close out this chapter, well mention a few things that are on the drawing board for
Axis 2.0. First and probably foremost, we envision an extensibility architecture that
allows new features (implementations for some of the specs well discuss later in this
book, for example) to be plugged in as easily as dropping a .jar file into an extensions
directory. Essentially, Axis will have a registry of available extensions, and each one can
register to be triggered in various ways, including when WSDL2Java sees particular
WSDL extensions.
The end result would be something like this: drop wssecurity.jar into your Axis
extensions directory, and then run WSDL2Java on a WSDL file containing policy state-
ments that require WS-Security to be engaged for the service. Since the extension has
registered interest in the security policy extensions, Axis hands control over to a class in
the extension JAR while its reading the WSDL. That class has access to the meta-data
model that WSDL2Java is building; it can do things like deploy custom handlers into the
request and response chains of the service, so that the SOAP extensions can be success-
fully generated and received.
On the server side, this could work much the same way; but rather than use WSDL as
the trigger, you could drop an option into your WSDD that would activate the exten-
sion. In other words, the WSDD would contain a higher-level extension marker instead
of individual handler deployments, which would make deploying the extension correctly
much easier (especially in cases with multiple handlers).
305 Summary
Here are some other directions the Axis team has discussed or intends to investigate:
n
A pull-based parser model instead of SAX
n
Support for JAX-RPC 2.0 / JAXB 2.0
n
Early support for WSDL 2.0
n
A rearchitecture of the WSDL processing code for cleanliness, functionality, and
speed
n
A performance pass to speed up the system in general
n
Defaulting to SOAP 1.2 instead of SOAP 1.1
n
Real remote deployment, allowing remote administrators to upload JAR files con-
taining service and data classes
Participating in the Axis Community
Axis, as weve mentioned, is an open source project. So, youre welcome to download the
source code any time you want. This is great when you want to figure out how some-
thing works underneath the APIs. It also means the Axis team welcomes new developers
into the community, especially if youre enthusiastic and ready to roll up your sleeves to
help. The package is functional, but there are still problems to be solved and a few places
where subsystems could use some careful refactoring.
If you might like to participate in this work, you can start by looking at the open
bugs list (which you can find off the main Axis Web page,
http://ws.apache.org/axis). If something in there seems like an interesting and rea-
sonably small piece of work, dive in and fix it, feeling free to ask questions on the Axis
developers mailing list axis-dev. The same is true if you have a great idea for a way to
improve the system.
When youre happy with your changes, submit a patch to the axis-dev mailing list,
and one of the team members will review and commit it for you. After you submit a few
patches for bug fixes or enhancements and have demonstrated both ability and commit-
ment to the project, you can be granted committer status yourself and become a full-
fledged member of the team.
If you arent necessarily interested in contributing to the project, but you have ques-
tions about using Axis, the axis-user mailing list is the place to go. Its an active forum
of Axis users, from beginner to advanced, trading information and tips.
Summary
We hope youve enjoyed this whirlwind tour through the Apache Axis SOAP engine. By
now you should feel comfortable building and consuming Web services, whether youre
starting from Java code or from WSDL descriptions.You understand the basics of the
type-mapping system, how to use WSDD to control meta-data and deploy components
306 Chapter 5 Implementing Web Services with Apache Axis
(services, mappings, and handlers), and what it takes to extend Axis in useful ways.
Perhaps youre even inspired to look at the Axis source code, and maybe even join the
team.
In the next chapter, well look at how services and users find each other using reg-
istries like UDDI.
Resources
n
Apache Axishttp://ws.apache.org/axis
n
log4jhttp://logging.apache.org/log4j/docs/
n
Jakarta Discovery Libraryhttp://jakarta.apache.org/commons/discovery/
n
Jakarta Commons Network Libraryhttp://jakarta.apache.org/commons/net/
n
JUnithttp://junit.org/
n
SOAPBuildershttp://groups.yahoo.com/group/soapbuilders
n
Axis-user mailing list, for questions and tips about using Axisaxis-user-
[email protected]
n
Axis-dev mailing list, for development discussion and serious technical issues
[email protected]
6
Discovering Web Services
THIS CHAPTER INTRODUCES THE TOPIC of discovering Web services and the role of a
service registry gin a service-oriented architecture. Well discuss several alternatives to
service registries, but focus on the Universal Description, Discovery, and Integration
(UDDI) gstandard. In addition to describing how a UDDI registry is used, well
describe how to use Web Services Definition Language (WSDL) in a UDDI registry.
Well also introduce some business partners of SkatesTown and discuss how a service
registry such as UDDI can be used to help integrate their Web services. Throughout this
chapter, well introduce sample Java code that gives examples of how to use a UDDI
registry.
What Is Service Discovery?
In the previous chapters, we explained how to describe and use a Web service. But
before you can use a Web service, you must discover its description and location. The
service discovery process establishes the relationship between the service requestor and
the service provider. Service discovery gdefines a process for locating service
providers and retrieving service description documents that have been published; its an
important aspect of a service-oriented architecture.
Role of Service Discovery in a Service-Oriented Architecture
Recall the service-oriented architecture approach from Chapter 1, Web Services
Overview and Service-Oriented Architectures (shown in Figure 6.1). A service-oriented
architecture is based on the interactions between three primary roles: service provider,
service registry, and service requestor. These roles interact using publish, find, and bind
operations. The service provider is the business that provides access to the Web service
and publishes the service description in a service registry. The service requestor finds the
service description in a service registry and uses the information in the description to
bind to a service.
308 Chapter 6 Discovering Web Services
Figure 6.1 Service-oriented architecture
In this view of the service-oriented architecture, the service registry is a logical concept.
In addition to representing the publication process, it also represents the service discov-
ery method used to locate information about the service provider and obtain the Web
service description.
Service Discovery Mechanisms
The role of the service registry in a service-oriented architecture can be fulfilled using
several different mechanisms. Figure 6.2 illustrates a continuum of service discovery
techniques.
There is a trade-off between the simplicity of the registry mechanism and the sophis-
tication of the publishing and searching techniques. Lets briefly discuss each of these
points on the continuum.
The simplest form of publication and service discovery is to request a copy of a serv-
ice description directly from a service provider. After receiving the request, the service
provider can email the service description as an attachment or provide it to the service
requestor on a transferable media, such as a diskette. Although this type of service discov-
ery is simple, it isnt very efficient or dynamic since it requires prior knowledge of the
Web service as well as the contact information for the service provider.
At the other end of the spectrum, publication and service discovery use a centralized
service registry. Service providers can use a registry to advertise their businesses and the
Web services they offer. UDDI is an example of this type of service registry. UDDI
allows definitions of businesses and services, detailed binding information, and constructs
to support referential categorization, identification, and indication of adherence to tech-
nical specifications. This level of function provides the basis for supporting a service
requestor when it wants to find a Web service dynamically.
Between these two extremes, there can be other publication and discovery methods.
As an example, a distributed publication and discovery method could provide references
to service descriptions at the service providers point of offering. The Web Services
Service
Registry
Service
Requestor
Service
Provider
Bind
Find Publish
309 What Is Service Discovery?
Inspection Language (WS-Inspection) provides this type of distributed support by
specifying how to inspect a Web site for available Web services. The WS-Inspection
specification (http://www-106.ibm.com/developerworks/webservices/library/
ws-wsilspec.html) defines the locations on a Web site where you can look for Web
service descriptions.
High
Simple
Static Dynamic
Find Methodology
F
u
n
c
t
i
o
n
a
l
i
t
y
UDDI
WSDL Repository
Discovery at Point of Offering
Email, FTP, Diskette
Figure 6.2 Types of service registries
Another approach is a repository of WSDL documents. This is similar in spirit to discov-
ery at the point of offering, since it uses HTTP GET as the primary means by which
the service requestor retrieves the service description. A WSDL repository may also pro-
vide additional features. For example, SalCentral (http://www.salcentral.com) offers
features such as notification when a service description has changed, searching tools, and
a basic categorization scheme. XMethods (http://www.xmethods.com) is another exam-
ple of a public repository of WSDL documents.
Service Discovery at Design Time and Runtime
There are two basic types of service discovery: static and dynamic. Static service discov-
ery generally occurs at application design time, whereas dynamic discovery occurs at
runtime. At application design time, a human designer uses a browser or other user inter-
face to perform a find operation on a service registry. The results of this operation are
examined, and the service description returned by the find operation is incorporated
310 Chapter 6 Discovering Web Services
into the application logic. In Chapter 5, Implementing Web Services with Apache
Axis, we discussed some of the tooling that can consume WSDL service descriptions
and generate code for integration with the application.
In many cases, the service interface definition is used to build a proxy that the appli-
cation logic uses to invoke the Web service and consume its response. When youre using
dynamic service discovery, the service implementation details, such as the network loca-
tion and network protocol to use, are left unbound at design time so that they can be
determined at runtime. At runtime, the application issues a find operation against the
service registry to locate one or more service implementation definitions that match the
service interface definition used by the application. Based on application logic such as
best price, best terms, and so on, the application chooses a Web service to invoke from
among the results of the find operation, extracts network location and other information
from the service implementation definition, and invokes the Web service.
Scenario Updates
As SkatesTown sells more skateboards, its network of suppliers expands. Lets examine
some of the new partners SkatesTown now deals with:
n
WeMakeIt Inc. is a large manufacturer of industrial components. Its business is to
be a components supplier to a wide variety of different finished goods manufac-
turers. WeMakeIt manufactures a wide range of components at its manufacturing
sites in the USA, Europe, and Asia. Of particular interest to SkatesTown, WeMakeIt
manufactures a line of small nylon wheels and wheel bearings ideal for skate-
boards. Joanna Pravard is the lead Web services developer for WeMakeIt. She is
responsible for all of WeMakeIts Web services technologies, including its UDDI
registries and its entry in the UDDI Business Registry.
n
A recently created e-marketplace called e-Torus has formed to facilitate efficient
buying and selling of wheels and wheel bearing components to finished goods
manufacturers. As part of the e-marketplace, e-Torus runs a private UDDI registry
listing all the manufacturers of wheels, bearings, and related components. This pri-
vate UDDI registry is also a place where service interface standards are established
for the marketplace, making B2B buying and selling of these components more
efficient and less error prone.
n
Al Rosen of Silver Bullet Consulting discovered e-Torus and got SkatesTown to
use its e-marketplace services. Through e-Torus, SkatesTown established its business
relationship with WeMakeIt Inc.
UDDI (Universal Description, Discovery,
and Integration)
The UDDI initiative was first announced on September 6, 2000. The first three UDDI
versions were developed with UDDI.org as the sponsoring organization. When UDDI
version 3.0 was completed, UDDI.org submitted the specifications to the Organization
311 UDDI (Universal Description, Discovery, and Integration)
for the Advancement of Structured Information Standards (OASIS) so that they could
evolve into formal standards. All the UDDI work is now done through the OASIS
UDDI Specification Technical Committee (http://www.oasis-open.org/
committees/tc_home.php?wg_abbrev=uddi-spec). In April 2003, the UDDI version
2.0 specifications were approved as a formal OASIS standard. At the time this book was
written, the UDDI version 3.0 specifications have been published as OASIS committee
specifications. Both specifications can be found at http://www.oasis-open.org/
committees/uddi-spec/doc/tcspecs.htm. This chapter will focus primarily on UDDI
version 2.0.
The purpose of UDDI is to facilitate service discovery both at design time and
dynamically at runtime. In typical Web services scenarios, service providers want to pub-
lish their service descriptions to a registry, and service requestors at either design time or
runtime want to query the registry for service descriptions. One important point about a
UDDI registry is that it isnt a repositorythe entries in a UDDI registry contain refer-
ences to other data (for example, a WSDL service description document).
There are two primary types of UDDI registries: public and private. The public reg-
istry is referred to as the UDDI Business Registry (UBR). The UBR is hosted by a
small number of companies (such as IBM and Microsoft), and each company hosts one
UDDI node that is replicated with the other UDDI nodes. This means you could find
the data published at the site hosted by IBM while searching through the site hosted by
Microsoft. Although the concept of a public registry was highly touted when UDDI was
first announced, this type of registry hasnt been widely used. A public registry doesnt
provide the level of trust that is required to allow a service requestor to select and use
any service provider listed in the registry.
Most of the interest in UDDI has been focused on private registries. A private reg-
istry can be hosted on the Internet or an intranet, but it usually has a specific purpose.
For example, a single enterprise may host a UDDI registry for the Web services it uses
internally, or a consortium such as e-Torus may host a registry that is used only by its
members.
UDDI Datatypes
Information in a UDDI version 2.0 registry is modeled using five datatypes:
businessEntity, businessService, bindingTemplate, tModel, and
publisherAssertion.
Figure 6.3 shows the relationship between these datatypes. To summarize this rela-
tionship, the businessEntity gelement provides information about a business and
can contain references to one or more businessService gelements. The business is
the service provider. The technical and business descriptions for a Web service are
defined in a businessService and its bindingTemplate gelements. Each
bindingTemplate element contains a reference to one or more tModels. A tModel g
element is used to define the technical specification for a service. A
publisherAssertion gelement is used to define a relationship between two or more
businessEntity elements.
312 Chapter 6 Discovering Web Services
Figure 6.3 UDDI datatypes
Lets look at some examples of these UDDI datatypes that will be described in the fol-
lowing sections of this chapter. Listing 6.1 presents a businessEntity element that con-
tains one businessService element; that businessService element contains two
bindingTemplate elements.
Listing 6.1 UDDI Business, Service, and Binding Definition
<businessEntity businessKey=54438690-573E-11D8-B936-000629DC0A53>
<name>SkatesTown</name>
<description>UDDI businessEntity for SkatesTown.</description>
<contacts>
<contact useType=Technical Information>
<description xml:lang=en>CTO for technical information</description>
<personName>Dean Carroll</personName>
<phone useType=Main Office>1.212.555.0001</phone>
<email useType=CTO>[email protected]</email>
<email useType=General Information>[email protected]</email>
<address useType=Main Office sortCode=10001>
<addressLine>2001 Skate Services Lane</addressLine>
<addressLine>New York, NY 10001</addressLine>
<addressLine>USA</addressLine>
</address>
</contact>
publisherAssertion businessEntity
businessService
bindingTemplate tModel
businessService
tModel
tModel
bindingTemplate
bindingTemplate
businessEntity
businessService
bindingTemplate tModel
313 UDDI (Universal Description, Discovery, and Integration)
<contact useType=Sales Information>
<description xml:lang=en>VP Sales</description>
<personName>Sandy Smith</personName>
<phone useType=Main Office>1.212.555.0001</phone>
<phone useType=Mobile>1.212.555.8888</phone>
<email useType=VP Sales>[email protected]</email>
<email useType=Sales Information>[email protected]</email>
<address useType=Main Office sortCode=10001>
<addressLine>2001 Skate Services Lane</addressLine>
<addressLine>New York, NY 10001</addressLine>
<addressLine>USA</addressLine>
</address>
</contact>
</contacts>
<businessServices>
<businessService serivceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB
businessKey=55BB30D8-565A-4EF9-BA2E-83118AED644D>
<name>Purchase Order Submission</name>
<description>SkatesTown purchase order submission service.</description>
<bindingTemplates>
<bindingTemplate bindingKey=2E6BAE12-04E3-DBC2-90DB-A96E21406F79
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>
Web based (HTTP) purchase order submission service.
</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission.html
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
tModelKey=uuid:68DE9E80-AD09-469D-8A37-088422BFBC36D>
<description>HTTP address</description>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
<bindingTemplate bindingKey=3F7ABC88-14F2-AEF2-41AE-F86E52908A11
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>SOAP based purchase order submission service.</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
tModelKey=uuid:7B581129-7926-5202-AB17-74A234F21BA5>
Listing 6.1 Continued
314 Chapter 6 Discovering Web Services
<description>
Reference to tModel Web service interface definition
</description>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
</bindingTemplates>
<categoryBag>
<keyedReference keyName=Sports equipment and accessories
keyValue=49221500
tModelKey=uuid:CD153257-086A-4237-B336-6BDCBDCC6634/>
</categoryBag>
</businessService>
</businessServices>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
</categoryBag>
</businessEntity>
Listing 6.2 contains the tModel elements that are referenced by the bindingTemplate
element in Listing 6.1, which defines a SOAP binding for the poSubmission Web
service. This tModel references the location of the WSDL document that contains the
technical specification for this service. Its important to note that the actual service
description isnt stored in a UDDI registry. The section Using WSDL with UDDI later
in this chapter describes how WSDL relates to UDDI entries.
Listing 6.2 UDDI tModel
<tModel tModelKey=uuid:7B581129-7926-5202-AB17-74A234F21BA5>
<name>Purchase order submission service</name>
<description xml:lang=en>
Service interface definition for purchase order submission service.
Listing 6.1 Continued
315 UDDI (Universal Description, Discovery, and Integration)
</description>
<overviewDoc>
<description xml:lang=en>
Reference to the WSDL document that contains the service
interface definition for the purchase order submission service.
</description>
<overviewURL>
http://www.skatestown.com/services/poSubmissionInterface.wsdl
</overviewURL>
</overviewDoc>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
<categoryBag>
<keyedReference keyName=uddi-org:types
keyValue=soapSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
<keyedReference keyName=uddi-org:types
keyValue=wsdlSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
<keyedReference keyName=Sports equipment and accessories
keyValue=49221500
tModelKey=uuid:CD153257-086A-4237-B336-6BDCBDCC6634/>
</categoryBag>
</tModel>
The publisherAssertion provides a method to link together two or more businesses
that are related to each other. The businesses could be subsidiaries of a larger enterprise,
or they could be members of the same industry consortium. As an example, a
publisherAssertion can be used to model the relationship between WeMakeIt Inc. and
its subsidiaries in Asia and Europe. Listing 6.3 contains the publisher assertion for
WeMakeIt and its Asian subsidiary.
Listing 6.3 UDDI publisherAssertion
<publisherAssertion>
<fromKey>
76CA56B2-789A-1AE8-AB3F-95239FBC235E
</fromKey>
<toKey>
ACFE42A4-77C2-8BE3-BA2E-83118AED644D
Listing 6.2 Continued
316 Chapter 6 Discovering Web Services
</toKey>
<keyedReference keyName=subsidiary
keyValue=parent-child
tModelKey=uuid:807A2C6A-EE22-470D-ADC7-E0424A337C03/>
</publisherAssertion>
In this example, the fromKey element contains a reference to WeMakeIt, the toKey ele-
ment references the businessEntity for WeMakeIt Asia Inc., and the keyedReference
element indicates the type of relationship between the two entities. For those service
requestors that use the services provided by WeMakeIt, this publisher assertion could be
used to find another service provider that may provide the same type of services.
What Is a tModel?
There are two primary uses for UDDI tModels. First, theyre used to define the technical
fingerprint for a Web service. This refers to any technical specifications or prearranged
agreements on how to conduct business. Second, a tModel can define a namespace that
is used to identify business entities or classify business entities, business services, and
tModels. These namespaces are used in the identifierBag and categoryBag elements;
their use is described in more detail in the next section Categorization and
Identification of Information.
Before you can invoke (or bind to) a Web service, you need to know the services
technical details, including information such as the message formats and transport proto-
cols that must be used to invoke the service. These details are supplied in the description
of the service. In Chapter 4, Listing 4.2, you saw an example of a WSDL service inter-
face definition for the poSubmission Web service. That definition contained the abstract
reusable service specification. Listing 4.2 contains the concrete implementation informa-
tion (the service implementation definition). By separating these two types of informa-
tion, you can reuse the abstract definition of the service.
For example, consider the situation where both SkatesTown and another member of
the e-Torus marketplace implement the same type of purchase order submission service.
The only difference between the two services is the location for the implementation of
the service. Would it make sense for both companies to publish the same service inter-
face definition? In a situation like this, e-Torus could abstract the service interface defini-
tion and publish it as a consortium standard. This would enable both companies (and any
other company in the consortium) to reference the service interface definition; they
would only have to publish the service implementation details. This would also help
service requestors for this type of service, since they could search the UDDI registry to
find implementations of the standard service interface definition. The service interface
definition is a perfect example of a tModel.
Service definitions that will be reused can be specified by different entities, such as an
industry consortium, a standards body, or a large corporation (for use by its suppliers).
When youre using one of these types of service definitions, its important to conform to
Listing 6.3 Continued
317 UDDI (Universal Description, Discovery, and Integration)
a known and predefined set of specifications. The tModel concept is the mechanism for
achieving this goal. It allows various entities (such as industry groups, standards bodies,
and businesses) to publish abstract service specifications that can then be used by other
entities that implement services. Using the previous example, e-Torus would register its
http://www.e-Torus.org/services/poSubmission.wsdl specification as a tModel.
This tModel would be referenced by all businesses in the consortium (such as
SkatesTown) that implemented the service interface. In this way, any service requestor
that wanted to find services that conform to the e-Torus service specification could
query the UDDI registry for services that reference that tModel.
Heres the tModel the e-Torus would publish:
<tModel tModelKey=uuid:5492ACB9-8812-6673-EF45-23C421C4A5C1>
<name>e-Torus purchase order submission service</name>
<description xml:lang=en>
This is the standard service interface definition for purchase
order submission service.
</description>
<overviewDoc>
<description xml:lang=en>
Reference to the WSDL document that contains the e-Torus standard
service interface definition for the purchase order submission service.
</description>
<overviewURL>
http://www.e-Torus.com/services/poSubmission.wsdl
</overviewURL>
</overviewDoc>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-222-2222
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
<categoryBag>
<keyedReference keyName=uddi-org:types
keyValue=soapSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
<keyedReference keyName=uddi-org:types
keyValue=wsdlSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
<keyedReference keyName=Sports equipment and accessories
keyValue=49221500
tModelKey=uuid:CD153257-086A-4237-B336-6BDCBDCC6634/>
</categoryBag>
</tModel>
318 Chapter 6 Discovering Web Services
The only change SkatesTown would make is in the bindingTemplate for the SOAP-
based Web service binding. The tModelInstanceInfo element must be updated to refer-
ence the e-Torus tModel listed previously:
<bindingTemplate bindingKey=3F7ABC88-14F2-AEF2-41AE-F86E52908A11
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>SOAP based purchase order submission service.</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo tModelKey=uuid:5492ACB9-8812-6673-EF45-
23C421C4A5C1>
<description>Reference to tModel Web service interface
definition</description>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
Categorization and Identification of Information
As we stated previously, one of the main uses of a UDDI registry is for static or dynamic
discovery of services. This is done by searching the entries in a registry at design time or
runtime. Depending on the number of entries in the registry and the type of search cri-
teria, the search result could be a large set of entries. To narrow the result set, UDDI
provides a method to perform intelligent searches through taxonomic categorization and
classification.
Categorization is the process of creating categories, whereas classification is the process
of assigning objects to these predefined categories. There are several types of classification
schemes, such as the Library of Congress Classification used in most libraries. For a large
space such as businesses and services, the most useful ones are hierarchical in nature. One
example is the classification scheme used by Yahoo! To find manufacturers of skateboards,
you would traverse the Yahoo! classification tree to get to Business_and_Economy/
Shopping_and_Services/Sports/Skateboarding/Deck_and_Truck_Makers/.
UDDI defines a set of built-in classification schemes (or taxonomies):
n
The North American Industry Classification System (NAICS) gfor classifying
businesses by industry (http://www.census.gov/epcd/www/naics.html)
n
The Universal Standard Products and Services Classification (UNSPSC) gfor
product and service classifications (http://eccma.org/unspsc)
n
The ISO 3166 gstandard for geographic location classifications
(http://www.din.de/gremien/nas/nabd/iso3166ma)
Each of these taxonomies is identified and referenced using a predefined tModel key.
Table 6.1 lists the tModel keys for each of these classification schemes.
319 UDDI (Universal Description, Discovery, and Integration)
Table 6.1 UDDI Built-in Classification Schemes
Name Type tModel Key
NAICS Business uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2
UNSPSC Product and Services uuid:CD153257-086A-4237-B336-6BDCBDCC6634
ISO 3166 Geographic uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88
In order to take advantage of these classification schemes, businesses need to provide the
relevant classification information as they publish their entries. This is done using the
categoryBag element. This element contains a set of keyedReference elements, each of
which has three attributes: tModelKey, keyName, and keyValue.
The tModelKey attribute is a simple but powerful extension mechanism that acts as a
namespace qualifier for the values specified in the other two attributes. For example, if
SkatesTown wanted to classify its business using the NAICS and ISO 3166 taxonomy,
the following categoryBag element would be used:
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
</categoryBag>
In this example, the business definition for SkatesTown is categorized as a Sporting and
Athletic Goods Manufacturing business with a key value of 33992 using the NAICS
taxonomy. The business location is defined as New York with a key value of US-NY using
the ISO 3166 geographical categorization scheme.
Other categorization schemes can be added to a UDDI registry. For example, the
Yahoo! classification scheme can be defined as a categorization type of tModel. It estab-
lishes a namespace that can be referenced by keyedReference elements within the
categoryBag element:
<tModel tModelKey=uuid:3D4EC875-E54F-4D8D-9CBF-346D48BCAD9C>
<name>Yahoo! Business Taxonomy</name>
<description xml:lang=en>Yahoo! Business Taxonomy</description>
<categoryBag>
<keyedReference keyName=Yahoo! Category
keyValue=categorization
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
</categoryBag>
</tModel>
After this tModel is published, another keyedReference element could be added to the
categoryBag element within the SkatesTown businessEntity element to indicate the
Yahoo! category associated with this business definition:
320 Chapter 6 Discovering Web Services
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
<keyedReference keyName=Yahoo Business Taxonomy
keyValue=Business_and_Economy/Shopping_and_Services/Sports/
Skateboarding/Deck_and_Truck_Makers/
tModelKey=uuid:3D4EC875-E54F-4D8D-9CBF-346D48BCAD9C/>
</categoryBag>
In addition to specifying categorization information, identification information may also
be provided through the identifierBag element. This type of information allows busi-
nesses or tModels to be associated with a declared identification scheme, such as a tax ID
or an industry group ID.
As an example, the D-U-N-S number (http://www.dnb.com) is defined as a taxono-
my tModel. SkatesTown can identify itself with a D-U-N-S number by using the fol-
lowing identifierBag element:
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
Since any type of identification information scheme can be registered in a UDDI reg-
istry, e-Torus can define its own business identification scheme using this model.
Businesses in the e-Torus marketplace can identify themselves using that e-Torus identi-
fication tModel as a namespace. The details of the e-Torus Registry Number tModel
look like this:
<tModel tModelKey=uuid:F2390501-A240-4470-8A5A-6088EE5B1A14>
<name>e-Torus Registry</name>
<description xml:lang=en>e-Torus Registry Number</description>
<categoryBag>
<keyedReference keyName=Unique identifiers for member companies
keyValue=identifier
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
</categoryBag>
</tModel>
To reference this identification scheme, the identifierBag element must be updated to
include an additional keyedReference element, where 12345 is the member number
for SkatesTown:
<identifierBag>
<keyedReference keyName=DUNS
321 Business Entity
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
<keyedReference keyName=eTorus Registry
keyValue=12345
tModelKey=uuid:F2390501-A240-4470-8A5A-6088EE5B1A14/>
</identifierBag>
Business Entity
The businessEntity element is the top-level element in the UDDI information data
structure. The structure is used to represent information about an entity or a business. Its
also used as a container for the businessService element and indirectly for the corre-
sponding implementation and binding details of all the services that an entity provides.
Business entity information is conceptually divided into three categories, referred to
as white pages, yellow pages, and green pages:
n
White pagesContain general contact information about the entity. The
SkatesTown entry would contain its name, address, and contact information such as
phone, fax, and email.
n
Yellow pagesContain classification information about the types and location of
the services the business entity offers. Again, for SkatesTown, this could be its clas-
sification as a sports equipment manufacturer and retailer, and more specifically as a
skateboard manufacturer and retailer.
n
Green pagesContain information about the details of how to invoke the offered
services. If SkatesTown were to offer its catalog online, its green pages entry would
have a reference to its catalog URL.
Listing 6.4 presents an example of a businessEntity definition for SkatesTown. This
businessEntity definition contains a business name, a human-readable description, and
contact information. It also contains a reference to the services it provides through the
businessServices element, business identification through the identifierBag ele-
ment, and business and geographical categorization though the categoryBag element.
Listing 6.4 UDDI businessEntity for SkatesTown
<businessEntity businessKey=54438690-573E-11D8-B936-000629DC0A53>
<name>SkatesTown</name>
<description xml:lang=en>UDDI businessEntity for SkatesTown.</description>
<contacts>
<contact useType=Technical Information>
<description>CTP for technical information</description>
<personName>Dean Carroll</personName>
<phone useType=Main Office>1.212.555.0001</phone>
<email useType=CTO>[email protected]</email>
<email useType=General Information>[email protected]</email>
<address useType=Main Office sortCode=10001>
322 Chapter 6 Discovering Web Services
<addressLine>2001 Skate Services Lane</addressLine>
<addressLine>New York, NY 10001</addressLine>
<addressLine>USA</addressLine>
</address>
</contact>
<contact useType=Sales Information>
<description xml:lang=en>VP Sales</description>
<personName>Sandy Smith</personName>
<phone useType=Main Office>1.212.555.0001</phone>
<phone useType=Mobile>1.212.555.8888</phone>
<email useType=VP Sales>[email protected]</email>
<email useType=Sales Information>[email protected]</email>
<address useType=Main Office sortCode=10001>
<addressLine>2001 Skate Services Lane</addressLine>
<addressLine>New York, NY 10001</addressLine>
<addressLine>USA</addressLine>
</address>
</contact>
</contacts>
<businessServices>
<!-- List of businessService elements go here -->
...
</businessServices>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
</categoryBag>
</businessEntity>
Business Service
The businessService element is the root element for describing a logical business
service, such as the services provided by SkatesTown. The different implementation
Listing 6.4 Continued
323 Business Entity
details and bindings for the same logical service are grouped under the same
businessService element by using the bindingTemplate element.
The following example shows a businessService element that contains the defini-
tion for the SkatesTown purchase order submission service:
<businessService serivceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB
businessKey=55BB30D8-565A-4EF9-BA2E-83118AED644D>
<name>Purchase Order Submission</name>
<description>SkatesTown purchase order submission service.</description>
<bindingTemplates>
<!-- List of bindingTemplate elements go here -->
...
</bindingTemplates>
<categoryBag>
<keyedReference keyName=Sports equipment and accessories
keyValue=49221500
tModelKey=uuid:CD153257-086A-4237-B336-6BDCBDCC6634/>
</categoryBag>
</businessService>
A businessService entry must contain a direct reference to the businessEntity entry
that its associated with. This is done using the businessKey attribute on the
businessService element.
Binding Template
The bindingTemplate element contains the technical information necessary to invoke a
specific Web service. The same logical service may have more than one type of binding.
For example, one service could have a SOAP-based HTTP binding, a HTTP browser-
based binding, or an email SMTP binding. Each of these bindings is described in a
separate bindingTemplate element that generally has a combination of access point
information and tModel references.
The following bindingTemplate element is the browser-based binding for the
SkatesTown purchase order submission service:
<bindingTemplate bindingKey=2E6BAE12-04E3-DBC2-90DB-A96E21406F79
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>
Web based (HTTP) purchase order submission service.
</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission.html
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
324 Chapter 6 Discovering Web Services
tModelKey=uuid:68DE9E80-AD09-469D-8A37-088422BFBC36D>
<description>HTTP address</description>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
The accessPoint element contains the location where the Web browser interface to the
purchase order submission service can be accessed (http://www.skatestown.com/
services/poSubmission.html). The tModel referenced by the tModelInstanceInfo
element indicates that the binding is classified as an HTTP or Web browserbased Web
service.
In addition to providing a Web browser interface to the purchase order submission
service, SkatesTown has a SOAP-based service interface. This interface is defined as a
second bindingTemplate element:
<bindingTemplate bindingKey=3F7ABC88-14F2-AEF2-41AE-F86E52908A11
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>SOAP based purchase order submission service.</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
tModelKey=uuid:7B581129-7926-5202-AB17-74A234F21BA5>
<description>Web service interface definition</description>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
The bindingTemplate contains a reference to the tModel that contains the technical
fingerprint (service interface definition) for the purchase order submission service.
Publisher Assertion
A publisherAssertion is used to model business relationships. This feature was intro-
duced in UDDI version 2.0 to address the publication needs of large, complex organiza-
tions. Any pair of businessEntity entries can be associated in some fashion, reflecting
their business relationship.
An assertion is made between two businessEntity entries: The fromKey element
contains the first businessEntity for the type of relationship, and the toKey element
contains the second businessEntity. The type of relationship is defined using the
keyedReference element. Within the keyedReference element, the tModelKey refer-
ences the relationship type system, and the keyName and keyValue attributes are used to
indicate the specific type of relationship.
When youre using the relationship type system defined in the UDDI specification,
you can use the following values for the keyValue attribute:
325 Business Entity
n
parent-childThe businessEntity referenced by the fromKey is the parent of
businessEntity referenced by the toKey.
n
peer-peerThe businessEntity referenced by the fromKey is a peer of the
businessEntity referenced by the toKey.
n
identityThe businessEntity referenced by the fromKey is the same organiza-
tion as the one referenced by the toKey.
The example in Listing 6.3, the UDDI publisherAssertion, modeled the relationship
between WeMakeIt Inc. and its subsidiary in Asia. For this type of publisherAssertion,
the keyValue attribute would be parent-child, since WeMakeIt Inc. is the parent com-
pany for WeMakeIt Asia Inc.
Using a UDDI Registry
A UDDI registry is itself an instance of a Web service. Entries in the registry can be
published and queried using a SOAP-based service interface. This means that a SOAP-
based message is used for all publish and inquiry operations. The WSDL service interface
definitions for a UDDI registry can be found at the following locations:
n
UDDI Inquiry API V2.0http://uddi.org/wsdl/inquire_v2.wsdl
n
UDDI Publication API V2.0http://uddi.org/wsdl/publish_v2.wsdl
n
UDDI API V3.0 portTypes
http://uddi.org/wsdl/uddi_api_v3_portType.wsdl
n
UDDI API V3.0 Bindingshttp://uddi.org/wsdl/uddi_api_v3_binding.wsdl
You can use a few Java programming interfaces to build client applications that use a
UDDI registry:
n
UDDI4JThe UDDI for Java (UDDI4J) API provides a UDDI-specific Java pro-
gramming model. When youre using UDDI4J, each UDDI datatype is represented
by a separate Java class, and the UDDIProxy class is used to interact with any UDDI
registry. UDDI4J is an open source project located at http://www.uddi4j.org.
n
JAXRThe Java API for XML Registries (JAXR) provides an API that can be
used to build Java applications that use business registries based on open standards,
such as ebXML and UDDI. The documentation for JAXR and a reference imple-
mentation are available at http://java.sun.com/xml/jaxr/.
n
JAX-RPCThe Java API for XML-based RPC (JAX-RPC) can be used to build
client applications that use Web services. It doesnt provide direct support for a
UDDI registry; but, as previously mentioned, a UDDI registry is a Web service.
This means JAX-RPC could be used to build a client application that uses a
UDDI registry. Work is going on in OASIS to enable a standard JAX-RPC pro-
gramming model to be generated from the WSDL and XML schema documents
for UDDI version 3.0. Information on JAX-RPC is located at
http://java.sun.com/xml/jaxrpc/.
326 Chapter 6 Discovering Web Services
At the time this book was written, both UDDI4J and JAXR were designed to be used
with UDDI version 2.0 registries. Both programming interfaces can be used with UDDI
version 3.0, but the functionality is limited to that which is available in UDDI
version 2.0.
Throughout the rest of this chapter, all the programming examples will use UDDI4J
version 2.0.2.
Publishing Service Descriptions
Most UDDI registries require you to register before you can publish any UDDI entries.
The registration process provides you with a publisher account (generally with a user ID
and password) that you can use to create entries in the registry. Entries in the registry are
owned by the publisher who created them, and only the owner can update or delete a
registry entry. The UDDI publication APIs provide support for creating, updating, and
deleting businessEntity, businessService, bindingTemplate, tModel, and
publisherAssertion entries.
To use a publication API call that creates, updates, or deletes entries in the UDDI
registry, you must first obtain an authentication token using the get_authToken API call.
Authentication tokens are opaque values required for all other publication API calls; they
represent an active session with the registry. This API call has the following format,
where the userID attribute contains the user ID obtained during registration, and the
cred attribute contains the password:
<get_authToken generic=2.0 xmlns=urn:uddi-org:api_v2
userID=userid
cred=password/>
Authentication tokens are typically valid only for a period of time defined by the reg-
istry. If an authentication token is no longer valid and you use a publication API, a mes-
sage is returned indicating that the token has expired. When an authentication token is
no longer needed, you can use the discard_authToken message to inform that registry
that it can be discarded. Doing so terminates any session state that was associated with
the token.
Table 6.2 gives a summary of the publishing APIs for the four primary datatypes.
Table 6.2 UDDI Publish APIs
Datatype Save API Delete API
bindingTemplate save_binding delete_binding
businessEntity save_business delete_business
serviceBusiness save_service delete_service
tModel save_tModel delete_tModel
The save APIs are used to create a new entry in the registry or update an existing entry.
The delete APIs are used to remove a UDDI entry from the registry. As an example,
327 Business Entity
heres the message that would be sent to publish (or save) the businessEntity for
SkatesTown:
<save_business generic=2.0 xmlns=urn:uddi-org:api_v2>
<authInfo>[authInfo value]</authInfo>
<businessEntity businessKey=55BB30D8-565A-4EF9-BA2E-83118AED644D>
<name>SkatesTown</name>
<description>UDDI business entity for SkatesTown</description>
<contacts>
...
</contacts>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
</identifierBag>
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
</categoryBag>
</businessEntity>
</save_business>
Listing 6.5 provides an example of how to use UDDI4J to process the preceding
save_business operation.
Listing 6.5 Publish BusinessEntity Example
import org.uddi4j.client.*;
import org.uddi4j.datatype.*;
import org.uddi4j.datatype.business.*;
import org.uddi4j.datatype.tmodel.*;
import org.uddi4j.response.*;
import org.uddi4j.util.*;
import java.util.*;
/**
* Chapter 6 - Publish BusinessEntity Example
*
* This example shows how to publish the businessEntity
* that represents SkatesTown.
328 Chapter 6 Discovering Web Services
*/
public class PublishBusinessEntity {
// Location of UDDI registry
String publishURL = https://uddi.ibm.com/testregistry/publishapi;
/**
* Create the businessEntity and then process the save_business operation.
*/
protected void saveBusiness(String userid, String password) throws Exception {
UDDIProxy uddiProxy = null;
// Add SSL support (this is IBMs SSL support but it can be replaced
// with other implementations)
System.setProperty(java.protocol.handler.pkgs,
com.ibm.net.ssl.internal.www.protocol);
java.security.Security.addProvider(new com.ibm.jsse.JSSEProvider());
// Create UDDI proxy
uddiProxy = new UDDIProxy();
uddiProxy.setPublishURL(publishURL);
// Create businessEntity
BusinessEntity businessEntity = new BusinessEntity();
businessEntity.setBusinessKey();
// Set name and description
businessEntity.setDefaultNameString(SkatesTown, en);
Vector description = new Vector();
description.add(new Description(UDDI businessEntity for SkatesTown., en));
businessEntity.setDescriptionVector(description);
// Create first contact
Contact ctoContact = new Contact(Dean Carroll);
ctoContact.setUseType(Technical Information);
ctoContact.setDefaultDescriptionString(CTO for technical information);
Vector phoneList = new Vector();
Phone mainPhone = new Phone(1.212.555.0001);
mainPhone.setUseType(Main Office);
phoneList.add(mainPhone);
ctoContact.setPhoneVector(phoneList);
Vector emailList = new Vector();
Email email = new Email([email protected]);
email.setUseType(CTO);
emailList.add(email);
email = new Email([email protected]);
Listing 6.5 Continued
329 Business Entity
email.setUseType(General Information);
emailList.add(email);
ctoContact.setEmailVector(emailList);
Vector skatesTownAddress = new Vector();
Address address = new Address();
address.setSortCode(10001);
address.setUseType(Main Office);
Vector addressLineList = new Vector();
addressLineList.add(2001 Skate Services Lane);
addressLineList.add(New York, NY 10001);
addressLineList.add(USA);
address.setAddressLineStrings(addressLineList);
skatesTownAddress.add(address);
ctoContact.setAddressVector(skatesTownAddress);
// Create second contact
Contact salesContact = new Contact(Sandy Smith);
salesContact.setUseType(Sales Information);
salesContact.setDefaultDescriptionString(VP Sales);
phoneList = new Vector();
phoneList.add(mainPhone);
Phone mobilePhone = new Phone(1.212.555.8888);
mobilePhone.setUseType(Mobile);
phoneList.add(mobilePhone);
salesContact.setPhoneVector(phoneList);
emailList = new Vector();
email = new Email([email protected]);
email.setUseType(VP Sales);
emailList.add(email);
email = new Email([email protected]);
email.setUseType(Sales Information);
emailList.add(email);
salesContact.setEmailVector(emailList);
salesContact.setAddressVector(skatesTownAddress);
// Set contacts
Contacts contacts = new Contacts();
contacts.add(ctoContact);
contacts.add(salesContact);
businessEntity.setContacts(contacts);
// Set identifierBag
IdentifierBag identifierBag = new IdentifierBag();
Vector keyedReferenceList = new Vector();
keyedReferenceList.add(new KeyedReference(DUNS, 00-111-1111,
Listing 6.5 Continued
330 Chapter 6 Discovering Web Services
uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823));
identifierBag.setKeyedReferenceVector(keyedReferenceList);
businessEntity.setIdentifierBag(identifierBag);
// Set categoryBag
CategoryBag categoryBag = new CategoryBag();
keyedReferenceList = new Vector();
keyedReferenceList.add(new KeyedReference(
Sporting and Athletic Goods Manufacturing, 33992,
uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2));
keyedReferenceList.add(new KeyedReference(New York, US-NY,
uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88));
categoryBag.setKeyedReferenceVector(keyedReferenceList);
businessEntity.setCategoryBag(categoryBag);
// Obtain authToken using get_authToken UDDI API
AuthToken authToken = uddiProxy.get_authToken(userid, password);
// Save businessEntity
Vector businessEntityList = new Vector();
businessEntityList.add(businessEntity);
BusinessDetail businessDetail =
uddiProxy.save_business(authToken.getAuthInfoString(), businessEntityList);
// Get businessKey for published businessEntity
String businessKey = ((BusinessEntity)
businessDetail.getBusinessEntityVector().elementAt(0)).getBusinessKey();
// Display businessKey
System.out.println(Published businessEntity key: + businessKey + .);
}
public static void main(String[] args) {
try {
PublishBusinessEntity publishBusinessEntity = new PublishBusinessEntity();
publishBusinessEntity.saveBusiness(args[0], args[1]);
}
catch (Exception e) {
System.out.println(EXCEPTION: + e.toString());
}
System.exit(0);
}
}
As previously mentioned, a publisher assertion is used to associate a pair of existing
Listing 6.5 Continued
331 Business Entity
businessEntity entries. An assertion isnt complete until the publishers of both
businessEntity entries have made the same assertion. Five APIs are used to process
publisher assertions:
n
add_publisherAssertionsAdds one or more publisherAssertions to the
publishers assertion collection.
n
delete_publisherAssertionsDeletes one or more publisherAssertions
from the publishers assertion collection.
n
get_publisherAssertionsGets the full list of publisherAssertions associated
with a publishers assertion collection.
n
get_assertionStatusReportDetermines the status of current and outstanding
assertions.
n
set_publisherAssertionsAdds new assertions or updates existing assertions.
The call operates on the entire set of assertions for a publisher.
The final publication API call is get_registeredInfo. This message is used to obtain a
complete list of businessEntity and tModel entries that are owned by the publisher
associated with the provided authentication token.
Finding Service Descriptions
When youre finding a service description, you can use two types of inquiry APIs: the
find APIs and the get APIs. Except for the find_binding API, the find APIs for the pri-
mary datatypes are used to retrieve a list of references (UDDI keys) to UDDI data
entries using specified search criteria. The find_binding API returns the contents of a
bindingTemplate. The get APIs are used to return the actual contents of a data entity.
Table 6.3 summarizes the inquiry APIs for the primary datatypes.
Table 6.3 UDDI Inquiry APIs
Datatype Find API Get API
bindingTemplate find_binding get_bindingDetail
businessEntity find_business get_businessDetail
serviceBusiness find_service get_serviceDetail
tModel find_tModel get_tModelDetail
Heres an example of the message that would be sent to find the businessEntity for
SkatesTown, using just identifier and categorization information:
<find_business maxRows=10 generic=2.0 xmlns=urn:uddi-org:api_v2>
<identifierBag>
<keyedReference keyName=DUNS
keyValue=00-111-1111
tModelKey=uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823/>
332 Chapter 6 Discovering Web Services
</identifierBag>
<categoryBag>
<keyedReference keyName=Sporting and Athletic Goods Manufacturing
keyValue=33992
tModelKey=uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/>
<keyedReference keyName=New York
keyValue=US-NY
tModelKey=uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88/>
</categoryBag>
</find_business>
The response to this message contains a list of businessInfo elements, each of which
contains a reference to a businessEntity that matched the find criteria:
<?xml version=1.0 encoding=UTF-8?>
<businessList generic=2.0 xmlns=urn:uddi-org:api_v2
operator=operatorName truncated=false>
<businessInfos>
<businessInfo businessKey=54438690-573E-11D8-B936-000629DC0A53>
<name>SkatesTown</name>
<serviceInfos>
...
</serviceInfos>
</businessInfo>
</businessInfos>
</businessList>
Each businessEntity is referenced by its businessKey. The businessKey can be used
to retrieve the actual contents of the businessEntity. The following message would be
used to retrieve the full contents of the businessEntity (which appears in Listing 6.4):
<?xml version=1.0 encoding=UTF-8?>
<get_businessDetail generic=2.0 xmlns=urn:uddi-org:api_v2>
<businessKey>54438690-573E-11D8-B936-000629DC0A53</businessKey>
</get_businessDetail>
Listing 6.6 provides an example of how to use UDDI4J to process the preceding
find_business and get_businessDetail operations.
Listing 6.6 Find BusinessEntity Example
import org.uddi4j.client.*;
import org.uddi4j.datatype.*;
import org.uddi4j.datatype.business.*;
import org.uddi4j.datatype.tmodel.*;
import org.uddi4j.response.*;
import org.uddi4j.util.*;
333 Business Entity
import java.util.*;
/**
* Chapter 6 - Find BusinessEntity Example
*
* This example shows how to find the businessEntity
* that represents SkatesTown.
*/
public class FindBusinessEntity {
// Location of UDDI registry
String inquiryURL = http://uddi.ibm.com/testregistry/inquiryapi;
/**
* Find the businessEntity and then get its details.
*/
protected void getBusinessDetail() throws Exception {
UDDIProxy uddiProxy = null;
// Create UDDI proxy
uddiProxy = new UDDIProxy();
uddiProxy.setInquiryURL(inquiryURL);
// Create identifierBag
IdentifierBag identifierBag = new IdentifierBag();
Vector keyedReferenceList = new Vector();
keyedReferenceList.add(new KeyedReference(DUNS, 00-111-1111,
uuid:8609C81E-EE1F-4D5A-B202-3EB13AD01823));
identifierBag.setKeyedReferenceVector(keyedReferenceList);
// Create categoryBag
CategoryBag categoryBag = new CategoryBag();
keyedReferenceList = new Vector();
keyedReferenceList.add(new KeyedReference(
Sporting and Athletic Goods Manufacturing, 33992,
uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2));
keyedReferenceList.add(new KeyedReference(New York, US-NY,
uuid:4E49A8D6-D5A2-4FC2-93A0-0411D8D19E88));
categoryBag.setKeyedReferenceVector(keyedReferenceList);
// Find the businessEntity using just the identifierBag
// and categoryBag as a search criteria
BusinessList businessList = uddiProxy.find_business((Vector) null,
(DiscoveryURLs) null,
identifierBag, categoryBag,
(TModelBag) null, (FindQualifiers) null, 10);
Listing 6.6 Continued
334 Chapter 6 Discovering Web Services
// Get businessKey for the first businessEntity found
String businessKey =
((BusinessInfo) businessList.getBusinessInfos().get(0)).getBusinessKey();
// Display businessKey
System.out.println(Key for businessEntity found: + businessKey + .);
// Get the businessEntity details
BusinessDetail businessDetail = uddiProxy.get_businessDetail(businessKey);
// Get first business name
Name businessName = (Name) ((BusinessEntity)
businessDetail.getBusinessEntityVector().elementAt(0)).getNameVector().get(0);
// Display business name
System.out.println(Name of businessEntity found: +
businessName.getText() + .);
}
public static void main(String[] args) {
try {
FindBusinessEntity findBusinessEntity = new FindBusinessEntity();
findBusinessEntity.getBusinessDetail();
}
catch (Exception e) {
System.out.println(EXCEPTION: + e.toString());
}
System.exit(0);
}
}
In addition to the find and get APIs for the primary UDDI datatypes, the following APIs
are also available:
n
find_relatedBusinessesUsed to find all the businesses affiliated with a speci-
fied business type. The affiliation would have been defined previously using the
save_publisherAssertion call.
n
get_businessDetailExtReturns extended businessEntity information for
one or more specified businessEntity entries. This message returns the same
information as the get_businessDetail message, but it may contain additional
information.
Listing 6.6 Continued
335 Business Entity
Whats New in UDDI Version 3.0
UDDI version 3.0 provides a significant set of improvements over UDDI version 2.0.
The major enhancements are described in this section.
Policy
Polices are used to define the specific operational behavior for a UDDI registry. This
feature was added to make it easier to use a UDDI registry in environments that have
different operational characteristics (for example, a test registry versus a production
registry).
A registry can set the following types of policies:
n
Policy delegationDefines the set of policies that can be delegated to nodes.
n
KeyingDefines the policies that affect key generation and format. As an example,
these policies could be set to allow publisher-assigned keys (described later).
n
APIsDescribes policies for the data confidentiality of the different sets of APIs.
n
Time policiesDefines how nodes in a registry synchronize their clocks.
n
User policiesDefines the policies for publication limits and transferring ownership
of UDDI data entities.
n
Data custodyDefines the policy for custody transfer between nodes. Custody
indicates the node where changes must be made to a data entity.
n
ReplicationDefines whether replication is supported, and what protocol should be
used.
n
SubscriptionDefines whether subscription is supported, as well as aspects such as
conditions for renewal and volume limits.
n
Value set policy Describes policies related to the external validation of values and
the associated caching behavior.
Security
Starting with UDDI version 3.0, entities published to a UDDI registry can be digitally
signed, providing a way to ensure the authenticity and integrity of data in the registry.
All five of the primary UDDI datatypes can be signed using XML digital signatures.You
can learn more about digital signatures in Chapter 9, Securing Web Services.
When publishing an entity in a UDDI registry, the publisher digitally signs the con-
tent of the entity and provides the digital signature as an element within the entity.
When a service requestor searches the registry, it may specify that its query should only
return entities that have been signed.
When SkatesTown starts using UDDI version 3.0 registries, the company will sign all
the entities it publishes. By doing so, SkatesTown assures its customers and business part-
ners that the data represented by those entities are valid.
336 Chapter 6 Discovering Web Services
Support for Multi-Registry Environments
UDDI version 3.0 introduces the concepts of root and affiliate registries. Affiliate reg-
istries rely on the root registry to ensure that key values are unique across both types of
registries. An example of a root registry is the UDDI Business Registry. Since the root
registry ensures unique key values, data can be easily shared between root and affiliate
registries. This support has helped enable two other new functions: subscription and
publisher-assigned keys.
In SkatesTowns environment, the UDDI registry hosted by e-Torus could be the
root registry, and the affiliate registries could be hosted by the members for the e-Torus
marketplace.
Subscription
You can use the new subscription function to receive notification when changes occur
in a registry. There are two types of subscriptions: entity-based and query-based. An
entity-based subscription notifies the subscriber when one or more entities have
changed. When using a query-based subscription, the subscriber is notified when the
result set for the query changes within a specified time period.
SkatesTown can use the subscription feature to track the services its competitors add,
or to track the services that are added to the e-marketplace registry. This mechanism can
also be used to keep the contents of two or more registries synchronized.
Publisher-Assigned Keys
When an entity is added to a UDDI registry, its assigned a unique key that is a URI.
This key is used to identify the entity in much the same way that a primary key is used
in a relational database. In version 3.0, assigning keys to an entity is controlled by policy,
and the policy can be defined to allow the publisher to specify the key for an entity
when its published.
This feature provides two benefits. First, it provides a method to move entities
between UDDI registries without having to create new keys. As an example, if you have
a test registry and a production registry, you can move a businessEntity from one to
the other without changing its business key. Second, the keys may contain values that
make them easier to use.
There are three types of keys: uuidKey, domainKey, and derivedKey. All the keys use
the format scheme : value, where the scheme is always uddi. The uuidKey is the
same as the UDDI version 2.0 (and version 1.0) keys. These keys contain a UUID
(Universally Unique Identifier) as a value. The domainKey has a valid hostname as a
value, and the derivedKey is a UDDI key with a key-specific string appended to it.
Here are some examples of these keys and how SkatesTown could use the domainKey
and derivedKey:
n
uuidKeyuddi:4CD7E4BC-648B-426D-9936-443EAAC8AE23
n
domainKeyuddi:www.SkatesTown.com
n
derivedKeyuddi:www.SkatesTown.com:PriceCheck,
uddi:www.SkatesTown.com:PurchaseOrderSubmission
337 Business Entity
Using WSDL with UDDI
UDDI provides a method for publishing and finding service descriptions. The UDDI
data entities provide support for defining both business and service information. The
service description information defined in WSDL is complementary to the information
found in a UDDI registry.
UDDI provides support for many different types of service descriptions. A Web serv-
ice, registered in UDDI as a businessService, can be described using WSDL, a plain
ASCII text document, a RosettaNet pip, RDF, or any other type of description mecha-
nism. As a result, UDDI has no direct support for WSDL or any other service descrip-
tion mechanism. In this section, we explore how to publish in a UDDI registry Web
services that are described using WSDL.
How to Publish WSDL-Based Service Descriptions
The UDDI organization has published two best-practices documents that describe how
to publish WSDL-based service descriptions in a UDDI registry:
n
Using WSDL in a UDDI Registry 1.08http://www.oasis-open.org/
committees/uddi-spec/doc/bp/uddi-spec-tc-bp-using-wsdl-v108-
20021110.htm
n
Using WSDL in a UDDI Registry,Version 2.0http://www.oasis-open.org/
committees/uddi-spec/doc/tn/uddi-spec-tc-tn-wsdl-v200-20031104.htm
The second document supplements the information in the first. The primary difference
is that the second document provides a method to model and represents individual Web
service artifacts. Since the first document is referenced by the WS-I Basic Profile (see
Chapter 13, Web Services Interoperability, for more details), Al Rosen has decided to
use these conventions as the process for publishing the SkatesTown WSDL service
descriptions in a UDDI registry.
Mapping from WSDL to UDDI
A WSDL service description contains an abstract definition for a set of operations and
messages, a concrete protocol binding for these operations and messages, and a network
endpoint specification for the binding. Figure 6.4 outlines how the major WSDL ele-
ments map into UDDI elements. Since the service interface represents a reusable defini-
tion of a service, a reference to it is published in a UDDI registry as a tModel. If the
service interface definition contains more than one binding element, the reference to
the service interface may include a pointer to a specific WSDL binding element. The
service implementation describes an instance of a service, and each instance is defined
using a WSDL service element. Each service element in a service implementation
document corresponds to a UDDI businessService element. In particular, the Web
service location (address) listed in a WSDL port element must be the same as the value
in the UDDI accessPoint element.
338 Chapter 6 Discovering Web Services
Figure 6.4 Mapping from WSDL to UDDI
How to Publish the Purchase Order Submission Service
Based on this overview, lets examine the steps that Al Rosen needs to follow in order
to publish the SkatesTown poSubmission Web service. First, the best-practices document
for using WSDL in a UDDI registry describes how to represent the service interface
definition as a tModel. The following tModel is replicated from Listing 6.2 with the rele-
vant elements highlighted:
<tModel tModelKey=uuid:7B581129-7926-5202-AB17-74A234F21BA5>
...
<overviewDoc>
<description xml:lang=en>
Reference to the WSDL document that contains the service
interface definition for the purchase order submission service.
</description>
<overviewURL>
http://www.skatestown.com/services/poSubmissionInterface.wsdl
WSDL
Service Interface
Definition
UDDI
tModel
types
message
Service Implementation
Definition
import
service
portType
binding
port
overviewDoc
overviewURL
businessService
binding Template
accessPoint
tModelInstanceInfo
tModelInstanceInfo
instanceDetails
overviewDoc
overviewURL
address
not covered in
the best practices
document
339 Business Entity
</overviewURL>
</overviewDoc>
...
<categoryBag>
...
<keyedReference keyName=uddi-org:types
keyValue=wsdlSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
...
</categoryBag>
</tModel>
For a tModel, the mapping from WSDL to UDDI is the following:
n
The overviewURL element must contain a reference to the WSDL binding ele-
ment in the poSubmission Web service interface definition. Since the
poSubmission.wsdl document contains only one binding element in it, there is
no need to directly reference the binding definition.
n
The keyedReference element in the categoryBag is used to indicate that this
tModel is categorized as a WSDL specification. This means the document refer-
enced by the overviewURL must be a WSDL document.
n
The value of the tModelKey attribute must be referenced by the bindingTemplate
thats created next.
After the tModel is published, Al can publish the following businessService for the
poSubmission Web service:
<businessService serivceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB
businessKey=55BB30D8-565A-4EF9-BA2E-83118AED644D>
<name>Purchase Order Submission</name>
<description>SkatesTown purchase order submission service.</description>
<bindingTemplates>
..
<bindingTemplate bindingKey=3F7ABC88-14F2-AEF2-41AE-F86E52908A11
serviceKey=4C379407-3E1E-DC97-B1C7-F68597DA4ADB>
<description>SOAP based purchase order submission service.</description>
<accessPoint URLType=http>
http://www.skatestown.com/services/poSubmission
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
tModelKey=uuid:7B581129-7926-5202-AB17-74A234F21BA5>
<description>
340 Chapter 6 Discovering Web Services
Reference to tModel with Web service interface definition
</description>
<instanceDetails>
<overviewDoc>
<description>
Reference to WSDL service implementation document.
</description>
<overviewURL>
http://www.skatestown.com/services/
poSubmissionImplementation.wsdl
</overviewURL>
</overviewDoc>
</instanceDetails>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
</bindingTemplates>
...
</businessService>
</businessServices>
The important content within the bindingTemplate element is as follows:
n
The accessPoint element contains the URL at which the Web service can be
invoked. This value must be the same as the address value from the port element
in the WSDL service implementation document.
n
The tModel referenced by the tModelInstanceInfo element must be the tModel
that contains the reference to the service interface definition for this service.
n
Al followed an extended convention that defined how to reference the service
implementation document from the bindingTemplate element. The overviewURL
element contains a reference to the WSDL service implementation document.
Although this isnt defined in the best-practices document, it does provide a way
for the service requestor to obtain additional WSDL-based information for this
service.
Publishing a Service Definition with Multiple Bindings
Lets look at another example that requires us to reference a single binding within a
WSDL service interface definition that contains multiple bindings (see Listing 6.7). The
e-Torus marketplace has decided to publish a standard version of both the PriceCheck
and poSubmission Web service definitions. It combined both service interface defini-
tions into one physical file, which is located on the e-Torus Web site:
http://www.etorus.com/services/OrderInterface.wsdl.
341 Business Entity
Listing 6.7 e-Torus Order Service Definition
<definitions name=e-Torus Order Services Interface
targetNamespace=http://www.e-Torus.com/services/OrderInterfaces ...>
...
<!-- Port type definitions -->
<portType name=PriceCheckPortType>
<operation name=checkPrice>
<input message=pc:PriceCheckRequest/>
<output message=pc:PriceCheckResponse/>
</operation>
</portType>
<portType name=poSubmissionPortType>
<operation name=doSubmission>
<input message=pos:poSubmissionRequest/>
<output message=pos:poSubmissionResponse/>
</operation>
</portType>
<!-- Binding definitions -->
<binding name=PriceCheckSOAPBinding type=pc:PriceCheckPortType>
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/http />
<operation name=checkPrice>
<soap:operation
soapAction=http://www.skatestown.com/services/PriceCheck/checkPrice />
<input>
<soap:body use=literal />
</input>
<output>
<soap:body use=literal />
</output>
</operation>
</binding>
<binding name=poSubmissionSOAPBinding
type=pos:poSubmissionPortType>
<soap:binding style=document
transport=http://schemas.xmlsoap.org/soap/http/>
<operation name=doSubmission>
<soap:operation
soapAction=http://www.skatestown.com/services/poSubmission/submitPO/>
<input>
<soap:body parts=purchaseOrder use=literal/>
</input>
<output>
342 Chapter 6 Discovering Web Services
<soap:body parts=invoice use=literal/>
</output>
</operation>
</binding>
</definitions>
When e-Torus publishes this service interface, it will create two tModel entries (one for
each binding definition). Here is the tModel for the binding named
poSubmissionSOAPBinding:
<tModel tModelKey=uuid:6783FF9C-1277-7823-BA89-34A976B1E6D2>
<name>e-Torus purchase order submission service</name>
<description xml:lang=en>
This is the standard service interface definition for purchase order
submission service.
</description>
<overviewDoc>
<description xml:lang=en>
Reference to the WSDL document that contains the e-Torus standard
service interface definition for the purchase order submission service.
</description>
<overviewURL>
http://www.e-Torus.com/services/OrderInterface.wsdl#
xmlns(wsdl=http://schemas.xmlsoap.org/wsdl/)
xpointer(//wsdl:binding[@name=poSubmissionSOAPBinding])
</overviewURL>
</overviewDoc>
...
<categoryBag>
<keyedReference keyName=uddi-org:types
keyValue=wsdlSpec
tModelKey=uuid:C1ACF26D-9672-4404-9D70-39B756E62AB4/>
...
</categoryBag>
</tModel>
The contents of this tModel are similar to the one that SkatesTown published. The pri-
mary difference is that the overviewURL contains an XPointer
(http://www.w3.org/TR/WD-xptr) reference to the binding named
poSubmissionSOAPBinding. To use this tModel, SkatesTown would have to update the
bindingTemplate in its businessService element to reference tModelKey:
uuid:6783FF9C-1277-7823-BA89-34A976B1E6D2.
Listing 6.7 Continued
343 Other Service Discovery Methods
Other Service Discovery Methods
There have been other attempts to define specifications for Web service discovery. Two
specifications that we review here are the Web Service Inspection Language (WS-
Inspection) and WS-ServiceGroup.
WS-Inspection
The Web Services Inspection Language (WS-Inspection) defines a method to discover
service descriptions at the service providers point of offering (http://www-
106.ibm.com/developerworks/webservices/library/ws-wsilspec.html). The WS-
Inspection specification defines two primary functions:
n
The XML format used to list references to existing Web servicesA WS-Inspection doc-
ument can reference a WSDL service description directly, and it can also reference
UDDI entries.
n
The set of conventions for locating WS-Inspection documents on a Web siteWS-
Inspection documents can be placed at common entry points for a Web site, or
references to WS-Inspection documents can appear within Web content docu-
ments, such as HTML pages.
WS-Inspection provides a basic method for service discovery, but it requires you to
know the location of a service provider so that you can inspect its Web site. Although
WS-Inspection provides a service-discovery methodology thats complementary to
UDDI (distributed versus centralized service discovery), it isnt being pursued as a Web
service standard because it didnt gain enough support.
WS-ServiceGroup
The WS-ServiceGroup specification is one of the WS-Resource Framework specifica-
tions (http://www.globus.org/wsrf). These specifications provide the basis for conver-
gence of the Grid and Web services worlds. The WS-ServiceGroup specification was
derived from the ServiceGroup portType in the OGSI 1.0 specification (http://www.
globus.org/research/papers/Final_OGSI_Specification_V1.0.pdf). WS-
ServiceGroup defines a method for grouping together Web services and WS-Resources
(a Web service that is associated with a stateful resource). A Web service that belongs to a
ServiceGroup is a Member, and each Member is associated with the ServiceGroup
through a ServiceGroupEntry. Members of a ServiceGroup must conform to the
membership rules and constraints for the ServiceGroup, so that meaningful queries can
be processed to locate entries in the ServiceGroup. The concept of a ServiceGroup
could be used to define an aggregation of Web services that form a basic service registry.
The queries that are processed against a ServiceGroup could be used to discover Web
services that are defined as Members of the ServiceGroup.
344 Chapter 6 Discovering Web Services
Summary
In this chapter, weve described the role of service registries such as UDDI within a
service-oriented architecture. We examined UDDI datatypes and APIs in some depth,
reviewed the use of UDDI for private service registries, and discussed the new features
that will be available in UDDI version 3.0. Finally, we examined the convention for
publishing WSDL-based Web services in a UDDI registry and how that convention can
make UDDI effective for doing dynamic discovery of Web services at runtime.
Resources
n
UDDI specificationshttp://www.oasis-open.org/committees/uddi-
spec/doc/tcspecs.htm
n
UDDI best practiceshttp://www.oasis-open.org/committees/uddi-
spec/doc/bps.htm
n
UDDI technical noteshttp://www.oasis-open.org/committees/uddi-
spec/doc/tns.htm
n
Web Service Inspection Language (WS-Inspection)http://www-106.ibm.com/
developerworks/webservices/library/ws-wsilspec.html
II
Enterprise Web Services
7 Web Services and J2EE
8 Web Services and Stateful Resources
9 Securing Web Services
10 Web Services Reliable Messaging
11 Web Services Transactions
12 Orchestrating Web Services
7
Web Services and J2EE
THIS CHAPTER INTRODUCES THE CONCEPTS of using SOAP, WSDL, and the Web serv-
ices stack with Java 2 Enterprise Edition (J2EE) g. Although a single chapter cant do
justice to a wide-ranging development platform, well show you how to enable Enterprise
JavaBean gcomponents as Web services using Axis and the JSR109 gJCP proposal.
Continuing our example scenario, the SkatesTown technical team has been working
in Java for a while and recently has been looking at moving some of the Java applications
that run on their server into the J2EE platform. Theyve heard that its secure,
transactional, managed, scalable, and robustall the things they want for their business
applications. But theyre also interested in Web services, and they want to be able to cre-
ate services easily. So, theyve decided to build a sample application called SkatesEJB,
which will be a pilot project to determine the value in using J2EE to implement their
Web services.
J2EE Overview
Java 2 Enterprise Edition (J2EE) is the platform for building enterprise applications in
Java. J2EE standardizes the services, programming model, and deployment for applica-
tions so that developers can build solutions that can be used on a variety of application
servers. J2EE has a number of application models:
n
Thin-client/browser-based applications use servlets and JavaServer Pages g(JSPs).
n
Thick/managed application clients use RMI-IIOP gto communicate with
server-based Enterprise JavaBeans (EJB) components.
n
Messaging applications use the Java Message Service to act on messages in queues
or from subscriptions.
To this list we can now add service-based applications, which offer services over
SOAP/HTTP to clients.
J2EE provides a framework that supports high quality of service (QoS). In other
words, J2EE lets you build transactional, secure, reliable applications that are available
348 Chapter 7 Web Services and J2EE
across a cluster of highly available servers. A J2EE application server provides a wide vari-
ety of capabilities including the following:
n
Workload and performance managementThread management, pooling, caching, and
cluster support
n
Security managementPassword and certificate management and validation, authen-
tication, and access control
n
Resource managementAccess to relational databases, transactional management sys-
tems, connectors to Enterprise Resource Planning (ERP) systems, and messaging
systems
n
Transaction managementTwo-phase commit support, transactional access to data-
bases, and distributed transactions
n
Management, configuration, and administrationDeployment, configuration, and
administration tools
These services are provided through the concept of a container.
Containers
A container is a logical entity in a J2EE server. Containers are entities that have contracts
with the components that are deployed into a serverbut they also help you understand
the way a J2EE system works. Components are deployed to a container, and the contain-
er manages the execution of those components as needed. The container provides isola-
tion by intercepting calls to the EJBs, allowing the container to manage aspects such as
threads, security, and transactions between components. The container also provides a
useful abstraction between components and the resources they use.
For example, when one component uses a database, the programmer uses a resource ref-
erence g(resource ref) to link to the database. Then the deployment engineer can con-
figure the actual database that maps to the reference. So, the code that is written and
compiled isnt hard-coded to a specific database.
This approach is also used for JMS destinations, URLs and URIs, and other EJB
components in the same or different applications. Later in the chapter, youll see an
example of an ejb-ref gthat virtualizes access to another EJB.
Enterprise JavaBeans
The core of J2EE is the EJB component. This is the programming model for writing and
deploying business logic into an application server. EJBs provide a component model for
creating business applications. EJB components are mainly focused on aspects such busi-
ness logic, as well as database and legacy server access, transactions, security, and thread-
ingin other words, how you build core business services in a Java environment.
EJBs initially look complex because they were designed to layer over ordinary Java.
For each component, there are multiple .java files. Ideally, the user has an Integrated
Development Environment (IDE) that shows each component as a whole as well as the
349 J2EE Overview
individual class files for that component. However, in the examples later in this book, we
simply use a text editor and basic tools for portability.
The EJB component model allows you to structure the business logic into discrete
connected components. EJB applications are loosely coupled, with well-defined inter-
faces.
EJBs come in a variety of styles:
n
Session beans gare business logic components that are instantiated on behalf of a
user and last no longer than the time the client program remains active and run-
ning (a session).
n
Entity beans gare components that are mapped into an underlying database or
persistent store (for example, a bank account, purchase order, telephone number, or
email address). Each instance must have a unique key under which it is stored and
retrieved.
n
Message-driven beans (MDBs) gare executed in response to a message at the serv-
er, usually a one-way, asynchronous JMS gmessage, but also including messages
from other systems such as SAP R/3 or PeopleSoft coming through a connector.
There is another distinction: between local and remote EJBs. Local EJBs can only be
called from within the same container, whereas remote EJBs are callable across a network
over distributed networking protocols (such as RMI-IIOP).
An application can be thought of as having distinct partsfor example:
n
The part that deals with the long-term storage of data in a database or other per-
sistent backend (local entity beans)
n
The part that provides business logic (stateless local session beans)
n
The part that provides the interface with thick clients or a graphical interface layer
(stateless or stateful remote session beans)
n
The graphical Web interface (servlets and JSPs)
n
The part that deals with messagingsending and receiving messages from other
systems (message-driven beans)
As you can see, the J2EE application model is a component model that provides the
right component types to support solid business applications.
EJB Lifecycle
Another aspect of J2EE thats very important to Web services is the lifecycle of an EJB.
Components have well-defined lifecyclestheyre instantiated, persisted, and destroyed.
EJBs define a home factory for creating and managing the lifecycle of components.
EJBs can be divided into those with no real lifecycle and those with a lifecycle.
Note
The Open Grid Services Infrastructure (OGSI) offers a model of services that have lifecycles. This is a point of
debate around Service-Oriented Architecturewhether services have lifecycles like objects or whether
theyre stateless.
350 Chapter 7 Web Services and J2EE
Stateless session beans are session beans with no real lifecyclethe container instantiates a
pool of these as needed. Although there are multiple instances of the component, each
instance is interchangeable with any other, because they have no state that differs from
method call to method call.
Message-driven beans (MDBs) have no lifecycle either. They are instantiated and
pooled by the containerso they are always available.
Stateful session beans (SFSBs) are explicitly created by an application, with parameters
in the create() method (part of the home interface). The instance of an SFSB contains
state information specific to that client, and therefore it must either be explicitly
destroyed or timed out by the container (otherwise the number would grow until the
systems capacity was used up).
Entity beans are stateful and have attributes that are stored in a database, file system, or
other persistent storage. They must be either created or found using a key. Because
theyre kept in a persistent datastore, the objects have a very extended lifetimeunlike
SFSBs, which last as long as the client code is running, these object instances remain
available until theyre explicitly deleted, surviving reboots, crashes, system migrations, and
so on. Another difference with SFSBs is that many clients can talk to one entity bean
instance.
The lifecycle-free model of services matches the model of stateless session beans and
MDBs. In fact, a common design pattern in EJB programming involves all distributed
interaction with the EJB application taking place through a faade of stateless session
beans and MDBs. This model most closely fits todays services infrastructure.
Initiatives such as WS-Addressing (http://www-106.ibm.com/developerworks/
library/specification/ws-add/) and the WS-Resource Framework (WSRF;
http://www.globus.org/wsrf/default.asp) allow stateful components to be addressed
as services. There are ways of doing it without these technologies (such as adding a key
to the end of the address URL), but they arent standardized. Apache Axis has recently
added support for WS-Addressing, but it isnt supported in Apache Axis or in the J2EE
specification at the time of this writing.
Note
Apache SOAP supports this approach. See http://ws.apache.org/soap/.
As youll see when we dive deeper into J2EE support for Web services, the only sup-
ported components that can be accessed as Web services are servlets, stateless session
beans, and MDBs. For more information about stateful services, see Chapter 8, Web
Services and Stateful Resources.
Roles: Development, Assembly, and Deployment
One of the most useful aspects of J2EE is the way the architecture was designed from
the start with ordinary people in mind. Early Web systems were often created from many
different technologies, so the developer had to be good at programming in multiple
351 J2EE Overview
languages and systems as well as understand the network, the database connections, sys-
tem administration, and so on. For example, many of the early dynamic Web technolo-
gies mixed HTML with the programming language in a single file. This meant that a
graphic designer couldnt modify the look and feel without risking breaking the logic
with the result the programmer frequently did the layout and HTML/Web design, or
spent valuable time cutting and pasting HTML into their code.
The J2EE model clearly separates roles such as Web programmer, business logic pro-
grammer, deployment engineer, and administrator. To achieve this separation, there are
clear contracts between the components and the infrastructure. For example, JSPs allow
the HTML and page design to be separate from the business logic.
There are two important roles: the assembler and the deployment engineer. Both are
like systems integrators. Like a brick layer, the assembler pulls together a set of compo-
nents to create an application. Then the deployment engineer comes and wires and con-
nects everything, like an electrician and plumber rolled into one. The mortar, solder, and
cabling that keep it all together are called deployment descriptors.
Deployment descriptors (DDs) are XML files that capture information about compo-
nents and applications. They help resolve aspects left undefined by the programmer, such
as resolving references, setting policies, and linking to other components.
The following snippet shows a sample DD for an EJB:
<?xml version=1.0?>
<ejb-jar>
<description>Simple Application</description>
<display-name>Simple</display-name>
<enterprise-beans>
<session>
<ejb-name>StockQuote</ejb-name>
<home>com.skatestown.StockQHome</home>
<remote>com.skatestown.StockQ</remote>
<ejb-class>
com.skatestown.StockQHome
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
As youll see later, the same approach has been applied to the Web services support in
J2EE 1.4, where a new DDwebservices.xmlhas been added. This file is effectively
the Java Standards version of the Axis WSDD file.
Benefits of Using Web Services with J2EE
J2EE is a high-quality, reliable, secure infrastructure for building complex Web and busi-
ness applications. Web services started more simply with no inherent security, reliability,
352 Chapter 7 Web Services and J2EE
or transactions, but these are now being added using the open and flexible model that
SOAP and Web services offer.
Many businesses are using Web services with J2EE to provide simple interoperable
access to existing J2EE applicationsfor example, creating a C/C++/C# or VB.NET
client to an EJB application. Before SOAP and Web services, this was possible but very
complex. J2EE uses a distributed object model called RMI-IIOP, which bridges the Java
object model and the CORBA (IIOP) protocol. Accessing J2EE components from out-
side Java used to require either bridge technology or complicated logic to access the
components using IIOP from C or C++. This was typically a painful process. SOAP and
Web services make this scenario much more appealing, and a number of companies are
using the SOAP approach in their systems today.
Why use J2EE with Web services? In other words, why would you write a J2EE
application and EJBs if you could simply deploy Java classes into an Axis server to create
Web services? The answer, as usual, depends on the individual circumstances. Many small
applications will work fine with the Java Axis approach. As the complexity and require-
ments on the application scale up, having a managed J2EE environment in which to run
components becomes more appropriate. In particular, the benefits increase as you start to
connect to databases, messaging systems, and other enterprise systems.
Security
The first benefit that a J2EE application server brings is security. Because the individual
methods (operations) of an EJB can be secured, and because J2EE has end-to-end secu-
rity, you can use either HTTPS or WS-Security to authenticate users and then offer
them services and/or operations with fine-grained access control. For example, a single
service may have bid and cancel operations, and a J2EE application server can be
configured with one group of ordinary users who can bid, and then another group of
superusers who can cancel. Because security in J2EE is tightly integrated into the Java
runtime, these security models extend to the Web services space, and so the access con-
trols are applied to incoming SOAP requests as well, based on the configuration and
administration of the EJBs. (For more details, see Chapter 9, Securing Web Services.)
Transaction Control Across Distributed Resources
The second benefit a J2EE application server offers is transaction control across distrib-
uted resources. Even if you arent using WS-AtomicTransaction and WS-Coordination
(see Chapter 11, Web Services Transactions), you may wish to ensure that the backend
resources youre communicating with are kept in sync with each other and are transac-
tional. For example, you may wish to have a database log of Web service transactions
held in an Oracle or DB2 RDB and then use a JMS system to send messages internally
to a legacy system. The J2EE server can make the database log entry and send the mes-
sage in the same unit of work. That way, even if the HTTP connection fails and the
SOAP response message sent to the client fails, your server has a log of what happened.
Although it doesnt offer full end-to-end transactionality, this type of architecture has
353 J2EE Overview
been used extensively in Web-based systems to provide a higher level of robustness at the
application level.
Container Managed Persistence
J2EE offers both a programming model for building business applicationsincluding the
tools and technology to map your objects into databases (entity beans)and a reliable,
scalable infrastructure for running those applications. As soon as the application moves
beyond the simplicity of a sample or first proof of concept, users find that the learning
curve and extra complexity of a J2EE solution is repaid in the savings in time and effort
that the model offers.
For example, in our sample application, the J2EE application server will automatically
save the state of the objects we create using a feature called Container Managed Persistence
(CMP). This means that instead of having to write database logic and SQL statements,
we can write fairly straightforward Java objects and have the container generate the SQL
and take care of all the database reads, writes, and updates. This approach makes it quick
and easy to build a Web service application with a database at its heart. And, this support
is independent of which database server is used.
J2EE Versions
A word about versions: While J2EE 1.4 has just been released, J2EE 1.3 is still the more
commonly used version in real systems at the time of this writing. J2EE 1.4 includes sig-
nificant Web services support, especially the standards JAX-RPC, JAX-R, and JSR 109
(Implementing Enterprise Web Services), as well as improvements specifically targeted
at producing WS-I Basic Profile gcompliance (http://ws-i.org/Profiles/Basic/
2003-08/BasicProfile-1.0a.html).
As a result, every J2EE application server will support the following:
n
A UDDI registry
n
Calling out to external Web services through JAX-RPC
n
Hosting Web services using EJBs and servlets
n
Calling out to UDDI servers
The rest of this chapter is split into three parts. First, we create the EJBs that form the
core of the application. This isnt a full tutorial on EJBs, but just a simple EJB application
that allows SkatesTown to get going.
Next, we investigate using Web services with an existing J2EE server by using the
Apache Axis toolkit. In this section, we assume that there is a running J2EE server such
as BEA WebLogic, JBOSS, or IBM WebSphere, and we look at using the Apache Axis
framework to connect to EJBs. Were doing this because the SkatesTown developers are
familiar with Axis and would like to understand how EJBs fit into the model they
already have; and although this is more difficult than the approach in the last part of the
chapter, not all application servers support that approach yet.
Finally, we look at how this model is simplified and updated when we utilize new
support from J2EE 1.4 to deploy a service directly into an enabled J2EE container. This
354 Chapter 7 Web Services and J2EE
support (called JSR109) simplifies the model considerably. In this part, well use IBM
WebSphere as the container, and well clearly call out the standard aspects versus the
application serverspecific aspects. Once J2EE 1.4 is widespread, this will be the stan-
dardized approach to using Web services with J2EE, and all J2EE 1.4 application servers
will need to support this model.
This last approach is much less work than using Axis, because the tooling and con-
tainer do much of the work for you; so, if you have a JSR109-enabled container, you
may wish to skip trying the Axis approach. However, we recommend reading the section
in order to understand the differences between the two approaches and how EJBs fit
into the Axis approach.
Using EJBs from Axis
Apache Axis (and its predecessor, Apache SOAP) supports using a stateless session bean
as the implementation of a service. One of the benefits of EJBs is the automatic persist-
ence mapping layer for entity beans. To demonstrate, were going to create a simple enti-
ty bean for SkatesTown, to capture prices of products. In order to expose this as a Web
service, well create a front-end using a stateless session bean. This is shown in Figure 7.1.
Figure 7.1 High-level overview of the components
The stateless session bean offers a stateless interface to the entity bean and allows it to be
easily mapped into a SOAP service via Axis.
This particular entity bean contains no business logic and simply acts as a mapping
between the database and the object model. However, in other models, the entity bean
could be mapped to more complex databases and could perform more logic.
Alternatively (or in addition), there could be more logic in the session bean. However,
this scenario demonstrates the aspects of EJBs simply.
Figure 7.2 shows the two components in our application graphically:
n
The Entity component has three properties: the productCode, which is the key
field, description, and price. There are also two home methods that allow the
creation and location of instances.
n
The front-end session bean represents the service interface to the entity bean. It
references the entity bean locally and provides two main business methods to add
or view product details.
Axis
EJB
provider
SkatesProduct
Stateless session bean
SkatesEntity
CMP Entity Bean
Database
(persistence)
355 Using EJBs from Axis
Figure 7.2 Component diagram showing the two EJBs in our sample
application
The Entity Bean
Remember that each EJB component has several files. The key files are the interface and
implementation classes, and of course the DD that ties all this together.
The entity bean interface is as follows:
package com.skatestown.ejb;
import javax.ejb.EJBLocalObject;
public interface SkatesEntity extends EJBLocalObject {
public void setProductCode(String productCode);
public String getProductCode() ;
public void setDescription(String Description) ;
public String getDescription() ;
public void setPrice(double Price) ;
public double getPrice();
}
Listing 7.1 shows the implementation class.
Listing 7.1 Entity Bean Implementation Class
package com.skatestown.ejb;
import javax.ejb.EntityBean;
public abstract class SkatesEntityBean implements EntityBean {
private javax.ejb.EntityContext entityContext;
public SkatesEntityBean() { }
create ()
addProduct ()
viewProduct ()
SkatesService
R*
R
R
productCode : String
description : String
price : double
create ()
findByPrimaryKey()
SkatesEntity
L*
L
Local Reference
skateEnt
a
a
2.0
356 Chapter 7 Web Services and J2EE
public void ejbActivate() { }
public void ejbPassivate() {}
public void ejbRemove() {}
public void ejbLoad() {}
public void ejbStore() {}
public javax.ejb.EntityContext getEntityContext() {
return entityContext;
}
public void
setEntityContext(javax.ejb.EntityContext entityContext) {
this.entityContext = entityContext;
}
public void unsetEntityContext() {
}
public String
ejbCreate(String pc) throws javax.ejb.CreateException {
this.setProductCode(pc);
this.setDescription(null);
this.setPrice(0.0D);
return null;
}
public void ejbPostCreate(String pc) {}
public abstract void
setProductCode(java.lang.String productCode);
public abstract java.lang.String getProductCode();
public abstract void
setDescription(java.lang.String Description);
public abstract java.lang.String getDescription();
public abstract void setPrice(double Price);
public abstract double getPrice();
}
If you arent an EJB programmer, youre probably wondering at this pointwheres the
actual code? Although this code is effectively performing database lookups and inserts,
Listing 7.1 Continued
357 Using EJBs from Axis
you might expect that it would include SQL or other database query statements. There is
no database code because the J2EE application server (and the deployment tools that
ship with it) uses the DD and the interface to generate and run the code that imple-
ments the database logic. This CMP means the EJB container will manage the mapping
of the properties of the entity bean component into the fields of the database table.
There are different possible mappings:
n
Bottom-upThe objects are created from a database schema.
n
Meet-in-the-middleA set of existing objects is mapped to an existing database
schema.
n
Top-downThe database tables are created from the object definitions.
In this case, well use top-down to automatically create a table in which to store our
data.
Of equal importance to the classes that make up the EJB is the DD for the bean:
<persistence-type>Container</persistence-type>
<prim-key-class>
java.lang.String
</prim-key-class>
<primkey-field>productCode</primkey-field>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>
Skates
</abstract-schema-name>
<cmp-field id=PRICE>
<field-name>price</field-name>
</cmp-field>
<cmp-field id=DESC>
<field-name>description</field-name>
</cmp-field>
<cmp-field id=PRODCODE>
<field-name>productCode</field-name>
</cmp-field>
As youll see later, the DD will be packaged together with the code into a JAR file,
which the application server will use to configure the component and code.
In this fragment, the first tag, persistence-type Container, says that the program-
mer will delegate the storage of the object instances to the container. The other type,
Bean Managed, requires the programmer to write logic to store the instance data in
some form of database. The EJB 2.0 specification updated the object to relational map-
ping style. Generally speaking, new projects should use the new 2.x CMP version as
defined by the <cmp-version> tag.
Each entity bean must specify a primary (or unique) key by which instances will be
known. The rest of the tags identify database names for the schema and the columns of
data.
358 Chapter 7 Web Services and J2EE
The session bean wont have much more business logic in it, but it forms the place
where the service interface is mapped into the data stored in the database.
The Session Bean
The session bean has similar interface and implementation classes (see Listings 7.2
and 7.3).
Listing 7.2 SkatesProducts.java Session Bean Interface
public interface SkatesProducts extends javax.ejb.EJBObject {
public void addProduct(Product prod)
throws java.rmi.RemoteException;
public Product viewProduct(String prodcode)
throws java.rmi.RemoteException;
}
Listing 7.3 SkatesProductsBean.java Session Bean Implementation
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class SkatesProductsBean implements javax.ejb.SessionBean {
private javax.ejb.SessionContext mySessionCtx;
public javax.ejb.SessionContext getSessionContext() {
return mySessionCtx; }
public void setSessionContext(javax.ejb.SessionContext ctx) {
mySessionCtx = ctx; }
public void ejbCreate() throws javax.ejb.CreateException {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
public void addProduct(Product prod) {
try {
InitialContext ic = new InitialContext();
SkatesEntityLocalHome selh =
(SkatesEntityLocalHome)
ic.lookup(java:comp/env/skateEnt);
SkatesEntityLocal skent =
selh.create(prod.getProductCode());
skent.setDescription(prod.getDescription());
skent.setPrice(prod.getPrice());
} catch (NamingException ne) { // handle naming
359 Using EJBs from Axis
} catch (CreateException ce) { // handle create
}
}
// viewProduct goes here
}
Because the entity is simply a view on the persistence layer, it utilizes the underlying
semantics of a data element (create, read, update, delete). The session bean has the inter-
face we wish to offer to the service requestor. We want to expose two verbs add and
view to the service requestor. Because the service interface were designing is stateless, we
need to map one (or more) of the parameters of the service request into the unique pri-
mary key the entity bean needs.
The implementation code shows some standard codethat is, its the same in most
EJBs. The ejbXXXXXX() methods, such as ejbCreate(), let you override methods that
are called by the container during the components event lifecycle. A more intelligently
designed bean would get a reference to the entity at component creation time, reuse it,
and therefore be more efficient. However, for this simple example its easier to get the
reference as needed.
In the addProduct() method shown, the logic gets a reference to the entity bean
using a local ejb-ref (local name for the entity bean) java:comp/env/skateEnt. The
java:comp/env/ piece is predefined (its the J2EE way of saying environment).
skateEnt is a name we chose to identify the entity bean. This reference is then
defined in the DD to point to the real internal name of the entity bean. This capability
allows EJBs to be wired together, so a different entity bean (perhaps with a different per-
sistence method) that implemented the same interface contract could be used without
recoding our session bean.
The code then calls the create() method on the entity to create a new product
instance, using the product code as the primary key. At this point, if there is already a
product in the database that has the same key, a createException will be thrown, which
will cause a fault in the Web service response.
There is similar code in the viewProduct() method shown here:
public Product viewProduct(String prodcode) {
Product prod = new ProductImpl();
try {
InitialContext ic = new InitialContext();
SkatesEntityHome home =
(SkatesEntityHome)
ic.lookup(java:comp/env/skatesEntity);
SkatesEntity skent =
home.findByPrimaryKey(prodcode);
prod.setProductCode(prodcode);
prod.setPrice(skent.getPrice());
Listing 7.3 Continued
360 Chapter 7 Web Services and J2EE
prod.setDescription(skent.getDescription());
}
catch (NamingException ne) { /* deal with exc */}
catch (FinderException fe) { /* deal with exc */}
return prod;
}
This code looks up the existing product instance and then returns the information as a
new Product object.
The Deployment Unit
Once the EJBs are written, you should deploy and test them before attempting to Web
serviceenable them. Typically, the EJBs might already exist. In some circumstances, the
EJBs may not offer the right service interface, in which case a layer of stateless session
beans can be implemented to offer a clean interface that can then be Web service
enabled. The EJBs are packaged in an EJB JAR, which is then packaged in an Enterprise
Application aRchive (EAR file) g.
The EJBs must have appropriate XML DDs. The tags containing the deployment
information for each bean are shown in the following snippets. Heres the DD fragment
for the entity bean:
<entity id=SkatesEntity>
<ejb-name>SkatesEntity</ejb-name>
<local-home>
com.skatestown.ejb.SkatesEntityLocalHome
</local-home>
<local>com.skatestown.ejb.SkatesEntityLocal</local>
<ejb-class>
com.skatestown.ejb.SkatesEntityBean
</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>SKATES</abstract-schema-name>
<cmp-field id=pc>
<field-name>productCode</field-name>
</cmp-field>
<cmp-field id=desc>
<field-name>description</field-name>
</cmp-field>
<cmp-field id=prc>
<field-name>price</field-name>
</cmp-field>
<primkey-field>productCode</primkey-field>
</entity>
361 Using EJBs from Axis
And heres the session bean fragment from the DD:
<session id=SkatesService>
<ejb-name>SkatesService</ejb-name>
<home>com.skatestown.ejb.SkatesServiceHome</home>
<remote>com.skatestown.ejb.SkatesService</remote>
<ejb-class>
com.skatestown.ejb.SkatesServiceBean
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-local-ref id=entref>
<description>
Reference to SkatesEntity
</description>
<ejb-ref-name>skateEnt</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>
com.skatestown.ejb.SkatesEntityLocalHome
</local-home>
<local>com.skatestown.ejb.SkatesEntityLocal</local>
<ejb-link>SkatesEntity</ejb-link>
</ejb-local-ref>
</session>
As youll see from looking at the session bean DD, it includes a reference from the ses-
sion bean to the entity bean. This is the point at which the java:comp/env/skateEnt
is defined.
The EJBs are now packaged into a JAR file together with the DD. The JAR contents
are as follows:
>jar tf skatesejb.jar
META-INF/ejb-jar.xml
com/skatestown/ejb/Product.class
com/skatestown/ejb/SkatesEntityBean.class
com/skatestown/ejb/SkatesEntityLocal.class
com/skatestown/ejb/SkatesEntityLocalHome.class
com/skatestown/ejb/SkatesService.class
com/skatestown/ejb/SkatesServiceBean.class
com/skatestown/ejb/SkatesServiceHome.class
META-INF/MANIFEST.MF
Exposing the EJBs via Axis
This EJB JAR can now be packaged together with Axis into an EAR application. Then
the whole can be deployed and the resulting application will let you access these EJBs
using SOAP/HTTP via Axis.
362 Chapter 7 Web Services and J2EE
The first task is to package the Axis Web application as a WAR file:
cd axis11\webapps
<java_home>\bin\jar cf axis.war *.*
Doing so creates a Web Application aRchive (WAR) file containing the Axis code.
The next step is to create an application DD that packages Axis.WAR and the
SkatesEJB.jar together into a single application, as shown here:
<?xml version=1.0 encoding=UTF-8?>
<!DOCTYPE application PUBLIC
-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN
http://java.sun.com/dtd/application_1_3.dtd>
<application id=SkatesTownEJBWS>
<display-name>Skates</display-name>
<module id=EjbModule_skates>
<ejb>skatesejb.jar</ejb>
</module>
<module id=WebModule_Axis>
<web>
<web-uri>axis.war</web-uri>
<context-root>axis</context-root>
</web>
</module>
</application>
Usually youd use a tool from the application server to do this and package the resulting
EAR file. The EAR file now contains the following:
n
skates.ear
n
axis.war
n
skatesejb.jar
n
META-INF/MANIFEST.MF
n
META-INF/application.xml
At this point we move away from pure interoperable J2EE and into the realm of a spe-
cific application server. The deployment tooling will augment skates.ear with server-
specific files. For example, WebSphere Application Server will generate a number of
additional classes and XML files that bind the EJBs weve written into the container. For
the examples in this book, we used the latest available WebSphere Application Server
version 5.1, which is available for trial download on the Web (http://www-106.
ibm.com/developerworks/websphere/downloads/WASsupport.html).
WebSphere allows you to do this outside the runtime using the application assembly
tool, and also from the administration console. Other application servers have appropriate
methods (for example, BEA WebLogic server has an ejbdeploy tool).
363 Using EJBs from Axis
WebSphere Deployment Process
In order to deploy this as an EJB application, you need to go through a server-specific
deployment process that configures the application to run on a given server. As an exam-
ple, well show you how to deploy this EAR file into WebSphere Application Server.
However, the sample code is pure J2EE and so will deploy in any EJB2.0 server.
The process followed in this book is simple; we use command-line tools and the Web
console. However, as of this version of WebSphere, a new tool called the Application
Server Toolkit (ASTK) is available, which makes the procedure considerably easier.
If you have a different J2EE application server, then follow the guidelines appropriate
to that server. The deployment options should be standard and easily managed. For more
information, you may wish to see the settings that were configured in WebSphere to
deploy the application.
A Simple View of the Deployment Process
The next stages may seem complicated, so heres a heads-up of what were about to do. You can think of
this process as wiring everything together:
1. Create a database.
2. Wire that to your application server.
3. Wire the entity bean to the database.
4. Wire the session bean to the entity bean.
5. Switch to Axis and create a WSDL file.
6. Wire the Axis service to the session bean.
Once youve completed these steps, everything should work!
In order to deploy into WebSphere, the Axis application must be modified to be a
Servlet 2.3 application (the default was 2.2). Doing so involves changing the !DOCTYPE
line of the web.xml file as follows:
<!DOCTYPE web-app PUBLIC
-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN
http://java.sun.com/dtd/web-app_2_3.dtd>
This is the only change required to convert it into a Servlet 2.3 Web application.
EJB Deployment
The major item thats completely server specific for this application is the database that
backs up the CMP entity bean. In order to enable it, you need to configure a new data-
base in your database server and create a new datasource in your application server to
manage connections to it.
364 Chapter 7 Web Services and J2EE
WebSphere comes with a lightweight database system called Cloudscape. The first
step is to create a new Cloudscape database in which to store the data from the CMP
entity bean in. Follow these steps:
1. Make a directory to store the database in:
>mkdir \db2j\databases\
2. Create a database. The easiest way to do so is to use the supplied ij utility. Its in
the <websphere>\cloudscape\bin\embedded directory. Start it with the ij com-
mand line:
C:\as51\cloudscape\bin\embedded>echo off
ij version 5.1 (c) 2001 IBM Corp.
ij>
3. Type the following line:
connect jdbc:db2j:c:\db2j\databases\skatesdb;create=true;
Then type
exit;
to quit ij.Youll need to come back to ij later to create the table.
You should now have a local database. The next step is to link it to WebSphere by creat-
ing a datasource: the virtualization of the database that allows your code to be linked to
the database without your having coded any DB-specific commands in the application.
To create a new datasource in WebSphere, you must first create a JDBC Provider. This
is a configuration that tells WebSphere about a given JDBC database and classes. Luckily,
Cloudscape is preconfigured into WebSphere, so you dont need to do much (see
Figure 7.3).
Creating a default Cloudscape provider works just fine:
1. In the left-hand pane, select Resources and then, under it, JDBC Providers. Click
New.
2. Select Cloudscape JDBC Provider.
Now that you have a JDBC Provider, you can add a datasource. This tells WebSphere
about the newly created SKATESDB database. Follow these steps:
1. Go into the new Cloudscape entry and select Data Sources. Click New.
2. Give the datasource the name SkatesDataSource, which automatically gives it a
JNDI name: jdbc/SkatesDataSource.
3. Check the box that says Use This Data Source In Container Managed Persistence
(CMP). If you dont, it wont work.
4. Go into Custom Properties at the bottom of the configuration page for
SkatesDataSource. In the custom entry databaseName, change the value to
match the new directory you created earlier: c:/db2j/databases/SkatesDB.
365 Using EJBs from Axis
5. Save your configuration. (For more information, see the WebSphere docs:
http://publib.boulder.ibm.com/infocenter/wsphelp/topic/
com.ibm.websphere.nd.doc/info/ae/ae/tdat_ccrtpds.html.)
Figure 7.3 The WebSphere Admin console
Now that your database resource is defined, you can configure and deploy the
application:
1. From the console, choose Applications and then choose Install New Application.
2. Browse to the Skates.ear file.
3. The wizard will take you through a number of steps; in most of them, you can
accept the default values. The ones you need to change are listed here:
n
In Deploy EJBs Option, the Database Type is CLOUDSCAPE_V5 and the
Database Schema is SKATES.
n
In JNDI Names for Beans, the value for SkatesEntity is
ejb/skates/SkatesEntityHome and the value for SkatesProduct is
ejb/skates/SkatesProductHome.
n
In Provide Default Datasource Mapping For Modules Containing 2.0 Entity
Beans, the value for Skatesejb.jar is jdbc/SkatesDataSource.
n
In Map EJB References To Beans, the value for SkatesProduct and
SkatesEntity is ejb/skates/SkatesEntityHome.
366 Chapter 7 Web Services and J2EE
4. Click Finish.You should see the following output:
ADMA5016I: Installation of Skates started.
ADMA5018I: Starting EJBDeploy on ear
c:\as51\wstemp\3433544\upload\skates4.ear..
Starting workbench.
Creating the project.
Building: /skatesejb.
Deploying jar skatesejb
Creating Top Down Map
Generating deployment code
Refreshing: /skatesejb/ejbModule.
Building: /skatesejb.
Invoking RMIC.
Generating DDL
Generating DDL
Writing output file
Shutting down workbench.
0 Errors, 0 Warnings, 0 Informational Messages
ADMA5007I: EJBDeploy completed on
C:\DOCUME~1\paul\LOCALS~1\Temp\app_fa7b3c6751\dpl\dpl_Skates.ear
ADMA5005I: Application Skates configured in WebSphere repository
ADMA5001I: Application binaries saved in
c:\as51\wstemp\3433544\workspace\cells
\ZAK-T40\applications\Skates.ear\Skates.ear
ADMA5011I: Cleanup of temp dir for app Skates done.
367 Using EJBs from Axis
ADMA5013I: Application Skates installed successfully.
Application Skates installed successfully.
5. Choose Save To The Master Configuration and restart the server.
This process creates the DDL database table definition SQL, which youll use to create
the Cloudscape tables:
1. In the Enterprise Applications pane, select the Skates application and click the
Export DDL button.
2. Save the file as skates.ddl in a temporary directory.
3. Back to ijfollow the bold steps shown here:
ij version 5.1 (c) 2001 IBM Corp.
ij> connect jdbc:db2j:c:\db2j\databases\skatesdb;
ij> run \temp\skates.ddl;
ij> -- Generated by Relational Schema Center on
Tue Feb 03 13:47:07 GMT 2004
CREATE SCHEMA SKATES;
0 rows inserted/updated/deleted
ij> CREATE TABLE SKATES.SKATESENTITY
(PRICE DOUBLE PRECISION NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
PRODUCTCODE VARCHAR(250) NOT NULL);
0 rows inserted/updated/deleted
ij> ALTER TABLE SKATES.SKATESENTITY
ADD CONSTRAINT PK_SKATESENTITY PRIMARY KEY (PRODUCTCODE);
0 rows inserted/updated/deleted
ij>
Once you have Axis and SkatesEJB deployed and running in a server, you need to test
that Axis is running in the new server.You can use the Enterprise Applications panel to
test by browsing http://server:port/axis and seeing whether you get an Axis Web
page (see Figure 7.4).
Click the Validate link and receive Axis happiness, as shown in Figure 7.5.
If both of those steps work, then the next task is to create a DD and deploy the
SkatesService service.
Configuring Axis to Invoke the SkatesService Session Bean
In this section, we describe how to configure the Axis server to enable Web service
access to your application. Having deployed the application, you need to set up the EJB
provider to be able to invoke the SkatesService bean. This means creating a WSDD
that contains the relevant information.
368 Chapter 7 Web Services and J2EE
Figure 7.4 Apache Axis homepage
Figure 7.5 Axis Happiness page
369 Using EJBs from Axis
If Axis was running on a separate server, you would also need to ensure that the
J2EE.JAR library and the client stubs for the SkatesService bean were in the Axis
classpath. The client stubs are produced as part of the deployment process; usually, the
deployed EJB JAR file will contain them. However, by deploying Axis and the EJBs as
part of the same enterprise application, the J2EE application server ensures that the EJB
client code is available to the Axis Web application. This makes life much easier.
Before running the following commands, create and move to a new directory. The
first aim is to create a WSDL file from the SkatesService remote interface. In order to
do this, you must run the Apache Java2WSDL tool. It needs both the EJB-JAR file
(skatesejb.jar) and the J2EE class library (j2ee.jar):
>set classpath=%axiscp%;.\skatesejb.jar;<appserv>\lib\j2ee.jar
>java org.apache.axis.wsdl.Java2WSDL
lhttp://server:port/axis/services/SkatesService
com.skatestown.ejb.SkatesProducts
For this to work, you first need to set the right Axis classpath into the environment vari-
able axiscp. In particular, you need the following files in your classpath:
n
axis.jar
n
commons-discovery.jar
n
commons-logging.jar
n
jaxrpc.jar
n
saaj.jar
n
wsdl4j.jar
You also need to find the J2EE.JAR library, which will be in your J2EE app servers
classpath.You should also set the correct servername and port for your server in
the URL.
If this works, you now have a SkatesService.wsdl file, as shown in Listing 7.4.
Listing 7.4 SkatesService.wsdl
<?xml version=1.0 encoding=UTF-8?>
<wsdl:definitions
targetNamespace=http://ejb.skatestown.com
xmlns=http://schemas.xmlsoap.org/wsdl/
xmlns:apachesoap=http://xml.apache.org/xml-soap
xmlns:impl=http://ejb.skatestown.com
xmlns:intf=http://ejb.skatestown.com
xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/
xmlns:wsdl=http://schemas.xmlsoap.org/wsdl/
xmlns:wsdlsoap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
<wsdl:types>
<schema targetNamespace=http://ejb.skatestown.com
xmlns=http://www.w3.org/2001/XMLSchema>
370 Chapter 7 Web Services and J2EE
<import namespace=http://schemas.xmlsoap.org/soap/encoding//>
<complexType name=Product>
<sequence>
<element name=description nillable=true type=xsd:string/>
<element name=price type=xsd:double/>
<element name=productCode nillable=true type=xsd:string/>
</sequence>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name=addProductResponse>
</wsdl:message>
<wsdl:message name=viewProductRequest>
<wsdl:part name=in0 type=xsd:string/>
</wsdl:message>
<wsdl:message name=viewProductResponse>
<wsdl:part name=viewProductReturn type=impl:Product/>
</wsdl:message>
<wsdl:message name=addProductRequest>
<wsdl:part name=in0 type=impl:Product/>
</wsdl:message>
<wsdl:portType name=SkatesProducts>
<wsdl:operation name=addProduct parameterOrder=in0>
<wsdl:input message=impl:addProductRequest name=addProductRequest/>
<wsdl:output message=impl:addProductResponse
name=addProductResponse/>
</wsdl:operation>
<wsdl:operation name=viewProduct parameterOrder=in0>
<wsdl:input message=impl:viewProductRequest name=viewProductRequest/>
<wsdl:output message=impl:viewProductResponse
name=viewProductResponse/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name=SkatesServiceSoapBinding type=impl:SkatesProducts>
<wsdlsoap:binding style=rpc
transport=http://schemas.xmlsoap.org/soap/http/>
<wsdl:operation name=addProduct>
<wsdlsoap:operation soapAction=/>
<wsdl:input name=addProductRequest>
<wsdlsoap:body
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://ejb.skatestown.com use=encoded/>
</wsdl:input>
<wsdl:output name=addProductResponse>
<wsdlsoap:body
Listing 7.4 Continued
371 Using EJBs from Axis
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://ejb.skatestown.com use=encoded/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name=viewProduct>
<wsdlsoap:operation soapAction=/>
<wsdl:input name=viewProductRequest>
<wsdlsoap:body
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://ejb.skatestown.com use=encoded/>
</wsdl:input>
<wsdl:output name=viewProductResponse>
<wsdlsoap:body
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://ejb.skatestown.com use=encoded/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name=SkatesProductsService>
<wsdl:port binding=impl:SkatesServiceSoapBinding name=SkatesService>
<wsdlsoap:address
location=http://localhost:9080/axis/services/SkatesService/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In order to help deploy this file, you can now run the WSDL2Java tool. This tool creates
a deploy.wsdd deployment XML file for Axis that will act as a good starting point; it
also creates code with which to call the service. However, the deploy.wsdd file is
designed for use with a standard Java class, so you must modify it to work with your EJB
application:
>java org.apache.axis.wsdl.WSDL2Java \
SkatesService.wsdl -server-side
The deploy.wsdd file shown in Listing 7.5 is now located in
com\skatestown\ejb\deploy.wsdd.
Listing 7.5 Generated deploy.wsdd File
<deployment
xmlns=http://xml.apache.org/axis/wsdd/
xmlns:java=http://xml.apache.org/axis/wsdd/providers/java>
<!-- Services from SkatesServiceService WSDL service -->
<service name=SkatesService
Listing 7.4 Continued
372 Chapter 7 Web Services and J2EE
provider=java:RPC style=rpc use=encoded>
<parameter name=wsdlTargetNamespace
value=http://ejb.skatestown.com/>
<parameter name=wsdlServiceElement
value=SkatesServiceService/>
<parameter name=wsdlServicePort
value=SkatesService/>
<parameter name=className
value=com.skatestown.ejb.SkatesServiceSoapBindingImpl/>
<parameter name=wsdlPortType
value=SkatesService/>
<operation name=addProduct
qname=operNS:addProduct
xmlns:operNS=http://ejb.skatestown.com >
<parameter name=in0
type=tns:Product
xmlns:tns=http://ejb.skatestown.com/>
</operation>
<operation name=viewProduct
qname=operNS:viewProduct
xmlns:operNS=http://ejb.skatestown.com
returnQName=viewProductReturn
returnType=rtns:Product
xmlns:rtns=http://ejb.skatestown.com >
<parameter name=in0
type=tns:string
xmlns:tns=http://www.w3.org/2001/XMLSchema/>
</operation>
<parameter
name=allowedMethods
value=addProduct viewProduct/>
<typeMapping
xmlns:ns=http://ejb.skatestown.com
qname=ns:Product
type=java:com.skatestown.ejb.Product
serializer=
org.apache.axis.encoding.ser.BeanSerializerFactory
deserializer=
org.apache.axis.encoding.ser.BeanDeserializerFactory
encodingStyle=
http://schemas.xmlsoap.org/soap/encoding//>
</service>
</deployment>
The bold sections in Listing 7.5 indicate the incorrect parts of the generated WSDD file.
The provider type is java:EJB instead of Java:RPC, and instead of a className
Listing 7.5 Continued
373 Using EJBs from Axis
parameter, the provider requires beanJndiName and homeInterfaceName parameters, as
shown here:
<service name=SkateService provider=java:EJB>
<parameter name=beanJndiName
value=ejb/skates/SkatesProductHome/>
<parameter name=homeInterfaceName
value=com.skatestown.ejb.SkatesProductsHome/>
...
...
</service>
With this file, you can now deploy the service to Axis:
>java org.apache.axis.client.AdminClient
com\skatestown\ejb\deploy.wsdd
lhttp://server:port/axis/servlet/AxisServlet
The response should be
Processing file skate.wsdd
<Admin>Done processing</Admin>
If you pull up a browser and browse the same servlet at http://server:port/axis/
servlet/AxisServlet, you should see the SkatesService listed, as shown in Figure
7.6.You can click (wsdl) to view the WSDL.
Figure 7.6 SkatesService listing in the Axis admin page
374 Chapter 7 Web Services and J2EE
To test the service, you need to generate a simple command-line client. To do so, create a
new directory and rerun WSDL2Java, but this time to generate client objects:
>java org.apache.axis.wsdl.WSDL2Java \
http://localhost:9080/axis/services/SkatesService?wsdl
Now test the service application using a simple test application like that shown in Listing
7.6. Use this command to run the test:
>javac com\skatestown\ejb\*.java
>java com.skatestown.ejb.SkatesTest
Listing 7.6 Test Application
package com.skatestown.ejb;
import java.net.URL;
public class SkatesTest
{
public static void main(String args[])
throws Exception
{
SkatesProductsService sps = new SkatesProductsServiceLocator();
SkatesProducts sp =
sps.getSkatesService(
new URL(http://zak:9080/axis/services/SkatesService));
Product p = new Product();
p.setProductCode(Supertastix58s);
p.setPrice(199.0);
p.setDescription
(The latest inlines from Supertastixare worth every penny!);
sp.addProduct(p);
Product p2 = sp.viewProduct(p.getProductCode());
System.out.println(\nProduct code = );
System.out.println(p2.getProductCode());
System.out.println(\nPrice = );
System.out.println(p2.getPrice());
System.out.println(\nDescription = );
System.out.println(p2.getDescription());
}
}
Note that youll need to change the host and port in this example in order for it to
work.
375 Using JSR109: Implementing Enterprise Web Services
The results of running the test application are as follows:
C:\book\axis>java com.skatestown.ejb.SkatesTest
Product code =
Supertastix58s
Price =
199.0
Description =
The latest inlines from Supertastixthese are worth every penny!
EJB Wrap-Up
This section has been long and a little complicated. The overheads of using EJBs arent
completely clear in such a small example, and weve also done many steps by hand that
would usually be done by a specialized J2EE tooland also by the JSR109 support
were about to look at. The main aspect thats complicated is the packaging of
applications into EAR files. The imposed discipline is excellent in situations where
careful control, versioning of applications, and staging are requiredusually in enterprise
Web systems. For our smaller scenario, this is probably overkill. The other complexity is
understanding the code structure and the linkages and references. These have a great
benefit in separating the application from the infrastructure, but at the cost of
complexity.
Finally, you can avoid much of the hard work described here by using a well-
integrated J2EE development environment. However, because this book isnt designed to
be tied to any given system, weve shown you the basic steps using command-line tools
and kept the vendor-specific aspects to a minimum.
Using JSR109: Implementing
Enterprise Web Services
The JSR109 specification (http://www.jcp.org/en/jsr/detail?id=109) is the way
that J2EE embraces Web services much more closely. JSR109 addresses the following
aspects of Web services and J2EE:
n
Implementation and deployment of Web services using J2EE
n
Client access to Web services from a J2EE application
n
Service publication
n
Security for Web services and mapping to J2EE security
But for SkatesTown, it means something even more useful. The steps to create and
deploy a Web service into an application server are reduced. There are still a number of
steps, but because the entire Axis part of this model is effectively included into the appli-
cation server, the result is less work.
376 Chapter 7 Web Services and J2EE
The steps required are as follows:
1. Create a WSDL file.
2. Create the DDs for the service.
3. Assemble the EJB JAR and EAR files.
4. Enable the EAR file for Web services (doing so automatically adds a generated
Web application to support the Web service endpoint).
5. Deploy the application.
Well guide you through each of these steps in the following sections. First, create a new
working directory, J109, to contain the updated code. Copy the skatesejb.jar file into
the directory, and unjar it:
>jar xvf skatesejb.jar
Youll use the WebSphere tools, which will replace the Axis WSDL2Java and Java2WSDL.
To do this, add the <websphere>\bin directory to your path.
Step 1: Creating the WSDL File
Run Java2WSDL on the SkatesProduct interface:
>set classpath=skatesejb.jar
>Java2WSDL
-implClass com.skatestown.ejb.SkatesProductsBean
com.skatestown.ejb.SkatesProducts
The implClass is optional; it uses the implementation class to find the names of the
arguments to create nicer-looking WSDL. Axis supports this as well, so we could have
used it in the previous part of the chapter. (This only works if you compile the code
with the g option, which adds debug information to the class files).
Now, create the WSDL file. For this project, we need to move it to META-INF\WSDL:
>mkdir META-INF
>mkdir META-INF\wsdl
>move SkatesProducts.wsdl META-INF\WSDL\
Step 2: Creating the Deployment Descriptors
The extended options for the WebSphere Java2WSDL command are as follows:
> WSDL2Java \
-verbose \
-role develop-server \
-container ejb \
-genJava no
META-INF\WSDL\SkatesProducts.wsdl
377 Using JSR109: Implementing Enterprise Web Services
WSWS3185I: Info: Parsing XML file: META-INF\WSDL\SkatesProducts.wsdl
WSWS3282I: Info: Generating META-INF\webservices.xml.
WSWS3282I: Info: Generating META-INF\ibm-webservices-bnd.xmi.
WSWS3282I: Info: Generating META-INF\ibm-webservices-ext.xmi.
WSWS3282I: Info: Generating META-INF\SkatesProducts_mapping.xml.
The options disable the generation of any Java classes, say that youre using an EJB as the
implementation of the service (JSR109 also allows servlets), and say that youre develop-
ing the server-side artifacts.
When this command is run, it creates four new files:
n
The webservices.xml file is the overall DD that configures the application server
to use this bean as the service.
n
Two .xmi files are additional bindings files, which contain additional information
that WebSphere needs to use the EJB as a service.
n
SkatesProducts_mapping.xml is a JAX-RPC mapping file that contains the
mapping between XML Schema types and Java types that is used in this instance.
We now need to edit webservices.xml and set the ejb-name of the session bean to
SkatesProduct:
<?xml version=1.0 encoding=UTF-8?>
<!DOCTYPE webservices
PUBLIC -//IBM Corporation, Inc.//DTD J2EE Web services 1.0//EN
http://www.ibm.com/webservices/dtd/j2ee_web_services_1_0.dtd>
<webservices>
<webservice-description>
<webservice-description-name>
SkatesProductsService
</webservice-description-name>
<wsdl-file>META-INF/wsdl/SkatesProducts.wsdl</wsdl-file>
<jaxrpc-mapping-file>META-INF/SkatesProducts_mapping.xml</jaxrpc-mapping-file>
<port-component>
<port-component-name>SkatesProducts</port-component-name>
<wsdl-port>
<namespaceURI>http://ejb.skatestown.com</namespaceURI>
<localpart>SkatesProducts</localpart>
</wsdl-port>
<service-endpoint-interface>
com.skatestown.ejb.SkatesProducts
</service-endpoint-interface>
<service-impl-bean>
<ejb-link>SkatesProduct</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>
378 Chapter 7 Web Services and J2EE
As you can see, this is much more like an Axis WSDD file than a WSDL file, in that it
configures the server to expose this service.
The descriptor does a number of things. For each service, it defines the following:
n
The service description name
n
The WSDL file associated with the service
n
A JAX-RPC type-mapping file that associates Java types with XML Schema types
n
One or more port components, each defining a QName, a service endpoint (busi-
ness) interface, the EJB which implements the interface, and (optionally) any JAX-
RPC handlers to run for that port
The JAX-RPC mapping file is pointless in this instance because we didnt specify any
mapping! Its pretty much mechanical. Its a big file, so well just show a snippet:
?xml version=1.0 encoding=UTF-8?>
<!DOCTYPE java-wsdl-mapping
PUBLIC -//IBM Corporation, Inc.//DTD J2EE JAX-RPC mapping 1.0//EN
http://www.ibm.com/webservices/dtd/j2ee_jaxrpc_mapping_1_0.dtd>
<java-wsdl-mapping id=JavaWSDLMapping_1075832345810>
<package-mapping id=PackageMapping_1075832345820>
<package-type>com.skatestown.ejb</package-type>
<namespaceURI>http://ejb.skatestown.com</namespaceURI>
</package-mapping>
<java-xml-type-mapping
id=JavaXMLTypeMapping_1075832345820>
<class-type>double</class-type>
<root-type-qname
id=RootTypeQname_1075832345820>
<namespaceURI>http://www.w3.org/2001/XMLSchema</namespaceURI>
<localpart>double</localpart>
</root-type-qname>
<qname-scope>simpleType</qname-scope>
</java-xml-type-mapping>
</java-wsdl-mapping>
Weve now created the required files for the EJB JAR.
Step 3: Assembling the Application Files
Lets create the EJB JAR file:
>jar cvf skates109.jar META-INF\* com\skatestown\ejb\*.class
Now we need a new application.xml file to create the EAR file. Its shown here:
<?xml version=1.0 encoding=UTF-8?>
<!DOCTYPE application
PUBLIC -//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN
http://java.sun.com/dtd/application_1_3.dtd>
379 Using JSR109: Implementing Enterprise Web Services
<application id=SkatesTown109>
<display-name>Skates109</display-name>
<module id=EjbModule_skates>
<ejb>skates109.jar</ejb>
</module>
</application>
This is just like the last one, except we only need the EJB JARthe tooling is about to
add the Web router for us. The tool that does this is called the endpoint enabler
(endptEnabler).
Step 4: Enabling the EAR File for Web Services
We run the endpoint enabler tool as follows:
>endptEnabler skates109.ear
WSWS2004I: IBM WebSphere Application Server Release 5
WSWS2005I: Web services Enterprise Archive Endpoint Enabler Tool.
WSWS2007I: (C) COPYRIGHT International Business Machines Corp. 1997, 2003.
WSWS2003I: Backing up EAR file to: skates109.ear~
WSWS2016I: Loading EAR file: skates109.ear
WSWS2017I: Found EJB Module: skates109.jar
WSWS2024I: Adding http router for EJB Module skates109.jar.
WSWS2036I: Saving EAR file skates109.ear...
WSWS2037I: Finished saving the EAR file.
WSWS2018I: Finished processing EAR file skates109.ear.
It automatically adds a new Web application into the EAR file.
Step 5: Deploying the Application
This process is just as before, except well choose different JNDI names so the applica-
tion doesnt clash with the other one. Follow these steps:
1. From the console, choose Applications and then Install New Application.
2. Browse to the Skates.ear file.
3. The wizard will take you through a number of steps, and in most of them you can
accept the default values. The ones you need to change are as follows:
n
In Deploy EJBs Option, the Database Type is CLOUDSCAPE_V5 and the
Database Schema is SKATES.
n
In JNDI Names For Beans, the value for SkatesEntity is
ejb/skates109/SkatesEntityHome and the value for SkatesProduct is
ejb/skates109/SkatesProductHome.
380 Chapter 7 Web Services and J2EE
n
In Provide Default Datasource Mapping For Modules Containing 2.0 Entity
Beans, the value for Skatesejb.jar is jdbc/SkatesDataSource.
n
In Map EJB References To Beans, the value for SkatesProduct and
SkatesEntity is ejb/skates109/SkatesEntityHome.
4. Click Finish.
Start the application, and then browse the WSDL file to see if its working:
http://localhost:9080/skates109/services/SkatesProducts/wsdl/SkatesProducts.wsdl
To test it, we can modify the URL in the test client we wrote earlier:
....
SkatesProductsService sps = new SkatesProductsServiceLocator();
SkatesProducts sp = sps.getSkatesService(
new URL(
http://localhost:9080/skates109/services/SkatesProducts));
....
Then run it. This time, we get a failure on the addProduct:
C:\book\axis>java com.skatestown.ejb.SkatesTest
Fault in adding product
Product code =
Supertastix58s
Price =
199.0
Description =
The latest inlines from Supertastixthese are worth every penny!
This, with a little thought, should be expected. The product entry Supertastix58s is
already in the database tablewe used the same datasource, and therefore the same data-
base, as the original installation. Since we ran this client before, it created the entry, and
the code in the EJB fails with an exception if there is an existing entry with the same
product code. This isnt actually a bug! Enterprising readers may wish to modify the
sample application to offer a choice between adding or viewing products.
JSR109 Client Code
Another aspect of JSR109 is the support for managing clients, which would have helped
with the test client we wrote earlier. JSR109 allows any managed Web service client to be
configured outside the code. In the same way that references to datasources, EJBs, URLs,
and so on can be managed by the container, 109 also supports this for service-refs g
(resource refs to Web services). This works for EJBs, servlets, or J2EE application clients
(known as thick or managed clients) making calls to Web services. The benefit is that a
381 Resources
reconfiguration of the application can retarget the Web serviceincluding changing the
WSDL and the list of JAX-RPC handlers gto be called (JAX-RPC handlers are com-
ponents that are run outside the application logic and can modify or look at the SOAP
message).
In this model, the client code would look like:
Service sps = ic.lookup(java:comp/env/skatesProductsRef);
SkatesProducts sp = sps.getPort(SkatesProducts.class);
sp.addProduct(...)
JSR109 Wrap-Up
JSR109/WSEE offers a well-defined way to deploy Web services into an application
server. Effectively, it standardizes much of what Axis does in a J2EE environment. The
benefits to the developer and deployment engineer are knowing that the applications
and deployment will be portable across application servers, and some simplificationfor
example, the requirement to package and deploy Axis as a Web application is removed.
JSR109 also offers a managed client environment that allows Web services to be referred
to logically using the service-ref capability. In the future, these facilities will be enhanced
by the Web Services Meta-data for Java standard (JSR181). JSR181 allows programmers
to annotate their code with meta-data tags that define how the code will be deployed as
a service, or use Web services.
Summary
In this chapter, weve shown how Enterprise JavaBeans can be exposed as Web services.
The two approaches show that Axis can be used to expose EJBs as services, providing a
way of exposing existing or newly created EJB business objects over SOAP; and that
JSR109 provides a simpler, more integrated approach to exposing stateless session beans
as Web services, using a DD and inbuilt J2EE 1.4 capabilities.
Using J2EE as a development environment for services adds both standardization and
enhanced quality of service. However, the real benefit comes when youre building com-
plex applications that integrate across reliable, transactional, and secure environments.
Resources
n
J2EEhttp://java.sun.com/j2ee
n
JSR109http://www.jcp.org/en/jsr/detail?id=109
8
Web Services and
Stateful Resources
ALMOST EVERY COMPUTER SYSTEM HAS SOME form of state or state management. By
state, we mean a set of persistent data or information items that have a lifetime longer
than a single request/response message exchange between a requestor and the Web serv-
ice. An airline reservation system manages the current state of flights, reservations, air-
plane capacity, seating arrangements, and so on. This state lasts much longer than the
duration of a particular set of message exchanges to create a reservation or cancel a
reservation. In a supply chain, the current state of the request for quotes (RFQs), pur-
chase orders, invoices and so on is managed. Most systems of any consequence have
some form of state. Many Web service interfaces allow for the manipulation of state; that
is, the existence of state is implied by the Web service interface. For example, a purchase
order Web service implies the presence of the purchase order state associated with the
various operations defined on the interface. Because these kinds of interfaces are com-
mon, its important to understand the relationship between Web services and state.
To this point in the evolution of Web services technology, no formal mechanism has
been proposed to represent the relationship between Web services and state. Currently,
each application deals with stateful resources in a slightly (and occasionally radically) dif-
ferent manner. The lack of a standard convention means there is limited motivation for
the industry to produce hardened, reusable middleware, tooling, design practices, and
experiences upon which Web services applications can be built. This results in increased
integration cost between systems that deal with stateful resources in different ways.
This chapter discusses a recent set of proposed standards that formalize the relation-
ship between Web services and state: WS-Resource Framework. The WS-Resource
Framework was developed by Computer Associates, Fujitsu, Globus (a major open-
source provider of Grid middleware), Hewlett-Packard, and IBM and has been submitted
to the OASIS standards organization. A set of five specifications and a white paper out-
line an approach to modeling stateful resources using Web services. The five specifications
are WS-ResourceProperties, WS-ResourceLifetime, WS-ServiceGroup,
384 Chapter 8 Web Services and Stateful Resources
WS-RenewableReferences, and WS-BaseFaults. Well examine these specifications in
more detail later in this chapter.
Built on top of the WS-Resource Framework is a family of specifications called WS-
Notification, which defines a Web services standard approach to asynchronous notifica-
tion message delivery, or the so-called publish/subscribe (pub/sub) pattern. Well also
review the WS-Notification specifications in this chapter.
Web Services and State
Fundamentally, Web services are best modeled as stateless message processors that accept
request messages, process them in some fashion, and (usually) formulate a response to
return to the requestor. Web services are typically implemented by stateless components
such as Java servlets or EJB stateless session beans. Its left to the implementation to figure
out how (or if) any information about the request needs to be stored more permanently
(such as in a database) or whether additional information needs to be acquired (such as a
lookup into a file in the filesystem) in order to properly process the Web service
message.
Therefore, a Web service (a stateless entity) is separate from any persistent state that it
might need in order to complete the processing of request messages. This notion of the
separation of Web service and state (a so-called first amendment of the Web services
constitution, to make a poor attempt at humor by historical and political analogy) is at
the heart of the WS-Resource Framework.
Aspects of State
State is essentially any piece of information, outside the contents of a Web services
request message, that a Web service needs in order to properly process the request. The
information that forms the state is often bundled together into a bag of state called a
stateful resource.
Several aspects of stateful resources are of interest to applications that wish to reason
about state. These include:
n
How a stateful resource is identified and referenced by other components in the
system
n
How messages can be sent to the stateful resource in order to act upon or query
its state values
n
How the stateful resource is created, either by an explicit Factory pattern opera-
tion (createResource) or an operation within the application such as
doSubmission from the purchase order application
n
How the stateful resource is terminated
n
How the elements of the resources state can be modified
All of these aspects are addressed by various aspects of the WS-Resource Framework.
385 WS-Resources
SkatesTown Scenario
Well examine the WS-Resource Framework in the context of an example from
SkatesTown. Recall that in Chapter 4, Describing Web Services, we described two
services: StockAvailableNotification and POSubmission. These services manipulated
stateful resources (purchase orders), although they did so in a very SkatesTown-
proprietary fashion. The proprietary nature of SkatesTowns implementation meant that
all of SkatesTowns business partners needed to build custom application interfaces to
deal just with SkatesTown. Consider the case of one of SkatesTowns customers, the
SkateBoard Warehouse. If this company uses Web services to deal with all of its suppliers,
not just SkatesTown, it will likely end up with proprietary interfaces to each of its sup-
pliers. This increases the integration costs of using Web servicesalthough the systems
are still cheaper to integrate using Web services than at the platform or programming-
language level.
However, when SkatesTown updates its purchase order system to take advantage of
the industry standards proposed by the WS-Resource Framework, it suddenly uses much
more common approaches to many facets of its purchase order system. By adopting
these common approaches, SkatesTown can take advantage of middleware and tooling
products; but, more importantly, SkatesTown and its business partners can integrate their
systems in a more standards-based and therefore cost-effective fashion.
The following list outlines the changes that SkatesTown needs to make to its existing
Web servicesbased purchase order system. These changes are detailed in the remainder
of this chapter:
n
The doSubmission operation of the POSubmission portType is updated to create
a PurchaseOrder stateful resource (as youll see in the next section, these are called
WS-Resources) and return a WS-Addressing endpoint reference to point to the
newly created PurchaseOrder resource and the Web service that allows requestors
to manipulate it.
n
A POPortType is introduced to model the operations that can be executed on a
PurchaseOrder resource. This PurchaseOrder service replaces the functionality
that was available in the StockAvailableNotification service.
n
The notifications that used to be available via registering with the
StockAvailableNotification are replaced by the way WS-Notification is used.
n
WS-ResourceLifetime is used to allow requestors to cancel PurchaseOrders and
to receive notifications when a PurchaseOrder expires.
n
WS-ResourceProperties is used to allow requestors to read the details on the
PurchaseOrder and update certain properties of the PurchaseOrder.
WS-Resources
At the heart of the WS-Resource Framework is the way that stateful resources are relat-
ed to Web services. As mentioned earlier, Web services and the stateful resources they act
386 Chapter 8 Web Services and Stateful Resources
upon are separate entities. Weve described many things about Web services, including
how to send messages to them (using SOAP), how to describe them (using WSDL), and
how to discover them (using UDDI, for example). However, we havent described how a
Web service relates to stateful resources.
A WS-Resource gis the combination of a Web service and a stateful resource. The
Web service provides a platform and programming-language-neutral means of sending
messages to the stateful resource; the stateful resource represents an identifiable unit of
state information.
The WS-Resource Framework doesnt dictate exactly how the stateful resource is
implemented; as is typical with Web services technologies, this sort of implementation
detail is hidden. The stateful resource associated with a Web service could be implement-
ed as a file in the filesystem, an XML document within an XML-enabled database, or a
private communication channel between the Web service and the native implementation
of a stateful resource in an operating system.
History: Open Grid Services Infrastructure (OGSI) 1.0
There was one other attempt to model the association between Web services and state. The Open Grid
Services Infrastructure (OGSI) was developed by the Global Grid Forum (GGF) between 2002 and 2004. The
OGSI standard represented a set of conventions on Web services, particularly WSDL and a collec