Concepts


We’ll cover WSDL and SOAP in an odd order. When you’re developing a Web service, the best way to do it is to work with WSDL and let tools generate skeleton code that you then fill in. For the most part, the skeleton code will keep you from having to know about the gory details of SOAP. At least, that’s the theory. Unfortunately, to write the WSDL description, you need to understand some things about SOAP. So, although we should be talking about WSDL first, we’ll discuss SOAP first and then WSDL. Once we’ve finished with these two, we’ll go on to JAX-RPC.

SOAP

You’ll recall from Chapter 8 that development of SOAP continued after XML-RPC was created. There have been three versions of SOAP: 0.9, 1.1, and, most recently, 1.2. SOAP has grown from its beginnings as an informal collaboration between Userland Software, DevelopMentor, and Microsoft, adding IBM/Lotus as collaborators, and finally moving to the W3C. At the time of this writing (August 2003), SOAP 1.1 is the version that’s most widely deployed. SOAP 1.2 was the first version of SOAP to be standardized by the W3C (in June 2003).

SOAP can be described many ways. One way to look at SOAP is that it’s a bigger brother to XML-RPC, meaning that it combines XML and HTTP to provide a mechanism for invoking functionality on remote machines. That was the original understanding of what SOAP was about, and it shows if you read the SOAP 0.9 or 1.1 specifications—a large chunk of the specification is devoted to rules for encoding types in XML, which is necessary for remote procedure call semantics. It’s also evident in Sun’s naming of JAX-RPC—the Java API for XML-based RPC.

At its simplest, SOAP is an XML vocabulary for describing one-way messages. The SOAP specification says that a message is an <Envelope>, which can contain an optional <Header> and a mandatory <Body> as its children. Everything else SOAP can be used for is built on that basic concept. This includes messaging modes (one-way versus request-response) and type-encoding rules. The key thing to remember is that you can put anything you want into either the <Header> or <Body> of a SOAP message (you should use namespaces to keep things tidy). What happens to the message is determined by a set of conventions.

SOAP RPC Request

