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 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:
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 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:
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 =
"
// 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="/axis/wsdd/"
xmlns:java="/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 = "
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:
definitions
are a WSDL document’s root elements and contain all the namespace definitions used in the rest of the document.-
types
describe the data type definitions used in message exchanges with the service. This section defines, for instance, how the parameters that pass to or return from method invocations are encoded in a programming language-neutral way.You can use any encoding format for your type system definitions. However, the WSDL specification document suggests using XML Schema Definitions, or XSD. XML Schema is a recent W3C standard that attempts to go beyond document type definitions (DTDs) in defining an XML document’s structure. XML Schema allows you to create a strong type system, similar to how Java describes a type system. In other words, you can unambiguously identify the data types of every element when encoding an XML document according to the XML Schema standard.
XML Schema defines a set of simple types, such as strings, doubles, or dates, and then specifies a mechanism for creating complex types out of those simple building blocks. That process resembles how you define complex types in a programming language, except with XML Schema, that definition remains language-independent—you can use those types from Java, C++, or C#. But XML Schema is not the only way to encode data types inside a WSDL document. The WSDL documentation itself recommends the
arrayType
data type for arrays, based on the namespace You will shortly see an example of that in the cruise Web service’s WSDL definition. message
defines the messages exchanged with the service. For instance, the return value from our cruise Web service transmits in message form. For each Web service message, the message section contains a message element, and each element specifies message parts as subelements.portType
defines the actual operations offered by the service. The cruise Web service has only one operation,cruiseDestinations
. That operation consists of two message exchanges: a request (when a client invokes the method) and the response (the array of cruise destinations). Each operation might also include messages that indicate faults (exceptions) occurring during invocation.binding
specifies the wire protocols used to access eachportType
. AportType
can have many bindings. In other words, a service might provide access to its methods (operations) via numerous different protocols, such as SOAP, HTTP, RMI-IIOP (Internet Inter-Orb Protocol), or RMI-JRMP (Java Remote Method Protocol, RMI’s default on-the-wire message protocol). The W3C’s WSDL Recommendation describes three different protocol bindings: SOAP, HTTP GET and POST, and MIME (Multipurpose Internet Mail Extensions) bindings. We will look at the SOAP binding while examining the cruise service’s WSDL.service
describes the set of ports to use when invoking the service. A service can have many ports, with each port having a name and a protocol binding. In addition, each port can have several URL addresses, which are also specified here.
Figure 2 summarizes these six elements.
Once a client retrieves a Web service’s WSDL description, it can examine the different ports that service offers and discover the names of the protocols those ports support from the service
section. The client can next examine what protocols those names refer to based on information in the binding
section. The client can then choose the ports that it knows how to interact with. For instance, if the client can communicate via SOAP, it might then choose the service ports with SOAP bindings. Finally, the client would discover the port types (operations), the messages those types entail, and those messages’ data types. Based on the information in the WSDL document alone, a Web service’s client can create a set of Java class files that enable it to directly invoke the service. Figure 3 illustrates the process of initiating service invocation based on a WSDL document’s information.
Create WSDL from Java source code
The easiest way to create a WSDL document for a Web service is with tools that automate the process. The Apache Axis package comes with one such tool, Java2WSDL. You can create a WSDL document for the cruise service with the following command (you should type this on a single line; also substitute the address to your own server’s address):
java org.apache.axis.wsdl.Java2WSDL -o cruise.wsdl
-l
-n "urn:cruise" -S "CruiseService" CruiseService
The parameters are as follows: -o
specifies the output file, -l
denotes the service’s location, -n
describes the service’s unique namespace, while -S
is the name we wish to give the service. The last parameter represents the name of the Java interface or class that defines the service. Given this command, the Java2WSDL tool produces the following document, saved in the cruise.wsdl
file:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="urn:cruise"
xmlns:impl="urn:cruise-impl"
xmlns:intf="urn:cruise"
xmlns:apachesoap="/xml-soap"
xmlns:wsdlsoap="
xmlns:soapenc="
xmlns:xsd="
xmlns:wsdl="
xmlns=">
<wsdl:types>
<schema xmlns=" targetNamespace="urn:cruise">
<import namespace="/>
<complexType name="ArrayOf_soapenc_string">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="soapenc:string[]"/>
</restriction>
</complexContent>
</complexType>
<element name="ArrayOf_soapenc_string"
nillable="true" type="intf:ArrayOf_soapenc_string"/>
</schema>
</wsdl:types>
<wsdl:message name="cruiseDestinationsResponse">
<wsdl:part name="return" type="intf:ArrayOf_soapenc_string"/>
</wsdl:message>
<wsdl:message name="cruiseDestinationsRequest">
</wsdl:message>
<wsdl:portType name="CruiseService">
<wsdl:operation name="cruiseDestinations">
<wsdl:input name="cruiseDestinationsRequest"
message="intf:cruiseDestinationsRequest"/>
<wsdl:output name="cruiseDestinationsResponse"
message="intf:cruiseDestinationsResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="AxisServletSoapBinding" type="intf:CruiseService">
<wsdlsoap:binding style="rpc" transport="
<wsdl:operation name="cruiseDestinations">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="cruiseDestinationsRequest">
<wsdlsoap:body use="encoded"
encodingStyle="
namespace="urn:cruise"/>
</wsdl:input>
<wsdl:output name="cruiseDestinationsResponse">
<wsdlsoap:body use="encoded"
encodingStyle="
namespace="urn:cruise"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CruiseService">
<wsdl:port name="AxisServlet" binding="intf:AxisServletSoapBinding">
<wsdlsoap:address location=""/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
This document features a WSDL document’s six required parts. A few things to note about this file:
- The WSDL document defines a target namespace of
targetNamespace="urn:cruise"
. - The Java2WSDL tool converts the service method’s array return type to
wsdl:arrayType
in the document’stypes
section. - The
message
section defines a request/response message pair, each named after the service method (for instance,<wsdl:message name="cruiseDestinationsResponse">
). - The WSDL file defines one operation for the service, corresponding to our Java class’s sole method,
<wsdl:operation name="cruiseDestinations">
. The request/response messages defined earlier constitute that service operation. - The service defines a single binding, based on SOAP:
<wsdlsoap:binding style="rpc" transport="
. The request and response messages make up that SOAP operation’s input and output. - Finally, the WSDL file designates a single port, based on Java2WSDL’s
-l
, or location, argument.
If you placed this WSDL file on the Web, anyone could download it and interface with your service. Note that the ability to create WSDL documents from Java source code is not limited to Apache Axis; most Web services tools feature this functionality, such as Glue, from The Mind Electric, and toolkits from Cape Clear Software, Sun Microsystems, IBM, BEA Systems, and others. I list several in Resources.
Create Java source code from WSDL
For Web service clients, tools are also available for creating Java classes from a WSDL document. As you may have guessed, the WSDL2Java tool (not to be confused with the Java2WSDL tool) from Apache Axis offers that functionality. Given the WSDL document we just created, cruise.wsdl
, you can generate Java classes with the following command (you might want to copy cruise.wsdl
to a new directory before running this command):
java org.apache.axis.wsdl.WSDL2Java cruise.wsdl
WSDL2Java first examines the namespace specified by the WSDL document and creates a Java package with an identical name, in our case, cruise
. It then places four Java source files inside that package. One of those files is CruiseService.java
:
package cruise;
public interface CruiseService extends java.rmi.Remote {
public java.lang.String[] cruiseDestinations() throws java.rmi.RemoteException;
}
That source code resembles our original Java interface except that it now adheres to the semantics of a Remote
Java interface (see A Note on Distributed Computing for more on the semantics of remote method invocation). The other class definitions help locate the service—based on the service endpoints—and interact with it via the SOAP bindings. A Web service client’s programmer can incorporate these Java classes into her application and thereby interact with the service.
JWSDL: Programmatically create and edit WSDL documents
While automatically generating WSDL definitions based on Java classes often proves useful, the Java APIs for WSDL, or JWSDL, provide programmatic access to creating, reading, and writing WSDL documents. The JWSDL is currently in development through the Java Community Process (JCP) as Java Specification Request (JSR) 110. At the time of this writing, IBM provides a reference implementation of the proposed specification.
Unlike tools such as Axis’s Java2WSDL, JWSDL does not map types or interfaces between WSDL descriptions and programming language types. Instead, programmatic interaction with WSDL documents offers different benefits: you can parse a WSDL file and examine the services it advertises along with those services’ access ports, the protocols supported on those ports, and the operations and messages defined in the WSDL document. The JWSDL specification defines the javax.wsdl
package.
Using JWSDL, you can discover services based on any of those technical attributes and invoke them dynamically. The on-the-wire protocol you decide to use, such as SOAP, then provides the type-mapping from XML to Java. When creating a WSDL document for a Web service, JWSDL offers complete flexibility in specifying and naming the elements on the WSDL description. Using JWSDL, we will extend the Web service client to discover any services that implement the CruiseService
, invoke them, and compile a master list of all available cruise destinations.
Recall that a service’s type consists of the service’s name, its operations’ names, and the definitions of those operations’ parameters and return types. For the cruise destination service, that means the following:
- The service name must be
CruiseService
within theurn:cruise
namespace - An operation with the name
cruiseDestinations
must be present and should consist of one request/response exchange - That operation must require no parameters and define a return type of
arrayType
(from the SOAP namespace)
Once we find a Web service that matches those characteristics, we want to discover where to contact that service—the service’s endpoint. In addition, since our client can interact with the service via SOAP only, we also want the service to offer at least one port with SOAP binding.
The JWSDL API allows us to inspect a set of WSDL documents, keeping only those matching the above criteria, and to extract from the matching documents information needed to contact each service. The JWSDL API separates design from implementation by offering a factory mechanism for accessing key API functionalities. The factory classes refer to interfaces, which an incarnation of the API can then fully implement. While I will use IBM’s JWSDL implementation, WSDL4J, this example should also work with other tools supporting JWSDL.
To read a WSDL file, you first must obtain a WSDLReader
from a factory and then parse the WSDL document into a definition
. (Recall that definition
is a WSDL document’s root element.) Thereafter, JWSDL allows you to read and manipulate every element of that WSDL document.
Writing operations follow a symmetric process: you create a WSDLWriter
from a factory and then write parts of a WSDL document as needed. JWSDL does not enforce semantic rules when writing new WSDL elements; it simply ensures correct syntax for the resulting document.
Finally, JWSDL also accounts for the extensible nature of WSDL definitions. The WSDL specification does not restrict protocol bindings to just SOAP, HTTP, and MIME. JWSDL provides ExtensibilityElement
s for those extensible document elements based on the WSDL specification. Even the SOAP binding for a Web service is specified via JWSDL’s extensibility infrastructure. Extensibility-related classes are located in the javax.wsdl.extensions
, javax.wsdl.extensions.http
, javax.wsdl.extensions.soap
, and javax.wsdl.extensions.mime
packages.
The following extension of the simple cruise service client accepts a set of WSDL files on the command line, finds the ones that are the CruiseService
type, discovers the URL for the service’s SOAP port binding, and invokes each service instance to compile a master list of cruise destinations:
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.*;
public class Client {
// Name of the Web service
private static final String serviceName = "CruiseService";
// Operation name
private static final String operationName = "cruiseDestinations";
// Name space for Cruise Service
private static final String cruiseNameSpace = "urn:cruise";
private static Vector destinations = new Vector();
// WSDL Factory
private static WSDLFactory factory;
// WSDL Reader
private static WSDLReader reader;
public static void main(String[] args) {
try {
factory = WSDLFactory.newInstance();
}
catch (WSDLException e) {
System.err.println("Can't create WSDLFactory: " + e.getMessage());
System.exit(-1);
}
reader = factory.newWSDLReader();
for (int i = 0; i < args.length; i++) {
try {
Object results = invokeService(args[i]);
if (results instanceof String[]) {
String[] res = (String[]) results;
for (int k = 0; k < res.length; k++) {
String cruiseDestination = res[k];
if (cruiseDestination != null &&
!destinations.contains(cruiseDestination)) {
destinations.add(cruiseDestination);
}
}
}
}
catch (WSDLException wsdlEx) {
System.err.println(wsdlEx.getMessage());
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
// Print all destinations
for (int i = 0; i < destinations.size(); i++) {
String dest = (String) destinations.elementAt(i);
System.out.println(dest);
}
}
private static Object invokeService(String wsdlFileName) throws WSDLException,
ServiceException, MalformedURLException, RemoteException {
if (wsdlFileName == null) {
return new String[0];
}
// Create definition from WSDL file
Definition def = reader.readWSDL(null, wsdlFileName);
// Obtain Service part of definition, if there is one
javax.wsdl.Service service = def.getService(new QName(cruiseNameSpace, serviceName));
Map ports = service.getPorts();
Collection vals = ports.values();
for (Iterator iterator = vals.iterator(); iterator.hasNext();) {
Port port = (Port) iterator.next();
List l = port.getExtensibilityElements();
for (Iterator it = l.iterator(); it.hasNext();) {
// The SOAP binding is an extensibility element of Port
ExtensibilityElement element = (ExtensibilityElement) it.next();
if (element instanceof SOAPAddress) {
SOAPAddress soapAddress = (SOAPAddress) element;
Service soapService = new Service();
String serviceEndpoint = soapAddress.getLocationURI();
Call call = (Call) soapService.createCall();
call.setTargetEndpointAddress(new URL(serviceEndpoint));
call.setOperationName(new QName(serviceName, operationName));
String[] results = (String[]) call.invoke(new Object[0]);
return results;
}
}
}
return new String[0];
}
}
To keep this example focused on JWSDL, our service discovery reads a set of WSDL documents specified on the command line. In my next Web Services column, I will extend this example a step further to show how those WSDL documents can also be retrieved from Web service registries, such as UDDI, using the Java API for XML Registries (JAXR). In addition, I will describe ways for making WSDL service definitions reusable, allowing several organizations’ Web services to share one service definition.
Type into Web services
Web service development tools automate the description of Java-based Web services in WSDL. Given a Java class file, tools such as Apache Axis can create a WSDL document, and, given a WSDL file, such tools can create Java source code that a service’s users can then incorporate into their own programs. For finer-grained, programmatic access to WSDL definitions, you can use the JWSDL API developed under JRS 110. With JWSDL, Web service clients can dynamically invoke Web services based on those services’ public interfaces or service types.