A StrutsWeb Service Sample Application


A Struts/Web Service Sample Application

Now it's time to move from theory to practice. This sample application is intended to illustrate a collection of what we consider to be best practices with regard to using Struts with Web Services.

This application is developed using Tomcat as our servlet/JSP container and Apache Axis as a Web Services server. Basic configuration information is provided directly in the chapter as the need for it arises.

Because Web Service implementations differ in the specifics of how they implement the SOAP protocol, it's possible that the code you develop here might need modification before it works with any other Web Services serverfor example, a .NET implementation. On the other hand, things might work with little or no modification. One of the benefits of using Axis in the sample application is that its adoption rate is accelerating and it's likely that if you use Struts to access Web Service, that service might be Axis-based.

To demonstrate this application, you'll walk through all the steps required to build similar applications on your own.

The Application: Updating Customer Information

In this basic application, a simple form is provided to allow information about a customer to be updated. Like any Struts form, the information is populated into a form bean when it's submitted. The form bean is then passed to an Action class for processing. After processing, an ActionForward is called to indicate which JSP file to display the results with.

All this processing is very basic and should be ingrained in your memory by now. The changes related to integrating with the Web Service are isolated in the Action class. The Action class must update the customer information by calling the Web Service that manages the customer information.

Figure 19.1 shows the form used to enter the information.

Figure 19.1. The sample Web Services application /strutsWS/index.jsp .

graphics/19fig01.gif

When you submit the form, the address of the customer changes. That's all. No magic, smoke, or mirrors. The Web Service that generates this new address simply chooses randomly between three hard-coded addresses and returns one.

The purpose of this application isn't to be fancy; it's to provide a working application that demonstrates good design fundamentals that you can copy and reuse.

In reviewing this sample application, you first go over configuring the system and building the application. Following that, you look through the application code itself.

Be forewarned that although we've done our best to make the configuration and build steps in this chapter accurate, updated versions of Axis, Tomcat, or Struts might make these directions obsolete. But even if this is this case, the overall approach should still be instructive.

Installing the Sample Application File

The sample application for this chapter is provided on the companion CD-ROM for this book. It's contained in the file strutsWS.zip . To install it, simply extract it into a directory where you can work on it.

For the rest of the chapter, directories that are a part of the build process are referred to using their parameter names as defined in the Ant build.xml file that's provided with the sample application.

After the sample application is installed, you can move to the next step and begin the process of building, deploying, and running the sample application.

Note

Although most of the following steps have already been completed in the sample application, this chapter proceeds as if they haven't. This is done so that you can understand how the application was built. Please follow all the steps closely!


COMPARING BUILDING AND DEPLOYING EJBS VERSUS WEB SERVICES

It's worth a few paragraphs to discuss a bit about the complexity of building and deploying Web Services versus building and deploying EJBs.

If you read the previous chapter on Struts/EJB integration, you'll notice that configuration of the Web Service server (Axis) is much simpler than configuration of the EJB Container (even though we used JBoss, among the easiest to use EJB containers around). The overall configure/build/deploy process for Axis is simpler than using an EJB container.

Of course, this simplification is somewhat dependent on your Web Services server. In this case, you're using Axis for development of both the client and the server code. Using the same SOAP implementation for both ends of the SOAP communications minimizes a lot of potential problems. You're not required to do thisafter all, SOAP implementations over HTTP should be interchangeable. To a great extent, this actually is the case.

In addition, deploying Web Services applications with Axis is relatively easy, as you'll see when we get into the application. Using WebLogic Server, for example, you're required to implement a Web Service as a session bean in the WebLogic EJB containeran Ant task is available that takes your session bean and implements a Web Service based on it. (Of course, if you're already using WebLogic, implementing session beans is something you probably do all the time anyway, so it's not likely to be a problem for you.)

