All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.spi.FactoryFinderProviderFactory Maven / Gradle / Ivy

Go to download

The private implementation classes of the Apache MyFaces Core JSF-2.0 Implementation

There is a newer version: 4.1.0-RC2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.spi;

import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.myfaces.shared.util.ClassUtils;

/**
 * 

{@link javax.faces.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 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) (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 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 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. *
  3. 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.
  4. *
  5. Add utility methods to retrieve the required objects and call the methods * using reflection from javax.faces.FactoryFinder
  6. *
* *

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 * */ public abstract class FactoryFinderProviderFactory { private static volatile FactoryFinderProviderFactory _instance = null; /** * Set the instance to be used by {@link javax.faces.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"

* * @param instance */ public static void setInstance(FactoryFinderProviderFactory instance) { // Now we need to make sure the volatile var FactoryFinder._initialized is // set to false, to make sure the right factory is fetched after this method // exists. It is just a fail-safe, because after all if the conditions to make // this call are met, _initialized should be false. try { Class clazz = ClassUtils.classForName("javax.faces.FactoryFinder"); Field field = clazz.getDeclaredField("_initialized"); field.setAccessible(true); if (field.getBoolean(null)) { Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName()); if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, "Called FactoryFinderProviderFactory.setFactory after " + "initialized FactoryFinder (first call to getFactory() or setFactory()). " + "This method should be called before " + "any 'web context' is initialized in the current 'classloader context'. " + "By that reason it will not be changed."); } else { _instance = instance; } field.setBoolean(null, false); } catch (Exception e) { // No Op Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName()); if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Cannot access field _initialized" + "from FactoryFinder ", e); } } /** * Retrieve the installed instance of this class to be used by * {@link javax.faces.FactoryFinder}. If no factory is set, return null * * @return */ public static FactoryFinderProviderFactory getInstance() { return _instance; } /** * Provide the FactoryFinderProvider to be used to resolve factories. * Subclasses must implement this method. * * @return */ public abstract FactoryFinderProvider getFactoryFinderProvider(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy