Developing Web Service Applications

You can publish as Web service operations the public methods of any class that contains a no-argument constructor. Also, methods that correspond to document-style operations, must return an org.w3c.dom.Document . You can find documentation for these classes at http://xml.apache.org/axis , and http://www.w3.org respectively.

This chapter contains the following sections:

Providing a Web Service

As a companion to this document, in projects/Calculator , you find the Calculator project. It's a simple WebObjects application project used to build an application that serves a Web service called Calculator. The service provides four operations: add , subtract , multiply , and divide . The operations take two parameters of type double and return a value of type double . The Calculator.java class, the workhorse of the Calculator Web service, is listed in Listing 3-1 .

Listing 3-1   Calculator.java class in Calculator project

public class Calculator extends Object {
 
    public static double add(double addend1, double addend2) {
        double sum = addend1 + addend2;
        return sum;
    }
 
    public static double subtract(double minuend, double subtrahend) {
        double difference = minuend - subtrahend;
        return difference;
    }
 
    public static double multiply(double multiplicand1, double multiplicand2) {
        double product = multiplicand1 * multiplicand2;
        return product;
    }
 
    public static double divide(double dividend, double divisor) {
        double quotient = dividend / divisor;
        return quotient;
    }
}

To provide a Web service based on Calculator.java the Application object registers Calculator.java as a Web service with the following method invocation:

WOWebServiceRegistrar.registerWebService(Calculator.class, true);

The WOWebServiceRegistrar class (com.webobjects.webservices.appserver ) provides methods to register and unregister classes as Web services, set a security delegate, register XSLT (Extensible Stylesheet Language Transformations) scripts for operations, and so on.

To become a Web service provider, build and run the Calculator application. To view the WSDL document for the Web service, point your Web browser to http://localhost:4210/WebObjects/Calculator.woa/ws/Calculator?wsdl .

Consuming a Web Service

The companion project Calculator_Client, located in project/Calculator_Client , contains the source files used to create the Calculator_Client application, which consumes the Calculator Web service described in Providing a Web Service . Its main class is CalculatorClient.java , shown in Listing 3-2 .

Listing 3-2   CalculatorClient.java class in Calculator_Client project

import java.net.*;
import java.util.Enumeration;
 
import com.webobjects.foundation.*;
import com.webobjects.webservices.client.*;
 
public class CalculatorClient extends Object {
 
    /**
     * Object through which the Web service's operations are invoked.
     */
    private WOWebServiceClient _serviceClient = null;
 
    /**
     * Address for the Web service's WSDL document.
     */
    private String _service_address =
        "http://localhost:4210/cgi-bin/WebObjects/Calculator.woa/ws/Calculator?wsdl";
 
    /**
     */
    public CalculatorClient() {
        super();
    }
 
    /**
     * Obtains the Web service's operation names.
     * @return the Web service's operation names.
     */
    public NSArray operations() {
        NSArray operations = (serviceClient().operationsDictionaryForService(serviceName())).allValues();
        NSMutableArray operation_names = new NSMutableArray();
        Enumeration operations_enumerator = operations.objectEnumerator();
        while (operations_enumerator.hasMoreElements()) {
            WOClientOperation operation = (WOClientOperation)operations_enumerator.nextElement();
            operation_names.addObject((String)operation.name());
        }
        return operation_names;
    }
 
    /**
     * Invokes the Web service's operations.
     * @param operation      operation to invoke;
     * @param arguments      argument list;
     * @return               value returned by the operation.
     */
    public Double invoke(String operation, Object[] arguments) {// 1
        Object result = serviceClient().invoke(serviceName(), operation, arguments);
        return (Double)result;
    }
 
    /**
     * Obtains the Web service name.
     * Normally one WSDL file describes one Web service,
     * but it could describe one or more services.
     * @return Web service name.
     */
    public String serviceName() {// 2
        return (String)serviceClient().serviceNames().objectAtIndex(0);
    }
 
    /**
     * Obtains an WOWebServiceClient through which service operations are invoked.
     * @return Web service–client object.
     */
    private WOWebServiceClient serviceClient() {
        if (_serviceClient == null) {
            _serviceClient = clientFromAddress(_service_address);
        }
        return _serviceClient;
    }
 