The EJB 2.1 specification (viewable at http://www.jcp.org/jsr/detail/153.jsp), makes it a requirement that every EJB container expose session beans as Web Services. After the leading EJB containers implement this standard, the effort to implement a Web Service or an EJB will be virtually the same!

Axis 1.0: A Flexible, Extensible Web Services Framework

When the first Apache Axis 1.0 release candidate was announced, the Axis team posted an email to the axis- user email list server. In it, they said that Axis

  • Is a flexible, extensible Web Services framework for Java developers

  • Has a complete implementation of Sun's JAX-RPC (Java API for XML-Based RPC) and SAAJ (SOAP with Attachments API for Java) specifications

  • Has successfully passed the JAX-RPC and SAAJ TCK (Technology Compatibility Kit) test suites

  • Is easy to use (including instant deployment, by dropping a Java source file into a Web app)

  • Supports bidirectional WSDL (Web Service Definition Language)<->Java generation, both via command-line tools and automatically in the runtime

  • Contains support for the new version of the DIME (Direct Internet Message Encapsulation) specification for attachments

  • Contains preliminary SOAP 1.2 support

Axis is freely available from its Web site (part of the Apache XML project at http://xml.apache.org/axis). A copy of Axis 1.0 is also included on the companion CD-ROM for this book.

Our sample application uses a number of features of Axis, including

  • Axis software is used as the Web Service client software for communicating from Struts to a Web Service server using the SOAP protocol.

  • Axis software is used as the Web Service server software for communicating from the Web Service server back to Struts using the SOAP protocol.

  • Axis software is used to serialize and deserialize a value object containing a series of properties. This is a step up in complexity from sending individual properties via SOAP. This additional complexity is added to make the application more useful and to leverage the value object design introduced in the previous chapter and continued here.

  • The Axis WSDL2Java tool is used to assist in generating all the classes required to take advantage of Axis. This tool makes using Axis simpler by automating a number of important steps for you.

The Web Services Server: Installing the Axis Server

In a Web Services application, there are both client and server components . In the sample application, both of these are based on Axis. The client portion of the sample application is the Struts application. The server portion of the application is based on Axis as well and is deployed in Tomcat as a separate Web application.

As a result, you have two Web applications: the Struts application acting as the Web Service client and the Axis server application.

The 1.0 release of Axis is included on the companion CD-ROM for this book. Begin the installation process by unzipping it into a convenient directory. When that's complete, the first major step is to install the Axis server application (without the sample application code to begin with) and validate that it's working correctly. Following are the steps to accomplish this first step:

  • Under the main Axis installation directory ( xml-axis-10 ) is a webapps subdirectory. In this subdirectory is the axis subdirectory. Copy the axis subdirectory (including all files and directories beneath it) into your Tomcat webapps directory. This installs a clean copy of the Axis server code. At this point, you might need to start (or restart) Tomcat.

  • Now open a browser and point it to http://localhost:8080/axis/index.html . If you don't see a simple HTML page with the phrase, "Hello! Welcome to Apache-Axis.", check to make sure that Tomcat is running and that you have the correct host and port number.

  • When you can see a page at http://localhost:8080 /axis/index.html , click on the link that says "Validate the local installation's configuration." This executes the JSP page http://localhost:8080/axis/happyaxis.jsp or what's known as the Happy Axis page. The Happy Axis page looks through your environment to determine whether you have all the required libraries.

Listing 19.1 presents the output from the Happy Axis page on my system at the time of this writing. (Note: All the formatting has been removed, but you can still look at the text to see how I have my system configured.)

If you have any problems making the Happy Axis page happy, check the locations of each library ( .jar ) file in Listing 19.1. Of course, you don't have to configure your system identicallythe only goal is to make the Happy Axis page happy!

Listing 19.1 Output from the Happy Axis Page
[View full width]
 Examining webapp configuration Needed Components Found SAAJ API (javax.xml.soap.SOAPMessage) at C:\dev\tomcat\webapps\axis\WEB-INF\lib\ graphics/ccc.gif saaj.jar Found JAX-RPC API (javax.xml.rpc.Service) at C:\dev\tomcat\webapps\axis\WEB-INF\lib\ graphics/ccc.gif jaxrpc.jar Found Apache-Axis (org.apache.axis.transport.http.AxisServlet) at C:\dev\tomcat\webapps\ graphics/ccc.gif axis\WEB-INF\lib\axis.jar Found Jakarta-Commons Discovery (org.apache.commons.discovery.Resource) at C:\dev\tomcat\ graphics/ccc.gif webapps\axis\WEB-INF\lib\commons-discovery.jar Found Jakarta-Commons Logging (org.apache.commons.logging.Log) at C:\dev\tomcat\webapps\ graphics/ccc.gif axis\WEB-INF\lib\commons-logging.jar Found IBM's WSDL4Java (com.ibm.wsdl.factory.WSDLFactoryImpl) at C:\dev\tomcat\webapps\ graphics/ccc.gif axis\WEB-INF\lib\wsdl4j.jar Found JAXP implementation (javax.xml.parsers.SAXParserFactory) at C:\dev\tomcat\common\ graphics/ccc.gif endorsed\xmlParserAPIs.jar Found Activation API (javax.activation.DataHandler) at C:\dev\tomcat\common\lib\ graphics/ccc.gif activation.jar Optional Components Found Mail API (javax.mail.internet.MimeMessage) at C:\dev\tomcat\common\lib\mail.jar Warning: could not find class org.apache.xml.security.Init from file xmlsec.jar XML Security is not supported See http://xml.apache.org/security/ The core axis libraries are present. 1 optional axis library is missing Note: On Tomcat 4.x, you may need to put libraries that contain java.* or javax.* packages into CATALINA_HOME/commons/lib Note: Even if everything this page probes for is present, there is no guarantee your web service will work, because there are many configuration options that we do not check for. These tests are necessary but not sufficient Examining Application Server Servlet version 2.3 XML Parser org.apache.xerces.jaxp.SAXParserImpl Examining System Properties java.runtime.name=Java(TM) 2 Runtime Environment, Standard Edition sun.boot.library.path=C:\jdk1.3.1_01\jre\bin java.vm.version=1.3.1_01 java.vm.vendor=Sun Microsystems Inc. java.vendor.url=http://java.sun.com/ path.separator=; java.vm.name=Java HotSpot(TM) Client VM file.encoding.pkg=sun.io java.vm.specification.name=Java Virtual Machine Specification user.dir=C:\dev\tomcat\bin java.runtime.version=1.3.1_01 java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment java.endorsed.dirs=c:\dev\tomcat\bin;c:\dev\tomcat\common\endorsed os.arch=x86 java.io.tmpdir=c:\dev\tomcat\temp line.separator= java.vm.specification.vendor=Sun Microsystems Inc. java.awt.fonts= java.naming.factory.url.pkgs=org.apache.naming os.name=Windows 2000 java.library.path=C:\jdk1.3.1_01\bin;.;C:\WINDOWS\System32;C:\WINDOWS;c:\mysql\bin;C:\ graphics/ccc.gif jdk1.3.1_01\bin;c:\dev\ant\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\ graphics/ccc.gif emacs-20.7\bin;c:\dev\cygwin\bin;c:\dev\maven\bin java.specification.name=Java Platform API Specification java.class.version=47.0 os.version=5.1 user.home=C:\Documents and Settings\a user.timezone=America/New_York catalina.useNaming=true java.awt.printerjob=sun.awt.windows.WPrinterJob file.encoding=Cp1252 java.specification.version=1.3 catalina.home=c:\dev\tomcat user.name=a java.class.path=C:\jdk1.3.1_01\lib\tools.jar;c:\dev\tomcat\bin\bootstrap.jar java.naming.factory.initial=org.apache.naming.java.javaURLContextFactory java.vm.specification.version=1.0 java.home=C:\jdk1.3.1_01\jre java.specification.vendor=Sun Microsystems Inc. user.language=en awt.toolkit=sun.awt.windows.WToolkit java.vm.info=mixed mode java.version=1.3.1_01 java.ext.dirs=C:\jdk1.3.1_01\jre\lib\ext sun.boot.class.path=C:\jdk1.3.1_01\jre\lib\rt.jar;C:\jdk1.3.1_01\jre\lib\i18n.jar;C:\jdk1. graphics/ccc.gif 3.1_01\jre\lib\sunrsasign.jar;C:\jdk1.3.1_01\jre\classes java.vendor=Sun Microsystems Inc. catalina.base=c:\dev\tomcat file.separator=\ java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport.cgi sun.cpu.endian=little sun.io.unicode.encoding=UnicodeLittle user.region=US sun.cpu.isalist=pentium i486 i386 Platform: Apache Tomcat/4.1.12 

Don't worry if your output is different than in the preceding listing. Many of the properties should be different because file locations on your machine will likely be different than mine. By default, the basic installation has all the library files you need for the sample application, so you should be ready to proceed.

You might have noticed in this listing that one of the optional libraries ( xmlsec.jar ) wasn't found. That's fineit isn't required for the sample application.

After your Happy Axis page is happy, your Web Services server is configured correctly and you're ready to move on to the next step : configuring and building the client application.

Configuring Axis in the Build Environment

Now that you've validated the basic Axis installation, it's time to build and deploy the sample Struts application. This isn't complex, but it takes a number of steps. All these steps are described in the following sections.

Copy the Axis Web Application into the Build Environment

The first step in preparing the build environment is to copy the axis Web application you just validated into the sample application build directory (this has actually already been done for you in the strutsWS application).

This step is required because a part of the sample application includes the business logic that lives in the Axis server. After all, there's not much use invoking a Web Service unless it actually does something!

To accomplish this, copy the axis Web application into the sample application main directory (into the directory strutsWS/axis ).

Configure the Build Parameters in the build.properties File

A build script ( build.xml ) has been provided for use with the Jakarta Ant build tool to build the application. The build.xml file is located in the top directory of the sample application. In addition, a build.properties file is also provided that has two properties that must be configured for your environment:

  • tomcat.home This is the top directory of your Tomcat installation. It's required for the build process to know where to deploy the application to.

  • webapps.home This points to the webapps directory that's directly beneath the tomcat.home . The build script copies .war files into this directory to deploy them.

After these two properties are set for your system, save the file and exit. Your environment is now set. The next steps have to do with using Axis to define and build a Web Service application.

The Web Services Client: Struts and Axis Integrated

Here's a review of the overall application and how you're going to integrate Axis code with Struts:

  • The application itself has a single data entry form as its only page. On this form there are six fields relating to a customer record: Id, Name, Address, City, State, and Zip.

  • When the form is posted, these values are posted to a form bean. The form bean is passed into the Action class so that the input can be processed .

  • The first important design element relating to this design is a value object, CustomerValueObject.java . Both the form bean ( CustomerForm.java ) and the Action class ( CustomerAction.java ) have been written to take advantage of the value object. For example, the form bean has two methods , getValueObject and setValueObject , that allow it to work with data a bean at a time. (Because these files are virtually identical to the same files presented in the previous chapter, they aren't reviewed again here.)

  • The Action class communicates with the Web Service in Axis using a facade class ( CustomerWSFacade.java ). Details of interacting with the Web Service are hidden inside this facade class. The Action class simply passes in a value object and receives an update value object back from the Web Service.

Note that this design is virtually identical to the design in the previous chapter. Because of the design modularity, all that had to be done to change the application from using an EJB server to using a Web Service for back-end processing was to replace the facade class.

Details of the facade class are provided later in the chapter after the code needed to interact with the Web Service has been explained.

Java2WSDL and WSDL2Java: Automating Axis Code Development

The Axis developers provide two invaluable utilities to help you build Web Services applications: Java2WSDL and WSDL2Java . You'll see how valuable they are in the next few sections as we go through the steps to build the sample application.

WSDL stands for Web Services Description Language and is used to provide a program language-independent description of a Web Service. WSDL is written using XML and provides information about the data types, operations, and access locations that make up the Web Service. This chapter doesn't provide detailed information about WSDSLyou can refer to one of the many good books available on SOAP and Web Services for that. However, the chapter presents the WSDL for the service you're building and provides an overview of it that should get you started.

At a high level, the process that you follow for building your Web Service code is

  • Create some basic Java classes and use them to generate WSDL using Java2WSDL .

  • Use WSDL2Java to create the Web Service client software. This code is integrated into Struts in a later step.

  • After that, use WSDL2Java again to generate the Web Service server classes that will be deployed in the Axis server.

  • After the code is all generated, the next step is to compile everything and then deploy the server code into Axis. The step will finish by validating that the web service is installed and working.

  • Finally, after the Web Service communications code is generated, compiled, deployed and working, the final step is to integrate the code into the Struts application.

Now let's dive in and start building the application!

Step 1: Generate the WSDL for the Application

Note

It's important to note that some steps in development of this sample application involve generating code automatically using the Java2WSDL and WSDL2Java utilities that are a part of Axis. These utilities are destructive in that every time they run, they overwrite all files they generate. For this reason, you should be very careful each time you run them that any files you want to keep are backed up in a safe location!


In this step, you use the Axis Java2WSDL utility to generate WSDL for the application. To do so, you must first create some Java for it to use to generate the WSDL.

The Java you must create consists of two classes. The first is the value object you're using. Java2WSDL determines the properties of the value object and generates the WSDL to allow it to be transferred using the Web Service. Remember that the Web Service server you're communicating with need not be written in Java. The WSDL generated here must carry all the information about the data being sent so that program at the other end can understand how to deserialize it.

The second class you must create is a simple, Java interface file. This interface file (similar to the remote interface for an EJB) defines the business methods that the Web Service exposes.

CustomerWSValueObject.java is the value object form bean used in this sample application. It's virtually identical to the value object from the previous chapter except for the class name and package. Its source code is shown in Listing 19.2.

Listing 19.2 The CustomerWSValueObject.java Source Listing
 package struts.ch19.customer; /**  * Example Value Object for showing integration of Struts with Web Services  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerWSValueObject {   public CustomerWSValueObject () {       this.name     = "";       this.custId   = "";       this.address  = "";       this.city     = "";       this.state    = "";       this.zip      = "";   }   public CustomerWSValueObject (String name,                                 String custId,                                 String address,                                 String city,                                 String state,                                 String zip                                ) {       this.name = name;       this.custId = custId;       this.address = address;       this.city = city;       this.state = state;       this.zip = zip;    }    private String name;    public String getName() { return this.name; }    public void setName(String nameus) { this.name = name; }    private String custId;    public String getCustId() { return this.custId; }    public void setCustId(String custId) { this.custId = custId; }    private String address;    public String getAddress() { return this.address; }    public void setAddress(String address) { this.address = address; }    private String city;    public String getCity() { return this.city; }    public void setCity(String city) { this.city = city; }    private String state;    public String getState() { return this.state; }    public void setState(String state) { this.state = state; }    private String zip;    public String getZip() { return this.zip; }    public void setZip(String zip) { this.zip = zip; } } 

As you can see, this is a basic value object for use in storing information about a customer.

The Java interface used in the sample application exposes only a single business method that's used to process address changes. The CustomerWS.java class is the Java interface used with this application to assist in generating the Web Service communications files. Its source code is shown in Listing 19.3.

Listing 19.3 The CustomerWS.java Source Listing
 package struts.ch19.customer; import struts.ch19.customer.CustomerWSValueObject; /**  * Interface describing a web service to process customer information changes  **/ public interface CustomerWS {     public CustomerWSValueObject addressChange( CustomerWSValueObject cvo ); } 

As you can see in the listing, the addressChange method accepts and returns a CustomerWSValueObject . This is how the Web Service is defined to allow passing data a bean at a time instead of a property at a time. After you run Java2WSDL next, you'll be able to see how this is defined in the WSDL.

To simplify the running of the Java2WSDL utility, Listing 19.4 provides a batch program that configures the environment and runs Java2WSDL. For details about all the options for Java2WSDL and WSDL2Java , see the Reference Guide included in the Axis documentation.

Note

Prior to running java2WSDL , the two Java files used here must be compiled. To simplify this, a command file compile.cmd has been provided. This file is provided as a convenience and isn't discussed further. Any method of compiling these files will do as long as

  • The class files are output in the same directory as would be used by the compile.cmd file. For this example, the output directory is required to be the same as the source directory. When you're done compiling, you should have both .java and .class files in the source directory.

  • The files must be compiled with the debug option on. This compiles symbols into the .class files that Java2WSDL uses to create the WSDL and an Axis-friendly value object.


Listing 19.4 The StrutsCh19Step1.cmd File for Generating WSDL
[View full width]
 @ECHO OFF set JAVA_HOME=C:\jdk1.3.1_01 set JAVAC=%JAVA_HOME%\bin\javac.exe set JAVA=%JAVA_HOME%\bin\java.exe set AXIS_LIBS=.\axis\WEB-INF\lib set XERCES=.\lib\xerces.jar set AXIS_SRC=.\src REM Build out the classpath set LCP=.;%JAVA_HOME%\lib\tools.jar REM Add Axis jars to the classpath set LCP=%LCP%;%AXIS_LIBS%\axis.jar set LCP=%LCP%;%AXIS_LIBS%\axis-ant.jar set LCP=%LCP%;%AXIS_LIBS%\commons-discovery.jar set LCP=%LCP%;%AXIS_LIBS%\commons-logging.jar set LCP=%LCP%;%AXIS_LIBS%\jaxrpc.jar set LCP=%LCP%;%AXIS_LIBS%\log4j-1.2.4.jar set LCP=%LCP%;%AXIS_LIBS%\saaj.jar set LCP=%LCP%;%AXIS_LIBS%\wsdl4j.jar REM Xerces Parser set LCP=%LCP%;%XERCES% REM Add the app class files to the classpath set LCP=%LCP%;%AXIS_SRC% %JAVA% -cp %LCP% org.apache.axis.wsdl.Java2WSDL -o %AXIS_SRC%\struts\ch19\customer\ graphics/ccc.gif customer.wsdl -l"http://localhost:8080/axis/services/Customer" -n "urn:Customer" graphics/ccc.gif -p"struts.ch19.customer" "urn:Customer" struts.ch19.customer.CustomerWS 

Note

This file is located in the strutsWS main directory (along with all the other command files for this chapter!). To run it, simply change to that directory and type its name.

Prior to running this file, you must edit it and modify the JAVA_HOME parameter to point to the JDK you're using.


Running this file batch file generates a WSDL file named customer.wsdl in the strutsWS/src/struts/ch19/customer directory. This file is shown in Listing 19.5.

Listing 19.5 The customer.wsdl WSDL File
[View full width]
 <?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="urn:Customer" xmlns="http://schemas.xmlsoap.org/wsdl/" graphics/ccc.gif xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="urn:Customer" xmlns: graphics/ccc.gif intf="urn:Customer" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns: graphics/ccc.gif wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/ graphics/ccc.gif soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  <wsdl:types>   <schema targetNamespace="urn:Customer" xmlns="http://www.w3.org/2001/XMLSchema">    <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>  <complexType name="CustomerWSValueObject">   <sequence>   <element name="address" nillable="true" type="xsd:string"/>   <element name="name" nillable="true" type="xsd:string"/>   <element name="zip" nillable="true" type="xsd:string"/>   <element name="custId" nillable="true" type="xsd:string"/>   <element name="state" nillable="true" type="xsd:string"/>   <element name="city" nillable="true" type="xsd:string"/>   </sequence>   </complexType>  <element name="CustomerWSValueObject" nillable="true" type="impl: graphics/ccc.gif CustomerWSValueObject"/>   </schema>  </wsdl:types>  <wsdl:message name="addressChangeResponse">   <wsdl:part name="addressChangeReturn" type="intf:CustomerWSValueObject"/>   </wsdl:message>   <wsdl:message name="addressChangeRequest">   <wsdl:part name="in0" type="intf:CustomerWSValueObject"/>   </wsdl:message>   <wsdl:portType name="CustomerWS">   <wsdl:operation name="addressChange" parameterOrder="in0">   <wsdl:input message="intf:addressChangeRequest" name="addressChangeRequest"/>   <wsdl:output message="intf:addressChangeResponse" name="addressChangeResponse"/>   </wsdl:operation>   </wsdl:portType>  <wsdl:binding name="CustomerSoapBinding" type="intf:CustomerWS">       <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>       <wsdl:operation name="addressChange">          <wsdlsoap:operation soapAction=""/>          <wsdl:input name="addressChangeRequest">             <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" graphics/ccc.gif namespace="urn:Customer" use="encoded"/>          </wsdl:input>          <wsdl:output name="addressChangeResponse">             <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" graphics/ccc.gif namespace="urn:Customer" use="encoded"/>          </wsdl:output>       </wsdl:operation>    </wsdl:binding>    <wsdl:service name="CustomerWSService">       <wsdl:port binding="intf:CustomerSoapBinding" name="Customer">          <wsdlsoap:address location="http://localhost:8080/axis/services/Customer"/>       </wsdl:port>    </wsdl:service> </wsdl:definitions> 

If you're not used to reading WSDL, don't worry about it for now. Two important parts are highlighted in bold . These pieces are repeated in XML fragments below as they're explained.

Here's the first XML fragment, which defines the value object:

  <complexType name="CustomerWSValueObject">   <sequence>   <element name="address" nillable="true" type="xsd:string"/>   <element name="name" nillable="true" type="xsd:string"/>   <element name="zip" nillable="true" type="xsd:string"/>   <element name="custId" nillable="true" type="xsd:string"/>   <element name="state" nillable="true" type="xsd:string"/>   <element name="city" nillable="true" type="xsd:string"/>   </sequence>   </complexType>  

As you can see, the WSDL created provides a language-independent description of the CustomerWSValueObject . Any other Web Services client could read this and understand how to deserialize the data, which is the key to Web Services interoperability, after all.

The next fragment to review is where the WSDL defines the operations that the Web Service supports:

  <wsdl:message name="addressChangeResponse">   <wsdl:part name="addressChangeReturn" type="intf:CustomerWSValueObject"/>   </wsdl:message>   <wsdl:message name="addressChangeRequest">   <wsdl:part name="in0" type="intf:CustomerWSValueObject"/>   </wsdl:message>   <wsdl:portType name="CustomerWS">   <wsdl:operation name="addressChange" parameterOrder="in0">   <wsdl:input message="intf:addressChangeRequest"   name="addressChangeRequest"/>   <wsdl:output message="intf:addressChangeResponse" name="addressChangeResponse"/>   </wsdl:operation>   </wsdl:portType>  

This fragment identifies two types of elements: Two message elements and a single portType element. Although it might seem out of order, let's discuss the portType element first.

A portType defines a collection of operation elements. The portType defined here is named CustomerWS after the CustomerWS.java file that Java2WSDL was run against. Within the portType element are defined one or more operation elements that specify the operations that the Web Service supports. In this case, there's only a single operation, named addressChange . It also specifies that addressChange takes a single input parameter, in0 . You'll see in a minute that in0 is a CustomerWSValueObject .

The message elements in this fragment provide definitions that are needed by the operation element that we just discussed. You can look at the fragment to see how this works: The message elements are defined and then the operation elements refer to them. In this case, two message elements define the input and output (or request and response) required by the addressChange operation. If you look at the part elements enclosed by each of the message elements, you'll see that the input and return parameters (named in0 and addressChangeReturn , respectively) are both of type CustomerWSValueObject .

To be honest, this is really all you need to know about WSDL for now. You can read the rest of the WSDL if you want, but it's not required to move forward with the sample application. The main thing to take away from this is to remember that WSDL provides an XML description of the Web Service that's language and platform independent.

Step 2: Generate the Web Service Client Code

The next step is to create the Java class files that are eventually incorporated into Struts to allow it to communicate with the Web Service server. Fortunately, Axis provides another utility to help with this.

The Axis WSDL2Java utility performs a transformation opposite to the one performed in the previous step 1. WSDL2Java takes the WSDL that was just created and uses it to generate a series of Java class files that can be used to communicate with the Web Service that the WSDL describes. (In addition to generating Java class files for use in communicating to a Web Service server, WSDL2Java can also generate the Java class files needed by the server, but that's skipping ahead to step 3!)

Another batch program is provided to assist you in running step 2. Listing 19.6 presents StrutsCh19Step2.cmd , a batch file that configures the environment that executes WSDL2Java to generate the code that runs in the Web Service client. For details about all the options for Java2WSDL and WSDL2Java , see the Reference Guide included in the Axis documentation.

Listing 19.6 StrutsCh19Step2.cmd Creates Java Classes for Accessing the Web Service Server
[View full width]
 @ECHO OFF set JAVA_HOME=C:\jdk1.3.1_01 set JAVAC=%JAVA_HOME%\bin\javac.exe set JAVA=%JAVA_HOME%\bin\java.exe set AXIS_LIBS=.\axis\WEB-INF\lib set XERCES=.\lib\xerces.jar set AXIS_SRC=.\src REM Build out the classpath set LCP=.;%JAVA_HOME%\lib\tools.jar REM Add Axis jars to the classpath set LCP=%LCP%;%AXIS_LIBS%\axis.jar set LCP=%LCP%;%AXIS_LIBS%\axis-ant.jar set LCP=%LCP%;%AXIS_LIBS%\commons-discovery.jar set LCP=%LCP%;%AXIS_LIBS%\commons-logging.jar set LCP=%LCP%;%AXIS_LIBS%\jaxrpc.jar set LCP=%LCP%;%AXIS_LIBS%\log4j-1.2.4.jar set LCP=%LCP%;%AXIS_LIBS%\saaj.jar set LCP=%LCP%;%AXIS_LIBS%\wsdl4j.jar REM Xerces Parser set LCP=%LCP%;%XERCES% REM Add the app class files to the classpath set LCP=%LCP%;%AXIS_SRC% %JAVA% -cp %LCP% org.apache.axis.wsdl.WSDL2Java -o %AXIS_SRC% -p struts.ch19.customer. graphics/ccc.gif client customer.wsdl 

Note

This file is located in the strutsWS main directory (along with all the other command files for this chapter!). To run it, simply change to that directory and type its name.

Prior to running this file, you must edit it and modify the JAVA_HOME parameter to point to the JDK you're using.


Running this file causes a number of Java source files to be created. Notice that the last command in the file specifies a package ( struts.ch19.customer.client ) that's different from the one in the initial CustomerWS.java and CustomerWSValueObject.java files (in case you don't remember, that was struts.ch19.customer ). This is to make it easy to keep the files required for the Web Service client separate from all the other files. It also makes your code more organized and easier to understand.

Following is a list of these files with a short description of what each is used for:

  • CustomerWSValueObject.java A new version of the value object defined earlier. WSDL2Java creates its own version of this file that is a little more formal and predictable for the Axis code to use.

  • CustomerWS.java A new version of the CustomerWS.java Java interface that you began with. WSDL2Java creates its own version of this file as well.

  • CustomerWSServiceLocator.java This generated Java file is the one you use to get a handle to the actual Web Service. This class is the one used in the Struts application to locate the Web Service and allow you to communicate with it.

  • CustomerWSService.java An interface that extends the JAX-RPC Service interface ( javax.xml.rpc.Service ). According to Sun's Javadoc, the service interface "acts as a factory of the following: Dynamic proxy for the target service endpoint." In other words, you can use this as a factory class to create a JAX-RPC service object. Use of this class is beyond the scope of this example.

  • CustomerSoapBindingStub.java Handles some of the guts of the actual SOAP communications. Use of this class is beyond the scope of this example.

This step in the process was just to generate these files. Actually using them comes later, in step 5, when you integrate them into the sample Struts application.

Step 3: Generate the Web Service Server Code

Now that you've created the Web Service client code, it's time to generate the corresponding code to run on the Web Service server.

Why do you need to do this? Why can't the server just know how to accept all the data that you send it? The answer is really in two parts.

The first part of the answer goes back to the earlier discussion about serialization/deserialization and knowing what data types to expect. This is one of the fundamental issues with Web Services: The code running on the server has to know what kinds of arguments to accept and how to deserialize them into meaningful data again after it receives them. So, the first reason to generate the server code is to handle communication of the data.

The second reason is because the Web Service server code must provide you with a place to tie in to so that you can create a Web Service that actually does something. It also must connect the code where you tie in your business logic back to the portType s and operation s that the Web Service exposes to the client code.

Now that you have a basic idea of what must be done, here's how to do it. Listing 19.7 presents StrutsCh19Step3.cmd , a batch file that configures the environment that executes the WSDL2Java to generate the code that runs on your Web Service server. For details about all the options for Java2WSDL and WSDL2Java , see the Reference Guide included in the Axis documentation.

Listing 19.7 StrutsCh19Step3.cmd Creates Java Classes for Use by the Web Service Server
[View full width]
 @ECHO OFF set JAVA_HOME=C:\jdk1.3.1_01 set JAVAC=%JAVA_HOME%\bin\javac.exe set JAVA=%JAVA_HOME%\bin\java.exe set AXIS_LIBS=.\axis\WEB-INF\lib set XERCES=.\lib\xerces.jar set AXIS_SRC=.\src REM Build out the classpath set LCP=.;%JAVA_HOME%\lib\tools.jar REM Add Axis jars to the classpath set LCP=%LCP%;%AXIS_LIBS%\axis.jar set LCP=%LCP%;%AXIS_LIBS%\axis-ant.jar set LCP=%LCP%;%AXIS_LIBS%\commons-discovery.jar set LCP=%LCP%;%AXIS_LIBS%\commons-logging.jar set LCP=%LCP%;%AXIS_LIBS%\jaxrpc.jar set LCP=%LCP%;%AXIS_LIBS%\log4j-1.2.4.jar set LCP=%LCP%;%AXIS_LIBS%\saaj.jar set LCP=%LCP%;%AXIS_LIBS%\wsdl4j.jar REM Xerces Parser set LCP=%LCP%;%XERCES% REM Add the app class files to the classpath set LCP=%LCP%;%AXIS_SRC% %JAVA% -cp %LCP% org.apache.axis.wsdl.WSDL2Java -o %AXIS_SRC% -s -S true -p struts.ch19. graphics/ccc.gif customer.server customer.wsdl 

Note

This file is located in the strutsWS main directory (along with all the other command files for this chapter). To run it, simply change to that directory and type its name.

Prior to running this file, you must edit it and modify the JAVA_HOME parameter to point to the JDK you're using.


Notice that the package (denoted by the -p option) is struts.ch19.customer.server . This makes it easier to isolate the files generated for the server.

Looking at the generated files, you can see that most of them are duplicates of the files that were generated for the client. Some of the files aren't used by the server, and some are. But it's best to deploy all the files with the server code just to be sure. The following are the four new files:

  • CustomerSoapBindingSkeleton.java This file coordinates low-level SOAP communications and is beyond the scope of this example.

  • CustomerSoapBindingImpl.java This is where you implement the Web Service server functionality. It's an empty implementation of the CustomerWS.java interface we started with. In this case, it contains a single method, addressChange , that you use to implement business logic to implement the address change function. You'll implement this functionality in step 5.

  • deploy.wsdd The Web Service Deployment Descriptor (hence, the .wsdd extension). Web Service files in Axis need deployment descriptors just as EJBs do in an EJB container.

  • undeploy .wsdd An undeploy descriptor. Use this when you need to remove a Web Service from Axis.

This step in the process was just to generate these files.

Step 4: Compile, Deploy, and Test the Web Service Server Code

Now that you've generated all the files, the next step is to compile everything and make sure that you can deploy the files into your Axis server and get them to run correctly.

Step 4 doesn't involve incorporating your Struts code or any code in the server that implements business logic. To begin, first make sure that you can deploy and access the Web Service. After that's done, you can move on to integrating your Struts code in Step 5.

To accomplish this build process, a build.xml Ant script has been provided. Assuming that you edited the build.properties file earlier in the chapter, you should be able to go ahead and just run the build process now.

To run the build, simply type ant deploy . You should see Ant compiling all the files and deploying two .war files strutsWS.war and axis.war into your Tomcat webapps directory.

The output from the build process is presented in Listing 19.8.

Listing 19.8 Output from a Successful Ant Build Process
 C:\dev\apps\strutsWS>ant deploy Buildfile: build.xml prepare:      [echo] Tomcat Home = c:/dev/tomcat      [echo] webapps Home = c:\dev\tomcat\webapps compile:     [javac] Compiling 13 source files to C:\dev\apps\strutsWS\object      [copy] Copying 13 files to C:\dev\apps\strutsWS\src build:      [copy] Copying 13 files to C:\dev\apps\strutsWS\build\WEB-INF\classes      [copy] Copying 8 files to C:\dev\apps\strutsWS\build\WEB-INF\lib       [jar] Building jar: C:\dev\apps\strutsWS\deploy\strutsWS.war      [copy] Copying 20 files to C:\dev\apps\strutsWS\axis\WEB-INF\classes       [jar] Building jar: C:\dev\apps\strutsWS\deploy\axis.war deploy:      [copy] Copying 1 file to C:\dev\tomcat\webapps      [copy] Copying 1 file to C:\dev\tomcat\webapps BUILD SUCCESSFUL Total time: 27 seconds 

Note

The build.xml file is located in the strutsWS main directory (along with all the other command files for this chapter). To run it, simply change to that directory and type ant deploy .

Prior to running this file, you must edit the build.properties file and update it with the paths for your Tomcat home directory and your Tomcat webapps directory.

The output of the build process is two .war files ( axis.war and strutsWS.war ) that are copied into your Tomcat webapps directory.

Note that after the build process is complete, you might need to restart Tomcat to get the new .war files to deploy and run. In fact, some users have had to stop Tomcat, delete the existing axis and strutsWS subdirectories in their Tomcat webapps directory, and then restart to get the updated .war files to deploy.


After the build is complete, Tomcat is running, and the .war files have been successfully deployed in Tomcat, you're ready for the last major piece in step 4: deploying the files into the Axis server.

Deploying the Web Service is the process of registering it with the Web Service server and providing the details of how it should be accessed and how to process the data associated with it. When you ran WSDL2Java the last time, a deployment descriptor was generated and named deploy.wsdd . This file, which is located in the strutsWS/src/struts/ch19/customer/server directory, is presented in Listing 19.9.

Listing 19.9 deploy.wsdd The Web Service Deployment Descriptor for the CustomerWS Web Service
[View full width]
 !-- Use this file to deploy some handlers/chains and services      --> <!-- Two ways to do this:                                           --> <!--   java org.apache.axis.client.AdminClient deploy.wsdd          --> <!--      after the axis server is running                          --> <!-- or                                                             --> <!--   java org.apache.axis.utils.Admin clientserver deploy.wsdd   --> <!--      from the same directory that the Axis engine runs         --> <deployment     xmlns="http://xml.apache.org/axis/wsdd/"     xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">   <!-- Services from CustomerWSService WSDL service -->   <service name="Customer" provider="java:RPC">       <parameter name="wsdlTargetNamespace" value="urn:Customer"/>       <parameter name="wsdlServiceElement" value="CustomerWSService"/>       <parameter name="wsdlServicePort" value="Customer"/>       <parameter name="className" value="struts.ch19.customer.server. graphics/ccc.gif CustomerSoapBindingSkeleton"/>       <parameter name="wsdlPortType" value="CustomerWS"/>       <parameter name="allowedMethods" value="*"/>       <typeMapping         xmlns:ns="urn:Customer"         qname="ns:CustomerWSValueObject"         type="java:struts.ch19.customer.server.CustomerWSValueObject"         serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"         deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"         encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"       />   </service> </deployment> 

As you can see, the Web Service deployment descriptor provides the Web Service server with the details for how to expose the Web Service, including how it's to be addressed by the client and how the data is to be serialized and deserialized.

Note the serializer and deserializer attributes to the typeMapping element. These attributes reference the BeanSerializerFactory and BeanDeserializerFactory classes. These are classes that are Axis-specific and make it easy to send simple JavaBeans as parameters across an Axis-based Web Service. This support for serializing/deserializing JavaBeans is built into Axis and is one of the reasons for its popularity. They might not be recognizable by other Web service implementations, however, so they should be used with caution.

To perform the deployment, a command file is provided. This file is named deployServer.cmd and it's shown in Listing 19.10.

Listing 19.10 deployServer.cmd A Command File to Deploy the CustomerWS Web Service
 @ECHO OFF set JAVA_HOME=C:\jdk1.3.1_01 set JAVAC=%JAVA_HOME%\bin\javac.exe set JAVA=%JAVA_HOME%\bin\java.exe set AXIS_LIBS=.\axis\WEB-INF\lib set XERCES=.\lib\xerces.jar set AXIS_SRC=.\src REM Build out the classpath set LCP=.;%JAVA_HOME%\lib\tools.jar REM Add Axis jars to the classpath set LCP=%LCP%;%AXIS_LIBS%\axis.jar set LCP=%LCP%;%AXIS_LIBS%\axis-ant.jar set LCP=%LCP%;%AXIS_LIBS%\commons-discovery.jar set LCP=%LCP%;%AXIS_LIBS%\commons-logging.jar set LCP=%LCP%;%AXIS_LIBS%\jaxrpc.jar set LCP=%LCP%;%AXIS_LIBS%\log4j-1.2.4.jar set LCP=%LCP%;%AXIS_LIBS%\saaj.jar set LCP=%LCP%;%AXIS_LIBS%\wsdl4j.jar REM Xerces Parser set LCP=%LCP%;%XERCES% REM Add the app class files to the classpath set LCP=%LCP%;%AXIS_SRC% set ADMIN=org.apache.axis.client.AdminClient %JAVA% -classpath %LCP% %ADMIN% %AXIS_SRC%\struts\ch19\customer\server\deploy.wsdd 

Note

The deployServer.cmd file is located in the strutsWS main directory (along with all the other command files for this chapter). To run it, simply change to that directory and type deployServer.cmd .

Prior to running this file, you must make sure that Tomcat is running and that the axis and strutsWS.war files have been properly deployed.


You'll know the deployment went correctly if you see the results:

 C:\dev\apps\strutsWS>deployserver - Processing file .\src\struts\ch19\customer\server\deploy.wsdd - <Admin>Done processing</Admin> 

After the Web Service is deployed, you should verify that the Web Service server shows it to be available. With Axis, this is a simple task. Just point your browser to

 http://localhost:8080/axis/index.html 

This is the same main page you used earlier to access the Happy Axis! page. This time, choose the option to "View the list of deployed Web services." In it, you should see your deployed Customer service. This means the deployment worked correctly. Clicking on the "wsdl" link brings back the WSDL for the Web Service.

After you've validated that the Customer Web Service is correctly deployed to Axis, you're ready to move to the final step!

Note

If you don't see the service deployed, make sure that the axis.war file that the build process generated is loaded correctly. You might have to stop Tomcat, manually remove the axis directory from Tomcat's webapps directory, and then restart Tomcat.


Step 5: Integrating with Struts and Building the Business Logic on the Server

If the steps so far seemed complicated, they're really not that bad. After you get into development and go through this cycle a few times, the process goes much smoother. Almost everything you've done so far is the one-time setup and learning that enables you to get up and running.

So, now that you've deployed and tested your Web Service server code, it's time for the last step: Actually integrating the code into your Struts application.

There are two parts to performing the integration: integrating the Axis code with your Struts classes for the Web Service client code and adding business logic to the Web Services server classes. Let's start by integrating the Web Services code with the Struts code.

The design of the Struts sample application makes it easy to complete the integration. Here's a quick overview:

  • The user enters her data and clicks the submit button.

  • After the form is submitted, the data is populated into the form bean.

  • The form bean is passed to the Action class.

  • The Action class extracts the data from the form bean in the form of a value object (using the class struts.ch19.customer.CustomerValueObject ).

  • The Action class sends the value object to the Web Service using a facade class. The facade class returns an updated value object back to the Action class from the Web Service.

  • All the code to communicate with the Axis server is hidden inside the facade class.

This design is identical to the design used to communicate with the JBoss EJB server in the previous chapter. In fact, virtually the only changes (other than changing the Java package names and a few of the actual Java class file names) are modifications to the facade class. Everything else is identical.

Speaking of changing Java package names, all the Struts code is in the package struts.ch19.customer.struts . This is parallel to the Axis client code in struts.ch19.customer.client and the Axis server code in struts.ch19.customer.server .

Before jumping into the facade class, it's good to review the Action class so that you can understand how the facade is used. Listing 19.11 presents the file CustomerAction.java file.

Listing 19.11 CustomerAction.java The Action Class for the Sample Struts/Axis Application
 package struts.ch19.customer.struts; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import struts.ch19.customer.struts.CustomerValueObject; /**  * Action class to demonstrate EJB Integration with Struts  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerAction extends Action {           /**      * Update the customer's address.      *      * @param mapping The ActionMapping from this struts-config.xml entry      * @param actionForm The ActionForm to process, if any      * @param request The JSP request object      * @param response The JSP response object      *      * @exception Exception if business logic throws an exception      */     public ActionForward execute(ActionMapping mapping,                                  ActionForm form,                                  HttpServletRequest request,                                  HttpServletResponse response)         throws Exception {         /*          * Cast the form bean to CustomerForm          */         CustomerForm cf = (CustomerForm) form;         /*          * Instantiate the facade - it hides the details          * for us and simplifies the Action class.          */         CustomerWSFacade facade = new CustomerWSFacade();         /*          * The Value Object allows us to pass data "a bean at a time".          * This increases speed, reduces maintenance and simplifies          * tha Action class as well. Add methods to the form bean to          * allow it to work with Value Objects too.          */         CustomerValueObject cvo = cf.getValueObject();         try {           /*            * Pass in a value object, and get one in return            */           cvo = facade.addressChange( cvo );         } catch (CustomerUpdateException cue) {           //  In a real application, this exception would be handled better.           //  return (mapping.findForward("CustUpdateError"));             cue.printStackTrace();         }         /*          * Update the form bean with all values returned at once.          */         cf.setValueObject(cvo);         return (mapping.findForward("default"));    } } 

The critical things here to note are the use of the facade class and the value object. This approach simplifies the design of the Action class a great deal.

Also notice that the value object used in the Action class is the Axis-independent version. It's identical to the original value object we used with Java2WSDL to create the original WSDL way back in step 1. This was done to make sure that the value object was independent of Axis in case we want to switch to a new architecture later. (Don't worry, you'll see the WSDL2Java -generated value object later when we review the facade class.)

Now that you've reviewed the Action class, it's time to review the facade class and understand exactly how the Struts/Axis integration works. The facade class, CustomerWSFacade.java , is presented in Listing 19.12.

Listing 19.12 CustomerWSFacade.java The Facade That Actually Performs the Struts/Axis Integration
 package struts.ch19.customer.struts; import java.util.Properties; import struts.ch19.customer.client.CustomerWS; import struts.ch19.customer.client.CustomerWSServiceLocator; import struts.ch19.customer.client.CustomerWSValueObject; /**  * Example facade which hides interactions with the back-end web service  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerWSFacade {   public CustomerWSFacade () { }   /**    * This method sends the value object to the Web Service    * for it to process. It's goal is to hide the implementation    * details of interacting with the Web Service.<p>    *    * Note that this method captures any Web Service related Exceptions    * and rethrows them as "application-specific" Exception    * (CustomerUpdateException). This helps to "decouple" the    * application from the web service backend.    *    * @param cvo_in    * @return CustomerValueObject    * @throws CustomerUpdateException    */   public CustomerValueObject addressChange( CustomerValueObject cvo_in )   throws CustomerUpdateException   {       try {           // Use the web service locator created by WSDL2Java           CustomerWSServiceLocator servLoc = new CustomerWSServiceLocator();           CustomerWS custService = servLoc.getCustomer();           // Create a WS value object - WS val obj is created by WSDL2Java           CustomerWSValueObject wsCustVO = new CustomerWSValueObject();           wsCustVO.setName(cvo_in.getName());           wsCustVO.setCustId(cvo_in.getCustId());           wsCustVO.setAddress(cvo_in.getAddress());           wsCustVO.setCity(cvo_in.getCity());           wsCustVO.setState(cvo_in.getState());            wsCustVO.setZip(cvo_in.getZip());           wsCustVO = custService.addressChange(wsCustVO);           cvo_in.setName(wsCustVO.getName());           cvo_in.setCustId(wsCustVO.getCustId());           cvo_in.setAddress(wsCustVO.getAddress());           cvo_in.setCity(wsCustVO.getCity());           cvo_in.setState(wsCustVO.getState());           cvo_in.setZip(wsCustVO.getZip());           return cvo_in;       } catch( Exception e ){         throw new CustomerUpdateException(e.toString());       }    } } 

To summarize how the facade works, it follows this path:

  • It first creates a service locator using the WSDL2Java -generated class CustomerWSServiceLocator , and then uses it to locate the CustomerWS Web Service.

  • After it's obtained the CustomerWS interface to the Web Service, it creates a value object for use with the Web Service and copies properties into it from the value object it received from the Action class. (The reason there are two value objects is explained later.)

  • It then calls the Web Service and invokes the addressChange operation. It passes a value object to the Web Service and receives an updated one in return.

  • The updated value object it received from the Web Service has its properties copied into the value object that will be returned to the Action class.

  • If any exceptions are caught, they're rethrown as the application-specific exception CustomerUpdateException . This ensures that the exception processing in the Action class doesn't have to handle any Web Service-specific exceptions. In this way the Action class doesn't know it's talking to a Web Serviceit only knows about the facade!

The fact that two kinds of value objects are used warrants a bit further explanation. The two types of value objects are struts.ch19.customer.struts.CustomerValueObject and struts.ch19.customer.client.CustomerWSValueObject . These two objects are actually very similar, but there's a good reason for having two. The reason for using two similar value objects is that doing so allows the Struts application to be completely isolated from the Axis code. This ensures that as the Axis code is modified or completely removed, little or no maintenance is required in the Action class or anywhere else in the Struts application.

This is borne out by looking at the changes to the application from Chapter 18 to now. Because the application uses a facade class and a value object that's independent of the back-end system, the only thing that had to be done when the application changed from using JBoss to using Axis as its back-end server was to change the facade. Even the value object didn't have to changeit's still the same as it was previously.

The final thing we must do to finish the integration between Struts and the Axis Web Service is to tie in the business logic on the Web Service server. This is straightforward as well.

When the Axis WSDL2Java utility generated the Web Service server code back in step 3, it created an empty class for you to put your business logic in. The class's name is CustomerSoapBindingImpl.java .

To demonstrate this, we'll use a before-and-after approach. First you'll be shown the file just as the WSDL2Java utility created it, and then you'll be shown the same file with the business logic code inserted.

Listing 19.13 contains the CustomerSoapBindingImpl.java file just as it was generated by the WSDL2Java utility.

Listing 19.13 CustomerSoapBindingImpl.java as It Was Created by WSDL2Java
[View full width]
 /**  * CustomerSoapBindingImpl.java  *  * This file was auto-generated from WSDL  * by the Apache Axis WSDL2Java emitter.  */ package struts.ch19.customer.server; public class CustomerSoapBindingImpl implements struts.ch19.customer.server.CustomerWS{     public struts.ch19.customer.server.CustomerWSValueObject addressChange(struts.ch19.customer.server.CustomerWSValueObject in0) throws java.rmi. graphics/ccc.gif RemoteException {         return null;     } } 

As you can see, there's not much there. It's just a stub class that returns a null . Listing 19.14 presents the same file after the business logic for this application has been added.

Listing 19.14 CustomerSoapBindingImpl.java with Business Logic Added
[View full width]
 /**  * CustomerSoapBindingImpl.java  *  * This file was auto-generated from WSDL  * by the Apache Axis WSDL2Java emitter.  *  * Following being automatically created by the Apache Axis WSDL2Java emitter,  * it was modified with the actual web service business logic required.  */ package struts.ch19.customer.server; import java.util.Random; public class CustomerSoapBindingImpl implements struts.ch19.customer.server.CustomerWS {     public struts.ch19.customer.server.CustomerWSValueObject addressChange(struts.ch19. graphics/ccc.gif customer.server.CustomerWSValueObject in0) throws java.rmi.RemoteException {       Random r = new Random();       switch (r.nextInt(3)) {           case 0:             in0.setAddress("Downing Street");             break;           case 1:             in0.setAddress("Via Del Museo De Prado");             break;           case 2:             in0.setAddress("The Outback Road");             break;       }       /*        * This allows watching activity in the Tomcat window (if one exists)        */       System.out.println( "CustomerSoapBindingImpl.addressChange() called." +                           " Set address to: " + in0.getAddress() );       return in0;     } } 

You can see that some simple logic was added to Listing 19.14 just to send back a random address and print a message to the standard output when the method was called. In practice, you'd more likely build the business logic in some other class and have this class simply parse the input parameters and then call other classes that would perform the actual work.

After you have modified this class with the preceding lines, re-run the Ant build process to deploy the new code to Tomcat. You might have to restart Tomcat again before testing the application.

After this final step is complete, you should be able to point your browser to the address http://localhost:8080/strutsWS/Customer.do to test the final application.

Note

If you run into problems at this point, it may be useful to try the following:


  • Verify that the Web Service is deployed by pointing your browser to http://localhost:8080/axis/index.html and viewing the installed services.

  • Try stopping Tomcat, deleting one or both of the axis and strutsWS subdirectories in the Tomcat webapps directory, and then restarting Tomcat.



Struts Kick Start
Struts Kick Start
ISBN: 0672324725
EAN: 2147483647
Year: 2002
Pages: 177

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net