Create Web services using Apache Axis and CastorHow to integrate Axis and Castor in a Document-style Web service client and server ![]() |
![]() | Level: Intermediate Kevin Gibbs, Software Engineer, IBM 30 Sep 2003 Recent work has pointed out the benefits of using Document-style Web services over RPC -- they're cleaner, more natural to XML, and facilitate object exchange. However, Document-style services can be less than straightforward to deploy using Axis, since Axis's data binding framework can be difficult to use, doesn't support some popular features of XML-Schema, and most importantly, lacks validation support. This article addresses those woes by providing a step-by-step tutorial which explains how to integrate Axis with the Castor data-binding framework, creating a best-of-both worlds Web service that combines the Web services prowess of Axis with the data-binding brawn of Castor. Three years ago, when Web services entered the radar of the technology community, the Remote Procedure Call (RPC) interface was the common implementation. Many people looked at SOAP and started down the familiar path of technologies like RMI, CORBA, and their own custom hacks for exposing server-side data and functions -- complex, closed systems that defined how to make and receive a request in a rigorous, yet highly encoded fashion. The RPC style of encoding Web services, by providing an automatic method of exposing and calling methods, quickly became popular. Yet RPC-style encoding is ultimately a limiting, unnatural use of its underlying technology, XML. It represents a misuse of technology -- when simple XML alone, in a Document-style service, provides all the expressibility desired. Keeping technology standards in the vein of the most natural, straightforward solutions, like Document style, is the true spirit of Web services, where interfaces are exposed, back-end and middleware systems are hidden, and dynamic discovery, binding, and endless reuse abound. This article shows how to use Castor XML binding to make Document-style Web services within an Apache Axis environment easier, cleaner, and more intuitive. This article begins with a discussion of Web service encoding methods and an explanation of why Castor and Axis together make a good solution. It provides instructions and explanations for all of the necessary steps to getting a Document-style Web service up and running -- everything from designing the schema and service to generating the service and client code. The article covers configuring Axis to use Castor and attempts to cover any "gotchas" a developer might encounter as they get their hands dirty. The two faces of Document-style encoding One challenge a developer encounters when working with Web services is that there are two invocation models: RPC and Document style. There are several good articles that address the differences of these two models in detail, but the following section will review these differences briefly to put the rest of the article in context. RPC-style encoding seems attractive because it is conceptually the same as other implementations architects and developers have worked with over the years, such as CORBA or RMI. Document-style encoding piques interest, but RPC is easy and makes demonstrating the technology straightforward, and so frequently Document style receives short shrift. However, the minute development is past the proof-of-technology point, there is an immediate need to send real, complex objects over the wire. Sending Strings and Integers, maybe even Arrays, is okay for show, but the real world uses complex data structures and models to encode data. To handle this, SOAP RPC implementations support complex object serialization and deserialization. As long as the object complies with the Java Bean specification, the object could be turned into XML and handled transparently to the developer. This was wonderfully seductive -- in a few simple lines of code, real business data objects were sailing over the wire with little concern for underlying implementation. But as it turns out, there are drawbacks to using complex objects over RPC. This approach often leads to integration issues. One implementation's serialization might not match another's deserialization, since the Java Bean to XML SOAP encoding process is ambiguous and not well-defined. Suddenly open technology comes to a screeching halt -- an Apache SOAP service had trouble working with .NET because of discrepancies in their implementations, and thus drove the need to keep services more open. Document-style Web services offer a satisfying mix of well-defined structures and interoperability. This is achieved through standard XML-Schemas for defining complex objects. In contrast to SOAP encoding, XML-Schema is a rigorous and well-understood standard for defining structures. XML-Schemas provide a great deal of flexibility in defining complex structures while ensuring all the promises of Web services -- language, platform, environment, and transport-independent with a common application programming interface. Document style, which gains all the benefits of XML-Schema, seems to be the solution to all your Web service headaches. However, Document style has some trade-offs. One of the trade-off problems the programmer is faced with is increased complexity. Suddenly, the developer has the arduous task of parsing an XML document and performing the necessary transformations to populate other data beans or method requests with the incoming data. This is true both for the server and client. For the brave of heart, this means writing a custom SAX handler. And SAX handlers aren't known for being particularly user friendly or easy to maintain. Solution in the making: Apache Axis Apache Axis is one of the most popular Web services toolkits out there. Axis supports both RPC and Document-style services, and so it would seem to be the right starting point for a Document-style service. As with any Document-style service, you still have the task of handling the incoming XML data somehow. Axis includes a handy tool to help solve this arduous task, called WSDL2Java. WSDL2Java can generate both code stubs for the client and server for your methods, and actual beans to model your data from the model defined in the XML-Schema. WSDL2Java will then populate these beans automatically from the XML. This process in general is called data binding, and it's one of the mainstays of the movement behind XML-Schema. WSDL2Java can be a little quirky, but in general it helps get things rolling. Clearly a tool like this is very useful to have, but unfortunately it is not the end-all for client stub generation -- it faces some real issues:
Another reason the development community has some mixed reactions and confusion about using Apache Axis is because it has grown with it through its beta phases. The quirky workarounds and configuration mechanisms many learned have changed from version to version. This is just a reality of a developing body of code. Initially, when Axis was first being produced, the primary focus was RPC-style Web services. As such the support for RPC services is greater, and its interfaces are more stable and well-known to developers. Until recently, Document-style documentation, examples, and sample configurations for Axis have been limited. With the advent of multiple internal configuration options for using Document style, such as wrapped, document, or message, developers have a few more decisions and complexities to go through in setting up their own Document-style service. While these configuration difficulties are not a great hurdle, by any means, working with RPC has traditionally been the quick, easy way to get a Web service off the ground. Certainly as Axis continues to develop, this will resolve itself. But for the here and now, with the explanations and step by step instructions in this article, working with Document-style services is about to get much easier. Axis and Castor: The best of both Castor is a stand-alone XML data binding package that provides a mapping from XML-Schema to Java objects. These Java objects look and feel like beans, but can be marshaled and un-marshaled to XML strings or streams, and importantly, validated against their original schema. Castor is an open-source effort that compliments the open technologies of Web services. It is backed by a very active development community, and it has in turn generated a lot of attention and Web content to help developers leverage the technology effectively. We will use Castor where Apache Axis and WSDL2Java fall short, to build a solution of best practices on all levels. We'll gain the benefits of an easy, intuitive data binding interface with a more fully supported schema implementation, while still leveraging all the Web services capabilities of the Axis framework. Figure 1 shows the relationship between Axis and Castor. Figure 1. Relationship between Axis and Castor in our solution ![]()
This article is intended for intermediate Java developers familiar with Web services and the various technologies involved in creating and deploying them. If you can write a basic WSDL and deploy a service with Axis, then you should be ready for everything in this article. Likewise, the article assumes some knowledge of XML and Schema. All the code was developed, tested and deployed using WebSphere Studio Application Developer and WebSphere Application Server versions 4.0.4 and 4.0.6. Other development and deployment environments or libraries could very well be substituted to achieve similar effect, but we'll concentrate on these environments here. Getting the latest Axis and Castor To build and run this project, you'll need Apache Axis and Exolab's Castor. Castor is available at http://www.castor.org. Instructions for download and installation are available there, which are very straightforward. Grabbing Apache Axis is a little more difficult, but not too much. At the time of this article's writing, the code needed to make Axis interoperate properly with Castor is not yet in a released beta, and is only found in CVS. By the time you read this, the functionality will very likely be in the latest released JARs. However, until it is, the best way to get the latest Axis with the org.apache.axis.ser.CastorSerializer and Deserializer code necessary for this project is to grab it from CVS or download a nightly build, both of which are available at http://ws.apache.org/axis/cvs.html. Follow the instructions there to download and build a copy of Apache Axis, which is easier than it seems, thanks to the Apache Ant build architecture. Getting Axis off the ground in WebSphere Our sample environment for this project is WebSphere Application Server V4.0.4. Getting Axis set up with WebSphere or another Java container isn't too difficult, but it's important to get the steps right. The Apache Axis documentation includes a guide for doing so (see Resources), or in If you're using WebSphere Application Studio V5.0, you may run into a "gotcha" on JAR conflicts between what's installed on WebSphere Application Developer V5 and what you are trying to use with Apache Axis. If you run into these problems, try this quick fix: in your Server Configuration for your test server, set the System Property (-D property) for "com.ibm.ws.classloader.warDelegationMode" to false in the Environment Options pane. You'll find this under Server perspective > the server you are using for our example > Environment Options > System Properties > Add....
Define the schema and the service The input that you'll need to developing your Web service is a schema to represent the data you'll be using and a service description to delineate your methods that you would like to expose. Describing how to write an XML-Schema or a WSDL is beyond the scope of this article, but documentation on how to do so abounds elsewhere. For the purpose of this article, we'll create an example service: the StockQuote service. Defining the schema for your data: StockQuote.xsd The first thing that your service needs is a description in XML-Schema of your data model. In Listing 1 is the simple data model that you'll use in your StockQuote service, which should be self-explanatory. Listing 1. Data Model for the StockQuote service form StockQuote.xsd
In addition to defining the elements and complexTypes that represent your data model, you must also define the representation of the input/output messages of your interface. These are the methods that your Web service will expose, and that you will point to in the WSDL, which you'll create in the next step, shown in Listing 2. Listing 2. Method Signatures for the StockQuote service from StockQuote.xsd
Defining the WSDL for your service: the StockQuote WSDL Next you must build a WSDL file for your service. You can do this in the standard fashion here, but with a few important notations. First, notice the bold highlight in Listing 3. In standard fashion you define the Listing 3. WSDL file for the StockQuote service
The only other thing of note here is setting up the WSDL to use Document-style encoding. Notice the bold highlight in Listing 4, which is the continuation of the WSDL. These sections are what will tell the WSDL, and correspondingly, WSDL2Java, that you are using a Document-style Web service, and that you should generate document (non-RPC) -style service requests. Listing 4. WSDL file for the StockQuote service, continued
Generate required code and stubs Now that you've got your service definition and schema prepared, you're ready to get your hands dirty with Axis and Castor. Both Axis and Castor require some code generation, and you'll be overlaying the generation of Castor's code on top of Axis's to get this "best of both worlds" solution of Axis's Web Service client and server code and Castor's data binding. This first section will show you how to generate the code, and the next section will show you how to reconfigure the generating mappings to get the interoperability you desire. Building client and service stubs using WSDL2Java WSDL2Java generates Java classes for data binding of the objects in your data model, client and server stub code for connecting to your methods, and service binding information for the Axis server. We are going to use the latter two parts, the stubs and service binding, and replace the data model code with the code generated by Castor. But to do this, we must first run WSDL2Java to generate stubs and service binding we will need. To generate the required files with WSDL2Java, run the following command: The command above will generate the files shown in Listing 5. Listing 5. WSDL2Java generated files
When you ran the command to generate the files with WSDL2Java, you provided several arguments. The
In other words, the entire
This article will use the wrapped style, as we think it's a little easier to read, and it will save you from writing a few lines of extra code. Generating a data binding with Castor Now we're ready to continue and generate the data model data binding with Castor, to replace the one that WSDL2Java created. Since Castor is not Web service-specific, it works with XSD files directly, and not with WSDL files. Hence, you pass it the StockQuote.xsd file, with the following command:
You pass Castor a few arguments on the command line. The The remaining options for Castor are specified in the castorbuilder.properties file. Most of these settings are not important, but there is one important setting: the
This setting determines how Castor generates classes from your schema. Depending on how you wrote your schema, you may want to use These two options are a little complicated to explain, but one option tends to be the natural one for each schema, depending on how your schema is defined. So, for your own schema, experiment with both methods until you come up with classes that you like. For this StockQuote.xsd example, we found that the
Configure Axis and Castor to work together It's time for the nitty-gritty: getting Axis to use Castor's generated classes instead of its own. This task is the most critical point of the article, and it shows you how to do the "tough" work of getting Axis to use Castor's classes in both the client and server code stubs. But don't worry -- with these step by step instructions, it won't be tough at all. To get all this off the ground involves changes to the Axis server stubs classes, and to the Axis-generated .wsdd file. Modifying the server stubs to use Castor classes To make the stubs use Castor classes, you simply modify the classnames in the stub files. As we have already mentioned, although frequently Castor and WSDL2Java will generate the same classnames, resulting in stubs with the same classnames, this is not always the case. Thus, it is important to check the WSDL2Java client and server stub code to make sure that they are set to return the proper classname for your Castor-generated objects. Looking at Listing 5 again, notice that two files, As it turns out, in our example, only one class name has changed: The change for the file For the file Now all references in the server code are pointing to the Castor classes. That means we're ready to modify the deploy.wsdd file to make use of these classes and the Castor configuration. Modifying and deploying the server-config.wsdd file The second task is modifying the deploy.wsdd. Let's start with an overview of that file, with the sections you'll need to modify highlighted, in Listing 6. Listing 6. Modified deploy.wsdd for StockQuote service
There are two types of changes we have to make to this file, and one "gotcha" to watch out for. All the changes are on the The second change to make is to change all of the serializers and deserializers in the There's one "gotcha" to watch out for here, and we've highlighted a place where it came up for us in the code above. The "gotcha" has to deal with how Axis and WSDL2Java deals with reading and writing XML. In our tests, we found that WSDL2Java occasionally generated invalid XML by misplacing a few '<' and '>' in the output. The Deploying the service using the deploy.wsdd file To run the deployment utility, make sure your working directory is set to the root Axis directory, in our case If the command executes successfully, no errors will be returned, and your service has been deployed. Now, simply restart your server, and you should be ready to go. Similarly, if you at some point later would like to undeploy your test service, run the command: Now that you've got all the code built, integrated Axis and Castor, and deployed the service in the .wsdd file, it's time to test everything out. Test your install out by pointing it to the endpoint you defined in the .wsdd file, in this case Figure 2. Axis running our StockQuote service ![]() If you can't get this message to display, then something went wrong in your set-up of Axis or the StockQuote service, so look over your configuration and the above steps again until everything's working as it should be. If you can get this message to display, then you're ready to write some code to make the
Everything's up and working now, but your server still doesn't do anything -- you haven't implemented the Writing the getStockQuote method Since this is a sample Web service, we're not really going to do much here. But this sample code points out just how easy it is to work with the data binding code generated by Castor and filled in by Axis. Here's our sample code for the Listing 7. Sample getStockQuote method, in StockQuoteSOAPBindingImpl.java
What's important to note is that the objects used here are the same ones used on the client, and have already been validated according to their schema by Castor, so any incoming data will be safe to use. If the incoming data doesn't validate, Castor will throw an exception before your method gets called. Similarly, you get the benefits of validation for data you return, as well -- if the data in your returned bean doesn't validate, Castor will throw an exception when Axis attempts to serialize the outgoing bean. Additionally, although our beans are pretty simple here -- just the
Now that you've got everything running with Axis and Castor on the server side, you're ready to build a client. Using the dynamic Axis Web services client and our knowledge of Castor, we might make a first attempt like that shown in Listing 8. Listing 8. First attempt at a client for the StockQuote service
This is OK, but it's not too automatic. However, with a little tweaking to the Axis-generated client stubs, we might be able to do better, and have a client that does all the set-up automatically. Modifying the client stubs to use Castor classes To make the stubs use Castor classes, simply modify the classnames in the stub files, just like in the server stubs -- although, frequently Castor and WSDL2Java will generate the same classnames, thus resulting in stubs with the same classnames. This is not always the case, so it is important to check and correct. The file that you'll need to modify is Modifying the client to use Castor serializers rather than Axis serializers Now, the crucial change: Modifying the client to use the Castor-specific serializer and deserializer classes, rather than the BeanSerializers that the WSDL2Java-generated code uses by default. In the server, we were able to modify a configuration file, The first thing you need to modify to get the client to use the Castor serializer and deserializer classes is to add them to the list of the possible serializers, created in the constructor Listing 9. Modified constructor in StockQuoteSOAPBindingStub.java
Listing 9 shows the modification to make to the constructor in bold. The change is relatively self-explanatory: it adds the The final step in updating the client is to change all the occurrences of For example, one of the changed code blocks will look like Listing 10, with the changes in bold: Listing 10. Sample modified block in StockQuoteSOAPBindingStub.java
Make this change on each remaining block in the constructor, and then you're done: the Axis generated client is now using Castor serialization and data binding. Now, you can write client code that looks like Listing 11. Listing 11. The final StockQuote client
It's safer, more automatic, and it's easy to work with. Moreover, you've now got a Web service using Axis for communication and Castor for validation and data binding, end to end.
Axis and Castor: Document-style fun for the whole family As you can see here, integrating Document-style services, Castor, and Axis isn't terribly difficult, just a bit complicated to set up. But once you're off the ground, you've got a Web service that gains all the flexibility and clarity of Document-style, the robust Web services support of Axis, and the validation and data binding prowess of Castor. Once you've got all this set up, there are a lot of other interesting directions you can go in. For instance, in just a few more lines of code, you can have your server-side Castor objects marshall themselves into an SQL database, using Castor JDO. You can also use the regular expression and validation support of Castor to clean up Web service data so that your service and client have less room for potential bugs in their data. Look for more information on these topics in future articles building on integrating Castor and Axis.
|