    /**
     * Obtains a Web service–client object through which
     * service operations can be invoked.
     * @return Web service–client object.
     */
    private static WOWebServiceClient clientFromAddress(String address) {
        WOWebServiceClient service_client = null;
 
        // Create the Web service's URL.
        URL url;
        try {
            url = new URL(address);
        }
        catch (MalformedURLException e) {
            url = null;
        }
 
        // Get a service-client object.
        service_client = new WOWebServiceClient(url);// 3
 
        return service_client;
    }
}

The following list highlights some aspects of CalculatorClient.java .

  1. The invoke method defines as parameters the operation name and its arguments. It uses the invoke method of WOWebServiceClient to invoke the Web service operation.

  2. The serviceName method returns the name of the first Web service in the list of Web services defined by the WSDL document used to create the WOWebServiceClient object. Most WSDL documents define one Web service, but a WSDL document can define more than one Web service.

  3. The WOWebServiceClient class (com.webobjects.webservices.client ) provides a one-argument constructor that takes a URL (java.net ) object that points to a WSDL document. Therefore, to create a WOWebServiceClient you must create a URL object from the URL (Uniform Resource Locator) of the appropriate WSDL document.

Build and run the Calculator_Client application. Your Web browser should show a page like the one shown in Figure 3-1 . If your Web browser didn't launch, launch it and connect to http://localhost:4210/cgi-bin/WebObjects/Calculator_Client.woa .

Figure 3-1  A possible user interface to the Calculator Web service
A possible user interface to the Calculator Web service

Using Sessions in Web Services

Using sessions during Web service consumption is simple. You get a session from one Web service and share it with other Web services served from the same application. For example, you can develop an application that provides several related Web services. A practical way to share information among the services is to store shared data in a session object.

The Session project, in projects/Session , showcases a simple Web service application that provides two Web services: LogIn and AccessData. The LogIn service accepts user data and stores in a session. To give the AccessData service access to the information recorded by LogIn, its session is set to the one used by LogIn.

The Session_Client project, in projects/Session_Client , implements a Web service client that consumes LogIn and AccessData. It sets user name and password properties through LogIn and retrieves them through AccessData. Listing 3-3 shows the logic behind this process.

Listing 3-3  Session_Client project—Application.java file

import com.webobjects.appserver.*;
import com.webobjects.foundation.*;
import com.webobjects.webservices.client.*;
 
public class Application extends WOApplication {
 
    public static void main(String argv[]) {
        WOApplication.main(argv, Application.class);
    }
 
    public Application() {
        super();
        System.out.println("Welcome to " + this.name() + "!");
 
        // Create the service client used to consume
        // both LogInService and AccessDataService.
        SecurityClient securityClient = new SecurityClient();
 
        // Log in as Susana with the password anasus.
        securityClient.logIn("Susana", "anasus");
 
        // Get session from LogInService.
        WOWebService.SessionInfo sessionInfo = securityClient.logInSessionInfo();
 
        // Set AccessDataService's session to the one obtained from LogInService.
        securityClient.setAccessDataSessionInfo(sessionInfo);
 
        // Get values of properties stored in session created by LogInService.
        String userName = securityClient.userName();
        String userPassword = securityClient.userPassword();
 
        // Print the properties' values.
        System.out.println();
        System.out.println("************************************************");
        System.out.println("User name from AccessDataService: " + userName);
        System.out.println("User password from AccessDataService: "  + userPassword);
        System.out.println("************************************************");
        System.out.println();
    }
}

Listing 3-4 shows the SessionClient class in the Session_Client project.

Listing 3-4  Session_Client project—SessionClient.java file

import java.net.*;
 
import com.webobjects.appserver.*;
import com.webobjects.foundation.*;
import com.webobjects.webservices.client.*;
 
/**
 * Used to consume the LogIn and AccessData Web services.
 */
public class SecurityClient extends Object {
 
    private WOWebServiceClient _logInClient = null;
    private WOWebServiceClient _accessDataClient = null;
 
    private final String LogInServiceAddress = "http://localhost:4220/cgi-bin/WebObjects/Security.woa/ws/LogIn?wsdl";
    private final String AccessDataServiceAddress = "http://localhost:4220/cgi-bin/WebObjects/Security.woa/ws/AccessData?wsdl";
 
    private final String LogInService = "LogIn";
    private final String AccessDataService = "AccessData";
 
 
    public SecurityClient() {
        super();
    }
 
    /**
     * Invokes the setUserInfo operation of the LogIn Web service.
     */
    public void logIn(String name, String password) {
        Object[] arguments = { name, password };
        logInClient().invoke(LogInService, "setUserInfo", arguments);
    }
 
