ServiceLocator for Web Service Clients

Sean Brydon

Problem Description

Many applications use the Service Locator pattern. Now that web services are becoming more common in applications, there is a need to update your service locator code to support web services. Also, if your application does not currently apply the service locator pattern, you should consider doing this.

Enterprise applications require a way to look up the references that provide access to distributed components and web services. Java[TM] 2 Platform, Enterprise Edition (J2EE[TM]) applications use Java Naming and Directory Interface (JNDI) to look up enterprise bean home interfaces, Java Message Service (JMS) components, data sources, connections, connection factories, and now web services. Repetitious lookup code makes code difficult to read and maintain. The Service Locator pattern centralizes this code into one class and eliminates redundant code that otherwise gets scattered around the application.

This solution is applicable in the following circumstances:

Solution

The service locator pattern has been around for a while, and many J2EE applications use a service locator class to lookup J2EE resources. Now that there are web services that can be looked up and a service referenced obtained, we need to consider how to do this to support web services.

This solution will do the following:

Review of ServiceLocator Pattern

Let's look at an older example from the Java Adventure Builder of a service locator with web services support added. As you can see from the code below, there are methods to get references to different kinds of J2EE resources, such as EJBHomes, JMS ConnectionFactories, or Data Sources such as databases.

public class ServiceLocator {

  private transient InitialContext ic;

  public ServiceLocator() throws ServiceLocatorException {
    try {
      ic = new InitialContext();
    } catch (Exception e) {
      throw new ServiceLocatorException(e);
    }
}

  /**
  * @return the EJB Home factory corresponding to the homeName
  */
  public EJBHome getRemoteHome(String jndiHomeName, Class className) throws ServiceLocatorException {
    try {
      Object objref = ic.lookup(jndiHomeName);
      return (EJBHome) PortableRemoteObject.narrow(objref, className);
    } catch (Exception e) {
      throw new ServiceLocatorException(e);
    }
  }

  /**
  * @return the factory for the factory to get JMS connections from
  */
  public ConnectionFactory getJMSConnectionFactory(String jmsConnFactoryName)
                                               throws ServiceLocatorException {
    try {
      return (ConnectionFactory) ic.lookup(jmsConnFactoryName);
    } catch (Exception e) {
    throw new ServiceLocatorException(e);
   }
  }

  /*
  * @return the DataSource corresponding to the name parameter
  */
  public DataSource getDataSource(String dataSourceName) throws ServiceLocatorException {
    try {
      return (DataSource)ic.lookup(dataSourceName);
    } catch (Exception e) {
    throw new ServiceLocatorException(e);
    }
  }
   ...//and other J2EE resources
}

Code Example 1: Service Locator for J2EE Resources, With No Support For Web Services

Let's look at some of the main participants in the Service Locator pattern:

UML diagram of service locator

On the client that will use a business object or service it first needs to look up and get a reference to the service. In using the service locator pattern you have the following key participants:

Now that we have some idea about the service locator, let's look at how to add the web service support.

Adding Web Service Support to a Service Locator

Now let's look at what methods you need to add to your service locator to support web services.

For this example, let's assume there is a deployed application with a running service, and your application is a war file with a servlet that will access that service. In J2EE, components such as a Servlet in a web app that wants to access a web service application would need to write code to call a service and also in the web.xml deployment descriptor include a reference to a service (see Code Example 2).

<service-ref>
    <description>Client of Some Service</description>
    <service-ref-name>service/
SomeService</service-ref-name>
    <service-interface>
        com.sun.j2ee.blueprints.myclientapplication.
SomeService
    </service-interface>
    <wsdl-file>WEB-INF/wsdl/SomeService.wsdl</wsdl-file>
    <jaxrpc-mapping-file>WEB-INF/someservice-mapping.xml</jaxrpc-mapping-file>
    <service-qname xmlns:servicens="urn:
SomeService">servicens:SomeService</service-qname>
</service-ref>

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

When your client application is deployed and a JNDI name is bound for this service-ref, then the application can just access the naming service to get access to the service. For this example, let's assume that the service reference is bound in the naming directory as "java:comp/env/service/SomeService". The code in the left side of Table 1 shows the code without using a service locator class. Now, this code needs to be refactored to use the service locator, as shown in the right side of the table.

Without Using a Service Locator Refactored to Use a Service Locator
String s= "java:comp/env/service/SomeService";

try {        
  Context ic = new InitialContext();
  Service myService =(Service)ic.lookup(s);
  SomeServiceSEI port =(SomeServiceSEI)

      myService
.getPort(SomeServiceSEI.class);

  //now just call the web service method
  port.someMethod(someParameter);

} catch(Exception exe){
           ...
}
String s= "java:comp/env/service/SomeService";

try {        
  ServiceLocator sLoc = new ServiceLocator();
 
SomeServiceSEI port = (SomeServiceSEI)
   
sLoc.getServicePort(s,SomeServiceSEI.class);
 
  //now just call the web service method
  port.someMethod(someParameter);

} catch(ServiceLocatorException sle){
   ...
} catch(Exception exe){
   ...
}

Table 1: Refactoring to Use the Service Locator Pattern

As you can see, it's just a simple change to update your service locator for web services. Although this is just a small change to the code, it is very useful since this code is often repeated in many places in the application. And over time, if you have repeated code in many places, it starts to get a bit inconsistent, messy, and hard to maintain. So the usage of a service locator can clean up the code a bit.

Additionally, the service locator class needs to be updated to support the lookup of a web service. Code Example 3 shows the service locator with support to look up web service references.

/**
 * Implements Service Locator pattern for Web services
 */
public class ServiceLocator {
    private transient InitialContext ic;
   
    public ServiceLocator() throws ServiceLocatorException  {
        try {
            ic = new InitialContext();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    public Remote getServicePort(String jndiHomeName, Class className) throws ServiceLocatorException {
        try {
            Service service = (Service) ic.lookup(jndiHomeName);
            return service.getPort(className);
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }
}

Code Example 3: ServiceLocator With Support for J2EE Components to Act as Web Service Clients

That's it. Its easy to use this pattern. It does add an extra class to your application, but it can clean up your code a bit.

Strategy: Adding Web Service Support to a Service Locator Implementing the Caching Strategy

Some applications design the service locator using the caching strategy. This is to avoid unnecessary JNDI initial context creation and service object lookups. A service locator that applies this caching strategy for web service lookup is shown below in Code Example 4.

public class ServiceLocator {
   
    private InitialContext ic;
    //used to hold references to resources
    private Map cache = Collections.synchronizedMap(new HashMap());
   
    private static ServiceLocator instance = new ServiceLocator();
   
    public static ServiceLocator getInstance() {
        return instance;
    }
   
    private ServiceLocator() throws ServiceLocatorException {
        try {
            ic = new InitialContext();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    public Remote getServicePort(String jndiHomeName, Class className) throws ServiceLocatorException {
        Remote servicePort = (Remote)
cache.get(jndiHomeName);
       
if (servicePort == null) {
          try {
            Service service = (Service) ic.lookup(jndiHomeName);
            servicePort = service.getPort(className);
            cache.put(jndiHomeName, servicePort);
          } catch (Exception e) {
            throw new ServiceLocatorException(e);
          }
        }
        return
servicePort;
    }
}

Code Example 4: ServiceLocator for Web Service References, With Caching Strategy

As you can see from these code examples, an application that needs to look up web services can leverage the service locator pattern in much the same way as it can to look up other J2EE resources.

References

For more information about this topic, refer to the following:


© 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.