3.2 Test Drive

The primary focus of this book is not on creating your own web services?there are numerous other texts that describe how to do this (including several from O'Reilly)?but on using existing web services in productive and useful ways. However, it is helpful to understand how to create a web service because it illustrates some of the complexities when consuming a web service. Here, we will create both a client and a server for a simple "Hello World" web service using Tomcat and Axis. In addition to the learning value, this exercise tests out the tools mentioned, ensuring you're ready for the more complex examples in the rest of the book.

First, copy the contents of the axis-1_1\webapps\axis directory to your Tomcat webapps directory, as shown in Figure 3-3.

Figure 3-3. Copying the Axis sample web application
figs/rww_0303.gif


After copying the files, launch Tomcat. After Tomcat launches, point your browser at http://localhost:8080/axis/. You should see the status message shown in Figure 3-4.

Figure 3-4. Axis opening page
figs/rww_0304.gif


Clicking the link to "Validate" your installation, you will find that you are missing three needed components, activation.jar, mail.jar, and xmlsec.jar.

The first, activation.jar, is required to get Axis off the launchpad. The second, mail.jar, is required for examples later in this book that use the SMTP. Links to these libraries (available from Sun Microsystems) are displayed directly in the error message.

As of this writing, the latest versions of the required components are Java Activation Framework 1.0.2 and JavaMail 1.3.1.


These two libraries are used by many web applications; you are best off placing them in the Tomcat shared/lib directory, as shown in Figure 3-5.

Figure 3-5. Installing the required Axis libraries
figs/rww_0305.gif


You must restart Tomcat to load the libraries. After restarting, reload the URL, http://localhost:8080/axis/happyaxis.jsp, to verify the needed libraries are now successfully loaded (as shown in Figure 3-6).

Figure 3-6. Axis validation page
figs/rww_0306.gif


The third optional library, xmlsec.jar (XML Security), digitally signs XML documents?a potentially useful capability but (sadly) one that is not supported by any of the web service offerings covered in this text.

3.2.1 Our First SOAP Server

Let's create a simple SOAP-based web service using Axis.

Axis supports a method of web services generation similar to the way you create a web page using a JSP: an ordinary Java source file, when given the .jws extension and placed in an Axis-enabled web application, is automatically converted into a Java web service (hence the .jws extension).

Create a file called CurrentDate.jws in the Axis webapp directory. The results should be as shown in Figure 3-7.

Figure 3-7. Creating CurrentDate.jws
figs/rww_0307.gif


The contents of CurrentDate.jws are shown in Example 3-1. As you can see, a .jws file is essentially an ordinary Java source file. Axis automatically compiles and wraps this file as a web service when requested by an appropriate client (such as a web browser or a SOAP client). The dynamic compilation process is similar to that provided by JSP; you can modify the JWS file, switch to your browser, hit refresh, and the file is automatically recompiled and made available as a web service?no need for manual compilation or a complex deployment process.

Example 3-1. Current date web service
public class CurrentDate

{

    public String now( )

    {

        return new java.util.Date( ).toString( );

    }



    public int add(int a, int b)

    {

        return a + b;

    }

}

Public classes and methods in .jws files wrapped and exposed by Axis are automatically made available to inquiring web services. Pointing our web browser at http://localhost:8080/axis/CurrentDate.jws, notice that Axis is aware of the *.jws file at this location, as shown in Figure 3-8.

Figure 3-8. Viewing CurrentDate.jws with a browser
figs/rww_0308.gif


The *.jws file isn't actually compiled until you request either the service or the WSDL file. Compilation errors are reported in the web browser, similar to JSP. Clicking on the link for the WSDL shows the automatically generated WSDL file, as seen in Figure 3-9.

Figure 3-9. Viewing CurrentDate.jws WSDL
figs/rww_0309.gif


The automatically generated WSDL file shows that the CurrentDate.jws has been compiled and is now available for use by clients.

3.2.2 Dynamic SOAP Client

By a dynamic SOAP client, I mean that the information about the web service is constructed at runtime, as opposed to being precompiled. This runtime construction of a web service request allows for very rapid development when initially connecting to a web service: simply cut and paste the relevant elements, such as the URL and the SOAP method names into your code. Example 3-2 shows a simple dynamic SOAP example, which accesses the SOAP Server as described earlier.

Example 3-2. Dynamic SOAP client
package com.cascadetg.ch03;



// axis.jar

import org.apache.axis.client.Call;

import org.apache.axis.client.Service;



// jaxrpc.jar

import javax.xml.namespace.QName;



public class DynamicCurrentDateClient

{



    public static void main(String[] args)

    {



        try

        {

            // Create a Axis client

            Service service = new Service( );



            // Create a method call object

            Call call = (Call)service.createCall( );



            // Point to the web service

            call.setTargetEndpointAddress(

                new java.net.URL(

                    "http://localhost:8080/axis/CurrentDate.jws"));



            // First, let's call the now method

            call.setOperationName(new QName("now"));



            System.out.print(

                "According to the web service, it is now ");

            System.out.println((String)call.invoke(new Object[0]));



            // Let's reuse the same method object, but this time,

            // call the add method

            call.setOperationName("add");



            // Create the parameters to pass to the add method.

            // Notice that we are creating Integer objects, which

            // are automatically bound to the xsd:int data type

            Object[] params = { new Integer(3), new Integer(4)};



            // Now, we call the add method, passing in the two

            // integers.

            System.out.print("The result of 3+4 is ");

            System.out.println((Integer)call.invoke(params));

        } catch (Exception e)

        {

            System.err.println(e.toString( ));

        }

    }

}

Unfortunately, as you can see in Example 3-2, the code contains a lot of bookkeeping (in particular, potentially dangerous casting). Also, there is no way for an IDE to assist with the development of dynamic client code; remote method names are passed as hardcoded strings. Most programming languages support accessing SOAP-based web services in this fashion.

3.2.3 Static SOAP Client

A static SOAP client uses a provided WSDL to generate a set of corresponding Java classes. This makes it easier to build and maintain an application, allowing for stricter type information and less bookkeeping. It also makes it easier for an IDE to assist you in your development (e.g., providing automatic code completion).

To generate client-side Java objects that can access the remote SOAP service we just created, run the Axis WSDL2Java tool as shown in Example 3-3.

Example 3-3. Axis client stub generation
C:\devenv\axis-1_1\lib>java -classpath commons-logging.jar;

    log4j-1.2.8.jar;wsdl4j.jar;axis.jar;commons-discovery.jar;

    jaxrpc.jar;saaj.jar 

  org.apache.axis.wsdl.WSDL2Java -p localhost 

    http://localhost:8080/axis/CurrentDate.jws?wsdl

The command shown in Example 3-3 should be entered as a single line, and if successful there will be no visible output (make sure Tomcat is running before running this command). Four files are generated as shown in Figure 3-10.

Figure 3-10. Generated client stub files
figs/rww_0310.gif


You'll want to copy the localhost directory to your source directory. Make sure you don't manually make changes to these source files; they are machine-generated, and they should be regenerated using WSDL2Java if changes to the WSDL are made (you may wish to automate this process using an Ant task: see http://ws.apache.org/axis/java/ant/axis-wsdl2java.html).

Example 3-4 shows a simple static client that uses the bindings generated by WSDL2Java. Notice the lack of casting and the natural passing of parameters to the add( ) method. The flow of the code is straightforward and common to other Axis-generated bindings used in this book; a Locator object is used to retrieve a Service. Methods called on the returned Service actually make requests to the remote service. Consider the underlying work being performed by the myDate.add(3, 4) line in the example shown: the parameters are bundled, transformed into a SOAP message, and sent; and the result is retrieved, converted back into a Java object, and returned.

Example 3-4. Static Axis client
package com.cascadetg.ch03;

import localhost.*;



public class StaticCurrentDateClient

{



    public static void main(String[] args)

    {

        CurrentDateService myService =

            new CurrentDateServiceLocator( );

        try

        {

            CurrentDate myDate = myService.getCurrentDate( );



            System.out.print(

                "According to the web service, it is now ");



            System.out.println(myDate.now( ));



            System.out.print("The result of 3+4 is ");

            System.out.println(myDate.add(3, 4));



        } catch (Exception e)

        {

            e.printStackTrace( );

        }



    }

}

In the remainder of this book, we access SOAP-based web services using the static method because it provides for a safer, easier method of access.

3.2.4 When WSDL Changes

You may be wondering how applications using statically generated bindings are affected when changes occur to the WSDL. Generally speaking, they work as you would expect; removing methods causes problems for applications that uses the methods, changes to signatures cause the problems you would expect, and new methods aren't called if not needed.

Applications can respond to WSDL changes much more effectively if they use generated bindings. Consider a WSDL file that adds a method: if the new method is needed, a new set of binding can instantly be generated. If a WSDL removes a method or changes a signature when the bindings are regenerated, the now-broken usage of that method appears as a compilation error. If dynamic bindings are used, there is no way to know that a failure was waiting until runtime, and finding out there is a problem on a production server is far worse than a compile-time error.