Sunday, 8 September 2013

Service Locator Design Pattern - Implementation

🚀 Level Up Your Java Architecture Skills!

Subscribe to Ram N Java for clean code walkthroughs, design pattern implementations, and step-by-step programming guides!

🔔 Subscribe to Ram N Java

Practical Implementation of Service Locator

In distributed Java enterprise applications, resolving business services via JNDI or remote directory lookups can be costly and repetitive. The Service Locator Design Pattern solves this by centralizing service lookups and employing an in-memory cache. Once a service is resolved, subsequent requests retrieve it instantly from the cache without triggering another expensive network call.

Building Blocks of the Implementation

  • Service (Interface): Defines the common business contract (such as getName() and execute()) implemented by concrete services.
  • Concrete Services: Concrete classes (e.g., Service1, Service2) that implement the business methods.
  • InitialContext: Simulates or encapsulates the JNDI lookup mechanism to instantiate requested services by name.
  • Cache: Stores already-instantiated service objects so repeat calls avoid directory lookups.
  • ServiceLocator: The central entry point containing a static method (e.g., getService(String jndiName)) that coordinates between the Cache and the InitialContext.
  • Client: The application component that queries the ServiceLocator to execute business logic cleanly.

Step-by-Step Execution Flow

  1. The Client requests a service by providing its JNDI name to the ServiceLocator.
  2. The ServiceLocator checks the Cache to see if an instance of that service already exists.
  3. Cache Hit: If found in the cache, the locator immediately returns the cached instance to the client.
  4. Cache Miss: If not found, the locator invokes the InitialContext to look up and instantiate the service.
  5. The new service instance is saved into the Cache for future lookups and then returned to the client.

Implementation Best Practices

  • Thread-Safe Caching: Ensure your cache data structure (e.g., ConcurrentHashMap or synchronized collections) handles concurrent lookups safely in multi-threaded environments.
  • Centralize Exception Handling: Catch and handle naming and context initialization exceptions inside the locator to avoid propagating low-level errors to clients.
  • Program to Interfaces: Always return the abstract Service interface rather than concrete classes to keep client code decoupled and flexible.

Recommended Next Videos

Broaden your understanding of enterprise tier architecture with these related tutorials from our channel:


Click here to watch in Youtube : https://www.youtube.com/watch?v=4MaBWg44fbs

Click the below Image to Enlarge





















Service.java

public interface Service
{
public void execute();
public String getName();
}


JMSService.java


public class JMSService implements Service
{

@Override
public void execute()
{
System.out.println("Executing JMSService");

}
@Override
public String getName()
{
return "JMSService";
}

}

EJBService.java


public class EJBService implements Service
{

@Override
public void execute()
{
System.out.println("Executing EJBService");
}

@Override
public String getName()
{
return "EJBService";
}

}

InitialContext.java


public class InitialContext
{
public Object lookup( String jndiName )
{
if( jndiName.equalsIgnoreCase("EJBService") )
{
// JNDI lookup code will come in real time implementation
System.out.println("Looking up and creating a new EJBService object");
return new EJBService();
}
else if( jndiName.equalsIgnoreCase("JMSService") )
{
   // JNDI lookup code will come in real time implementation
System.out.println("Looking up and creating a new JMSService object");
return new JMSService();
}
return null;
}
}

Cache.java


import java.util.ArrayList;
import java.util.List;

public class Cache
{
private List<Service> services;

public Cache()
{
services = new ArrayList<Service>();
}

public Service getService( String serviceName )
{
for( Service service : services )
{
if( service.getName().equalsIgnoreCase(serviceName) )
{
System.out.println("Return cached  " + serviceName + " object");
return service;
}
}
return null;
}

public void addService( Service newService )
{
boolean exists = false;
for( Service service : services )
{
if( service.getName().equalsIgnoreCase(newService.getName()) )
{
exists = true;
}
}
if( !exists )
{
services.add(newService);
}
}
}

ServiceLocator.java


public class ServiceLocator
{
private static Cache cache;

static
{
cache = new Cache();
}

public static Service getService( String jndiName )
{
// First check the service object available in cache
Service service = cache.getService(jndiName);

if( service != null )
{
return service;
}

/*
* If service object not available in cache do the lookup using
* JNDI initial context and get the service object and add it to
* the cache for future use
*/
InitialContext context = new InitialContext();
Service service1 = (Service) context.lookup(jndiName);
cache.addService(service1);
return service1;
}
}


Client.java


public class Client
{

public static void main( String[] args )
{
Service service = ServiceLocator.getService("EJBService");
service.execute();
System.out.println();
service = ServiceLocator.getService("JMSService");
service.execute();
System.out.println();
service = ServiceLocator.getService("EJBService");
service.execute();
System.out.println();
service = ServiceLocator.getService("JMSService");
service.execute();

}

}

Output

Looking up and creating a new EJBService object
Executing EJBService

Looking up and creating a new JMSService object
Executing JMSService

Return cached  EJBService object
Executing EJBService

Return cached  JMSService object
Executing JMSService

No comments:

Post a Comment

Tutorials