This article was published in the November 2002 issue of the IBM developerWorks journal.
Most of the work I will be doing in this article is based on open source tools from the Apache XML project, including Axis beta 3; the WSIF; the Web Services Inspection Language (WS-Inspection); the Web Services Description Language for Java Toolkit (WSDL4J) API; and the Universal Description, Discovery and Integration for Java (UDDI4J) API. (See the Resources section for more details on these tools and how to get them.) To emphasize the open, cross-platform nature of SOAP applications, I will look at clients written in Perl, Python, and PHP.
To begin, I will take a simple piece of Java code and make it a web service. The sample application emulates an American toy called the Magic Eight Ball. The toy is a giant billiard ball filled with blue liquid and a clear window that looks in on a shape, which floats in the liquid, with 20 sides. On each of the 20 sides is an answer to a yes-or-no question. To receive guidance from the Eight Ball, you shake it as you think of a question, then look down at the Eight Ball. The 20-sided shape (an icosahedron) floats to the window and one of the 20 answers is visible, telling you the answer to your question.
The Java version of the Eight Ball is a random number generator that returns one of 20 strings at random, as shown in Listing 1.
Listing 1. Look to (but don't shake) EightBall.java for answers
import java.util.Random;
import java.lang.Double;
import java.util.Date;
public class EightBall {
static String answers[] = {"Yes.",
"Outlook not so good.",
// The other 17 answers were
// removed for brevity.
"Don't count on it."};
public static String getAnswer() {
return askQuestion("");
}
public static String askQuestion
String question) {
java.util.Random r = new Random
(new Date().getTime());
java.lang.Double d = new Double
((r.nextDouble()*20)-1);
return new String(answers[d.intValue()]);
}
public static void main(String args[]) {
System.out.println(getAnswer());
}
}
|
The most interesting aspect of this code is that it is not aware of the Web, networking, SOAP, or XML. Despite that, you can still take this code and make it a web service without any changes. That is good news for the example, and it is even better news for anyone trying to add web services interfaces to existing applications. In many cases, you can do that without any changes as well.
Before you actually deploy the code, you should familiarize yourself with three methods. The main method simply enables you to invoke the Eight Ball from a command line; you will not use it from your web services clients. The getAnswer and askQuestion methods are the ones that you will expose as web services. The askQuestion method, when given a String, returns 1 of the 20 answers selected at random. The getAnswer takes no parameters and merely calls askQuestion with a blank String.
To deploy the code as a web service hosted by Axis, you need to create a deployment descriptor, as shown in Listing 2.
Listing 2. Deployment descriptor
<deployment xmlns=
"http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/
wsdd/providers/java">
<service name="urn:EightBall"
provider="java:RPC">
<parameter name="className"
value="EightBall"/>
<parameter name="allowedMethods"
value="getAnswer askQuestion"/>
</service>
</deployment>
|
The deployment descriptor defines several things:
- The name of the service (urn:EightBall).
- What type of service this is (java:RPC means this is an RPC-style service written in Java code).
- The name of the Java class that provides the service (EightBall).
- The name of the methods (getAnswer and askQuestion) supported by this service.
I used the AdminClient class to get Axis to process this data, as follows:
java org.apache.axis.client.AdminClient DeployEightBall.wsdd
Once Axis has processed the deployment descriptor, you need to move the EightBall.class file to a directory where Axis can find it. In this case, that is the axis\WEB-INF\classes directory; where that particular directory exists on your system depends on the servlet engine you are using to host Axis. For Jakarta Tomcat 4.0.3, the directory is jakarta-tomcat-4.0.3\webapps.
Now that you have deployed your Java code as a web service, you can write a client application. You will do this in a number of different ways and different languages. Regardless of how you write your client application, the application has to know five things:
- The name of the machine that is hosting the service.
- The name of the service.
- The name of the method that you want to invoke.
- The parameters for the method that you want to invoke (this means you have to know how many parameters there are and what their data types are).
- What the SOAPAction field should contain (I will cover this when I describe web services discovery; do not worry about it for now).
The first application will be a Java client written using the Axis client API. The four steps in using the Axis API are:
- Create a Service object.
- Use the Service object to create a Call object.
- Set the properties of the Call object (method name, parameters, and so on).
- Use the invoke method of the Call object.
There are several things worth noting in Listing 3, which shows the bulk of the client code. First, notice that your client application does have to know about SOAP, XML, and so forth, even though your service did not. Next, you create the Service and Call objects and start setting the properties of the Call object. This includes setting the address of the machine that hosts the web service (http://localhost:8070/axis/services/), defining the name of the service (urn:EightBall), and defining the name of the method (getAnswer). Once you have set all the properties, you can use the invoke method as shown in Listing 4.
Listing 3. EightBallClient1 segment using the Axis API
import javax.xml.rpc.namespace.QName;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.utils.Options;
public class EightBallClient1 {
public static void main(String [] args)
throws Exception {
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(
new java.net.URL
("http://localhost:8070/axis/
services/"));
call.setOperationName(
new QName("urn:EightBall",
"getAnswer"));
|
Listing 4. Use invoke method
try {
System.out.println(call.invoke(
new Object[] { }));
} catch (java.rmi.RemoteException re) {
System.out.println("Error - " + re);
}
|
Notice that invoke passes an array of Java Objects as its sole parameter. Because everything in Java code ultimately inherits from java.lang.Object, you can put absolutely anything inside this array. In the first client application, you are using the getAnswer method, which does not have any parameters. That means you create the empty array of Java Objects and leave it at that. In more advanced clients, you can put more interesting and complex items inside the array.
The invoke method builds a SOAP envelope that contains your request and sends it off to the machine that you set as the endpoint address. The machine that hosts the web service delivers the data from the SOAP request to the service, which then processes that request and generates a response. The response comes back to your client as another SOAP envelope; utilities in the Axis toolkit enable you to write the contents of the SOAP envelope with a System.out.println statement.
Finally, you use the invoke method inside a Java code try block. Many things can go wrong with a distributed application, so it is best to anticipate the problems ahead of time. For example, the remote machine might not be found, it might not respond in a certain amount of time, a message sent to or from the service could be lost or delayed, and so forth.
Here is how your client application looks from the command line:
D:\webservices\eightball>java EightBallClient1 It is decidedly so. |
Although this may not be the most visually appealing application you have ever seen, consider what you have done with little effort:
- You took an ordinary piece of code and exposed its methods across a network.
- You wrote a client application that anyone else can use on that network to invoke methods of a Java object.
- Because SOAP is language neutral, you can access the methods of that Java object from any programming language or operating system you are likely to use.
Those are three enormous accomplishments. If you wanted to do this with other remote technologies, it would take considerably more effort. With CORBA, you would have to generate language bindings for the various languages you wanted to support. With RMI, you could only write clients in Java code. SOAP enables you to create a flexible system in a short period of time.
Before I go on to more advanced client applications, note that Axis ships with an extremely useful tracing tool for SOAP messages. Here is the command that starts the tracing tool:
java org.apache.axis.utils.tcpmon 8070 localhost 8080
The arguments to the tcpmon class tell it to monitor port 8070 for SOAP envelopes. The tcpmon class writes any SOAP envelope that comes to port 8070 to the tracing window and sends it to localhost port 8080. When the SOAP response comes back from port 8080, tcpmon writes the response to the tracing window and sends it back to the client. Figure 1 shows how the tracing window looked when you ran the client application. In Figure 1, you can see the actual request and response messages as they go back and forth between the client and the service.
Figure 1. AXIS SOAP Tracing window

For the first client application, you used the getAnswer method, which does not take any parameters. Your next client application will use the askQuestion method, which requires a single String (the question you are asking) as a parameter. That means you have to do a little more work before you use Call.invoke. Fortunately, the amount of extra work is not significant. Listing 5 shows the important part of the code.
Listing 5. Call the EightBall service with a parameter
<question xsi:type="xsd:string">
Will Deutschland win the World Cup?
</question>
call.setTargetEndpointAddress(new
java.net.URL
("http://localhost:8070/axis/services/"));
call.setOperationName(new
QName("urn:EightBall", "askQuestion"));
call.addParameter("question",
XMLType.XSD_STRING, ParameterMode.IN);
call.setReturnType(
org.apache.axis.encoding.XMLType.XSD_STRING);
try {
String question =
"Will Deutschland win the World Cup?";
String ret = (String) call.invoke
(new Object[] { question });
System.out.print(question + "\n ");
System.out.println(ret);
}
|
Note that Listing 5 does a couple of additional things. First, it uses the addParameter method to add a new parameter to the Call object. You are defining that the name of the parameter is question, it is of type string (as defined by the XML Schema standard), and it is an input parameter. The next item that it uses is the setReturn Type method to tell the Call object that the data returned by this web service is a string as well. That sets up the Call object with all the properties you need. Inside the try block, you create a Java String that contains your question, and you pass that string in the array of Objects. You also could put the String itself into the array when you use the invoke method.
When your SOAP request goes to the server, it contains your parameter as well. The parameter looks like Listing 6.
Listing 6. Parameter
String ret = (String) call.invoke
(new Object[] { "Will Deutschland..." });
|
(I wrote this while presenting at a conference in Berlin, so the question is a shameless attempt to pander to the audience.) If you need to pass multiple parameters to a web service, you can use the addParameter method as many times as you need and put all of the objects you need in the array used by the invoke method. If there is a mismatch between the number or types of arguments (you use the addParameter method twice, but only pass one object in the invoke method, for example), you will get an exception.
Write clients in other languages
Everything you have done to this point has been in Java code. You have been able to do some impressive things, but one of the goals of SOAP is interoperability. To illustrate just how interoperable SOAP is, I will describe clients written in three other languages, all of which can access methods of your Java class without any problems.
To write your Perl client, you will use Pavel Kulchenko's excellent SOAP::Lite library. Listing 7 has the entire client code.
Listing 7. Perl client code
use SOAP::Lite;
print ("Perl SOAP client:\n");
print SOAP::Lite
-> uri('urn:EightBall')
-> proxy
('http://localhost:8070/axis/services/')
-> askQuestion('Will I win the lottery?')
-> result;
|
As you may expect from a scripting language, the Perl client is brief. Despite the obvious differences in syntax, this client still defines the same basic information:
- The address of the machine that is hosting the service (http://local-host:8070/axis/services/).
- The name of the service (urn:EightBall).
- The name of the method you want to invoke (askQuestion).
- The parameter for the method that you want to invoke (the string "Will I win the lottery?").
When you run this client at the command line, you get the expected results:
D:\webservices\eightball>eightballclient.pl Perl SOAP client: Signs point to yes. |
Next, I will describe a Python client. The Python client uses the SOAP.py library. Listing 8 has the entire source code.
Listing 8. Python client code
#!/usr/bin/env python import SOAP print "Python SOAP client: " server = SOAP.SOAPProxy( "http://localhost:8070/axis/services/", namespace = 'urn:EightBall', encoding = None) print " ", server.askQuestion(SOAP.stringType( 'Will I win the lottery?')); |
Again, you define the same items: the hostname, service name, and so forth. The syntax is different, but the results are the same:
D:\webservices\eightball>eightballclient.py
Python SOAP client:
Concentrate and ask again.
|
Continuing the tour of popular scripting languages that begin with the letter "P," I will examine a PHP client. The PHP client is somewhat different in that you are embedding the SOAP call inside an HTML page. When the user requests a given URL (http://localhost/eightballclient.php, in this case), the PHP libraries invoke your web service and put the results from the service inside the HTML page. Listing 9 shows the PHP source code for the page.
Listing 9. PHP client source code
<html>
<head>
<title>PHPSOAPclienttest</title>
</head>
<body>
<center>
<h1>WillDeutschland
wintheWorldCup?</h1>
<p>Theanswerfromthe
MagicEightBall:</p>
<p>
<fontsize="+3"color="blue">
<?php
require_once('nusoap.php');
$parameters=array('question'=>'Will
DeutschlandwintheWorldCup?');
$soapclient=
newsoapclient(
'http://localhost:8070/axis/services/');
echo$soapclient->call('askQuestion',
$parameters,'urn:EightBall');
?>
</font>
</p>
</center>
</body>
</html>
|
Again, you are using the same information, but you are embedding the results from the web service in an HTML page. Figure 2 shows how the page looks. This page was built with the NuSOAP PHP library.
Figure 2. The EightBall predicts through PHP

Generate a web service description
At this point, you have taken your simple piece of code and written a couple of client applications for it. If you had a Web Services Description Language (WSDL) file that described your web service, you could use tools to generate client applications. Fortunately for us, Axis automatically will generate a WSDL file from your deployed service. The URL of your web service is http://localhost:8080/axis/services/urn:EightBall; to get the WSDL description of the service, simply append ?WSDL to the URL. Listing 10 shows part of the generated WSDL file. Obviously, this file is not for human consumption; it is for development tools to use as input.
Listing 10. EightBall sample WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions...>
...
<wsdl:operation name="getAnswer">
<wsdl:input message=
"intf:getAnswerRequest"/>
<wsdl:output message=
"intf:getAnswerResponse"/>
</wsdl:operation>
...
</wsdl:portType>
<wsdl:binding
name="urn:EightBallSoapBinding"
type="intf:EightBall">
<wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/
soap/http"/>
...
<wsdl:operation name="askQuestion">
...
</wsdl:binding>
<wsdl:service name="EightBallService">
<wsdl:port binding=
"intf:urn:EightBallSoapBinding"
name="urn:EightBall">
<wsdlsoap:address
location=
"http://localhost:8080/axis/
services/urn:EightBall"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
Use the WSDL description in a client
One of the many wonderful features of SOAP::Lite is that it can use a WSDL file directly. Here is a Perl client (Listing 11) that gets all of the necessary details about the web service from the WSDL file and invokes the getAnswer method based on the information in the description.
Listing 11. Perl client WSDL
use SOAP::Lite;
print SOAP::Lite
-> service('http://localhost:8080/axis/
services/urn:EightBall?WSDL')
-> getAnswer(), "\n";
|
Although this code is not much shorter than the original version of your Perl client, it has one very important difference: it discovers the details of how the web service is implemented, and it uses those details to invoke the service. In all of your earlier clients, you hardcoded the details, such as the hostname, the service name, and so forth. For the rest of this article, I will describe a couple of approaches to web services discovery.
IBM recently donated WSIF to the Apache XML Project. WSIF is designed to isolate developers from the details of invoking a web service; with WSIF, you simply point the library at the WSDL description of the service that you want to invoke, and the framework takes it from there. Listing 12 shows the crux of the code that invokes the web service.
Listing 12. Invoke a web service with WSIF
WSIFService svc =
ServiceFactory.newInstance().
getService(wsdlLocation);
WSIFPort defPort = svc.getPort();
WSIFOperation myOp =
defPort.createOperation("askQuestion");
WSIFMessage inMsg =
myOp.createInputMessage();
WSIFMessage outMsg =
myOp.createOutputMessage();
WSIFMessage faultMsg =
myOp.createFaultMessage();
myOp.executeRequestResponse
(inMsg, outMsg, faultMsg);
System.out.println("Result:"+
outMessage.getStringPart("return"));
|
Just from looking at the code in Listing 12, you cannot tell much about the kind of service it is invoking. That is by design; WSIF shields you from having to know those details. The information about how to invoke the service comes from the WSDL file. The WSIF library discovers those details at run time. Speaking of discovery ...
There are two significant approaches to web services discovery: one is to use WS-Inspection. With WS-Inspection, you know the URL of your service provider, so you ask the service provider for a WS-Inspection document that lists all of its web services. The other approach is to use Universal Description, Discovery and Integration (UDDI). The UDDI specification defines what a registry of web services should look like. With UDDI, your client application will discover the URL of the service provider. If you want to publicize your web service, you will store its description in the registry and create a client that discovers the WSDL description in the UDDI registry and invokes the web service.
Depending on the type of service you are providing and the needs of your customers, you might use both approaches. If you want to provide services for the general public, putting them in a UDDI registry is the simplest way to do that. If you want to provide services for a few partners or customers only, using WS-Inspection is the simplest approach.
In both cases, you are using a mechanism to discover some of the information you needed when you built your client applications earlier. Here are your five elements that the client application needs to know and how they are handled with your two approaches to web services discovery:
- The name of the machine that's hosting the service. In WS-Inspection, you know the name of the machine; you go directly to it and ask for a list of its services. In UDDI, you go to the registry and discover the name of the machine.
- The name of the service. You discover this whether you are using WS-Inspection or UDDI.
- The name of the method you want to invoke. While it is technically possible to discover this, it is unlikely that you would ever want to. Yes, you can discover a method you have never heard of before and send it some data and hope for the best, but that is not a good idea.
- The parameters for the method. Even if you decided to discover the name of the method, your client application needs some prior knowledge of the kind of data you are going to pass to the web service. Tools can generate the code that passes two strings to a given service (the WSDL file tells the tool method x takes two strings as its arguments), but it is up to you to write the business logic to figure out what those two strings are.
- What the SOAPAction field should contain. You can discover this from the WSDL file. Different vendors process the SOAPAction field differently, so discovering this information is a good idea. For example, if Axis hosts the web service, the SOAPAction field is ignored completely. If it is hosted by Microsoft .NET, the SOAPAction field must be formatted in a particular way. When you are invoking a web service hosted by a SOAP toolkit that requires the SOAPAction field to be in a certain format, your client application has to know what that format is.
Of these five items, it makes sense to discover items 1, 2, and 5. You usually do not find out the method name (item 3), and discovering item 4 does not make any sense. You can discover from a WSDL file that a given web service takes two strings as its parameters, but you still have to write the business logic that figures out what those two strings should be. Picking any two strings at random (your name and credit card number, for example) and sending them to a web service you just discovered is not a good idea.
WS-Inspection is a proposed standard for web services discovery. As I discussed earlier, WS-Inspection assumes you know the name of the machine that is hosting the service. You go to that machine and ask for a list of services; you assume here that the list is in the file inspection.wsil. If the machine hosting the service is http://localhost/, you get the file http://localhost/inspection.wsil to find the list of web services hosted on that machine. Listing 13 shows a simple WSIL file that references the EightBall service.
Listing 13. A simple WS-Inspection file
<?xml version="1.0"?>
<inspection
xmlns="http://schemas.xmlsoap.org/ws
/2001/10/inspection/"
xmlns:wsilwsdl=
"http://schemas.xmlsoap.org/ws
/2001/10/inspection/wsdl/">
<service>
<abstract xml:lang="en-US">
WSDL description for the
Eight Ball service
</abstract>
<name xml:lang=
"en-US">urn:EightBall</name>
<descriptionreferencedNamespace=
"http://schemas.xmlsoap.org/wsdl/"
location=
"http://localhost/EightBall.wsdl"/>
</service>
</inspection>
|
The two most significant items in this file are the name element inside the service element (the value urn:EightBall) and the location attribute of the description element (http://localhost/EightBall.wsdl). In the WS-Inspection discovery scenario, the client application searches the WS-Inspection file for a particular service (one named urn:EightBall) and gets the URL of the WSDL document that describes the service. Given the WSDL document, it finds the remaining details of the web service and invokes it.
Setup for WS-Inspection discovery
To set up your web service so that WS-Inspection can discover it, you follow three relatively simple steps:
- Generate the WSDL file. The Axis toolkit did this for you.
- Store it as EightBall.wsdl in the document root of your web server.
- Create the WS-Inspection file. Currently, you have to do this by hand, although future versions of Axis may generate the WS-Inspection file as well. You created the inspection.wsil file and stored it in the document root of your web server.
That's it! Once you complete these steps, you can write a client that asks for a list of all web services supported by this server, select one of those services, and invoke it.
The last topic (and, not coincidentally, the most complicated) is UDDI. With UDDI, you store the description of your service in a UDDI registry. Your client searches the registry for the appropriate service, finds its details (its WSDL file), and invokes the service. If for some reason the service fails, the client could go back to the registry, find another provider of the same service, and attempt to invoke that provider's version of the service. The UDDI approach is more flexible, but it is a lot more work as well.
UDDI discovery is more complex:
- Split the WSDL file into two files. One file describes the interface to your service, while the other file describes your implementation of it. In other words, the interface WSDL file will say, "There's something called an EightBall service, and it has methods named getAnswer and askQuestion, and here are the inputs to those methods and the outputs from them, and so forth." The implementation WSDL file will say, "I implement the EightBall interface, and here are the details that you need to access my implementation of this service."
- Register the interface WSDL file as a TModel. A TModel is a UDDI construct that refers to some kind of standard. (It is an intentionally vague concept that can be confusing.) Note that the UDDI specification defines them as tModels, lowercase t, while the UDDI4J library defines a Java class named TModel, capital T. I refer to them both as TModels in this article.
- Create a BusinessEntity for your company. This represents the organization providing the web service.
- Create a BusinessService for this particular web service. Your BusinessService will reference both the BusinessEntity and the TModel; that tells anyone searching the registry that your company provides the BusinessService, and that the service supports the EightBall Interface as defined in your TModel.
I will go through each of these steps in the following sections.
Split the WSDL file into two parts
The interface WSDL file contains the type, message, and portType elements. This
defines the methods of the web service. The implementation WSDL file contains the binding,
service, and port elements, and uses an import statement to include the implementation WSDL
file. Figure 3 shows how to split the WSDL file.
Figure 3. WSDL split file structure

Register the WSDL file as a TModel
The UDDI specifications define a TModel as a reference to a standard. The definition
is completely open-ended, as those standards can be anything from a government regulation to
a vague design that you and I agreed upon over dinner last night. In this case, you are defining
the interface to the EightBall service as a standard. You will create a TModel object
in the UDDI registry and give that TModel a unique ID. When you define your implementation
of the EightBall service, you will reference the TModel to indicate that your service supports
that standard. Listing 14 shows the XML element that defines your new TModel.
Listing 14. EightBall XML TModel
<tModel tModelKey="">
<name>EightBall Interface</name>
<description xml:lang="en">
The interface-only definition
of the EightBall service.
</description>
<overviewDoc>
<description xml:lang="en">
The mystical powers of the EightBall,
channeled into Java.
</description>
<overviewURL>
http://localhost/eightball.org/
eightballinterface.wsdl
</overviewURL>
</overviewDoc>
<categoryBag>
<keyedReference
tModelKey="UUID:C1ACF26D-9..."
keyName="uddi-org:types"
keyValue="wsdlSpec"/>
</categoryBag>
</tModel>
|
The UDDI4J library puts this XML element inside a SOAP envelope and sends it to the registry; if the TModel is created successfully, you receive a TModelDetail element that describes the properties of your new TModel. The most important of these properties is the unique ID of the TModel.
Create a BusinessEntity
Next, you create a BusinessEntity to represent your service provider. Several items can
contain a BusinessEntity; Listing 15 shows the XML that your sample program uses.
Listing 15. EightBall BusinessEntity markup
<businessEntity businessKey="">
<discoveryURLs>
<discoveryURL useType="wsil">
http://localhost/inspection.wsil
</discoveryURL>
</discoveryURLs>
<name>DougCo Software</name>
<description xml:lang="en">
Sample business created for
the UDDI discovery demo.
</description>
<contacts>
<contact useType="Technical contact">
<personName>Doug Tidwell</personName>
<phone useType=
"voice">1-919-555-5583</phone>
<phone useType=
"fax">1-919-555-2389</phone>
<phone useType=
"mobile">1-919-555-9385</phone>
<email>dtidwell@us.ibm.com</email>
<address>
<addressLine>1234 Main Street
</addressLine>
<addressLine>Anytown, TX 73958
</addressLine>
</address>
</contact>
</contacts>
<categoryBag>
<keyedReference keyName=
"uddi-org:iso-ch:3166-1999"
keyValue="US-NC" tModelKey=
"UUID: 4E49A8D6-D5A2-4FC2-93A0-
0411D8D19E88"/>
<keyedReferencekeyName= "ntis-gov:naics:1997"
keyValue="541511" tModelKey=
"UUID: C0B9FE13-179F-413D-8A5B-5
004DB8E5BB2"/>
<keyedReference keyName="unspsc-org:unspsc"
keyValue="43.16.26.05.00" tModelKey=
"UUID: CD153257-086A-4237-B336-6BDCBD
CC6634"/>
</categoryBag>
</businessEntity>
|
There are several things inside your BusinessEntity. You gave your business a name and a description and defined a contact person at the company. You also defined a discovery URL that references the WSIL file for your services. The keyed references at the bottom of the markup refer to three different TModels:
- uddi-org:iso-ch:3166-1999. Represents ISO names for geographic locations (US-NC stands for North Carolina in the United States).
- ntis-gov:naics:1997. Represents the North American Industry Classification System (541511 means you are in the Custom Computer Programming Services business).
- unspsc-org:unspsc. Represents the United Nations Standard Products and Services Code, a categorization scheme for goods and services (43.16.26.05.00 means you produce Networking Developers Software).
With your BusinessEntity, you defined your business and labeled it with several categories.
Create a BusinessService
When you create your BusinessService, you will provide the appropriate details to reference
the WSDL file for your implementation. Of course, you also will reference the ID of the BusinessEntity
that you created for your company and the ID of the TModel that you created for the EightBall interface.
Listing 16 shows the markup.
Listing 16. EightBall BusinessService markup
<businessService serviceKey=""
businessKey="00EB06FC-3C45-42B8-B394-1
F7F35602B2E">
<name>EightBallservice</name>
<descriptionxml:lang="en">
ThepoweroftheMagicEightBall,
channelledthroughcode.
</description>
<bindingTemplates>
<bindingTemplatebindingKey=""
serviceKey="">
<descriptionxml:lang="en">
Tiesthisimplementationtothe
EightBallInterface.
</description>
<accessPointURLType="http">
http://localhost:8080/axis/services/
urn:EightBall
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfotModelKey=
"UUID:0B6E1227-329A-4657-89B5-
35DA36CA2554">
<descriptionxml:lang="en">
Implementationofthe
EightBallInterface.
</description>
<instanceDetails>
<overviewDoc>
<overviewURL>
http://localhost/
EightBallImplementation.wsdl
</overviewURL>
</overviewDoc>
</instanceDetails>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
</bindingTemplates>
</businessService>
|
The three crucial parts of your BusinessService definition are the reference to the business key, the TModel key, and the URL of the WSDL document that describes your implementation of the EightBall service.
To write a client application that performs discovery through UDDI, you need to search for BusinessServices that reference the appropriate TModel. When you find one, you will get the details about that service through a series of follow-up queries to the registry. Your ultimate goal is to retrieve the URL of the WSDL file; once you do that, you can use WSIF to invoke the web service from the WSDL file. The first step in your UDDI discovery code is to retrieve the TModel key from a properties file. When you created the TModel, you saved its key to a properties file. When you need to use the key, you simply retrieve its value from the file, as shown in Listing 17.
Listing 17. Retrieve key value from file
TModelBag tmb = new TModelBag();
TModelKey tmk = new TModelKey();
File testFile = new File
("eightball.properties");
if (testFile.exists()){
FileInputStream propStream =
new FileInputStream(testFile);
props.load(propStream);
propStream.close();
}
tmk.setText(props.getProperty("TModelKey");
Vector tModelKeys = newVector();
tModelKeys.add(tmk);
tmb.setTModelKeyVector(tModelKeys);
|
This creates a TModelBag structure, which is an unordered collection of TModels. You will use this structure as part of your search criteria. You will ask the UDDI registry to give you a list of all the BusinessService objects that reference all of the TModels in the TModelBag.
Assuming the UDDI registry finds a match, you will take the first one and find the binding templates associated with that service, as shown in Listing 18. You will look at the other properties of the service to find the URL of the WSDL document that describes this implementation of the web service.
Listing 18. Java code to find the EightBall WSDL URL
ServiceList sl = proxy.find_service
("", null, null, tmb, null, 0);
Vector serviceInfoVector =
sl.getServiceInfos().getServiceInfoVector();
String serviceKey =
((ServiceInfo)(serviceInfoVector.
elementAt(0)).getServiceKey();
BindingDetail bd = proxy.find_binding(null,
serviceKey, tmb, 0);
Vector bindingTemplates=
bd.getBindingTemplateVector();
BindingTemplate bt = (BindingTemplate)
bindingTemplates.elementAt(0);
AccessPoint ap = bt.getAccessPoint();
Vector tModels =
bt.getTModelInstanceDetails().
getTModelInstanceInfoVector();
TModelInstanceInfo tmii=
(TModelInstanceInfo)tModels.elementAt(0);
InstanceDetails id =
tmii.getInstanceDetails();
OverviewDoc od = id.getOverviewDoc();
OverviewURL ou = od.getOverviewURL();
|
The OverviewURL is the URL of the WSDL document that describes this implementation of the EightBall service. Once you have the URL, you invoke the service with WSIF as you did before.
In this article, you created a web service from a simple piece of code. You deployed the service, wrote clients for it in a number of different programming languages, and looked at two approaches to web service discovery. Along the way, you looked at a variety of technologies, including SOAP, WSDL, UDDI, WSIL, and WSIF. Best of all, everything this article discusses is standards-compliant code, written in Java code, and built using open-source software. Good luck using this as a springboard to your own web services applications!
- See the source code for applications in this article and additional author resources.
- Find out more about Axis.
- View the specifications for the standards used in this article:
- SOAP
- WSDL
- UDDI
- WSIL
- WSIF
- Check out this listing of all current open standards and specifications that define the web services family of protocols.
- See also the SOAP Lite library, the SOAP.py library and the NuSOAP library.
- Download the IBM alphaWorks WSTK.
Doug Tidwell is a Senior Programmer and a developer Works evangelist for new technologies. He spends most of his time writing sample code, articles, and presentations, most of which he gives away. You can find non-free samples of his work in the O'Reilly books XSLT (ISBN 0-596-00053-7) and Programming Web Services with SOAP (co-written with James Snell and Pavel Kulchenko, ISBN 0-596-00095-2). When not on an airplane or a stage, he lives in Raleigh with his wife, cooking teacher Sheri Castle, and their daughter Lily.






