Hi
I thought a lot about FactoryFinder and how we can "extend" it. I'll do a
resume, describing the problem in detail and providing some alternatives.
This could be a little bit redundant, but it is necessary to gain a better
understanding about what's going on and the direction to take.
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...."
On the javadoc there is more information from the point of view of the
author (classloading stuff and so on), but just ignore it for a moment,
because it only makes things confusing.
In few words, this class allows to find JSF factory classes. The necessary
information to create factory instances is loaded on initialization time,
but which locations contains such information (for more information see JSF
2.0 spec section 11.4.2) (we are only interested 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 javax.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 we don't have 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 javax.faces.FactoryFinder
I provided a patch a patch for this one
(MYFACES-2995-FactoryFinderProvider-1.patch)
This patch has the following advantages:
1. No additional module is required
2. The default code works if no FactoryFinderProviderFactory is set, so the
spec javadoc is respected.
3. Code that is used as service provider interface (SPI) is where it should
be (on myfaces-impl class org.apache.myfaces.spi).
4. Solution is included on myfaces-bundle, without the need of maintain
another FactoryFinder instance.
It could be good if you can try it and if it works for you David, we can
close this issue.
regards,
Leonardo