ServiceLocator for Web Service Clients: Design Details

This solution is illustrated by the Java[TM] 2 Platform, Enterprise Edition (J2EE[TM] platform) component as a client of a web service application available under the bpcatalog project hosted at java.net. This application illustrates how J2EE components can call web services. The application was enhanced to apply the Service Locator pattern in its design. This application shows a Servlet component accessing a web service that is deployed as part of another application. The concepts are basically the same for any J2EE component (Servlet, JSP[TM] page, EJB[TM] component that needs to access a web service.

The key parts of this application are
This application accesses a deployed web service (another application). Because this client  is a J2EE application, in this case a web application war file, it must specify a service reference in its web.xml file. Here is a code snippet for its web.xml:

<service-ref>
  <description>Schema defined Purchase Order Service Client</description>
  <service-ref-name>service/SchemaDefinedPurchaseOrderService</service-ref-name>
  <service-interface>
 com.sun.j2ee.blueprints.docoriented.client.objectposervice.SchemaDefinedPurchaseOrderService 
  </service-interface>
  <wsdl-file>WEB-INF/wsdl/SchemaDefinedPurchaseOrderService.wsdl</wsdl-file>
 <jaxrpc-mapping-file>WEB-INF/schemadefinedpurchaseorderservice-mapping.xml</jaxrpc-mapping-file>
  <service-qname xmlns:servicens="urn:SchemaDefinedPurchaseOrderService">servicens:SchemaDefinedPurchaseOrderService</service-qname>
</service-ref> 


Code Example 1: Service Reference in a web.xml Deployment Descriptor

In addition to including a service reference, the application would need to bind that service-ref to a JNDI name at deployment time so that the client code could look it up. Now lets take a look at some of the key classes which use and implement the service locator pattern.

SchemaPOServiceBD.java

Let's start by looking at the code for the SchemaPOServiceBD.java class. This class uses the service locator. This class just has a single method, which is called and acts as a delegate to access the web service. This class implements the Business Delegate pattern. The key part of this submitPO method is that it uses the service locator. It uses the service locator to get a reference to the web service, and then calls the web service.

public class SchemaPOServiceBD {
  private ServiceLocator serviceLocator;

  public SchemaPOServiceBD(){    
        serviceLocator = new ServiceLocator();  
    }

  public String submitPO(com.sun.j2ee.blueprints.docoriented.client.PurchaseOrder po) throws RequestHandlerException {
       
    try {      
      SchemaDefinedPurchaseOrderServiceSEI port = (SchemaDefinedPurchaseOrderServiceSEI)         
      serviceLocator.getServicePort(JNDINames.SCHEMA_SERVICE_REF, SchemaDefinedPurchaseOrderServiceSEI.class);
                     
      //convert po object into object for service type of order
      // that is to be sent to the endpoint
      ...    

      String ret = port.submitPO(order);
      return ret;
    } catch ...
     ....
  }
}

ServiceLocator.java

Now let's take a look at the ServiceLocator.java class. As you can see, the implementation is like a service locator for other resources, except in this case, we have added support to get references to web services. Since the service locator contains all the lookup code to obtain a service reference, classes can use this service locator and avoid copying and pasting this lookup code into a lot of different places. This helps clean up the code in your application.

import javax.naming.*;
import java.rmi.Remote;
import javax.xml.rpc.*;

/**
 * Implements Service Locator pattern for Web services
 */
public class ServiceLocator {
   
    private transient InitialContext ic;

    public ServiceLocator() throws ServiceLocatorException  {
        try {
            setInitialContext();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    private void setInitialContext() throws javax.naming.NamingException {
      ic = new InitialContext();
    }

    /**
     * Service class acts as a factory of the Dynamic proxy for the target service endpoint.
     * @see java.xml.rpc.Service.java
     * @return the Service instance
     */
    public Remote getServicePort(String jndiName, Class className) throws ServiceLocatorException {
        try {
            if (ic == null) setInitialContext();
            Service service = (Service) ic.lookup(jndiName);
            return service.getPort(className);
        } catch (Exception e) {
            throw new ServiceLocatorException("ServiceLocator can not lookup jndiName=" + jndiName + " and className=" + className,  e);
        }
    } 
}

ServiceLocatorException.java

Now let's take a look at the ServiceLocatorException.java class. Notice that we chose to have this exception extend a RuntimeException. Because an exception occurring while looking up a resource in a naming directory is an unrecoverable exception for the user, we chose to treat this an unchecked system exception. Also note that we used exception chaining. In the J2EE client example application that uses the service locator, we had the main servlet use the servlet error page mapping mechanism in web.xml to map this exception and all Runtime exceptions to a JSP error page which prints out a system error message.

package com.sun.j2ee.blueprints.docoriented.client;

public class ServiceLocatorException extends RuntimeException {
    public ServiceLocatorException() {}
    public ServiceLocatorException(String msg) { super(msg); }
    public ServiceLocatorException(String msg, Throwable cause) { super(msg, cause); }
    public ServiceLocatorException(Throwable cause) { super(cause); }
}


© Sun Microsystems 2005. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.