Web services promise a standards-based platform for distributed computing for the Internet, focused on simplicity and flexibility. One key Web service technology is WSDL, the Web Service Description Language. Using WSDL, developers can describe their Web services in abstract form, similar to existing Interface Description Languages (IDLs) used in other distributed computing frameworks such as CORBA. WSDL also allows Web service developers to specify concrete bindings to a service. And these bindings describe how the abstract service description is mapped to a specific access protocol. This portion of WSDL is extensible, which means that anybody can come up with their own binding so that it may be possible to access the service through some customized protocol.
Using Web services is therefore something of a challenge. There can be multiple bindings to the same service. Some of these bindings may be suitable in some situations, others in different situations. The binding itself may represent a mapping of the abstract service description to any arbitrary protocol for accessing the service.While having multiple choices in the way the service is used and allowing binding extensions is useful, it makes it difficult for clients to have a uniform way of viewing the service.
I begin this article with a discussion of current client-side APIs and their capabilities. I will use this discussion to motivate the need for WSIF, the Web Services Invocation Framework, and then continue with an overview of WSIF.
Current invocation styles and their drawbacks
SOAP bindings for Web services are part of the WSDL specification. This protocol has implementations and tools available in most programming languages, in many cases cost-free. Thus, it gets developers platform-independence for Web services at little cost.
Therefore, it is not surprising that when most developers think of using a Web service, what comes their minds is assembling a SOAP message and sending it across the network to the service endpoint, using some SOAP client API. For example, with Apache SOAP, the client would create and populate a Call object. This encapsulates the service endpoint, the identification of the SOAP operation to be invoked, the parameters that have to be sent, etc. While this works for SOAP, it is limited in its use as a general model for invoking Web services, for the following reasons:
- Web services aren't just SOAP services
Web services are thought to be synonymous with services offered over SOAP. This is a limited view of Web services. Any piece of code with a WSDL description of its functional aspects and access protocols can be considered to be a Web service. The WSDL specification defines a SOAP binding for Web services, but it is in principle possible to add binding extensions so that, for example, an EJB may be offered as a Web service, using RMI/IIOP as the access protocol. Or you can even envision any arbitrary Java class being treated as a Web service, with native Java invocations as the access protocol. With this broader definition of a Web service, you need a binding independent mechanism for service invocation. - Tying client code to a particular protocol implementation is restricting
Having client code tightly bound to a client library for a particular protocol implementation results in hard-to-maintain code. Let's say you had an application that invokes Web services using Apache SOAP v2.1 on the client side. If you wanted to take advantage of new features and bug fixes that came out in v2.2, you would have had to update all your client code, a time consuming task that would involve the usual migration headaches. Similarly if you wanted to move from Apache SOAP to a different SOAP implementation, the process would be non-trivial. What is needed is a protocol implementation independent mechanism for service invocation. - Incorporating new bindings into client code is hard
WSDL allows extensibility elements for defining new bindings. This allows developers to define bindings that allow code that uses some custom protocol to work as a Web service. In practice, however, achieving this is hard. The client APIs for using this protocol would have to be designed. The application itself may use just the abstract interface of the Web service, so some tools would have to be written to generate the stubs that enable an abstraction layer. Again, these are non-trivial and time consuming tasks. What is needed is a service invocation mechanism that allows bindings to be updated or new bindings to be plugged in easily. - Multiple bindings can be used in flexible ways
For example, imagine that you have successfully deployed an application that uses a Web service offering multiple bindings. To make this example more concrete, suppose you have a SOAP binding for the service and a local Java binding that allows you to treat the local service implementation (a Java class) as a Web service. Obviously, the local Java binding for the service can only be used if the client is deployed in the same environment as the service itself, and, if this is indeed the case, it is far more efficient to communicate with the service by making direct Java calls rather than using the SOAP binding. The Java binding acts as a kind of shortcut access mechanism. Clients that want to exploit the availability of multiple bindings would then have to possess the ability of switching the actual binding to be used based on run-time information. So in order to take advantage of Web services that offer multiple bindings, you need a service invocation mechanism that allows you to switch between the available service bindings at runtime, without having to generate or recompile a stub.
The Web Service Invocation Framework (WSIF) is a toolkit that provides a simple API for invoking Web services, no matter how or where the services are provided. It has all the features I identified in the discussion above:
- It has an API that provides binding independent access to any Web service.
- It comes with a port type compiler to generate a stub that allows invocation using the abstract service interface.
- It allows stubless (completely dynamic) invocation of Web services.
- An updated implementation of a binding can be plugged into WSIF at runtime.
- A a new binding can be plugged in at runtime.
- It allows the choice of a binding to be deferred until runtime.
For the purposes of discussion, I'll use the ubiquitous stock quote program, the "Hello World" example of Web services. Consider the following abstract service description using WSDL shown in Listing 1.
<?xml version="1.0" ?>
<definitions targetNamespace="http://www.ibm.com/namespace/wsif/samples/stockquote-interface"
xmlns:tns="http://www.ibm.com/namespace/wsif/samples/stockquote-interface"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="GetQuoteInput">
<part name="symbol" type="xsd:string"/>
</message>
<message name="GetQuoteOutput">
<part name="quote" type="xsd:float"/>
</message>
<portType name="StockquotePT">
<operation name="getQuote">
<input message="tns:GetQuoteInput"/>
<output message="tns:GetQuoteOutput"/>
</operation>
</portType>
</definitions>
|
Simple enough: this describes a service with just one operation offered through one port type. This operation expects a string, to be interpreted as a ticker symbol for a stock, and returns a current quote for that stock, a float value. In order to actually use this service, you need some binding that defines an access mechanism and a service endpoint for that binding. Listing 2 shows a SOAP binding of the service:
<?xml version="1.0" ?>
<definitions targetNamespace="http://www.ibm.com/namespace/wsif/samples/stockquote"
xmlns:tns="http://www.ibm.com/namespace/wsif/samples/stockquote"
xmlns:tns-int="http://www.ibm.com/namespace/wsif/samples/stockquote-interface"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:java="http://schemas.xmlsoap.org/wsdl/java/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<import namespace="http://www.ibm.com/namespace/wsif/samples/stockquote-interface"
location="stockquote-interface.wsdl"/>
<binding name="SOAPBinding" type="tns-int:StockquotePT">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getQuote">
<soap:operation soapAction="http://example.com/GetTradePrice"/>
<input>
<soap:body use="encoded"
namespace="urn:xmltoday-delayed-quotes"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded"
namespace="urn:xmltoday-delayed-quotes"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="StockquoteService">
<documentation>Stock quote service</documentation>
<port name="SOAPPort" binding="tns:SOAPBinding">
<soap:address location="http://localhost:8080/soap/servlet/rpcrouter"/>
</port>
</service>
</definitions>
|
This is a standard SOAP binding using the RPC style and HTTP as the transport protocol to communicate with the service. In the port section of the document I define a URI where the service can be accessed
using the SOAP binding. In Listing 3 I'll contrast using this service with Apache SOAP 2.2 client-side API versus using it with WSIF's client API.
Listing 3: Apache SOAP API versus WSIF API for service access
Apache SOAP API
// Step 1: identify the service
Call call = new Call();
call.setTargetObjectURI("urn:xmltoday-delayed-quotes");
// Step 2: identify the operation
call.setMethodName("getQuote");
call.setEncodingStyleURI(encodingStyleURI);
// Step 3: identify the parameters
Vector params = new Vector();
params.addElement(new Parameter("symbol",
String.class, symbol, null));
call.setParams(params);
// Step 4: execute the operation
Response resp = call.invoke(url,
"http://example.com/GetTradePrice");
// Step 5: extract the result or fault
if(resp.generatedFault())
{
Fault fault = resp.getFault();
System.out.println("Ouch, the call failed: ");
System.out.println(" Fault Code = " +
fault.getFaultCode());
System.out.println(" Fault String = " +
fault.getFaultString());
throw new SOAPException("Execution failed " + fault);
}
else
{
Parameter result = resp.getReturnValue();
return ((Float) result.getValue()).floatValue();
}
WSIF API
// Step 1: identify the service
Definition def = WSIFUtils.readWSDL(null,wsdlLocation);
// Step 2: identify the operation (choose an
// appropriate service port)
WSIFDynamicPortFactory portFactory =
new WSIFDynamicPortFactory(def, null, null);
// Get default port
WSIFPort port = portFactory.getPort();
// The user can also explicitly select a port
// to use.
// WSIFPort port =
// portFactory.getPort("SOAPPort");
// Step 3: identify the parameters
// Prepare the input message
WSIFMessage input = port.createInputMessage();
input.setPart("symbol",
new WSIFJavaPart(String.class, symbol));
// Prepare a placeholder for the output value
WSIFMessage output = port.createOutputMessage();
// Step 4: execute the operation
port.executeRequestResponseOperation("getQuote",
input, output, null);
// Step 5: extract the result or fault
WSIFPart part = output.getPart("quote");
return ((Float)part.getJavaValue()).floatValue();
|
As you can see, WSIF's API is driven by the abstract service description in WSDL; it is completely divorced from the actual binding used. This invocation API is WSDL-oriented and more natural to work with as it uses WSDL terms to refer to message parts, operations, etc. When you read a WSDL description, it is intuitive to think of picking a port that supports the port type needed, then invoking the operation by providing the necessary abstract input message consisting of the required parts (without worrying about how the message is mapped to a specific binding protocol); this is exactly how the WSIF API was designed.
The usual APIs such as the Apache SOAP API shown above use concepts central to a particular protocol, such as target URIs and encoding styles in the case of SOAP. This is inevitable since the API is not layered over WSDL, but designed for a particular protocol.
Two invocation models using WSIF
WSIF allows Web services to be invoked in two ways. One is a stubless dynamic invocation that requires using the WSIF API directly, and the other is invocation via a generated stub that allows the application to work with Java interfaces (corresponding directly with WSDL port types) and hides the WSIF API.
All the information required to access a Web service is available through WSDL -- the abstract interface, the binding and the service endpoint. If you go through the client API example above, you notice that the only information supplied by the user is the location of the WSDL file for the service and the symbol for which a stock quote is required. It is quite straightforward to then use the WSFL API to load this service at runtime and make the invocation.
The WSIF distribution contains a DynamicInvoker which demonstrates how to execute any arbitrary operation of a WSDL. It takes the URI for the WSDL file and the required operation parameters as command line arguments (in this first
implementation only simple types such as strings are accepted) and uses the WSIF API directly to do the work. An example of its use for invoking the stock quote service is shown in Listing 4; you can find details in the documentation included with the WSIF distribution.
Since this kind of invocation does not result in generation of stub classes and does not require a separate compilation cycle, it is very convenient.
java clients.DynamicInvoker http://services.xmethods.net/soap/urn: xmethods-delayed-quotes.wsdl getQuote IBM Reading WSDL document from 'http://services.xmethods.net/soap/urn: xmethods-delayed-quotes.wsdl' Preparing WSIF dynamic invocation Executing operation getQuote Result: Result=108.8 Done! |
Stubless invocation is not desirable in cases where the application wants a more abstract view of the service and does not want to deal with the WSIF client API. WSIF comes with a port type compiler which, given a WSDL, generates a client stub for each port type along with Java equivalents (in the form of JavaBeans) of the complex types used. Applications that use the generated stubs can use the abstract service interface and are thus insulated from the protocol and the WSIF client API. These stubs use a default port for the actual service invocation. An example of using the stock quote service via a stub generated by WSIF's port type compiler is shown in Listing 5.
StockquotePT service = new StockquotePTStub(WSIFUtils.readWSDL(null, wsdlLocation),
null, null);
float quote = service.getQuote("IBM");
System.err.println (">> Received quote: "+quote);
|
It is worth noting that in case the stubs reside in some managed environment like an application server, they can be customised; for example, the factory responsible for returning an appropriate port can be changed so that the port selection algorithm is customized, or the actual port used for invocation itself can be changed.
In this article I have outlined the need for WSIF and examined its principal features of binding-independent service access through a high level API, and the availability of a port type compiler that generates a customizable stub to use a service through its abstract interface. The comparison between service access through a SOAP client API directly and use of the WSIF API shows how WSIF moves the whole problem of Web service invocation from a binding-centric viewpoint to a more abstract level, driven by the WSDL description of the service. This is one of WSIF's design principles, and enables service bindings to be viewed as pieces of code required for invocation according to a particular protocol; these can then be plugged in at any time.
In a future article, I will examine WSIF's architecture and see how it allows new or updated binding implementations to be plugged in, how it allows a customized type system to be used for Web services, and how the runtime environment can be made to use customized heuristics for selecting between multiple ports.
- Download the WSIF distribution on alphaworks and try out the easier samples. This give you a first-hand example of the different invocation styles supported by WSIF and its advantages over protocol-specific client APIs.
- Go over the WSDL specification to see what kinds of extensions are allowed; you can also study how WSDL's extension mechanism if used to define a SOAP binding for accessing Web services.
- Go over the SOAP specification itself.
- If you haven't programmed with Web services before, the Web Services ToolKit is a good starting point.
- Take a look at WSDL4J, an extensible WSDL parsing framework over which WSIF has been built.
Nirmal K. Mukhi is a Research Associate at IBM's T J Watson Research Lab where he has been working on various Web services technologies since November 2000. His other interests include AI, creative writing, and outdated computer games. You can reach Nirmal at nmukhi@us.ibm.com.
Comments (Undergoing maintenance)





