Now that the hype surrounding Web services has begun to subside, it's time to take a hard look at implementing this real-world technology. First, we'll analyze the basic architecture of a Web service and we'll explore a couple of Web service examples. Then we'll be ready to tackle some actual Java services. We will build a Java client, access a simple Web service, and then build a client to access a more complicated service.
Finally, we'll take a brief look at the current state of Web services and see what direction the technology and its emerging architecture seems likely to head.
Architecture of a Web Service
Three qualities set a Web service architecture apart from a comparable XML-driven Web-based application:
- published A Web service can be published to a remote registry
- locatedA Web service can be located by querying the remote registry with which it is registered
- invokedA Web service, once located, can be invoked across a network by sending an appropriate, XML-formatted request.
These characteristics are very important to understanding the architectural models that are being employed for Web services and the general motivation behind these technologies.
In a simplistic sense, Web services merely represent a further evolution in distributed computing technologies. They are a set of standardized technologies that operate on common protocols to facilitate the access of remote services in a standardized, vendor-neutral way. As these technologies mature, however, Web services will encompass additional features (security, transaction-handling, session-handling, etc.) to facilitate robust, dynamic business services.
Figure 1 displays a typical Web services architecture. XML requests are sent to the server and intercepted by a listener on the server. This listener ensures that the request is routed to the proper Web service on the server. The Web service parses the XML and fulfills the client request, probably invoking business services and perhaps even other Web services. If other business services are accessed, some type of middleware will likely be used (CORBA, EJB, JMS, DCOM, etc.). Part of the beauty of Web services is that the client need not be aware of what middleware the server uses. This decoupling allows for extensive scalability on the server without needing to make a single change on the client side of the interaction.
Web Service Examples
Now let's explore a couple of examples to put all of this in perspective. Consider an online news service that delivers news highlights and full news articles for information portals such as MSN.com, MyNetscape.com, and Yahoo!. This service has a database full of news headlines, article summaries, and full-blown articles available by subscription to other businesses (news and information portals, giant intranet home pages, wireless content providers, etc.).
Currently, establishing a dialogue between two disparate systems in an exchange is rather involved. Either the news service must create multiple interfaces to access its data (a COM object, a servlet, a pure text or XML port, etc.), or the news service selects one type of interface and potential clients must adapt to that. Either way, B2B collaboration is slowed and potential business is lost due to time spent developing and/or integrating with the partner's system.
Web services offer a dramatic solution to the current limitations of software and Web development, both from a technical standpoint, as well as a financial standpoint. By leveraging standard industry protocols and wire formats for B2B collaboration and information exchange, applications can be developed more quickly and more businesses can be sold the same product or service, unmodified. Now, any XML-capable information system could potentially access the news database. This service could be sold to hundreds of clients, without any need for customization or adaptation.
A second example is a credit card validation service. Credit card validation is a necessity for an e-commerce site or e-commerce storefront; there does not exist a standard industry mechanism for passing credit card information for validation. If a client is unhappy with the usage fee or quality of service being received, the process of changing to another provider is costly and time-consuming at best. If card validation companies exposed their validation services as Web services, a client could much more easily move to another provider because there would be a standardized protocol and wire format for invoking the validation service. The industry might even produce an XML schema document describing a standardized interface for these services to use. This promotes competition and allows a business to quickly and easily change to a provider that it believes will provide better service.
Why would the credit card validation companies choose to open themselves to competition? If the market demands a standardized mechanism in the form of Web services, someone will meet that need. When they do, others will follow. The fact is, this model could be emulated by any electronic service (loan approval, background checks, document services, shipping/delivery, etc.).
Now, let's begin to actually program two sample Web services.
A Simple Web Service
To demonstrate a simple Web service, we will create a Java client to access an existing Web service available on the Net. The service we will be accessing calculates the rate of exchange between the currencies of two countries. A SOAP message is sent to the service containing two parameters of type String representing the names of the two countries. The service then parses the SOAP envelope, extracts the parameter values, processes the request and sends a response back to the client as type double. The details for this service can be found at http://www.xmethods.com/ve2/ViewListing.po?serviceid=5.
To build this Web service client, we will use Apache Axis, which can be downloaded from Apache's Web site. Axis is a complete rearchitecture of the Apache SOAP 2.x project from the ground up. The result is a faster, more flexible, more powerful, and easier-to-use framework for designing, developing, and deploying SOAP-based Web services. To use the Apache Axis client API, you will also need a namespace-aware XML parser such as Apache Xerces.
Once you have obtained a copy of the Apache Axis distribution and a JAXP-compliant parser, then you need to make their classes available for your applications. To do this, place three JAR files into the computer's classpath environment variable: %AXIS_DIST%/lib/axis.jar, %AXIS_DIST%/lib/log4j-core.jar and xerces.jar, or crimson.jar and jaxp.jar (or whatever parser you are using).
Now you are ready to construct the client application. We begin by importing the necessary classes into our application:
import org.apache.axis.client.Service;
import org.apache.axis.client.Call;
The next step will be to declare a few constants to represent the fixed properties of the Web service that we will be accessing. With SOAP-RPC (two-way synchronous messaging), this means that we need to identify a URL where the service endpoint resides, a unique namespace URI for the method, and the method name of the service itself. So, we declare three variables to contain the properties required for locating and accessing our Web service:
private static final String ENDPOINT_URL =
"http://services.xmethods.net:80/soap";
private static final String OPERATION_NAME = "getRate";
private static final String NAMESPACE_URI =
"urn:xmethods-CurrencyExchange";
The Apache Axis client API encapsulates the construction of a SOAP envelope, marshalling of parameters, as well as the service invocation and unmarshalling of return value, all within a Call object.
Service service;
Call call;
Object[] params;
Float response;
try {
service = new Service();
call = ( Call )service.createCall();
call.setTargetEndpointAddress( new java.net.URL(ENDPOINT_URL) );
call.setOperationName( OPERATION_NAME );
call.setProperty( Call.NAMESPACE, NAMESPACE_URI );
Once we have prepared the Call object, we need to retrieve the names of the two countries that were passed in at the command line and place them within an Object array. A full list of the available countries can be found at the bottom of the service description page indicated earlier.
params = new Object[] { args[0], args[1] };
Finally, we are ready to invoke the service, passing in the Object[] and casting the result to type Float.
response = ( Float )call.invoke( params );
System.out.println( response.floatValue() );
} catch( Exception e ) {
e.printStackTrace();
}//end try/catch
At this point, you should be ready to compile the code and test it by running the application and passing in the names of two countries from the aforementioned list into the application.
A Complex Web Service
This next Web service that we will explore is more complicated on two accounts. In this example we will actually be building and deploying both the client-side and the server-side of the exchange. We will thus be building the Web service itself. Second, we will be exchanging Java objects between the client and the Web service, requiring some additional settings to handle the serialization of the objects into XML so they can be transported within the SOAP message body. The development and deployment of the application is far more interesting than the actual context of the example. The client application is a WidgetFactory that sends widgets of all colors shapes and sizes to the WidgetConsumer (the Web service).
We won't require any changes to the environment settings for this example, so we can dive right into building the Widget class (a JavaBean), the WidgetConsumer class (the Web Service), and finally the WidgetFactory class (the client). The Widget class is an arbitrary JavaBean consisting of the following properties:
- a private variable of type String called color
- a private variable of type String called shape
- a private variable of type Double called weight
- public getter and setter methods for each property
- a default constructor (no parameters)public Widget()
Next we build the Web service itself, the WidgetConsumer class. The WidgetConsumer class is even less impressive than the Widget class. It contains only two members:
- a default constructorpublic WidgetConsumer
- a single business methodpublic String receiveWidget( Widget w ). This method returns a string identifying the properties of the widget sent by the factory.
That's it? Where are all the fancy classes that need to be imported, and what about the elaborate infrastructure that a Web services exchange requires? In short, what's all the hype about?
The beauty of Web services is that they define a neutral, standards-based interface for software applications. These can be existing Web applications, legacy applications that need to be Web-enabled, standard Java classes, Perl scripts, or any other component that could benefit from exposing a language, platform, and vendor-neutral interface.
The reality is that there is a substantial amount of routing, marshalling/unmarshalling, parsing, formatting, etc. that must go on in order to enable an XML protocol such as SOAP to activate a Java application as though it were a native object, but fortunately, there are many excellent frameworks that handle all of those ugly details for you. Apache Axis is one such Web service framework.
Now on to the Java client. The Java client is significantly more complicated but is still very straightforward. We begin by importing the necessary classes and packages:
import org.apache.axis.client.*;
import org.apache.axis.encoding.*;
import org.apache.axis.utils.Options;
import javax.xml.rpc.namespace.QName;
Next, we make the class declaration and set up some variables to hold the invocation properties:
public class WidgetFactory {
private static final String ENDPOINT_URL = "local://";
private static final String NAMESPACE_URI = "urn:Widget";
private static final String SERVICE_NAME = "WidgetConsumer";
private static final String OPERATION_NAME = "receiveWidget";
The endpoint URL, local://, indicates that the service will be deployed on the Axis internal test server. In a real-world scenario, you would want to integrate Axis with a J2EE application server, but for experimentation purposes, the internal test server works great. The other interesting property value is the service name variable. The service name is a logical name that is used within the SOAP message and on the SOAP server to refer to a particular service as a logical entity that can be accessed, deployed, and undeployed.
The next thing we need is the main method for the WidgetFactory. This is the only method in our client class, and it is the starting point for the entire exchange.
if ( args.length != 3 ) {
System.err.println(
"Usage: java WidgetFactory [color] [shape] [weight]" );
System.exit(0);
}//end if
Service service;
Call call;
QName qname;
String response = null;
Widget widget;
After ensuring that the proper information has been passed in, we proceed to declare the variables that we will be using to access the WidgetConsumer service.
try {
// Build the call
service = new Service();
call = ( Call )service.createCall();
call.setTargetEndpointAddress(
new java.net.URL( ENDPOINT_URL ) );
call.setProperty( Call.NAMESPACE, SERVICE_NAME );
call.setOperationName( OPERATION_NAME );
Next we start a try/catch block and build the Call object that will be used to invoke the service. All of this so far has been almost identical to the previous client we built. The really interesting part is next:
// Set up serialization encoding for the Widget object
qname = new QName( NAMESPACE_URI, "WGT" );
call.addSerializer( Widget.class, qname,
new BeanSerializer( Widget.class ) );
call.addParameter( "arg1," new XMLType( qname ),
Call.PARAM_MODE_IN );
We begin by creating a qualified name object. Then we add a serializer to the Call object, passing in the Java class to be serialized, the fully qualified name for the XML element, and an object to actually perform the serialization. The BeanSerializer class is provided by the Axis API, and is capable of serializing Java objects of any class so long as the class adheres to the JavaBeans specification. Finally, we set up a parameter on the Call object that will later house our serialized Widget.
//Build the Widget Object
widget = new Widget();
widget.setColor( args[0] );
widget.setShape( args[1] );
widget.setWeight( Double.parseDouble( args[2] ) );
response = ( String )call.invoke( new Object[]{widget} );
} catch( Exception e ) {
e.printStackTrace();
}//end try/catch
System.out.println( response );
}//end main()
}//end class WidgetFactory
Finally, we build the Widget object, pass it as a parameter into the service call and display the response to the screen. Once you've compiled the code successfully, you're ready to embark on the deployment process.
Deploying the Service
Apache Axis provides two means of deploying Web services: a hot deploy feature through the use of the .jws extension, and a less-elegant solutionXML deployment descriptors. Because of the serialization that our Web service requires, we have no option but to use the deployment descriptors. We need two XML files: One for deploying the service (<span class="pf">deploy.wsdd</span>) and one for undeploying the service (<span class="pf">undeploy.wsdd</span>). The complete deployment descriptor for our service can be found below:
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="WidgetConsumer" provider="java:RPC">
<parameter name="className" value="WidgetConsumer"/>
<parameter name="methodName" value="receiveWidget"/>
<beanMapping qname="myNS:WGT" xmlns:myNS="urn:Widget"
languageSpecificType="java:Widget"/>
</service>
</deployment>
Rather than building the file from scratch, you can steal one from one of the sample applications that ships with Axis. If you do, then the boldface type indicates the data that should be changed. Also, it may be necessary to include the entire <beanMapping/> element because only one of the samples includes it.
The undeployment descriptor is significantly smaller. The entire file can be found below:
<undeployment xmlns="http://xml.apache.org/axis/wsdd/">
<service name="WidgetConsumer"/>
</undeployment>
It simply uses the logical name of one or more services to indicate which services to undeploy.
With those two files created, we are ready to deploy and test the service. Be sure that all three files have been compiled and that the deployment descriptors are in the same directory as the class files. To deploy the service, issue the following command:
java org.apache.axis.client.AdminClient -llocal:///AdminService deploy.wsdd
You should see a message indicating that the descriptor has been successfully processed. To undeploy, run the same command but substitute the deploy.wsdd file for the undeploy.wsdd file. Then you may start up the WidgetFactory, passing in a color, shape, and numeric weight for a widget.
And there you have it. Congratulations!
Web Services Today and Tomorrow
Web Services represent a significant evolution in the areas of distributed computing and industry collaboration. Although they are just now beginning to move from the R&D phase and into production, they have already made a substantial impact upon the way we view software architecture, B2B collaboration, and system integration.
In the next few months, you'll begin to see Web service technologies incorporated into your corporate intranet and B2B extranets as businesses begin to realize the incredible savings in time, money, and headaches that these standards can provide. In the next year or two, Web services will become increasingly prevalent and begin to take on new dimensions. Applications will begin to consider contextual factors such as weather, geography, time of year, time of day, etc. when performing services on our behalf. Likewise, on-demand, Web service-like technologies will find their way into our cellular phones, PDAs, cars, and many other corners of our lives.
 |
DevX Java Pro Kyle Gabhartis the director of the Java Technology Group for Objective Solutions, a Richardson, TX-based knowledge-transfer group specializing in Java technologies. He also serves as a senior mentor, trainer, and consultant for Objective Solutions, specializing in J2EE, XML, and Web Services technologies. Kyle can be reached at java@objectsoln.com.
|