Monday, October 17, 2011

How Are the Per Web Application Singleton Objects Created in the JSF Framework

In the JSF framework, there are several key classes that are the singletons for the application. They are created using the following way:
FactoryFinder --> Factory --> Singleton Object.
To guarantee that the object is a singleton, the FactoryFinder and the Factory do the following:
  1. The Factories are also singletons. So you can just use plain code to get a field object of it as a singleton too.
  2. The FactoryFinder defines all its fields and methods to be static. And it puts the code to retrieve the factoies in a synchronized block. So when you get a factory object from FactoryFinder, it will be a singleton.
  3. Now since the web app uses the static method of FactoryFinder to get the factory object, will different web applications deployed on the same server share the same factory object? The answer is "No" and it is very important. The FactoryFinder uses classloader of the specific web application to achieve this result.

Note 1: If the purpose is to just create a singleton, then there is no need to use Factory. I think the main purpose to use Factory here is to give a mechanism so the different implementations of the singleton classes can be used. Factory uses some algorithm to find and load the implementation classes.

Note 2: The factories are loaded at the startup time of the server. This is before the web application is used by any user.

In FactoryFinder, there are four factories that can be created. They are: ApplicationFactory, FacesContextFactory, LifecycleFactory, and RenderKitFactory.

Take the Application singleton as the example.

From javadoc, javax.faces.application.Application represents a per-web-application singleton object. It is created by the following:
Application application = ApplicationFactory.getApplication();

The implementation ApplicationFactoryImpl is simply the following:
private Application application;

   public Application getApplication(){
      if (application == null )
      {
         application = new ApplicationImpl();
       }
       return applicaiton;
   }
You can see that this code does not deal with thread safety. And it is not static. So this code itself does not guarantee that the returned applicaiton object will be a singleton.

The javax.faces.application.ApplicationFactory is also a per-web-application singleton. It is created by the following in FactoryFinder:
private static HashMap applicationMaps = new HashMap();
  
   public static Object getFactory(String factoryName) throws FacesException{
    validateFactoryName(factoryName);
    synchronized (applicationMaps) {
            Map appMap = getApplicationMap();
   
              Object 
      factory = null,
      factoryOrList = appMap.get(factoryName);
                if (factoryOrList != null && (!(factoryOrList instanceof List))) {
     
                    return (factoryOrList);
                }
        
                factory = getImplementationInstance(classLoader, factoryName,
          (List) factoryOrList);
         
                if (null == factory) {
                    ResourceBundle rb = LOGGER.getResourceBundle();
                    String message = rb.getString("severe.no_factory");
                    message = MessageFormat.format(message, factoryName);
                    throw new IllegalStateException(message);
                }
    
                appMap.put(factoryName, factory);
                return (factory);
        }
   
   }

So in this method, the synchronized code is used. it is guaranteed that the singleton will be returned.
Let us look at the code in more detail to see why the returned factory object is per-web application.
The line Map appMap = getApplicationMap(); creates a local Map variable appMap. The method called is the following:
private static HashMap applicationMaps = new HashMap();

private static Map getApplicationMap() {
        // Identify the web application class loader
        ClassLoader classLoader = getClassLoader();
 Map result = null;
 
 // Return any previously instantiated factory instance (of the
 // specified name) for this web application
 result = (HashMap) applicationMaps.get(classLoader);
 if (result == null) {
     result = new HashMap();
     applicationMaps.put(classLoader, result);
 }
 return result;
    }
So it is a map from the classLoader to another map. This classLoader object is different for different web applications deployed on the same server. The method is the following:
private static ClassLoader getClassLoader() throws FacesException {

        // J2EE 1.3 (and later) containers are required to make the
        // web application class loader visible through the context
        // class loader of the current thread.
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            throw new FacesException("getContextClassLoader");
        }
        return (cl);

    }
You can see that it uses the current thread to get the ClassLoader object. The static instance variable applicationMaps of the class FactoryFinder contains the following information:
classLoader --> {factoryName --> factoryObject}
So in a more strict sense, the factoryObject singleton is not a singleton across the JVM. It is a singleton inside each web applicaiton.

No comments:

Post a Comment