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. Make sure that the following jars are in your classpath before beginning: %AXIS_DIST%/lib/axis.jar; %AXIS_DIST%/lib/jaxrpc.jar; %AXIS_DIST%/lib/commons-logging.jar; %AXIS_DIST%/lib/tt-bytecode.jar and your parsers jar file.
We begin by importing the necessary classes and packages:
import org.apache.axis.client.*;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.encoding.ser.*;
import javax.xml.rpc.ParameterMode;
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.setOperationName( new QName(SERVICE_NAME, OPERATION_NAME) );
NNext 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.registerTypeMapping( Widget.class, qname,
new BeanSerializerFactory( Widget.class, qname ),
new BeanDeserializerFactory( Widget.class, qname ) );
call.addParameter( "arg1", qname, ParameterMode.PARAM_MODE_IN );
call.setReturnType( XMLType.XSD_STRING );
We begin by creating a qualified name object. Then we register a serializer and a deserializer with the Call object to handle the Widget custom data type. To do this, we invoke the registerTypeMapping() method, passing in the Java class to be serialized, the fully qualified name for the XML element, and an object to actually perform the serialization and another to handle the deserialization. The BeanSerializerFactory and BeanDeserializerFactory classes are provided by the Axis API, and are capable of serializing Java objects of any class so long as the class adheres to the JavaBeans specification. Next, we set up a parameter on the Call object that will later house our serialized Widget. Next, we specify the return type expected from the service.
//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.