I like your type: Describe and invoke Web services based on service type

WSDL tools and techniques

My previous Web Services column focused on describing a Web service according to business classifications or geographic regions—categories meaningful to business professionals or administrators. In this article, I focus on describing a Web service according to categories meaningful to programmers.

Business professionals and administrators primarily deal in business categories or organizational entities—the nouns of their trade. By contrast, a developer's daily work centers on types. Everything that you manipulate through a programming language has a type. For instance, a variable might be an integer or a floating-point type, which both constitute primitive types. A variable can also be a string or a person—more complex object-type varieties. Each programming language presents a different type system that serves as the basic dictionary of your application's data.

Most languages also give you a way to define new types, built out of the language's basic types. When you create a person type in Java, it might consist of firstName and lastName fields, both of which would be strings—a type that Java already provides. Writing a Java program typically involves creating an increasingly sophisticated type system and defining operations (methods) on those types.

When you interact with a Web service from a Java program—whether that program is a servlet or standalone application—that Web service must be represented as a Java type or a series of types inside your program. Likewise, when you wish to advertise your Web service so that others can invoke it over the Web, you must define it with types that other programs across the network understand.

If you must consider only interaction between Java programs, you can just use the Java type system or the custom types you defined on top of the basic Java types, such as person. Java Remote Method Invocation (RMI) is a service-oriented framework that relies on the Java type system for service definition: if both programs are written in Java, they can just communicate on the Internet via RMI. XML-based Web services, on the other hand, require that you define a Web service in a type system not tied to any specific programming language; you must map types defined in a programming language to language-neutral types, and vice versa.

A service's type consists of the service's name, the names of the operations (methods) it offers, as well as the names and types of those methods' parameters and return types. Continuing my earlier example of a cruise reservation Web service, consider a programmer at the imaginary Theseus Cruise Lines. His company operates the Ship of Theseus, which travels between the island of Crete and the city of Athens in the Mediterranean Sea. The following simple Java interface describes a service's type—consisting of one method—that returns an array of strings with the cruise line's destinations. In the article's next section, we will convert this type definition to a non-Java-specific format.

public interface CruiseService {
    public String[] cruiseDestinations(); 
}

In addition to a service's type, when you want to invoke someone else's Web service, you also must know the technical details of locating and connecting to that service. That typically means that a service provider must advertise its service's access points, or endpoints, and the protocols supported by those access points. The URL http://www.javaworld.com, for instance, specifies the HTTP protocol, and www.javaworld.com is the address used to contact the service (the service's endpoint).

The W3C (World Wide Web Consortium) recommends an XML metalanguage, the Web Services Description Language, or WSDL, for describing a service's type and access information. A WSDL-based document can be associated with a Web service in Web service registries, such as UDDI (Universal Description, Discovery, and Integration) or ebXML, along with other information describing that service. Someone searching for your service can retrieve that WSDL document from a URL, convert the service-specific types to language-specific types, and then use the service's endpoint and protocol information to contact, or invoke, the service. Figure 1 illustrates the process of advertising a service's type, endpoints, and protocols:

Figure 1. Create and use a Web service description

Axis power

As a programmer at Theseus Cruise Lines, you can easily implement the above service interface with the following class:

public class CruiseServiceImpl implements CruiseService {
    public String[] cruiseDestinations() {  
        // Or get this info from a database, etc.
        return dests;
    }
    private static final String[] dests = 
        {
            "Athens",
            "Crete"
        };
}

In this example, we let a client invoke the cruiseDestintations() method via the Web using SOAP (Simple Object Access Protocol). Even if you are unfamiliar with SOAP, this example is simple to follow. Several toolkits exist to turn this short Java program into a Web service with no coding. For this example, we will use the latest version of the Apache SOAP toolkit, Axis. I list other tools in Resources below.

You can download the Axis distribution from http://xml.apache.org and install it in your favorite servlet container, such as Tomcat. Currently, that installation proves simple: once you've expanded the downloaded archive file, copy the axis subdirectory to Tomcat's webapps directory. (If you use another servlet container, copy that subdirectory to wherever your servlet tool expects Web applications.) At that point, you've SOAP-enabled your Web server.

Next, copy the CruiseServiceImpl.java file to the axis directory under the Web server, as CruiseServiceImpl.jws. That jws extension stands for Java Web service. If you've installed Tomcat as /usr/local/jakarta-tomcat, the Java source code file must be at /usr/local/jakarta-tomcat/axis/CruiseServiceImpl.jws. The Web server will dynamically compile that class when the service is first invoked (similarly to how JSPs (JavaServer Pages) compile). Since CruiseServiceImpl implements the CruiseService interface, the CruiseService class must be available to the Web server for that compilation to succeed. Therefore, compile CruiseService.java and place the resulting class file in Axis's WEB-INF/classes subdirectory. At that point, you now have a Web service that anyone capable of connecting to your Web server can invoke. If your Web server's URL is www.theseus-cruises.com and your server runs on port 8080 (the default for Tomcat), then the cruise destination Web service will be available at: http://www.theseus-cruises.com:8080/axis/CruiseServiceImpl.jws.

