Service Locator Pattern
The Service Locator Pattern is used when we want to locate various services using JNDI lookup. Considering the cost of looking up for a service in JNDI is high, the Service Locator pattern leverages caching techniques. When a service is requested for the first time, the Service Locator looks up the service in JNDI and caches the service object. For subsequent requests for the same service, the Service Locator looks up the service in its cache, which greatly improves the application's performance. The following are the entities of this design pattern.
Service - The actual service that handles the request. A reference to this service can be found in the JNDI server.
Context / Initial Context - The JNDI Context contains a reference to the service to be looked up.
Service Locator - The Service Locator is a single point of contact to get services by JNDI lookup and caching services.
Cache - The Cache stores references to services to reuse them.
Client - The Client is the object that calls the service through the ServiceLocator.
Implementation
We will create ServiceLocator, InitialContext, Cache, Service as various objects representing the entities. Service1 and Service2 represent the concrete services.
ServiceLocatorPatternDemo class is used as a client here, which will use ServiceLocator to demonstrate the Service Locator design pattern.
Step 1
Create the Service interface.
Service.java
public interface Service {
public String getName();
public void execute();
}
Step 2
Create concrete services.
Service1.java
public class Service1 implements Service {
public void execute(){
System.out.println("Executing Service1");
}
@Override
public String getName() {
return "Service1";
}
}
Service2.java
public class Service2 implements Service {
public void execute(){
System.out.println("Executing Service2");
}
@Override
public String getName() {
return "Service2";
}
}
Step 3
Create InitialContext for JNDI lookup.
InitialContext.java
public class InitialContext {
public Object lookup(String jndiName){
if(jndiName.equalsIgnoreCase("SERVICE1")){
System.out.println("Looking up and creating a new Service1 object");
return new Service1();
}else if (jndiName.equalsIgnoreCase("SERVICE2")){
System.out.println("Looking up and creating a new Service2 object");
return new Service2();
}
return null;
}
}
Step 4
Create Cache.
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("Returning 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);
}
}
}
Step 5
Create the Service Locator.
ServiceLocator.java
public class ServiceLocator {
private static Cache cache;
static {
cache = new Cache();
}
public static Service getService(String jndiName){
Service service = cache.getService(jndiName);
if(service != null){
return service;
}
InitialContext context = new InitialContext();
Service service1 = (Service)context.lookup(jndiName);
cache.addService(service1);
return service1;
}
}
Step 6
Use ServiceLocator to demonstrate the Service Locator design pattern.
ServiceLocatorPatternDemo.java
public class ServiceLocatorPatternDemo {
public static void main(String[] args) {
Service service = ServiceLocator.getService("Service1");
service.execute();
service = ServiceLocator.getService("Service2");
service.execute();
service = ServiceLocator.getService("Service1");
service.execute();
service = ServiceLocator.getService("Service2");
service.execute();
}
}
Step 7
Execute the program, output results:
Looking up and creating a new Service1 object
Executing Service1
Looking up and creating a new Service2 object
Executing Service2
Returning cached Service1 object
Executing Service1
Returning cached Service2 object
Executing Service2