📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
Table of contents |
---|
Problem |
Forces |
Solution |
Structure - Class Diagram, Sequence Diagram |
Participants and Responsibilities |
Implementation |
Consequences |
Applicability |
References |
Problem
Forces
- You want to use the JNDI API to look up and use business components, such as enterprise beans and JMS components, and services such as data sources.
- You want to centralize and reuse the implementation of lookup mechanisms for J2EE application clients.
- You want to encapsulate vendor dependencies for registry implementations and hide the dependency and complexity from the clients.
- You want to avoid performance overhead related to initial context creation and service lookups.
- You want to reestablish a connection to a previously accessed enterprise bean instance, using its Handle object.
Solution
Structure
Class diagram
Sequence Diagram
Participants and Responsibilities
Implementation
public interface Service {
/*
* The human readable name of the service
*/
String getName();
/*
* Unique ID of the particular service
*/
int getId();
/*
* The workflow method that defines what this service does
*/
void execute();
}
public class ServiceImpl implements Service {
private final String serviceName;
private final int id;
/**
* Constructor
*/
public ServiceImpl(String serviceName) {
// set the service name
this.serviceName = serviceName;
// Generate a random id to this service object
this.id = (int) Math.floor(Math.random() * 1000) + 1;
}
@Override
public String getName() {
return serviceName;
}
@Override
public int getId() {
return id;
}
@Override
public void execute() {
System.out.println("Service " + getName() + " is now executing with id " + getId());
}
}
public class InitContext {
/**
* Perform the lookup based on the service name. The returned object will need to be casted into a
* {@link Service}
*
* @param serviceName a string
* @return an {@link Object}
*/
public Object lookup(String serviceName) {
if (serviceName.equals("jndi/serviceA")) {
System.out.println("Looking up service A and creating new service for A");
return new ServiceImpl("jndi/serviceA");
} else if (serviceName.equals("jndi/serviceB")) {
System.out.println("Looking up service B and creating new service for B");
return new ServiceImpl("jndi/serviceB");
} else {
return null;
}
}
}
public class ServiceCache {
private final Map<String, Service> serviceCache;
public ServiceCache() {
serviceCache = new HashMap<>();
}
/**
* Get the service from the cache. null if no service is found matching the name
*
* @param serviceName a string
* @return {@link Service}
*/
public Service getService(String serviceName) {
Service cachedService = null;
for (String serviceJndiName : serviceCache.keySet()) {
if (serviceJndiName.equals(serviceName)) {
cachedService = serviceCache.get(serviceJndiName);
System.out.println("(cache call) Fetched service " + cachedService.getName() + "("
+ cachedService.getId() + ") from cache... !");
}
}
return cachedService;
}
/**
* Adds the service into the cache map
*
* @param newService a {@link Service}
*/
public void addService(Service newService) {
serviceCache.put(newService.getName(), newService);
}
}
public final class ServiceLocator {
private static ServiceCache serviceCache = new ServiceCache();
private ServiceLocator() {
}
public static Service getService(String serviceJndiName) {
Service serviceObj = serviceCache.getService(serviceJndiName);
if (serviceObj != null) {
return serviceObj;
} else {
/*
* If we are unable to retrive anything from cache, then lookup the service and add it in the
* cache map
*/
InitContext ctx = new InitContext();
serviceObj = (Service) ctx.lookup(serviceJndiName);
if (serviceObj != null) { // Only cache a service if it actually exists
serviceCache.addService(serviceObj);
}
return serviceObj;
}
}
}
public class TestServiceLocatorPattern{
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
Service service = ServiceLocator.getService("jndi/serviceA");
service.execute();
service = ServiceLocator.getService("jndi/serviceB");
service.execute();
service = ServiceLocator.getService("jndi/serviceA");
service.execute();
service = ServiceLocator.getService("jndi/serviceA");
service.execute();
}
}
Applicability
Typical Use Case
- when network hits are expensive and time-consuming
- lookups of services are done quite frequently
- a large number of services are being used
Consequences
- Abstracts complexity
- Provides uniform service access to clients
- Facilitates adding EJB business components
- Improves network performance
- Improves client performance by caching
References
Related Patterns
- Data Transfer Object Design Pattern in Java
- Service Locator Design Pattern in Java
- Business Delegate Design Pattern in Java
- Converter Design Pattern in Java
- Transfer Object Assembler Pattern in Java
- Value List Handler Pattern in Java
Comments
Post a Comment
Leave Comment