Repair invalid cached services in the Service Locator pattern

The Verified Service Locator pattern ensures cached services' validity

The Service Locator pattern locates J2EE (Java 2 Platform, Enterprise Edition) services for clients and thus abstracts the complexity of network operation and J2EE service lookup as EJB (Enterprise JavaBean) Home and JMS (Java Message Service) component factories. The Service Locator hides the lookup process's implementation details and complexity from clients. To improve application performance, Service Locator caches service objects to eliminate unnecessary JNDI (Java Naming and Directory Interface) activity that occurs in a lookup operation.

When you opt to use the Service Locator pattern in J2EE development, you expect no side effects. However, cached services can be invalid, generating unexpected errors in your application. To avoid these errors, enhance the Service Locator pattern to neutralize potential problems with cached services. In this article, I present a way to empower the Service Locator pattern with a verification mechanism that recognizes and eliminates invalid cached services.

Context

Figure 1 shows the Service Locator pattern behavior.

Figure 1. Service Locator execution

For example, to acquire an EJB Home object for the first time, Service Locator first creates a JNDI initial context object and performs a lookup on the EJB Home object. Because this operation utilizes significant resources and because multiple clients repeatedly require the same EJB Home object, Service Locator caches the object, and consecutive calls use the cached service.

Problem

I used Service Locator in several projects without stumbling across any problems. However, when I used the pattern with my last project, the service invocation failed after the server restarted.

Figure 2 shows how a server restart generated the undesired error.

Figure 2. Service failure after a server restart

When a service has been invoked, Service Locator returns the cached service for consecutive invocations. In my project environment, the server restart invalidated the cached services; the client noticed this invalid service when it received an error while attempting to execute the service (Step 4 in Figure 2).

Unfortunately, Service Locator doesn't realize that it betrays the client by returning an invalid cached service. This is a contradictory situation: Service Locator, with its great benefits, brings this drawback. This article doesn't focus on the possible situations that invalidate the Service Locator's cached services; rather, it explores a solution for overcoming this problem.

You could address this situation in one of the following ways:

  • Don't use Service Locator: This approach solves the problem, but you lose the pattern's benefits.
  • Treat the error when it happens: With this approach, you add more code to the client code to solve the problem created by Service Locator; however, your reasoning for using Service Locator in the first place was to reduce code complexity. Furthermore, each different client will have to solve the same problem.
  • Use the proposed enhanced Service Locator instead: I explain this solution below in detail.

Solution

Enhance Service Locator by empowering it with a mechanism for periodically verifying the cached services.

Some critical points:

  • You might ask: Why not verify the service immediately before returning it to the client? To do so, Service Locator would need to know how to verify each service, making the pattern application specific and less reusable. Furthermore, when invoking a service, the client would need to wait for the service verification, decreasing performance.
  • The solution must be simple and not affect Service Locator usage and benefits.

The proposed enhanced Service Locator has a verifier mechanism that checks the cached services' validity by monitoring them during a cycle of a specified frequency. A low-priority thread executing during the verification cycle triggers the validation, identifying and eliminating invalid cached services, and ensuring the services' validity before a client attempts to use them. That way, the consumed validation time happens later, not while a client waits for a service response. The enhanced Service Locator is called Verified Service Locator, and I present its usage, structure, and participants below.

Usage

Figure 3 shows how a client uses Verified Service Locator.

Figure 3. Sequence diagram: Verified Service Locator usage

Verified Service Locator differs from Service Locator in the following ways:

  • You must develop an application-specific class that implements a ServiceVerifiable interface (explained in the "Participants and Responsibilities" section below).
  • You must invoke VerifiedServiceLocator.setVerifier(...) to start the verification mechanism. The parameters are an object of the application-specific class and a verifying frequency (also detailed in "Participants and Responsibilities"). Listing 1 below shows a sample JSP (JavaServer Page) initializing VerifiedServiceLocator:

Listing 1. Initialize VerifiedServiceLocator

 <%@ page import="enhancedServiceLocator.VerifiedServiceLocator" %>
<%@ page import="enhancedServiceLocator.test.MyAppServiceVerifiable" %>
<%!
  public void jspInit()
  {
    // Sets the VerifiedServiceLocator
    // to verify cached services            
    // every 5 minutes
    VerifiedServiceLocator.setVerifier(5, new MyAppServiceVerifiable ());
  }
%>    

After initialization, Verified Service Locator works as Service Locator does. Listing 2 shows a sample JSP invoking the lookUp() method:

Listing 2. Normal Service Locator usage

<%@ page import="enhancedServiceLocator.VerifiedServiceLocator" %>
<%@ page import="enhancedServiceLocator.test.MyEJBHome" %>
<%@ page import="enhancedServiceLocator.test.MyRemote" %>
<%
  VerifiedServiceLocator  serviceLocator =   
    VerifiedServiceLocator.getInstance();
  MyEJBHome myEJBHome =   
    (MyEJBHome) serviceLocator.lookup
      (MyEJBHome.class, "myEJBJNDI");
  MyRemote myRemote =   myEJBHomee.create();   
  out.println( "output from ejb: "+myRemote.greetings( ) );
%>

Structure

Figure 4 shows the relationships among this solution's participants.

Figure 4. Class diagram: Verified Service Locator structure

The class diagram presents the classes VerifiedServiceLocator, ServiceLocatorVerifier, and AlarmClock, plus an app-specific class; the interfaces ServiceVerifiable and WaitingAlarm; and the relationships between them. They form the solution that enhances the Service Locator pattern.

Participants and responsibilities

Figure 5 shows how this solution's participants interact. Notice the interaction numbers: interactions 1, 2, and 3 happen only during the initialization process; 4 and 5 happen once per verification cycle; and 6, 7, and 8 occur only when a cached service raises an exception.

Figure 5. Collaboration diagram: Verified Service Locator execution

VerifiedServiceLocator

To enhance Service Locator, Verified Service Locator adds two methods:

  • public static void setVerifier(int _verificationFrequency_Minutes, ServiceVerifiable _appSpecificObj): The client invokes this method to set up the verifier mechanism; the parameters are the frequency of the verification cycle and the object of the class that implements the ServiceVerifiable interface.
  • public void cleanCache(): Once the ServiceLocatorVerifier recognizes that a cached service has raised an exception during the checkServices() method execution, it will invoke the cleanCache() method to clean the services objects in the cache. Thus, the next time a client gets a service, it will be a valid cached service.

The code for the VerifiedServiceLocator class follows:

