Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The problem with this pattern is that it creates a hard reference to a class instance into the class itself. As long as this instance is not released, the class will not be unloadable. At the end, this leads to the server being unable to reclaim the space for the entire webapp class loader.

Many of the workarounds below will sacrifice some of the virtues of the original pattern, for the sake of the server integrity. It is up to the designer/developer to decide whether these sacrifices are worth the benefits.

Workaround 1: Move the class to another classloader

This workaround is for the case this class should be shared between webapps, or if the server must will contain only one webapp. That is, we need to used use the same instance across several webapps in the same server, or there is no need to worry about it. In this case, the class will need to be deployed on a shared classloader. This means this class must be in the shared/lib or shared/classes directory.

This way, the class will be loaded by a parent classloader, and not by the webapp classloader itself, so no resources need to be reclaimed on webapp reloadings.

This workaround may not always fit well with your code or design. In particular, care must be taken to avoid the singleton to keep references to classes loaded through the webapp classloader, because such references would prevent the classloader from being deallocated. A servlet context listener could be used to get rid of those references before the context is destroyed.

Workaround 2: Use commons-discovery

If you need to have a singleton instance for each webapp, you could use commons-discovery. This library provides a class named DiscoverSingleton that can be used to implement singletons in your webapp.

...

we could release all cached references to the instantiated singletons. Of course, this listener should be registered on the web.xml descriptor before any other listener that could use a singleton.

Workaround 3: Use ServletContext attributes

This refactoring will work well provided the ServletContext instance is available, as a local variable or as a parameter.

It will be more efficient than using commons-discovery, but has the disadvantage of making your code depend on the web layer (ServletContext class). Anyway, I have found out that, in some cases, it is a reasonable approach.

There are many ways to do this refactoring, so I will just present one implementation that works well for me:

No Format

public class SingletonFactory implements ServletContextListener {
  public static final String MY_CLASS = "...";

  private static MyClass mcInstance = new MyClass();

  /**
   * @see ServletContextListener#contextInitialized(ServletContextEvent)
   */
  public void contextInitialized(ServletContextEvent event) {
    ServletContext ctx = event.getServletContext();
    ctx.setAttribute(MY_CLASS, mcInstance);
  }

  /**
   * @see ServletContextListener#contextDestroyed(ServletContextEvent)
   */
  public void contextDestroyed(ServletContextEvent event) {
    mcInstance = null;
  }

  /**
   * Optional method for getting the MyClass singleton instance.
   */
  public static MyClass getMyClassInstance(ServletContext ctx) {
    return mcInstance;
  }
}
  • Register the listener in the web.xml descriptor
  • Replace the calls to MyClass.getInstance() by:
No Format

  MyClass instance = (MyClass)ctx.getAttribute(SingletonFactory.MY_CLASS);

  /* or */

  MyClass instance = SingletonFactory.getMyClassInstance(ctx);