Class FactoryFinderProviderFactory


  • public abstract class FactoryFinderProviderFactory
    extends Object

    FactoryFinder is a class with three methods:

    public final class FactoryFinder { public static Object getFactory(String factoryName) throws FacesException {...} public static void setFactory(String factoryName, String implName) {...} public static void releaseFactories() throws FacesException {...} }

    The javadoc describe the intention of FactoryFinder class:

    "... FactoryFinder implements the standard discovery algorithm for all factory objects specified in the JavaServer Faces APIs. For a given factory class name, a corresponding implementation class is searched for based on the following algorithm...."

    In few words, this class allows to find Faces factory classes. The necessary information to create factory instances is loaded on initialization time, but which locations contains such information (for more information see Faces 2.0 spec section 11.4.2) (here the only interest is in jsf factories initialization information) ?

    • Look factories on META-INF/services/[factoryClassName]
    • Look META-INF/faces-config.xml or META-INF/[prefix].faces-config.xml
    • Look the files pointed by jakarta.faces.CONFIG_FILES web config param (note WEB-INF/web.xml is taken into consideration)
    • Look the applicationFacesConfig on WEB-INF/faces-config.xml

    Based on the previous facts, the first conclusion to take into account arise: Configuration information is gathered per "web context". What is a "web context"? In simple terms, is the "space" where a web application is deployed. Let's suppose an EAR file with two WAR files: a.war and b.war. Both contains different "web applications" and when are deployed has different "web context", so both can provide different factory configuration, because both has different WEB-INF/web.xml and WEB-INF/faces-config.xml files.

    Now, given a request, how the web container identify a "web context"? At start, it receives the request information and based on that it decides which web application should process it. After that, it assign to a thread from is thread pool to be processed and the control is passed to the proper filters/servlets.

    So, if there is not a servlet context/portlet context/whatever context, how to identify a "web context"? The answer is using the thread, but the one who knows how to do that is the web container, not the jsf implementation.

    The existing problem is caused by a "shortcut" taken to make things easier. Instead use the current "thread", it is taken as advantage the fact that each web application deployed has a different classloader. That is true for a lot of application servers, so the current implementation of FactoryFinder is based on that fact too and has worked well since the beginning.

    Now let's examine in detail how a "single classloader per EAR" option could work. If the EAR has two WAR files (a.war and b.war), we have two web context, and the initialization code is executed twice. When all FactoryFinder methods are called?

    • FactoryFinder.setFactory is called on initialization
    • FactoryFinder.releaseFactories is called on shutdown
    • FactoryFinder.getFactory is called after initialization configuration is done but before shutdown call to FactoryFinder.setFactory

    Remember all methods of FactoryFinder are static.

    One possible solution could be:

    1. Create a class called FactoryFinderProvider, that has the same three method but in a non static version.
    2. A singleton component is provided that holds the information of the FactoryFinderProviderFactory. This one works per classloader, so the singleton is implemented using an static variable. To configure it, the static method should be called when the "classloader realm" is initialized, before any web context is started (the WAR is deployed). Usually the EAR is started as a single entity, so this should occur when the EAR starts, but before the WAR files are started (or the web context are created). The singleton will be responsible to decide which FactoryFinderProvider should be used, based on the current thread information.
    3. Add utility methods to retrieve the required objects and call the methods using reflection from jakarta.faces.FactoryFinder

    This class implements the proposed solution. Note by definition, this factory cannot be configured using SPI standard algorithm (look for META-INF/services/[factory_class_name]).

    Since:
    2.0.5
    Author:
    Leonardo Uribe
    • Constructor Detail

      • FactoryFinderProviderFactory

        public FactoryFinderProviderFactory()
    • Method Detail

      • setInstance

        public static void setInstance​(FactoryFinderProviderFactory instance)
        Set the instance to be used by FactoryFinder to resolve factories.

        This method should be called before any "web context" is initialized in the current "classloader context". For example, if a EAR file contains two WAR files, this method should be called before initialize any WAR, since each one requires a different "web context"

        Parameters:
        instance -
      • getFactoryFinderProvider

        public abstract FactoryFinderProvider getFactoryFinderProvider()
        Provide the FactoryFinderProvider to be used to resolve factories. Subclasses must implement this method.
        Returns: