When talking about Web services, there are principally two roles: the client and the server. The "client" uses the Web service while the "server" provides the Web service for use by various clients within the enterprise and beyond. This article focuses exclusively on the client side of Web services and does so through a real-world example. This example is the validation of U.S. geographical addresses via a commercially available Web service.
Nearly all enterprise business systems manage and process geographical address data. Examples include customer addresses, billing addresses, shipping addresses, delivery addresses, and more. The relevant business question is, what is the business benefit of ensuring these addresses are correct? Will correct addresses reduce shipping costs? Will correct addresses also reduce delivery costs? Furthermore, will they reduce other costs associated with redundant addresses? In 2003, one should ask oneself, with the availability of address validating Web services, why should any of the business-critical addresses of a large corporation be at risk of being incorrect?
One example of the small number of commercial addresses validating Web services available today is DOTS for Address Validation provided by ServiceObjects. ServiceObjects, an early innovator of Web services, provides this Web service as well as a number of similar utility Web services. Examples include stock quotes and company information, weather information, IP address-to-location determination, nearest ATM location identification, reverse telephone number lookup, yellow pages, e-mail address validation, shipping comparisons, package tracking, and more. The example presented will focus solely on address validation.
Identifying and Understanding the Web Service
The first step in using a Web service is identifying the Web service to use. The most important item to obtain is a reference to the Web Services Definition Language (WSDL) file. The WSDL file describes the interface, or the functional capabilities, of the Web service. In the DOTS for Address Validation example, the WSDL is located at:
http://ws2.serviceobjects.net/av/AddressValidate.asmx?WSDL
For input, this Web service takes a formatted input address and a license key through a number of parameters. Although an individual Web service can support multiple methods, this example has only one method, ValidateAddress(). Each Web service method has a number of arguments. In the ValidateAddress() example, the method takes five parameters, each of type String. These include address, city, state, postalCode, and licenseKey. An example input address is that of the author's employer, eFORCE, with corporate headquarters located at 4120 Point Eden Way, Hayward, CA, 94545. In addition to the input address, a license key is required. For this specific Web service the value "0" designates a trial license key which can be used for testing purposes. This example illustrates one way vendors are accommodating licensing of Web services as standards evolve.
For output, the Web service returns a response. In the illustrated example the response may contain an Address comprised of the fields of the validated address or an Err if any errors occurred. When passed the above address of eFORCE, the Web service responds with 4120 POINT EDEN WAY, HAYWARD, CA, 94545-3703 having validated the address and filled in the proper zip+4 value.
It is interesting to note that while WSDL is a semantic equivalent of several predecessor definition languages, such as CORBA IDL, and it is based upon XML, it is not meant for human readability. Reading and writing WSDL are best left to tools.
Setting Up the Environment
There are several different tools for building and using Web services from within BEA WebLogic Server version 7.0. This example will leverage the command-line utilities.
The first step is to set up the environment. Execute either setWLSEnv.cmd or setWLSEnv.sh depending upon your operating system. This script is located in the directory NL_HOME/server/bin where NL_HOME is the top-level directory of the WebLogic Platform installation. At this point the Web services client generation utility, clientgen, should be in the CLASSPATH. This can be confirmed with the command:
java weblogic.webservices.clientgen |
This should produce usage help for clientgen.
Generating the Proxy
The easiest way to utilize a Web service is to create a static-binding proxy. To generate such a proxy for the example address validation Web service, the following command is utilized:
java Weblogic.Webservices.clientgen \-wsdl http://ws2.serviceobjects.net/av/Address Validate.asmx?WSDL \ -clientJar av.jar \ -packageName com.serviceobjects.www |
This will produce an av.jar file that will need to be placed in the CLASSPATH.
Writing a Robust Wrapper
It is often a good practice to create wrappers for underlying third-party implementations. It is also often a good habit to test components outside the scope of the application server where possible (see Listing 1). The first class used in the wrapper is an address value object.
Listing 1
package com.eforceglobal.wldj.address;
import java.io.Serializable;
public class Address implements Serializable {
private String address = "";
private String city = "";
private String state = "";
private String zip = "";
private Address() {}
Address(String address, String city, String state, String zip) {
this.address = address;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getAddress() { return address; }
public String getCity() { return city; }
public String getState() { return state; }
public String getZip() { return zip; }
public String toString() {
return getAddress() + "\n" +
getCity() + ", " + getState() + " " + getZip();
}
}
|
In this example, the value object is immutable. The immutable pattern is leveraged so the AddressFactory must be utilized by client classes to obtain an Address object.
The AddressFactory is the class utilized by client classes to obtain a valid address. Notice that this class includes a simple main() which will be used later for simple unit testing (see Listing 2).
Listing 2
package com.eforceglobal.wldj.address;
public class AddressFactory {
private AddressFactory() { }
public static Address getAddress(String address, String city, String state,
String zip) throws InvalidAddressException, AddressValidationException {
AddressValidator validator = new DOTSAddressValidator();
return validator.validate(address, city, state, zip);
}
public static void main(String[] args) {
try {
System.out.println(AddressFactory.getAddress("4120 Point Eden Way",
"Hayward", "CA", "94545"));
} catch (InvalidAddressException e) {
System.out.println("Invalid address!");
System.out.println(e.getMessage());
} catch (AddressValidationException e) {
System.out.println("Address validation failed!");
System.out.println(e.getMessage());
}
}
}
-packageName com.serviceobjects.www
|
To obtain a validated address, client classes simply call the factory method of the AddressFactory as follows:
Address address = AddressFactory.getAddress( "4120 Point Eden Way", "Hayward", "CA", "94545"); |
The AddressFactory class uses several classes in its implementation. InvalidAddressException and AddressValidationException are application exceptions used to designate a bad input address and an internal processing error respectively. In addition to these two exception classes, the AddressFactory uses an AddressValidator interface and a DOTSAddressValidator implementation of that interface.
The AddressValidator interface represents a business interface for address validation. Underlying implementations must implement the validate() method.
package com.eforceglobal.wldj.address;
interface AddressValidator {
Address validate(String address, String city, String state,
String zip) throws InvalidAddressException,
AddressValidationException;
}
|
The core functionality that then ties the vendor-independent wrapper to the underlying DOTS implementation is the DOTSAddressValidator class. In this class, the implemented validate() method delegates to the previously generated proxy code using WebLogic's clientgen tool (see Listing 3).
Listing 3
package com.eforceglobal.wldj.address;
import java.io.IOException;
import java.rmi.RemoteException;
import com.serviceobjects.www.DOTSAddressValidate;
import com.serviceobjects.www.DOTSAddressValidate_Impl;
import com.serviceobjects.www.DOTSAddressValidateSoap;
import com.serviceobjects.www.Err;
class DOTSAddressValidator implements AddressValidator {
private static final String LICENSE_KEY = "0";
private static DOTSAddressValidate factory = null;
DOTSAddressValidator() { }
public static synchronized void init() throws IOException {
if (null == factory) {
System.setProperty("javax.xml.soap.MessageFactory",
"Weblogic.Webservices.core.soap.MessageFactoryImpl");
System.setProperty("javax.xml.rpc.ServiceFactory",
"Weblogic.Webservices.core.rp.ServiceFactoryImpl");
factory = new DOTSAddressValidate_Impl();
}
}
public Address validate(String address, String city,
String state, String zip) throws
InvalidAddressException, AddressValidationException {
if (null == factory) {
try {
init();
} catch (IOException e) {
throw new AddressValidationException(e.getMessage());
}
}
try {
DOTSAddressValidateSoap remote = factory.getDOTSAddressValidateSoap();
com.serviceobjects.www.Address dotsAddress = remote.validateAddress(
address, city, state, zip, LICENSE_KEY);
Err err = dotsAddress.getError();
if (null != err) {
throw new InvalidAddressException(err.getNumber() + ":" +
err.getLocation() + ":" + err.getDesc());
}
return new Address(dotsAddress.getAddress(), dotsAddress.getCity(),
dotsAddress.getState(), dotsAddress.getZip());
} catch (RemoteException e) {
throw new AddressValidationException(e.getMessage());
}
}
}
|
The first initialization task that the init() method performs is setting the two system properties to instruct the JAX-RPC to use the WebLogic implementation. This initialization must be done when testing or running outside WebLogic Server and only needs to be done once – on application start-up.
The second initialization task that the init() method performs is to initialize a reference to a DOTSAddressValidate. This interface is a JAX-RPC service. The generated proxy service implementation, DOTSAddressValidate_Impl, is an implementation of javax.xml.rpc.Service. The interface is a factory and is, in many ways, analogous to the home interface in EJB client-side programming. This type of initialization only needs to be done once as this factory reference can be shared across concurrent requests.
Once these two initialization tasks have been performed, the class is ready for ongoing processing. For each client request, the validate() method performs several steps.
The first step that the validate() method performs is to create a new reference to the Web service "business" interface, here DOTSAddressValidateSoap. This step is analogous to the utilization of a home interface to obtain a reference to a remote interface in EJB client-side programming. Notice that a remote is created for each user request. This enables multiple concurrent threads in this method to share the factory but each has different implementations of the business interface.
The second step that the validate() method performs is to delegate to the generated proxy code by calling the validateAddress() method. The generated code will serialize the request from Java into SOAP, post it to the server, receive the response, and deserialize it back from SOAP into Java. Notice that the programmer did not need to delve into any details of SOAP data structures or even the JAX-RPC API.
In addition to the steps described above, the validate() method maps between underlying technology-specific exceptions to business exceptions. In providing such a wrapper, the Web tier, or GUI, client programmer does not need to know whether the AddressFactory is implemented via an EJB, Web service, database call, or other approach. This illustrates the power of encapsulation being applied to Web services.
Once the response has been obtained, it is returned to the client.
Trying It Out
To compile this class, it is necessary to have both the generated proxy described above as well as WebLogic's JAX-RPC implementation in the CLASSPATH. The generated proxy is the av.jar file created above. The JAX-RPC implementation is in webserviceclient.jar and is distributed as part of WebLogic Server version 7.0. Once these two JAR files are in the CLASSPATH, the above classes can be compiled.
The example can be tested from the command line, with the command:
java com.eforceglobal.address.wldj.AddressFactory "4120 Point Eden Way" Hayward CA 94545 |
Next Steps
One enhancement that could be made to the above implementation is to support "best effort" address validation. For instance, if the Web service is down, the address is not validated.
A second enhancement that is possible is to provide support for caching. The wrapper could first check a local memory cache and then call the Web service if not present in the cache. This could save money on a Web service that charges on a per-use basis as well as provide higher performance provided the data latency is acceptable. However, care would need to be taken in making sure the implementation complies with the Web service's license.
A solution that completely abstracts the ServiceObjects implementation provides "best effort" validation as well as does caching is possible in only 200 lines of code.
The sample code presented in this article as well as solutions for the two enhancements above are available from the author via e-mail.
More Information
In addition to the capabilities of ServiceObjects, there are a number of other commercial Web services providers. Amazon.com now provides access to their product catalog via Web services. Google provides limited access to their search engine. Microsoft provides maps and driving directions. You can use the techniques described in this article to integrate with those Web services from WebLogic.
In addition to the above Web services, two excellent industry sites can be used for tracking progress now being made on Web services – SalCentral and XMethods. These sites have detailed information about Web services that can easily be reviewed and tested.
Summary
Commercial Web services are now available. Some of these services, such as address validation, are useful in nearly every enterprise application.
To use these Web services, client-side Web service programming is not difficult. With the capabilities now in WebLogic, it is possible for developers to avoid many of the details of SOAP, WSDL, and JAX-RPC. Additionally, with the application of patterns such as facade, value object, immutable, factory method, cache, smart proxy, and more, it's easy to abstract other developers from robust implementations.
Web services are now here, perhaps only a few hundred lines of code away from today's enterprise applications based upon BEA WebLogic Server.
References
BEA WebLogic Server 7.0 Web Services Documentation: http://edocs.bea.com/wls/docs70/webservices.html
ServiceObjects: http://www.serviceobjects.com
Amazon.com Web Services: http://www.amazon.com/Webservices
Google Web Services: http://www.google.com
XMethods: http://www.xmethods.org
SalCentral: http://www.salcentral.com
Copyright © 2002 SYS-CON Media, Inc.