Using the Axis SOAP library, creating a client program is only slightly more involved. The following is that client's entire source code:

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.utils.Options;
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import java.net.URL;
public class CruiseClient {
    public static void main(String [] args)
    {
        try {
           // Specify the service's endpoint
           // Substitute your machine's address here
           String endPoint = 
               "http://www.theseus-cruises.com:8080/axis/CruiseServiceImpl.jws";
           // Set up the remote method call
           Service  service = new Service();
           Call call = (Call) service.createCall();
           call.setTargetEndpointAddress( new java.net.URL(endPoint));
           call.setOperationName( new QName("CruiseServiceImpl", "cruiseDestinations") );
           // Perform the remote call
           String[] ret = (String[])call.invoke(new Object[0]);
           System.out.println("Destinations:");
           for (int i = 0; i < ret.length; i++) {
               String s = ret[i];
               System.out.println(s);
           }            
        } catch (Exception e) {
           e.printStackTrace();
        }
    }
}

This Web service client sets up a remote call, performs that call, and prints the results. We first specify the service's endpoint address and the name of the Web service operation to invoke. That name is a qualified name, or QName: it qualifies the method name with a namespace, which is the Web service class name in this case. Compile and run this program. If your Web server is running and you've installed Axis and CruiseServiceImpl.jws, the client should print the strings Athens and Crete—the results returned from the Web service.

The Web service deployment descriptor

Our minimalist cruise destination Web service shows the level of simplicity the Axis/Apache SOAP APIs and tools bring to Web services development. But that example proves inflexible. For one, the client must explicitly reference the service's implementation class, CruiseServiceImpl. A better service would require the client to only know the service interface's name or any name that refers to the service. The Axis runtime would then map that service name to the implementation class. A Web service deployment descriptor (WSDD) will help achieve such flexibility.

A WSDD is an XML file that describes the Web service's deployment details. The following example is specific to Apache Axis, but most other SOAP tools similarly define a deployment descriptor. Below is a simple deployment descriptor for the cruise destination service:

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
       xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <service name="CruiseService" provider="java:RPC">
        <parameter name="className" value="CruiseServiceImpl"/>
        <parameter name="allowedMethods" value="*"/>
        <parameter name="scope" value="request"/>
    </service>
</deployment>

That document first declares the relevant XML namespaces and then defines the service. We specify the service's name as well as the service invocation mechanism. The latter assumes the values java:RPC in this example, referring to the Axis built-in Java remote procedure call (RPC)/SOAP provider. The rest of the description consists of a set of parameters: First, the service implementation class's name (CruiseServiceImpl), then a list of the methods we wish to provide access to (in this case, all methods in CruiseServiceImpl). The final parameter specifies the CruiseServiceImpl object's scope; request specifies a request's length, meaning that every request will entail the creation of a new CruiseServiceImpl. You can keep a single CruiseServiceImpl instance for a session or for the entire application's lifetime.

Save this document in file deploy.wsdd and register the cruise schedule service with the Axis runtime using the following command:

 java org.apache.axis.client.AdminClient deploy.wsdd

Two changes are now necessary in the client: First, the Web service's endpoint URL must now refer to the Axis RPC servlet:

 String endPoint = "http://192.168.1.10:8080/axis/servlet/AxisServlet";

Second, the client must account for the change in the service's name: instead of the implementation class's name, we can now refer to the service by the name given in the WSDD file:

 ...call.setOperationName( new QName("CruiseService", "cruiseDestinations") );
....

With these changes, you can recompile the client and run it again. The response should be identical to the earlier results, with the strings Athens and Crete printed to the standard output. This time, however, the client is not tied to a specific service implementation: you can swap CruiseServiceImpl for ImprovedCruiseServiceImpl without Web service clients knowing about that change.

What's in a WSDL

Not having to know a specific service implementation increases the flexibility of Web service interaction. However, we still want to reach increasing levels of abstraction in a service's definition. Next, we'll extend the client so that it also doesn't need to know a service's location URL. Instead, the client searches Web service registries for all cruise companies implementing CruiseService, contacts each, and creates a master list of all the available destinations. For that to work, you must register a service's technical description in those registries.

Web service registries, such as UDDI, do not mandate the use of any specific service description language. You could describe your service's technical details any way you like—with plain text, XML, and so forth. However, WSDL is rapidly emerging as a standard for Web service description. While it is not yet an "official" W3C standard—currently, it is a W3C "recommendation"—several tools support it. Many tools sport the capability to automatically generate WSDL documents from Java code and, concomitantly, to generate Java code from WSDL definitions.

Recall that to facilitate Web service interaction, a WSDL document must define a service's type and provide information about invoking that Web service. Also remember that a service's type consists of the service's name, the data types it exchanges during service invocation, and the definitions of the service's methods. Information about service invocation includes the message pattern between the service and its callers, the on-the-wire protocol used during that message exchange, as well as the address for contacting the service. The six key elements that make up a WSDL document correspond to those items:

1 2 3 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more