The WSDL document, which is XML, is structured as follows:
-
The document or root element is named
definitions
. This is appropriate because the WSDL defines the web service thoroughly enough that utilities such as wsimport can use the WSDL to generate code, typically but not exclusively client-side support code. -
The first child element of
definitions
, namedtypes
, is technically optional but almost always present in a modern WSDL. This element contains (or links to) an XML Schema or the equivalent—a grammar that specifies the data types for the messages involved in the service. In a modern SOAP-based web service, the arguments passed to web service operations are typed—but the SOAP messages themselves are also typed. For this reason, the receiver of a SOAP message can check, typically at the library level, whether the received message satisfies the constraints that the message’s type impose. -
Next come one or more
message
elements, which list the messages whose data types are given in thetypes
section immediately above. Everymessage
has a correspondingcomplexType
entry in the schema from thetypes
section, assuming that thetypes
section is nonempty. -
The
portType
section comes next. There is always exactly oneportType
element. TheportType
is essentially the service interface: a specification of the service’s operations and the message patterns that the operations exemplify. For example, in the request/response pattern, the client begins the conversation with a request message and the service counters with a response message. In the solicit/response pattern, by contrast, the service starts the conversation with a solicitation message and the client counters with a response. There is also the one-way pattern (client to server only) and the notification pattern (server to client only). Richer conversational patterns can be built out of these simpler ones. Themessage
items in the preceding section are the components of an operation, and theportType
section defines anoperation
by placingmessage
items in a specific order. -
Next come one or more
binding
sections, which provide implementation detail such as the transport used in the service (for instance, HTTP rather than SMTP), the service style, and the SOAP version (that is, 1.1 or 1.2). By default, Java generates a singlebinding
section but DotNet generates two: one for SOAP 1.1 and another for SOAP 1.2. The last section, named
service
, brings all of the previous details together to define key attributes such as the service endpoint—that is, the URL at which the service can be accessed. Nested in theservice
element are one or moreport
subelements, where aport
is aportType
plus abinding
:port
=
portType
+
binding
Since there is only one
portType
in a WSDL, the number ofport
subelements equals the number ofbinding
elements.
The biggest section in a WSDL is typically the types
section because an XML Schema tends to be wordy.
An example from Amazon, introduced shortly, illustrates. For now, the WSDL (see Example 4-11) for the RandService
is only about a page or so in size.
Example 4-11. The dynamically generated WSDL for the RandService
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
definitions
xmlns:
soap
=
"http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:
tns
=
"http://rand/"
xmlns:
xsd
=
"http://www.w3.org/2001/XMLSchema"
xmlns
=
"http://schemas.xmlsoap.org/wsdl/"
targetNamespace
=
"http://rand/"
name
=
"RandServiceService"
>
<
types
>
<
xsd:
schema
>
<
xsd:
import
namespace
=
"http://rand/"
schemaLocation
=
"http://localhost:8888/rs?xsd=1"
></
xsd:
import
>
</
xsd:
schema
>
</
types
>
<
message
name
=
"next1"
>
<
part
name
=
"parameters"
element
=
"tns:next1"
></
part
>
</
message
>
<
message
name
=
"next1Response"
>
<
part
name
=
"parameters"
element
=
"tns:next1Response"
></
part
>
</
message
>
<
message
name
=
"nextN"
>
<
part
name
=
"parameters"
element
=
"tns:nextN"
></
part
>
</
message
>
<
message
name
=
"nextNResponse"
>
<
part
name
=
"parameters"
element
=
"tns:nextNResponse"
></
part
>
</
message
>
<
portType
name
=
"RandService"
>
<
operation
name
=
"next1"
>
<
input
message
=
"tns:next1"
></
input
>
<
output
message
=
"tns:next1Response"
></
output
>
</
operation
>
<
operation
name
=
"nextN"
>
<
input
message
=
"tns:nextN"
></
input
>
<
output
message
=
"tns:nextNResponse"
></
output
>
</
operation
>
</
portType
>
<
binding
name
=
"RandServicePortBinding"
type
=
"tns:RandService"
>
<
soap:
binding
transport
=
"http://schemas.xmlsoap.org/soap/http"
style
=
"document"
></
soap:
binding
>
<
operation
name
=
"next1"
>
<
soap:
operation
soapAction
=
""
></
soap:
operation
>
<
input
>
<
soap:
body
use
=
"literal"
></
soap:
body
>
</
input
>
<
output
>
<
soap:
body
use
=
"literal"
></
soap:
body
>
</
output
>
</
operation
>
<
operation
name
=
"nextN"
>
<
soap:
operation
soapAction
=
""
></
soap:
operation
>
<
input
>
<
soap:
body
use
=
"literal"
></
soap:
body
>
</
input
>
<
output
>
<
soap:
body
use
=
"literal"
></
soap:
body
>
</
output
>
</
operation
>
</
binding
>
<
service
name
=
"RandServiceService"
>
<
port
name
=
"RandServicePort"
binding
=
"tns:RandServicePortBinding"
>
<
soap:
address
location
=
"http://localhost:8888/rs"
></
soap:
address
>
</
port
>
</
service
>
</
definitions
>
The first three WSDL sections (types
, message
, and portType
) present the service
abstractly in that no implementation details are present. The binding
and
service
sections provide the concrete detail by specifying, for example, the type
of transport used in the service as well as the service endpoint.
The portType
is of particular interest because it characterizes the service in terms
of operations, not simply messages;
operations consist of one or more messages exchanged in a specified pattern. The two areas of
immediate interest in the WSDL for a programmer writing a client against a service would be
the portType
and the service
; the portType
section informs the programmer about what calls can be
made against the service, and the service
section gives the service endpoint, the URL through
which the service can be reached.
XML is not fun to read, but the basic profile WSDL for the RandService
is
not unduly forbidding. Perhaps the best way to read the document is from
top to bottom.
This section contains or links to an XML Schema or equivalent. (In the case of Java, the schema is a separate document shown in Example 4-3; in the case of DotNet, the schema is included in the WSDL.) To understand how the schema relates to its WSDL, consider this segment of the XML Schema from Example 4-3:
<
xs:
element
name
=
"nextNResponse"
type
=
"tns:nextNResponse"
>
</
xs:
element
>
...
<
xs:
complexType
name
=
"nextNResponse"
>
<
xs:
sequence
>
<
xs:
element
name
=
"return"
type
=
"xs:int"
minOccurs
=
"0"
maxOccurs
=
"unbounded"
>
</
xs:
element
>
</
xs:
sequence
>
</
xs:
complexType
>
The xs:element
in line 1 has a specified type
, in this case tns:nextNResponse
.
The type is the complexType
in line 2. XML Schema has built-in simple types such as
xsd:int
and xsd:string
, but XML Schema is also extensible in that new
complex types can be added as needed. The complexType
in this case is for the nextNResponse
message that the service returns to the client. Here
is that message from the WSDL in Example 4-11:
<
message
name
=
"nextNResponse"
>
<
part
name
=
"parameters"
element
=
"tns:nextNResponse"
></
part
>
</
message
>
The message
has an element
attribute (line 1) with tns:nextNResponse
as the value; tns:nextNResponse
is the name of the element
in line 1 of the XML Schema. The WSDL, in defining a message, points back to the
XML Schema section that provides the data type for the message.
The complexType
section of the WSDL indicates
that a nextNResponse
message returns zero or more integers (XML type xs:int
).
The zero leaves open the possibility that the service, in this case written in Java,
might return null
instead of an actual array or equivalent (e.g.,
List<Integer>
). At this point a human editor might intervene by changing
the minOccurs
in line 3 from 0
to 1
. (If the minOccurs
attribute were
dropped altogether, the value would default to 1.) The dynamically generated
WSDL may not capture the intended design of a service; hence, the WSDL may need to
be edited by hand.
Each message
element in the WSDL points to an
element and, more important, to a complexType
in the WSDL’s XML Schema. The result
is that all of the messages are typed. The RandService
exposes two operations
and each follows the request/response pattern; hence, the WSDL has four
message
elements: two for the next1
and nextN
requests and two for the
corresponding responses named next1Response
and nextNResponse
,
respectively.
This section contains one or more operation
elements, each of which defines an operation in terms of messages defined in
the immediately preceding section. For example, here is the definition
for the nextN operation:
<
operation
name
=
"nextN"
>
<
input
message
=
"tns:nextN"
></
input
>
<
output
message
=
"tns:nextNResponse"
></
output
>
</
operation
>
The input
message precedes the output
message, which signals that the pattern
is request/response. Were the order reversed, the pattern would be
solicit/response. The term input is to be understood from the service’s
perspective: an input
message goes into the service and an output
message
comes out from the service. Each input
and output
element names the
message defined in a message
section, which in turn refers to an XML Schema
complexType
. Accordingly, each operation
can be linked to the typed messages
that make up the operation
.
This section and the next, service
, provide
implementation details about the service. In theory, but rarely in practice, there are
several options or degrees of freedom with respect to the service that the WSDL
defines, and a binding
section selects among these options. One option for a
SOAP-based service such as
the RandService
is the SOAP version: 1.1 or 1.2. SOAP 1.1 is the default in Java; hence,
the one and only binding
section is for SOAP 1.1. In DotNet, a dynamically generated
WSDL usually has two binding
sections: one for SOAP 1.1 and the other for SOAP 1.2. However,
the very same DotNet WSDL typically has only one service endpoint or URL; this means
the same deployed service is for SOAP 1.1 and SOAP 1.2, thereby signaling that no
difference between the two SOAP versions comes into play for the service.
There are three other options to be considered: transport (line 1) and style (line 2) are
two of the three. Here is the first subelement in the binding
section, a
subelement that makes choices on these two options:
<
soap:
binding
transport
=
"http://schemas.xmlsoap.org/soap/http"
style
=
"document"
></
soap:
binding
>
The transport
value is a URI that ends with soap/http
, which can be summed up as
SOAP over HTTP. Another option would be SMTP (Simple Mail Transport Protocol) or even
TCP (Transmission Control Protocol, which underlies HTTP), but in
practice, HTTP is the dominant transport. HTTP in this context includes HTTPS. The
other option (line 2) concerns the service style
, in this case set to document
.
A web service in document
style always has an XML Schema or equivalent that types the
service’s constituent messages. The other choice for style
is misleadingly named
rpc
, which is short for remote procedure call. The name is misleading
because a document
-style service such as the RandService
can and typically does
follow the request/response pattern, which is the RPC pattern. In the context of a
WSDL, rpc
style really means that messages themselves are not typed, only their
arguments and return values are typed. The WSDL for an rpc
style service may have no types
section at all or only an abbreviated one. In modern SOAP-based services,
document
style dominates and represents best practice. Indeed, both Java and DotNet
toyed for a time with the idea of dropping support altogether for rpc
style. The
issue of rpc
style will come up again later but only briefly.
The document
style deserves to be the default. This style can support services with
rich, explicitly defined Java data types such as Employee
or ChessTournament
because the
service’s WSDL can define, for the XML side, the required types in an XML Schema. Any
service pattern, including request/response, is possible under the document style.
The last option concerns use
, more accurately called encoding, because
the choice determines how the service’s data types are to be encoded and decoded.
The WSDL has to specify how the data types used
in an implementation language such as Java are to be serialized into and
deserialized out of WSDL-compliant types—the types laid out in the WSDL’s XML Schema
or equivalent (see Example 4-12). For example, Java and Ruby have similar but subtly different
data types. In a conversation based on SOAP messages, a conversation in which the
SOAP remains transparent, the two languages would need the ability to serialize from instances of
native types to XML and to deserialize from XML to instances of native types.
Example 4-12. Encoding and decoding XML
encode
decode
Java
types
-------->
XML
Schema
types
-------->
Ruby
types
Java
types
<--------
XML
Schema
types
<--------
Ruby
types
decode
encode
The attribute
use
=
'
literal
'
means the service’s type definitions in the WSDL literally follow the
WSDL’s schema. The alternative to literal
is named encoded
, which means that
the service’s type definitions come from implicit encoding rules, typically
the rules in the SOAP 1.1 specification. However, the use of encoded
does not comply with WS-I (Web Services Interoperability) standards.
This section brings the pieces together. Recall that a WSDL has but one
portType
section but may have multiple binding
sections. The service
element has port
subelements, where a port
is a portType
linked to a
binding
; hence, the number of port
subelements equals the number of
binding
sections in the WSDL. In this example, there is one binding
and, therefore, one port
subelement:
<
port
name
=
"RandServicePort"
binding
=
"tns:RandServicePortBinding"
>
<
soap:
address
location
=
"http://localhost:8888/rs"
></
soap:
address
>
</
port
>
The address
subelement specifies a location
(line 1), whose value is commonly
called the service endpoint. A web service with two significantly different
bindings (for instance, one for HTTP and another for SMTP) would have
different location
values to reflect the different bindings.
The foregoing examination of the WSDL, and in particular its XML Schema, prompts an obvious question: Which Java data types bind to which XML Schema data types? Table 4-1 summarizes the bindings.
Table 4-1. Java and XML Schema data type bindings
Java data type | XML schema data type |
---|---|
boolean | xsd:boolean |
byte | xsd:byte |
short | xsd:short |
short | xsd:unsignedByte |
int | xsd:int |
int | xsd:unsignedShort |
long | xsd:long |
long | xsd:unsignedInt |
float | xsd:float |
double | xsd:double |
byte[ ] | xsd:hexBinary |
byte[ ] | xsd:base64Binary |
java.math.BigInteger | xsd:integer |
java.math.BigDecimal | xsd:decimal |
java.lang.String | xsd:string |
java.lang.String | xsd:anySimpleType |
javax.xml.datatype.XMLGregorianCalendar | xsd:dateTime |
javax.xml.datatype.XMLGregorianCalendar | xsd:time |
javax.xml.datatype.XMLGregorianCalendar | xsd:date |
javax.xml.datatype.XMLGregorianCalendar | xsd:g |
javax.xml.datatype.Duration | xsd:duration |
javax.xml.namespace.QName | xsd:QName |
javax.xml.namespace.QName | xsd:NOTATION |
java.lang.Object | xsd:anySimpleType |
The bindings in Table 4-1 are automatic in the sense that, in a JAX-WS service, the SOAP infrastructure
does the conversions without application intervention. Conversions also are automatic for arrays of
any type in Table 4-1. For example, an array of BigInteger
instances converts automatically to
an array of xsd:integer
instances, and vice versa. Programmer-defined classes whose properties reduce to any type
in Table 4-1 or to arrays of these likewise convert automatically. For example, an Employee
class
that has properties such as firstName
(String
), lastName
(String
), id
(int
),
salary
(float
), age
(short
), hobbies
(String[ ]
), and the like would convert
automatically to XML Schema types. The upshot is that the vast majority of the data types used in
everyday Java programming convert automatically to and from XML Schema types. The glaring exception is the
Map
—a collection of key/value pairs. However, a Map
is readily implemented as a pair of
coordinated arrays: one for the keys, the other for the values.
The source for the RandService
class begins as follows:
@WebService
public
class
RandService
{
...
The default style, document
, could be overridden with an additional annotation:
@WebService
@SOAPBinding
(
style
=
Style
.
RPC
)
// versus Style.DOCUMENT, the default
public
class
RandService
{
...
The RandService
is simple enough that the difference would be transparent to
clients against the service. Of interest here is how the different styles
impact the underlying SOAP messages.
Consider a very simple SOAP-based service with operations named add
, subtract
,
multiply
, and divide
. Each operation expects two arguments, the numbers on which
to operate. Under the original SOAP 1.1
specification, a request message for document
style—what is now called
unwrapped or bare document style—would look like Example 4-13:
Example 4-13. Unwrapped document style
<?
xml
version
=
"1.0"
?>
<!--
Unwrapped
document
style
-->
<
soapenv:
Envelope
xmlns:
soapenv
=
"http://schemas.xmlsoap.org/soap/envelope/"
xmlns:
xsd
=
"http://www.w3.org/2001/XMLSchema"
>
<
soapenv:
Body
>
<
num1
xmlns:
ans
=
"http://arith/"
>
27
</
num1
>
<
num2
xmlns:
ans
=
"http://arith/"
>
94
</
num2
>
</
soapenv:
Body
>
</
soapenv:
Envelope
>
The Body
of the SOAP message contains two elements at the same level, the elements
tagged num1
and num2
; each element is a child of the soapenv:Body
element.
The glaring omission is the name of the operation, for instance, add
. This name
might occur instead, for example, in the request URL:
http:
//some.server.org/add
It is peculiar that the SOAP envelope should contain the named arguments but not
the named operation. Under rpc
style, however, the operation would be the
one and only child of the Body
element; the operation then would have, as its own
child elements, the arguments. Here is the contrasting SOAP message in rpc style or,
what now comes to the same thing, wrapped document style (Example 4-14).
Example 4-14. Wrapped document style, the same as rpc style
<?
xml
version
=
"1.0"
?>
<!--
Wrapped
document
or
rpc
style
-->
<
soapenv:
Envelope
xmlns:
soapenv
=
"http://schemas.xmlsoap.org/soap/envelope/"
xmlns:
xsd
=
"http://www.w3.org/2001/XMLSchema"
>
<
soapenv:
Body
>
<
add
xmlns:
ans
=
"http://arith/"
>
<
num1
>
27
</
num1
>
<
num2
>
94
</
num2
>
</
addNums
>
</
soapenv:
Body
>
</
soapenv:
Envelope
>
The add
element (line 1) now acts as a wrapper for the argument elements, in this case num1
and num2
.
The wrapped convention, unofficial but dominant in SOAP frameworks, gives a document
-style service the look and feel
of an rpc
-style service—at the message level. The document style still has the advantage of a
full XML Schema that types the messages.
In Java as in DotNet, the default style for any SOAP-based service is wrapped document; hence, a service such
as RandService
, with only the @WebService
annotation, is wrapped document in style. This
style is often shortened to wrapped doc/lit: wrapped document style with literal encoding.
The wsimport utility produces, from a WSDL document, code that directly supports client calls against a web service. This same code can be used, with a few adjustments, to program a service. This section illustrates with a simple example.
Here are two operations for a temperature conversion service written in C#:
[
WebMethod
]
public
double
c2f
(
double
t
)
{
return
32.0
+
(
t
*
9.0
/
5.0
);
}
[
WebMethod
]
public
double
f2c
(
double
t
)
{
return
(
5.0
/
9.0
)
*
(
t
-
32.0
);
}
The c2f
operation converts from centigrade to fahrenheit and the f2c
method converts from fahrenheit to
centigrade.
DotNet, by default, generates a WSDL with SOAP 1.1 and SOAP 1.2 bindings. This temperature conversion service is simple enough that the two bindings have the same implementation. In general, however, the wsimport utility can handle multiple bindings with the -extension flag. Assuming that the WSDL for the service is in the file tc.wsdl, the command:
%
wsimport
-
p
tempConvert
-
keep
-
extension
tc
.
wsdl
generates the usual artifacts: Java .class files that represent the c2f
and f2c
request messages and their
corresponding responses, together with various support files. Of interest here is the interface
—the Java file that represents the portType
section of the WSDL. Here is the file, cleaned up for
readability:
package
tempConvert
;
import
javax.jws.WebMethod
;
import
javax.jws.WebParam
;
import
javax.jws.WebResult
;
import
javax.jws.WebService
;
import
javax.xml.bind.annotation.XmlSeeAlso
;
import
javax.xml.ws.RequestWrapper
;
import
javax.xml.ws.ResponseWrapper
;
@WebService
(
name
=
"ServiceSoap"
,
targetNamespace
=
"http://tempConvertURI.org/"
)
@XmlSeeAlso
({
ObjectFactory
.
class
})
public
interface
ServiceSoap
{
@WebMethod
(
operationName
=
"c2f"
,
action
=
"http://tempConvertURI.org/c2f"
)
@WebResult
(
name
=
"c2fResult"
,
targetNamespace
=
"http://tempConvertURI.org/"
)
@RequestWrapper
(
localName
=
"c2f"
,
targetNamespace
=
"http://tempConvertURI.org/"
,
className
=
"tempConvert.C2F"
)
@ResponseWrapper
(
localName
=
"c2fResponse"
,
targetNamespace
=
"http://tempConvertURI.org/"
,
className
=
"tempConvert.C2FResponse"
)
public
double
c2F
(
@WebParam
(
name
=
"t"
,
targetNamespace
=
"http://tempConvertURI.org/"
)
double
t
);
@WebMethod
(
operationName
=
"f2c"
,
action
=
"http://tempConvertURI.org/f2c"
)
@WebResult
(
name
=
"f2cResult"
,
targetNamespace
=
"http://tempConvertURI.org/"
)
@RequestWrapper
(
localName
=
"f2c"
,
targetNamespace
=
"http://tempConvertURI.org/"
,
className
=
"tempConvert.F2C"
)
@ResponseWrapper
(
localName
=
"f2cResponse"
,
targetNamespace
=
"http://tempConvertURI.org/"
,
className
=
"tempConvert.F2CResponse"
)
public
double
f2C
(
@WebParam
(
name
=
"t"
,
targetNamespace
=
"http://tempConvertURI.org/"
)
double
t
);
}
The ServiceSoap
interface, like any interface, declares but does not define methods, which
in this case represent service operations. If the semantics of these two operations
c2f
and f2c
are understood, then converting this wsimport artifact to a web service
is straightforward:
Change the
interface
to a POJOclass
....
public
class
ServiceSoap
{
...
Implement the c2f and f2c operations by defining the methods. Java and C# are sufficiently close that the two implementations would be indistinguishable. For example, here is the body of c2f in either language.
public
double
c2f
(
double
t
)
{
return
32.0
+
(
t
*
9.0
/
5.0
);
}
Not every language is as close to Java as C#, of course. Whatever the original implementation of a service, the challenge is the same: to understand what a service operation is supposed to do so that the operation can be re-implemented in Java.
Publish the service with, for example,
Endpoint
or a web server such as Tomcat or Jetty.Although the wsimport utility could be used to help write a SOAP-based service in Java, the main use of this utility is still in support of clients against a SOAP-based service. The point to underscore is that the WSDL is sufficiently rich in detail to support useful code on either the service or the client side. The next section returns to the Amazon E-Commerce service to illustrate this very point.
Get Java Web Services: Up and Running, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.