public class VerifiedServiceLocator
{
  private static ServiceLocatorVerifier serviceLocatorVerifier;
  private Map cachedServices;
  ...
  public static void setVerifier(
    int _verificationFrequency_Minutes,  
    ServiceVerifiable _appSpecificObj   
  {
     if (serviceLocatorVerifier == null)  
     {
       serviceLocatorVerifier =
         new ServiceLocatorVerifier(
           _verificationFrequency_Minutes,
           _appSpecificObj);
      }
  }
  public void cleanCache()   
  {
      cachedServices.clear();
  }
  ...
  // Normal methods of a Service Locator
}

ServiceLocatorVerifier

The ServiceLocatorVerifier handles the verification process for the cached services in Service Locator. A ServiceLocatorVerifier object is internally constructed when the client invokes VerifiedServiceLocator.setVerifier(int _verificationFrequency_Minutes, ServiceVerifiable _appSpecificObj).

Once constructed, ServiceLocatorVerifier starts its alarm clock, which triggers itself every verificationFrequency_Minutes, signaling that it's time to invoke appSpecificObj.checkServices(). When this method execution raises any Exception, ServiceLocatorVerifier invokes appSpecificObj.followError(Exception exc) and VerifiedServiceLocator.cleanCache().

When ServiceLocatorVerifier invokes appSpecificObj.followError(Exception exc), the specific application can treat the error in any way it wishes—by logging the exception or sending an automatic email, for instance.

When ServiceLocatorVerifier invokes VerifiedServiceLocator.cleanCache(), the services in the cache are cleaned, so the next time a client invokes lookup(), a new service returns. That solves the problems caused by invalid cached services. The solution does not solve other error types, but a discussion of those types would reach beyond the ServiceLocatorVerifier's scope, as its solution is specific: the Service Locator Verifier enhances Service Locator only to manage the invalid cached services problem.

The code for the ServiceLocatorVerifier class follows:

class ServiceLocatorVerifier implements WaitingAlarm
{
  private VerifiedServiceLocator serviceLocator;
  private AlarmClock myAlarmClock;
  private long serverVerifierPeriod;
  private ServiceVerifiable appSpecificServiceVerifiable;
  public ServiceLocatorVerifier(
    int _serverVerifierPeriod_Minutes,  
    ServiceVerifiable _appSpecificServiceVerifiable)
  {
    serviceLocator = VerifiedServiceLocator.getInstance();
    // Convert minutes to milliseconds
    serverVerifierPeriod = _serverVerifierPeriod_Minutes * 60 * 1000;
    appSpecificServiceVerifiable = _appSpecificServiceVerifiable;
    this.startAlarmClock();
  }
  private void startAlarmClock()
  {
    // Create the alarmClock
    myAlarmClock = new AlarmClock(this, serverVerifierPeriod);
    // Start the alarmClock
    myAlarmClock.start();
  }
  /**
  *  This method is called when the alarmClock rings.
  *  So if the alarm is ringing, it is time to check
  *  the cached services.
  */
  public void alarmRinging()
  {
    try
    {
      appSpecificServiceVerifiable.checkServices();
    }
    catch( Exception exc)
    {
      serviceLocator.cleanCache();
      appSpecificServiceVerifiable.followError(exc);
    }
  }
  protected void finalize() throws Throwable
  {
    // Turn off my alarm clock
    myAlarmClock.interrupt();
  }
}

ServiceVerifiable

ServiceLocatorVerifier uses ServiceVerifiable to reach client-specific verifications and treatment. To use the Verifiable Service Locator enhanced pattern, you must code a class that implements the ServiceVerifiable interface. To implement this interface, you must provide two methods:

  • checkServices() throws Exception: The code inside this method should invoke the VerifiedServiceLocator.lookup() for each service you consider important to verify. This method must throw Exception (or just not catch it). The ServiceLocatorVerifier acts only when checkServices() throws Exception raises an Exception.
  • followError(Exception exc): This method is invoked when ServiceLocatorVerifier gets an Exception from checkServices(); this method implementation allows the developer to act on the Exception (to log it, for instance). You can opt to have an empty method—{}—so nothing happens and the interface is still respected.

The code for the ServiceVerifiable interface follows below:

public interface ServiceVerifiable
{
  public void checkServices() throws Exception;
  public void followError(Exception exc);
}

App-specific class

The application-specific class implements the ServiceVerifiable interface. An object of this class is passed as a parameter to the initialization method (see Listing 1).

Listing 3 provides a sample application-specific class that implements the ServiceVerifiable interface. In this simple sample, the only service to be verified is an EJBHome.

Listing 3. A sample application-specific class implements ServiceVerifiable

 public class MyAppServiceVerifiable implements ServiceVerifiable  
{
  public void checkServices() throws Exception
  {
    VerifiedServiceLocator serviceLocator =
      VerifiedServiceLocator.getInstance();
    MyEJBHome myEJBHome =
      (MyEJBHome) serviceLocator.lookup
        (MyEJBHome.class, "myEJBJNDI");
    MyRemote myRemote = myEJBHomee.create();
    try
    {
      myRemote .remove();
    }  
    catch (javax.ejb.RemoveException e)
    {
      e.printStackTrace();
    }
  }
  
  public void followError(Exception exc) { }
}

The app-specific class is specific to each application using Verified Service Locator. By implementing that class, you select the cached services that you want verified.

AlarmClock

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more