To make this discussion more concrete, let’s look at a SOAP message that represents a remote procedure invocation transported using HTTP. As you can see, this is a normal HTTP request (a POST):

  1: POST /apache-xml-book-web-axis/services/BookService HTTP/1.0   2: Content-Type: text/xml; charset=utf-8   3: Accept: application/soap+xml, text/*

In order to get the listing to fit in this space, we removed two entries from the list of Accept: headers: application/dime and multipart/related. Taken together, they allow SOAP attachments to be returned as the result of the procedure call, using either the DIME or SOAP with Attachments specification.

The next few lines are just regular HTTP headers. We’re most interested in the SOAP message, which starts at line 11:

  4: User-Agent: Axis/1.1   5: Host: 127.0.0.1   6: Cache-Control: no-cache   7: Pragma: no-cache   8: SOAPAction: ""   9: Content-Length: 840  10:   11: <?xml version="1.0" encoding="UTF-8"?>

This is plain XML, so you have an XML declaration with version and encoding attributes.

The message consists of a single root element named Envelope, which is taken from the namespace associated with the soapenv namespace prefix. (You’ll see lots of namespaces when you’re working with SOAP, so get used to it.) Here, there are declarations for the soapenv prefix, which is bound to the URI for the SOAP 1.1 envelope namespace. The xsd and xsi namespaces are bound to the XML Schema and XML Schema Instance namespaces, per convention:

 12: <soapenv:Envelope  13:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  14:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  15:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

Note that no header is attached to this Envelope. If there were a Header element or elements, they would have to be in a namespace. That’s not true of elements in the Body of the message—they don’t have to be in a namespace.

Turning to the Body of the Envelope, you find a single element: a createBook element taken from the ns1 namespace, which is bound to a URI that looks oddly like a reversed fully qualified Java classname. The createBook element also has a soapenv:encodingStyle attribute, which is set to the URI for the SOAP encoding:

 16:  <soapenv:Body>  17:   <ns1:createBook  18:    soapenv:encodingStyle=  19:     "http://schemas.xmlsoap.org/soap/encoding/"  20:    xmlns:ns1="http://BookService.ch9.apachexml.sauria.com">

What’s going on here? The answer is that you’re seeing a usage convention at work. When you use SOAP for a procedure call, the convention says that a SOAP strut is used as the content of the Body. In SOAP, a struct is an element with child elements, one per element of the struct. The name of the struct element is the name of the struct type, and the child elements are called accessor elements. The type name of the struct is the name of the procedure to be executed. The accessors of the procedure struct are the arguments to the procedure. The soapenv:encodingStyle attribute specifies the rules used to convert programming language types into SOAP data types. The ns1 namespace is a way to clearly define where the values of the procedure call struct are taken from. You know that namespace URIs must be unique values and don’t need any special meaning.

From here on, you see a sequence of accessor elements. Each accessor corresponds to a parameter of the method being called. The xsi:type attribute is used to tell the SOAP runtime what the type of the data is. In this example, all the types are drawn from the XML Schema data types. There you have it, a full SOAP message.

 21:    <author xsi:type="xsd:string">Theodore W. Leung</author>  22:    <title  23:     xsi:type="xsd:string">  24:     Professional XML Development with Apache Tools  25:    </title>  26:    <isbn xsi:type="xsd:string">0-7645-4355-5</isbn>  27:    <month xsi:type="xsd:string">December</month>  28:    <year xsi:type="xsd:int">2003</year>  29:    <publisher  30:     xsi:type="xsd:string">  31:     Wrox  32:    </publisher>  33:    <address xsi:type="xsd:string">Indianapolis, Indiana</address>  34:   </ns1:createBook>  35:  </soapenv:Body>  36: </soapenv:Envelope>

SOAP RPC Response

Just to complete the picture, let’s look at what happens when the called procedure returns. Because you’re using HTTP to transport the XML messages, a reasonable route for the procedure return value is the HTTP response that corresponds to the HTTP request used to transport the invocation message. And sure enough, that’s what you find.

  1: HTTP/1.1 200 OK   2: Content-Type: text/xml; charset=utf-8

The HTTP 200 status code means that everything went ok, and the Content-Type: tells you that you’re getting XML as the response payload.

  3: Date: Fri, 22 Aug 2003 23:53:27 GMT   4: Server: Apache Coyote/1.0   5: Connection: close   6:

Once again, after the HTTP headers, you see an XML document, which contains a SOAP envelope.

  7: <?xml version="1.0" encoding="UTF-8"?>   8: <soapenv:Envelope   9:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  10:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  11:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  12:  <soapenv:Body>

Everything looks the same as the Envelope from the procedure invocation. The Envelope element declares a bunch of namespaces that are used by the rest of the Envelope. The difference shows up when you get to the body. Once again, you’re looking at a SOAP struct, which is interpreted by convention. The struct element, <createBookReturn> contains as single accessor <createBookReturn> which is the return value. If the called procedure defined input/output arguments, then they would appear as accessors after the return value.

 13:   <ns1:createBookResponse  14:    soapenv:encodingStyle=  15:     "http://schemas.xmlsoap.org/soap/encoding/"  16:    xmlns:ns1="http://BookService.ch9.apachexml.sauria.com">  17:    <createBookReturn  18:     xsi:type="xsd:boolean">false</createBookReturn>  19:   </ns1:createBookResponse>  20:  </soapenv:Body>  21: </soapenv:Envelope>

SOAP RPC Fault

If a remote invocation doesn’t go well, you get error feedback both from the HTTP response and from the XML returned in the response. You get an HTTP 500 error status instead of a 200. You also get some content in the response, again, as XML:

  1: HTTP/1.1 500 Internal Server Error   2: Content-Type: text/xml; charset=utf-8   3: Date: Sat, 23 Aug 2003 02:18:00 GMT   4: Server: Apache Coyote/1.0   5: Connection: close   6:  

The designers of SOAP tried to keep things uniform, so the XML that comes back in the response is also a SOAP envelope. This time, it’s a SOAP fault envelope:

  7: <?xml version="1.0" encoding="UTF-8"?>   8: <soapenv:Envelope   9:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  10:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  11:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  12:  <soapenv:Body>

The difference between a SOAP fault envelope and the other envelopes you’ve seen is that the child element of the SOAP Body element is a SOAP Fault element:

 13:   <soapenv:Fault>  14:    <faultcode>soapenv:Server.userException</faultcode>  15:    <faultstring>java.lang.IllegalArgumentException</faultstring>  16:    <detail/>  17:   </soapenv:Fault>  18:  </soapenv:Body>  19: </soapenv:Envelope>

The Fault element contains three elements: <faultcode>, which is an error code meant for processing by a program; <faultstring>, which is a text string meant for processing by a human; and an optional <detail> element, which can be use to return application-specific fault information.

As you can see, it takes two SOAP messages—one for the request and one for the response—to accomplish a remote procedure invocation. SOAP is all about messages, and these messages can be combined to accomplish more complicated tasks such as remote procedure invocation. All that SOAP provides is a way to describe the contents of messages and breaking them into a header and a body. The SOAP encoding is used to convert programming language types into XML schema types. The encoding isn’t required; you can easily specify your own encoding by changing the value of the soapenv:encodingStyle attribute. If you’re going to use an encoding, you should probably stick to the SOAP encoding. For the types defined by the SOAP encoding, this gives you a way to invoke procedures on services, regardless of the language in which the requestor and provider are written.

As long as the types being used can be encoded using the SOAP encoding, you should be able to interoperate. It’s this last sentence where things get a little difficult. The SOAP encoding doesn’t cover every type available in every programming language. In Java, it doesn’t really cover objects, although the JAX-RPC specification tries to fill this hole. The problem is that if you want to transfer types that aren’t covered by the SOAP encoding, you have to make up your own. And now you have an interoperability problem, because you can’t guarantee that anyone else can understand your encoding unless you tell them how.

For this reason, people are starting to shift away from using the SOAP encoding and are moving toward making the contents of SOAP Body elements be XML elements that are specified by an XML Schema definition. Because XML Schema can express relatively rich types, this gives a much larger palette of types (and combination rules) to work from. You’ll hear this referred to a literal mode SOAP.

WSDL

The Web Services Description Language (WSDL) uses XML to describe Web services. The original specification was authored by IBM and Microsoft, and version 1.1 was submitted to the W3C for standardization. The first W3C version will be WSDL 1.2, which is currently a W3C Working Draft. WSDL 1.1 is deployed in the field and is supported by a number of Web services toolkits, including Axis.

You need to be familiar with a number of concepts in order to work with WSDL. A port type is an abstract description of a Web service. It’s a set of operations. Conceptually, these are the operations a service requestor requests in order to access the service’s functionality. Operations rely on messages to get their work done. An operation can have an input message and/or an output message. A service requestor causes an input message to be sent to the service when it invokes an operation on the service. Messages are composed of parts—the individual pieces. In the case of an input message, which represents the operation name and arguments, a part is associated with each argument. WSDL maps parts onto the XML Schema type system.

You can begin to see the relationship between WSDL and SOAP. The messages described by WSDL could be SOAP messages. It’s important to understand that WSDL doesn’t say the message is a SOAP message. All the concepts we’ve talked about to this point are abstract and could be implemented using SOAP messages over HTTP or ASN.1 messages over UDP. WSDL doesn’t care. It just defines the operations available on a port type, the messages that make up the operations, and the parts that make up the messages. The following figure illustrates these concepts in a graphical manner:

click to expand

Fortunately, that’s not all WSDL does. WSDL has a notion of bindings. A binding allows you to connect WSDL operations and port types to wire protocols and data formats to obtain a description of a service that can be invoked by real computers. When you combine a binding with a port type you get a port, which is a thing that can be invoked by a program. A collection of such ports is called a service. The relationship between port types, bindings, ports, and services is shown in this figure:

click to expand

WSDL specifies a binding for SOAP over HTTP, for HTTP GET and POST, and for MIME. Other bindings are under development. Given a WSDL description that uses the SOAP over HTTP binding, you can write tools that generate SOAP-based Web services from the WSDL file. This is much easier than trying to write all the code by hand, and if the tools are consistent in their interpretation of the WSDL specification, then there should be good interoperability between services created this way. At the WSDL 1.1 level, there are still some interoperability problems. Many of these issues will be addressed in WSDL 1.2. In the meantime, the Web Services Interoperability Organization (WS-I) has issued a Basic Profile that includes a number of guidelines for using SOAP 1.1 and WSDL 1.1 in an interoperable manner. You can find the WS-I at www.ws-i.org.

A Real WSDL File

Let’s dive into a real WSDL file. This WSDL file describes a Web service that represents a book service. The service supports one operation at the moment, createBook, which takes as arguments the information needed for a book record. Invoking the operation creates a book record in the Web service. The signature of the createBook method might look like this in Java:

    public boolean createBook(String author, String title, String isbn,         String month, int year, String publisher, String address) 

Now, on to the WSDL file. The root element of a WSDL document is a <definitions> element taken from the wsdl namespace:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace="http://BookService.ch9.apachexml.sauria.com"   4:  xmlns="http://schemas.xmlsoap.org/wsdl/"   5:  xmlns:impl="http://BookService.ch9.apachexml.sauria.com"   6:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   7:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"   8:  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

As you can see from the number of namespace declarations on the <definitions> element, you’ll be using a lot of namespaces. Most of these uses are self evident, but we’ll point out anything relevant as we go. The order of the elements in a WSDL document is significant. It starts with messages and parts and builds up until it gets to services. This allows WSDL processing tools to work in a single pass:

Next is a pair of message declarations. As you can see, each message has a name and is composed of one or more parts. The parts are named, and the order in which they appear in the message is significant. Each message part is also associated with a type. In this example, you’re using the type attribute to associate a message part with an XML Schema type (either complex or simple types are allowed). You can also associate parts with elements by using the element attribute. In this case, the type associated with the element is the type of the message part:

  9:   10:  <wsdl:message name="createBookResponse">  11:   <wsdl:part name="createBookReturn" type="xsd:boolean"/>  12:  </wsdl:message>  13:   14:  <wsdl:message name="createBookRequest">  15:   <wsdl:part name="author" type="xsd:string"/>  16:   <wsdl:part name="title" type="xsd:string"/>  17:   <wsdl:part name="isbn" type="xsd:string"/>  18:   <wsdl:part name="month" type="xsd:string"/>  19:   <wsdl:part name="year" type="xsd:int"/>  20:   <wsdl:part name="publisher" type="xsd:string"/>  21:   <wsdl:part name="address" type="xsd:string"/>  22:  </wsdl:message>

Once you’ve defined all the messages you need, you can use those messages to create operations. Because operations are tied to port types, you need a <portType> element. The <portType> is named and contains a sequence of operations. This example contains a single operation named createBook. The createBook operation has an input message and an output message. The input message is bound to the createBookRequest message, and the output message is bound to the createBookResponse message. Note that each of the message names is namespace-prefixed with impl. The impl prefix is bound to the same namespace URI as the targetNamespace of the WSDL document. Because you created the two messages with unprefixed names, they are in the targetNamespace and require prefixing when used as the value of the message attribute.

The <operation> element also has a parameterOrder attribute. This optional attribute provides a way to record the original RPC signature if the operation corresponds to an RPC invocation. It specifies the order of the parts in terms of their order in the RPC signature:

 23:   24:  <wsdl:portType name="BookHandler">  25:   <wsdl:operation  26:    name="createBook"  27:    parameterOrder=  28:     "author title isbn month year publisher address">  29:    <wsdl:input message="impl:createBookRequest"  30:     name="createBookRequest"/>  31:    <wsdl:output message="impl:createBookResponse"  32:     name="createBookResponse"/>  33:   </wsdl:operation>  34:  </wsdl:portType>

One other note about the parts involved in input and output messages: Nothing about the way messages are defined prevents the same part from appearing in both an input and output message. This gives you a convenient way to represent in/out parameters. If a part appears only in an input message, it’s an in parameter. If it appears only in an output message, it’s an out parameter. When a part appears in both the input and output messages of an operation, then it’s an in/out parameter.

The <binding> element creates a binding between a WSDL port type and a message format and transport protocol. Here you’re creating a binding to SOAP of HTTP:

 35:   36:  <wsdl:binding  37:   name="BookServiceSoapBinding" type="impl:BookHandler">  38:   <wsdlsoap:binding style="rpc"  39:    transport="http://schemas.xmlsoap.org/soap/http"/>

The <binding> element looks a lot like the <portType> element. It establishes a name for the binding, and the type attribute give the name of the portType (again, namespace qualified) the binding is for. The first child of the <binding> element is another <binding> element, but this one is taken from the wsdlsoap namespace, which is the namespace for the WSDL SOAP binding. The <wsdlsoap:binding> element specifies that the transport being used is HTTP (via the transport attribute). Elements like <wsdlsoap:binding> are known as extensibility elements.

The style attribute on <wsdlsoap> binding determines the mode for the operation. There are two modes: rpc and document. (The default value is "document".) The rpc mode signals that the usage pattern of the messages is RPC-oriented (request and response message), whereas document means the usage is XML document oriented (possibly request and response, possibly request only or response only). In practice, this difference confuses a lot of people because there’s no reason you can’t use the document style to perform remote procedure calls. In fact, the Microsoft .NET tools do this, and the WS-I basic profile mandates document-literal mode for interoperability (we’re talking about the document part now—we’ll get to the literal part in a moment). The use of rpc versus document style does have one concrete effect. When WSDL is bound to SOAP, each WSDL message part appears as an element in the Body of the SOAP envelope. In rpc style, the Body of a SOAP message is defined to be a single wrapper (or struct) element that contains the part elements as children, whereas in document style, the message parts appear directly as children of the SOAP Body element. So, if you see a SOAP message that has multiple children of the <Body> element, you can be fairly sure it was generated from document-style WSDL. If you see only a single child, you can’t always tell, because a SOAP message generated from WSDL document style may follow the structural rules (wrapper element around message part elements) used by rpc style.

The <wsdlsoap:operation> extensibility element allows you to supply a value for the SOAPAction header. Some SOAP engines use the value of this header to route the service request:

 40:   <wsdl:operation name="createBook">  41:    <wsdlsoap:operation soapAction=""/>

The <wsdlsoap:body> extensibility elements tell how to interpret the message associated with an input or output message. The namespace attribute determines the namespace to use for the message. The use attribute determine whether the message’s parts are encoded. This attribute has two possible values: "encoded" and "literal". If the value is "encoded", then the message parts represent abstract types, and each message part is encoded using the encoding specified by the encodingStyle attribute. If the value is "literal", then the message parts aren’t encoded, but each message part is associated with an XML Schema complex or simple type:

 42:    <wsdl:input name="createBookRequest">  43:     <wsdlsoap:body  44:       encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  45:       namespace="http://BookService.ch9.apachexml.sauria.com"  46:       use="encoded"/>

This is the other half of the document-literal mode mentioned earlier. As you can see, there are four possible combinations of style and use: rpc-encoded, rpc-literal, document-encoded, and document-literal. It’s important to be aware of rpc-encoded (popular on Java-based SOAP implementations) and document-literal (the future of Web services as mandated by the WS-I).

A <wsdlsoap:body> extensibility element is needed for each <input> or <output> element in each <operation>:

 47:    </wsdl:input>  48:    <wsdl:output name="createBookResponse">  49:     <wsdlsoap:body  50:      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  51:      namespace="http://BookService.ch9.apachexml.sauria.com"  52:      use="encoded"/>  53:    </wsdl:output>  54:   </wsdl:operation>  55:  </wsdl:binding>

Once you have a complete binding, you’re ready to put the pieces together. A WSDL <port> is a combination of a <portType> with a binding. The way WSDL documents are written, a WSDL binding element already names the portType it’s associated with. You can see this via the binding attribute on <port>. All that’s left to do is connect the binding to a location that provides the service. Because this process is binding specific, you do it with an extensibility element from the wsdlsoap namespace, <wsdlsoap:address>, which specifies the service location via the location attribute. Here you see a URI that handles service requests for this service:

 56:   57:  <wsdl:service name="BookHandlerService">  58:   <wsdl:port  59:    binding="impl:BookServiceSoapBinding"  60:    name="BookService">  61:    <wsdlsoap:address  62:     location="http://localhost:8080/apache-xml-book-web-axis/  63: services/BookService"/>  64:    </wsdl:port>  65:  </wsdl:service>  66:   67: </wsdl:definitions>

A WSDL service is just a collection of ports. There’s no reason you can’t specify multiple ports that use the same binding but have different addresses.

A Document-Literal WSDL File

This WSDL description is for an rpc-encoded Web service. Many of the currently deployed Java Web services use the rpc-encoded style. The introduction of the WS-I Basic Profile will shift usage toward a document-literal encoding style, so document-literal is the future of interoperable Web services. With that in mind, let’s look at a WSDL file for a document-literal Web service:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace="http://SBookService.ch9.apachexml.sauria.com"   4:  xmlns="http://schemas.xmlsoap.org/wsdl/"   5:  xmlns:impl="http://SBookService.ch9.apachexml.sauria.com"   6:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   7:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"   8:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   9:  xmlns:book="http://sauria.com/schemas/apache-xml-book/book" >  10:   11:  <wsdl:types>  12:   <xsd:schema   13:    targetNamespace=  14:        "http://sauria.com/schemas/apache-xml-book/book"   15:    xmlns:book="http://sauria.com/schemas/apache-xml-book/book"   16:    elementFormDefault="qualified">  17:    <xsd:element name="address" type="xsd:string"/>  18:    <xsd:element name="author" type="xsd:string"/>  19:    <xsd:element name="book">  20:     <xsd:complexType>  21:      <xsd:sequence>  22:       <xsd:element ref="book:title"/>  23:       <xsd:element ref="book:author"/>  24:       <xsd:element ref="book:isbn"/>  25:       <xsd:element ref="book:month"/>  26:       <xsd:element ref="book:year"/>  27:       <xsd:element ref="book:publisher"/>  28:       <xsd:element ref="book:address"/>  29:      </xsd:sequence>  30:      <xsd:attribute name="version" type="xsd:string"  31:                     use="required"/>  32:     </xsd:complexType>  33:    </xsd:element>  34:    <xsd:element name="isbn" type="xsd:string"/>  35:    <xsd:element name="month" type="xsd:string"/>  36:    <xsd:element name="publisher" type="xsd:string"/>  37:    <xsd:element name="title" type="xsd:string"/>  38:    <xsd:element name="year" type="xsd:short"/>  39:   </xsd:schema>  40:  </wsdl:types>

The <types> element wasn’t present in the first WSDL file we looked at. The content of the <types> element is an XML schema. The schema you’re using here is the same schema you’ve used for books throughout the examples in previous chapters.

The types in a <types> element are meant to be used to define message parts. Here’s the difference from the previous WSDL file: Instead of the createBookRequest consisting of a sequence of message parts, one per piece of information needed to create a book, in this version of the file there’s only a single message part, which takes its type from the <book> element of the XML schema in the <types> element. Note that the <part> element uses the element attribute to get the type of the <book> element:

 41:   42:  <wsdl:message name="createBookResponse">  43:   <wsdl:part name="createBookReturn" type="xsd:boolean"/>  44:  </wsdl:message>  45:   46:  <wsdl:message name="createBookRequest">  47:   <wsdl:part name="book" element="book:book"/>  48:  </wsdl:message>  49:   50:  <wsdl:portType name="SBookHandler">  51:   <wsdl:operation name="createBook" parameterOrder="book">  52:    <wsdl:input message="impl:createBookRequest"  53:                name="createBookRequest"/>  54:    <wsdl:output message="impl:createBookResponse"  55:                 name="createBookResponse"/>  56:   </wsdl:operation>  57:  </wsdl:portType>

The remainder of the changes are in the <binding> for the service. The binding style is "document", as required for a document-literal Web service:

 58:   59:  <wsdl:binding name="SBookServiceSoapBinding"  60:                type="impl:SBookHandler">  61:   <wsdlsoap:binding style="document"  62:             transport="http://schemas.xmlsoap.org/soap/http"/>

Because you’re using document-literal style, the use attribute on <body> is set to "literal" and the encodingStyle attribute has been removed, because it’s no longer needed:

 63:   64:   <wsdl:operation name="createBook">  65:    <wsdlsoap:operation soapAction=""/>  66:   67:    <wsdl:input name="createBookRequest">  68:     <wsdlsoap:body  69:          namespace="http://SBookService.ch9.apachexml.sauria.com"  70:          use="literal"/>  71:    </wsdl:input>  72:   73:    <wsdl:output name="createBookResponse">  74:     <wsdlsoap:body  75:          namespace="http://SBookService.ch9.apachexml.sauria.com"  76:          use="literal"/>  77:    </wsdl:output>  78:   </wsdl:operation>  79:  </wsdl:binding>  80:   81:  <wsdl:service name="SBookHandlerService">  82:   <wsdl:port binding="impl:SBookServiceSoapBinding"  83:                name="SBookService">  84:    <wsdlsoap:address  85:       location="http://localhost:8080/apache-xml-book-web-axis/  86: services/SBookService"/>  87:   </wsdl:port>  88:  </wsdl:service>  89: </wsdl:definitions>

As you can see, not many changes are needed to convert a WSDL file to document-literal style. The introduction of the <types> element isn’t necessary, but we wanted to give you some exposure to how it’s used.

To give you an idea of how switching to document-literal affects the SOAP messages, here are the SOAP request and response messages for the earlier WSDL file. For comparison, the messages from earlier in the chapter were generated by a service generate from the following WSDL file. Notice that the book element in lines 7-15 looks exactly like a book element from Chapter 1:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <soapenv:Envelope   3:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   4:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   5:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   6:  <soapenv:Body>   7:   <book xmlns="http://sauria.com/schemas/apache-xml-book/book">   8:    <title>Professional XML Development with Apache Tools</title>   9:    <author>Ted Leung</author>  10:    <isbn>0-7645-4355-5</isbn>  11:    <month>December</month>  12:    <year>2003</year>  13:    <publisher>Wrox</publisher>  14:    <address>Indianapolis, Indiana</address>  15:   </book>  16:  </soapenv:Body>  17: </soapenv:Envelope>

The response message hasn’t changed much:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <soapenv:Envelope   3:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   4:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   5:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   6:  <soapenv:Body>   7:   <createBookReturn xsi:type="xsd:boolean"   8:                     xmlns="">false</createBookReturn>   9:  </soapenv:Body>  10: </soapenv:Envelope>

JAX-RPC

The last important standard supported by Axis is JAX-RPC, the Java API for XML-based RPC. JAX-RPC provides a layer over a SOAP/WSDL runtime and is supposed to insulate applications from the vendor-specific aspects of the stack. This API is developed as part of the Java Community Process and is currently in Version 1.0.

The JAX-RPC model is based on trying to make Web services calls as similar to RMI calls as possible. The programming model is based on mapping WSDL concept to Java concepts. The concepts start in the middle of the WSDL constructs and work their way out. In JAX-RPC, the thing that corresponds to a portType is a Service Endpoint Interface (SEI). The methods on the SEI correspond to WSDL operations, with messages and ports being subsumed into the arguments to the SEI methods. Working in the other direction, there are three different client-side representations of a port: stub objects generated by tools from WSDL, a dynamic proxy-based calling interface, and a completely dynamic call interface that should be familiar to users of the CORBA Dynamic Invocation Interface (DII). On the server side, services are programmed to implement the Service interface.

JAX-RPC also defines a standardizes set of mappings from XML schema/WSDL types to Java types, so it’s easy to generate Java classes from a WSDL document. It also defines the reverse mapping, from Java types to XML/WSDL types. One feature of this mapping is the notion of Holder classes, which are used when an operation has an in/out parameter. Holder classes are wrappers that allow the in/out parameter-passing semantics to be preserved. The JAX-RPC specification defines holders for every Java type that can be mapped to by an XML/WSDL type. The JAX-RPC type mapping also includes a definition for mapping Java Bean-like Java objects (which must implement the java.io.Serializable interface and public default constructor) to XML. We’ll cover this information in more detail when we look at Axis’s tools for working with WSDL. The important thing to know is that two different Java implementations using JAX-RPC should be able to interoperate as long as they’re using the type mappings for JAX-RPC. This interoperability doesn’t automatically extend to SOAP runtimes written in other languages.

On the server/service side, JAX-RPC provides a simple API for managing the life cycle of a service, via the ServletLifeCycleInterface. This assumes that the JAX-RPC implementation is based on a servlet container. A service implementer can use this interface to gain access to data available only from the servlet container.

The last piece of functionality that JAX-RPC provides is the ability to install handlers into the SOAP message path, on both the service requestor and the service provider. Handlers are pieces of code that are allowed to perform operations on a SOAP message before they reach their destination. Handlers perform computations based on the values of headers in the messages or provide features such as message logging, encryption, or digital signatures.

In Axis, you’ll primarily deal with JAX-RPC via the various schemes for creating service requestors. We’ll take you through each of the schemes, explaining the differences along the way. All the schemes implement a request to a service defined by the last WSDL document we showed you.

Using JAX-RPC Stubs

The most static way of writing a service provider is to use a JAX-RPC-compliant tool to generate a set of stub classes from the WSDL document. The class BookHandlerServiceLocator is generated by the WSDL2Java tool and is a factory class you can use to get a BookService class, which represents a port. That port implements the interface BookHandler, which is the SEI for the service:

  1: /*   2:  *    3:  * BookServiceStubMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9;   9:   10: import java.rmi.RemoteException;  11:   12: import com.sauria.apachexml.ch9.BookService.BookHandlerServiceLocator;  13:    14: import com.sauria.apachexml.ch9.BookService.BookHandler;  15:   16: public class BookServiceStubMain {  17:   18:     public static void main(String[] args) {  19:   20:         BookHandler port = null;  21:   22:         try {  23:             port = (BookHandler)  24:                 new BookHandlerServiceLocator().getBookService();

Once you’ve obtained an object that implements the SEI, it becomes straightforward to invoke an operation on the service by calling the appropriate method on the SEI:

 25:         }  26:         catch (javax.xml.rpc.ServiceException jre) {  27:             if(jre.getLinkedCause()!=null)  28:                 jre.getLinkedCause().printStackTrace();  29:         }  30:   31:         boolean result = false;  32:         try {  33:             result = port.createBook(  34:                 "Ted Leung",  35:                 "Professional XML Development with Apache Tools",  36:                 "0-7645-4355-5",  37:                 "December",  38:                 2003,   39:                 "Wrox",   40:                 "Indianapolis, Indiana");  41:         } catch (RemoteException re) {  42:             re.printStackTrace();  43:         }  44:         System.out.println(result);  45:     }  46: }

Using a JAX-RPC Dynamic Proxy

The advantages of the stub approach are that everything is pretty type safe, there are no performance impacts from dynamic behavior, and the classes can be generated from a tool. The next approach we’ll look at, the dynamic proxy approach, is a bit more flexible. With the dynamic proxy, you need to supply an SEI and, optionally, a WSDL file, and the dynamic proxy takes care of producing a usable object that implements the SEI:

  1: /*   2:  *    3:  * BookServiceDynProxyMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9;   9:   10: import java.net.MalformedURLException;  11: import java.net.URL;  12: import java.rmi.RemoteException;  13:   14: import javax.xml.namespace.QName;  15: import javax.xml.rpc.Service;  16: import javax.xml.rpc.ServiceException;  17: import javax.xml.rpc.ServiceFactory;  18:   19: import com.sauria.apachexml.ch9.BookService.BookHandler;  20:   21: public class BookServiceDynProxyMain {  22:     static String router =  23:        "http://localhost:8080/apache-xml-book-web-axis/services";  24:     static String service = "BookService";

Lines 23-24 are just to help keep the lines short enough to fit on a book page.

You use the static newInstance method on the ServiceFactory class to get a new ServiceFactory:

 25:   26:     public static void main(String[] args) {  27:         try {  28:             ServiceFactory sf = ServiceFactory.newInstance();

Before you can use the ServiceFactory to get a service, you need to tell it the URL of a WSDL file and the QName of the service you’re interested in using. The way Axis is implemented, accessing the URI of a Web service via get and with the parameter wsdl (written ?wsdl in the URI) causes Axis to return a WSDL description of the service. Line 29 takes advantage of that functionality to provide the WSDL file you need. The QName of the service is obtained from the WSDL document by creating a QName out of the targetNamespace for the WSDL document and the name of the Service element:

 29:             URL u = new URL(router+"/"+service+"?wsdl");  30:             QName serviceName =   31:                 new QName(  32:                   "http://BookService.ch9.apachexml.sauria.com",  33:                   "BookHandlerService");  34:             Service s = sf.createService(u, serviceName);

Once you’ve obtained a service object, you need to ask for the appropriate port. To do this, you need the QName of the port (constructed from the WSDL document just like the service name) and the Class object of the SEI. The resulting port implements the SEI, so after you get it, it’s just a matter of calling the correct method:

 35:               36:             QName portName =  37:                 new QName(  38:                   "http://BookService.ch9.apachexml.sauria.com",  39:                   "BookHandlerService");  40:             BookHandler h =   41:                 (BookHandler) s.getPort(portName,   42:                                         BookHandler.class);  43:               44:             boolean value = h.createBook(  45:                 "Theodore W. Leung",  46:                 "Professional XML Development with Apache Tools",  47:                 "0-7645-4355-5",  48:                 "December",   49:                 2003,   50:                 "Wrox",   51:                 "Indianapolis, Indiana");  52:             System.out.println(value);  53:         } catch (RemoteException re) {  54:             re.printStackTrace();  55:         } catch (MalformedURLException mue) {  56:             mue.printStackTrace();  57:         } catch (ServiceException se) {  58:             se.printStackTrace();  59:         }  60:     }  61: }

Using the JAX-RPC Dynamic Invocation Interface

The final scheme for creating a service requestor is the Dynamic Invocation Interface (DII). When you’re using the DII, you create the entire invocation of the Web service at runtime. This includes the location and name of the service and port as well as the name of the operation and its arguments. This is the most flexible of the methods but also the most computationally expensive:

  1: /*   2:  *    3:  * BookServiceDIIMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9;   9:   10: import java.rmi.RemoteException;  11:   12: import javax.xml.namespace.QName;  13: import javax.xml.rpc.Call;  14: import javax.xml.rpc.ParameterMode;  15: import javax.xml.rpc.Service;  16: import javax.xml.rpc.ServiceException;  17: import javax.xml.rpc.ServiceFactory;  18: import javax.xml.rpc.encoding.XMLType;  19:   20: public class BookServiceDIIMain {  21:     static String router =  22:        "http://localhost:8081/apache-xml-book-web-axis/services";  23:     static String service = "BookService";

You need an empty call object to begin with, so you create a ServiceFactory instance (line 28) and then use that factory instance to create an empty Service (line 30). Using that empty Service, you finally create your empty Call object (line 32):

 24:   25:     public static void main(String[] args) {  26:         Call call = null;  27:         try {  28:             ServiceFactory sf = ServiceFactory.newInstance();  29:   30:             Service s = sf.createService(null);  31:   32:             call = s.createCall();

You set the target endpoint address to be the URI for the service and set the operation name to be the namespace-qualified name of the operation:

 33:         } catch (ServiceException se) {  34:             se.printStackTrace();  35:         }  36:           37:         call.setTargetEndpointAddress(router+"/"+service);  38:         call.setOperationName(  39:             new QName(  40:                 "http://BookService.ch9.apachexml.sauria.com",  41:                 "createBook"));

After you set the endpoint address and operation name, you need to tell the Call object about the parameters for the operation as well as the return type. Each parameter requires the name of the parameter, its type, and whether it’s an IN, OUT, or INOUT parameter:

 42:                   43:         ParameterMode in = ParameterMode.IN;  44:         call.addParameter("author", XMLType.XSD_STRING, in);  45:         call.addParameter("title", XMLType.XSD_STRING,  in);  46:         call.addParameter("isbn", XMLType.XSD_STRING, in);  47:         call.addParameter("month", XMLType.XSD_STRING, in);  48:         call.addParameter("year", XMLType.XSD_INT, in);  49:         call.addParameter("publisher", XMLType.XSD_STRING, in);  50:         call.addParameter("address", XMLType.XSD_STRING, in);  51:         call.setReturnType(XMLType.XSD_BOOLEAN);

Because you’re composing the Call at runtime, there’s no way to specify the arguments in a type-safe manner. The best that you can do is to put the arguments into an Object array:

 52:           53:         Object params[] = new Object[] {  54:             "Theodore W. Leung",  55:             "Professional XML Development with Apache Tools",  56:             "0-7645-4355-5",  57:             "December",  58:             new Integer(2003),   59:             "Wrox",   60:             "Indianapolis, Indiana"  61:         };

Once you have the arguments, you can invoke the service using the call object and the parameter array:

 62:   63:         try {  64:             Object result = call.invoke(params);  65:             System.out.println(result);  66:         } catch (RemoteException re) {  67:             re.printStackTrace();  68:         }  69:     }  70: }

Using DII from WSDL

A variation of the DII scheme uses information from a WSDL file to provide the type information about the arguments and return type of the operation being invoked:

  1: /*   2:  *    3:  * BookServiceDIIWSDLMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9;   9:   10: import java.net.MalformedURLException;  11: import java.net.URL;  12: import java.rmi.RemoteException;  13:   14: import javax.xml.namespace.QName;  15: import javax.xml.rpc.Call;  16: import javax.xml.rpc.Service;  17: import javax.xml.rpc.ServiceException;  18: import javax.xml.rpc.ServiceFactory;  19:   20: public class BookServiceDIIWSDLMain {  21:     static String router =  22:        "http://localhost:8080/apache-xml-book-web-axis/services";  23:     static String service = "BookService";  24:   25:     public static void main(String[] args) {  26:         Call call = null;  27:         try {  28:             ServiceFactory sf = ServiceFactory.newInstance();  29:   30:             URL u = new URL(router+"/"+service+"?wsdl");  31:             QName serviceName =   32:                 new QName(  33:                   "http://BookService.ch9.apachexml.sauria.com",  34:                   "BookHandlerService");  35:             Service s = sf.createService(u, serviceName);

Instead of creating a blank service, you create one using a WSDL file, supplying the qualified name of the Service you wish to access.Then you use a different version of the createCall method to create a Call object that’s preconfigured with the parameter and return types for a particular operation. The operation is specified using its namespace-qualified name:

 36:   37:             QName portName =   38:                 new QName(  39:                   "http://BookService.ch9.apachexml.sauria.com",  40:                   "BookService");           41:             call = s.createCall(portName, "createBook");

After you’ve obtained your customized Call object, the invocation of the service is just as in the other variation. You build up an Object array of the arguments and pass this array to the invoke method on the Call object:

 42:         } catch (MalformedURLException mue) {  43:             mue.printStackTrace();  44:         } catch (ServiceException se) {  45:             se.printStackTrace();  46:         }  47:   48:         Object params[] = new Object[] {  49:             "Theodore W. Leung",  50:             "Professional XML Development with Apache Tools",  51:             "0-7645-4355-5",  52:             "December",  53:             new Integer(2003),   54:             "Wrox",   55:             "Indianapolis, Indiana"  56:         };  57:   58:         try {  59:             Object result = call.invoke(params);  60:             System.out.println(result);  61:         } catch (RemoteException re) {  62:             re.printStackTrace();  63:         }  64:     }  65: }

We want to show you an example of using the JAX-RPC handlers. Unfortunately, the usage has some areas that are Axis specific, so we’ll wait until we’ve covered all the necessary Axis-related information.




Professional XML Development with Apache Tools. Xerces, Xalan, FOP, Cocoon, Axis, Xindice
Professional XML Development with Apache Tools: Xerces, Xalan, FOP, Cocoon, Axis, Xindice (Wrox Professional Guides)
ISBN: 0764543555
EAN: 2147483647
Year: 2003
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net