    /**
     * Invokes the userName operation of the AccessData Web service.
     * @return user name stored in shared session object.
     */
    public String userName() {
        Object result = accessDataClient().invoke(AccessDataService, "userName", null);
        return (String)result;
    }
 
    /**
     * Invokes the userPassword operation of the AccessData Web service.
     * @return user password stored in shared session object.
     */
    public String userPassword() {
        Object result = accessDataClient().invoke(AccessDataService, "userPassword", null);
        return (String)result;
    }
 
    /**
     * Obtains a Web service client through which LogIn operations are invoked.
     * @return a Web service client for LogIn.
     */
    protected WOWebServiceClient logInClient() {
        if (_logInClient == null) {
            _logInClient = clientFromAddress(LogInServiceAddress);
        }
        return _logInClient;
    }
 
    /**
     * Obtains a Web service client through which AccessData operations are invoked.
     * @return a Web service client for AccessData.
     */
    protected WOWebServiceClient accessDataClient() {
        if (_accessDataClient == null) {
            _accessDataClient = clientFromAddress(AccessDataServiceAddress);
        }
        return _accessDataClient;
    }
 
    /**
     * Obtains session information from LogInService.
     * @return session information from LogInService.
     */
    public WOWebService.SessionInfo logInSessionInfo() {
        return logInClient().sessionInfoForServiceNamed(LogInService);
    }
 
    /**
     * Sets the session used by AccessDataService.
     */
    public void setAccessDataSessionInfo(WOWebService.SessionInfo sessionInfo) {
        accessDataClient().setSessionInfoForServiceNamed(sessionInfo, AccessDataService);
    }
 
    /**
     * Obtains a Web service client through which
     * service operations are invoked.
     * @return Web service client object.
     */
    private WOWebServiceClient clientFromAddress(String address) {
        WOWebServiceClient service_client = null;
 
        // Create the Web service's URL.
        URL url;
        try {
            url = new URL(address);
        }
        catch (MalformedURLException e) {
            url = null;
        }
 
        // Get a service-client object.
        service_client = new WOWebServiceClient(url);
 
        return service_client;
    }
}

Listing 3-5 shows the LogIn class in the Session project.

Listing 3-5  Session project—LogIn.java file

import org.apache.axis.MessageContext;
 
import com.webobjects.appserver.*;
import com.webobjects.foundation.*;
 
/**
 * Implements the LogIn Web service.
 */
public class LogIn {
    public static final String USER_NAME = "userName";
    public static final String USER_PASSWORD = "userPassword";
 
    /**
     * Sets a user's name and password properties in a session.
     */
    public void setUserInfo(String userName, String userPassword) {
        WOSession session = serviceSession();
        session.setObjectForKey(userName, USER_NAME);
        session.setObjectForKey(userPassword, USER_PASSWORD);
    }
 
    /**
     * Retrieves the session from the current context.
     * @return current context's session.
     */
    private WOSession serviceSession() {
        WOContext context = (WOContext)MessageContext.getCurrentContext().getProperty("com.webobjects.appserver.WOContext");
        WOSession session = context.session();
        return session;
    }
}

Listing 3-6 shows the AccessData class in the Session project.

Listing 3-6  Session project—AccessData.java file

import org.apache.axis.MessageContext;
 
import com.webobjects.appserver.*;
import com.webobjects.foundation.*;
 
/**
 * Implements the AccessData Web service.
 */
public class AccessData {
 
    /**
     * Obtains the value of the USER_NAME property from the session.
     * @return user name.
     */
    public String userName() {
        String userName = null;
        WOSession session = serviceSession();
        if (session != null) {
            userName = (String)session.objectForKey(LogIn.USER_NAME);
        }
        return userName;
    }
 
    /**
     * Obtains the value of the USER_PASSWORD property from the session.
     * @return user password.
     */
    public String userPassword() {
        String userPassword = null;
        WOSession session = serviceSession();
        if (session != null) {
            userPassword = (String)session.objectForKey(LogIn.USER_PASSWORD);
        }
        return userPassword;
    }
 
    /**
     * Retrieves the session from the current context.
     * @return current context's session.
     */
    private WOSession serviceSession() {
        WOContext context = (WOContext)MessageContext.getCurrentContext().getProperty(
                                            "com.webobjects.appserver.WOContext");
        WOSession session = context.session();
        return session;
    }
}

Accessing the WOContext Object

