Simplified Implementation of the Service Locator Pattern

The Service Locator Pattern helps to encourage modular application design. The Dependency Inversion Principle states that modules should depend upon abstractions instead of other modules. Using JNDI we can merge the design pattern and development practice.

So, in places where a module needs to use the services of another module, create an interface that defines the methods required. Next add a final static String to the interface defining a JNDI name for the interface and ensure the interface extends Serializable. Next create a factory class to return an instance of the interface from a JNDI lookup using the name specified in the interface. An alternative, to eliminate duplicate code in the facotry classes, is to create a single factory class that provide a method taking the string as a parameter. ex:
IMyDataSource = (IMyDataSource) Locator.instance().getService(IMyDataSource.JNDI_NAME);

The initialization routine of the application can bind the JNDI names to the actual implementors of the interfaces. Now, whenever a service is required of a module, the component requiring it will retrieve the actual objects performing the service from the factory classes. I prefer to place all of these interfaces and their factory classes into a separate package from any implementation or application code. This package then acts as a directory of the services available for the application.

What benefits are gained with this methodology? First, a very clean design with no cross dependencies between modules. This means the application should have a lower cost of maintenance. Also, each component can be tested in isolation. If using JUnit, the JNDI names can be bound to mock objects to enhance testing capabilities. Services can be swapped out while the application is running, not just config time. Processor instensive services can be placed on remote machines allowing for transparent clustering. If the JNDI lookups become a performance issue, changing the factory class to simply return instances is a trivial change.

Following is an example implementation of a Locator class:

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
*
@author Paul E. Davis
*/
public class Locator {

 
private static Locator instance;
 
protected static Object lock = new Object();

 
private Locator() {
  }

 
public static Locator instance() {
   
if (instance == null) {
     
synchronized (lock) {
       
if (instance == null) {
         
instance = new Locator();
       
}
      }
    }
   
return instance;
 
}

 
public Object getService(String name) {
   
return lookupService(name);
 
}

 
private Object lookupService(String name) {
   
Object out = null;
   
try {
     
Context ctx = new InitialContext();
      out = ctx.lookup
(name);
   
} catch (NamingException e) {
     
// TODO Auto-generated catch block
     
e.printStackTrace();
   
}
   
return out;
 
}
}


Sponsors:

About willCode4Beer