Sometimes you may need to access the context (com.webobjects.appserver.WOContext ) of an HTTP request. For example, you can use the WOContext object to store data you want to share between methods or classes as an operation is processed. To access the WOContext object associated with an operation's invocation, use the code in Listing 3-7 .

Listing 3-7  Accessing the WOContext object from a Web service class

import com.webobjects.appserver.WOContext;
import org.apache.axis.MessageContext;
...
MessageContext message_context = MessageContext.getCurrentContext();
WOContext context = (WOContext)message_context.getProperty("com.webobjects.appserver.WOContext");

Adding Security to Web Services

You can add security processing to Web services by creating a security delegate class that implements methods of the WOSecurityDelegate interface in the com.webobjects.webservices.support package. These methods are

The projects Security and Security_Client in projects/Security and projects/Security_Client , respectively, show how the methods of a security-delegate class are invoked before and after an operation is executed.

Web Service Deployment Descriptors

Under Axis, Web services are deployed using XML-based files known as Web service deployment descriptors (WSDD). The Resources group of Web service application projects contains one or two of these files, named client.wsdd and server.wsdd . Application projects that only provide Web services have the server.wsdd file, while projects that consume services contain both.

Listing 3-8 shows the server.wsdd file of a Web service provider project.

Listing 3-8  The server.wsdd file of a Web service provider project

<?xml version="1.0" encoding="UTF-8"?>
<deployment
    xmlns="http://xml.apache.org/axis/wsdd/"
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <globalConfiguration>
        <parameter name="sendMultiRefs" value="true"/>
        <parameter name="sendXsiTypes" value="true"/>
        <parameter name="sendXMLDeclaration" value="true"/>
        <requestFlow>
            <handler type="java:com.webobjects.webservices.support._private.WOSecurityHandler"/>
            <handler type="java:com.webobjects.appserver._private.WOServerSessionHandler"/>
        </requestFlow>
        <responseFlow>
            <handler type="java:com.webobjects.appserver._private.WOServerSessionHandler"/>
            <handler type="java:com.webobjects.webservices.support._private.WOSecurityHandler"/>
        </responseFlow>
    </globalConfiguration>
    <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
    <handler name="HTTPActionHandler" type="java:org.apache.axis.handlers.http.HTTPActionHandler"/>
    <handler name="RPCDispatcher" type="java:org.apache.axis.providers.java.RPCProvider"/>
    <handler name="MsgDispatcher" type="java:org.apache.axis.providers.java.MsgProvider"/>
    <transport name="http">
        <requestFlow>
            <handler type="HTTPActionHandler"/>
            <handler type="URLMapper"/>
        </requestFlow>
    </transport>
</deployment>

Listing 3-9 shows the client.wsdd file of a Web service consumer project.

Listing 3-9  The client.wsdd file of a Web service consumer project

<?xml version="1.0" encoding="UTF-8"?>
<deployment
    xmlns="http://xml.apache.org/axis/wsdd/"
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <globalConfiguration>
        <parameter name="sendMultiRefs" value="true"/>
        <parameter name="sendXsiTypes" value="true"/>
        <parameter name="sendXMLDeclaration" value="true"/>
        <requestFlow>
            <handler type="java:com.webobjects.webservices.support._private.WOSecurityHandler"/>
            <handler type="java:com.webobjects.webservices.client._private.WOClientSessionHandler"/>
        </requestFlow>
        <responseFlow>
            <handler type="java:com.webobjects.webservices.client._private.WOClientSessionHandler"/>
            <handler type="java:com.webobjects.webservices.support._private.WOSecurityHandler"/>
        </responseFlow>
    </globalConfiguration>
    <transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/>
    <transport name="https" pivot="java:org.apache.axis.transport.http.HTTPSender"/>
    <transport name="local" pivot="java:org.apache.axis.transport.local.LocalSender"/>
</deployment>

You can edit the server.wsdd and the client.wsdd files to add handlers or to add Web services that have static WSDL documents. However, you must not remove any of the handlers defined in those files by default. Also, you should consult the Axis documentation before making any changes to the WSDD files.

Adding Web Service Support to Existing Projects

To add Web service–provider support to an existing project, you have to add the JavaWebServiceSupport framework to it. To add Web service–client support, you need to add the JavaWebServiceSupport and JavaWebServiceClient frameworks. The frameworks are located in /System/Library/Frameworks ($NEXT_ROOT/Library/Frameworks on Windows).