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

javax.faces.FactoryFinder Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.faces;


import com.sun.faces.spi.InjectionProvider;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;


/**
 * 

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. Items are listed in order of decreasing * search precedence:

*
    *
  • If the JavaServer Faces configuration file bundled into the * WEB-INF directory of the webapp contains a * factory entry of the given factory class name, that * factory is used.

  • *
  • If the JavaServer Faces configuration files named by the * javax.faces.CONFIG_FILES ServletContext init * parameter contain any factory entries of the given * factory class name, those injectionProvider are used, with the last one taking * precedence.

  • *
  • If there are any JavaServer Faces configuration files bundled * into the META-INF directory of any jars on the * ServletContext's resource paths, the * factory entries of the given factory class name in those * files are used, with the last one taking precedence.

  • *
  • If a META-INF/services/{factory-class-name} * resource is visible to the web application class loader for the * calling application (typically as a injectionProvider of being present in the * manifest of a JAR file), its first line is read and assumed to be the * name of the factory implementation class to use.

  • *
  • If none of the above steps yield a match, the JavaServer Faces * implementation specific class is used.

  • *
*

If any of the injectionProvider found on any of the steps above happen to * have a one-argument constructor, with argument the type being the * abstract factory class, that constructor is invoked, and the previous * match is passed to the constructor. For example, say the container * vendor provided an implementation of {@link * javax.faces.context.FacesContextFactory}, and identified it in * META-INF/services/javax.faces.context.FacesContextFactory * in a jar on the webapp ClassLoader. Also say this implementation * provided by the container vendor had a one argument constructor that * took a FacesContextFactory instance. The * FactoryFinder system would call that one-argument * constructor, passing the implementation of * FacesContextFactory provided by the JavaServer Faces * implementation.

*

If a Factory implementation does not provide a proper one-argument * constructor, it must provide a zero-arguments constructor in order to * be successfully instantiated.

*

Once the name of the factory implementation class is located, the * web application class loader for the calling application is requested * to load this class, and a corresponding instance of the class will be * created. A side effect of this rule is that each web application * will receive its own instance of each factory class, whether the * JavaServer Faces implementation is included within the web * application or is made visible through the container's facilities for * shared libraries.

*/ public final class FactoryFinder { // ----------------------------------------------------------- Constructors /** * Package-private constructor to disable instantiation of this class. */ FactoryFinder() { } // ----------------------------------------------------- Manifest Constants /** *

The property name for the * {@link javax.faces.application.ApplicationFactory} class name.

*/ public final static String APPLICATION_FACTORY = "javax.faces.application.ApplicationFactory"; /** *

The property name for the {@link * javax.faces.context.ExceptionHandlerFactory} class name.

*/ public final static String EXCEPTION_HANDLER_FACTORY = "javax.faces.context.ExceptionHandlerFactory"; /** *

The property name for the {@link * javax.faces.context.ExternalContextFactory} class name.

*/ public final static String EXTERNAL_CONTEXT_FACTORY = "javax.faces.context.ExternalContextFactory"; /** *

The property name for the * {@link javax.faces.context.FacesContextFactory} class name.

*/ public final static String FACES_CONTEXT_FACTORY = "javax.faces.context.FacesContextFactory"; /** *

The property name for the * {@link javax.faces.view.facelets.FaceletCacheFactory} class name.

* * @since 2.1 */ public final static String FACELET_CACHE_FACTORY = "javax.faces.view.facelets.FaceletCacheFactory"; /** *

The property name for the * {@link javax.faces.context.FlashFactory} class name.

* * @since 2.2 */ public final static String FLASH_FACTORY = "javax.faces.context.FlashFactory"; /** *

The property name for the * {@link javax.faces.flow.FlowHandlerFactory} class name.

* * @since 2.2 */ public final static String FLOW_HANDLER_FACTORY = "javax.faces.flow.FlowHandlerFactory"; /** *

The property name for the {@link * javax.faces.context.PartialViewContextFactory} class name.

*/ public final static String PARTIAL_VIEW_CONTEXT_FACTORY = "javax.faces.context.PartialViewContextFactory"; /** *

The property name for the {@link * javax.faces.component.visit.VisitContextFactory} class name.

*/ public final static String VISIT_CONTEXT_FACTORY = "javax.faces.component.visit.VisitContextFactory"; /** *

The property name for the * {@link javax.faces.lifecycle.LifecycleFactory} class name.

*/ public final static String LIFECYCLE_FACTORY = "javax.faces.lifecycle.LifecycleFactory"; /** *

The property name for the * {@link javax.faces.render.RenderKitFactory} class name.

*/ public final static String RENDER_KIT_FACTORY = "javax.faces.render.RenderKitFactory"; /** *

The property name for the {@link * javax.faces.view.ViewDeclarationLanguage} class name.

*/ public final static String VIEW_DECLARATION_LANGUAGE_FACTORY = "javax.faces.view.ViewDeclarationLanguageFactory"; /** *

The property name for the {@link * javax.faces.view.facelets.TagHandlerDelegate} class name.

*/ public final static String TAG_HANDLER_DELEGATE_FACTORY = "javax.faces.view.facelets.TagHandlerDelegateFactory"; // ------------------------------------------------------- Static Variables private static final FactoryManagerCache FACTORIES_CACHE; /** *

The set of JavaServer Faces factory classes for which the factory * discovery mechanism is supported. The entries in this list must be * alphabetically ordered according to the entire string of the * *value* of each of the literals, not just * the last part of the literal!

*/ private static final String[] FACTORY_NAMES; /** *

Map of Class instances for the our factory names.

*/ private static Map factoryClasses; private static final Logger LOGGER; static { FACTORIES_CACHE = new FactoryManagerCache(); FACTORY_NAMES = new String [] { APPLICATION_FACTORY, VISIT_CONTEXT_FACTORY, EXCEPTION_HANDLER_FACTORY, EXTERNAL_CONTEXT_FACTORY, FACES_CONTEXT_FACTORY, FLASH_FACTORY, FLOW_HANDLER_FACTORY, PARTIAL_VIEW_CONTEXT_FACTORY, LIFECYCLE_FACTORY, RENDER_KIT_FACTORY, VIEW_DECLARATION_LANGUAGE_FACTORY, FACELET_CACHE_FACTORY, TAG_HANDLER_DELEGATE_FACTORY }; // Ensure the factory names are sorted. // Arrays.sort(FACTORY_NAMES); factoryClasses = new HashMap(FACTORY_NAMES.length); factoryClasses.put(APPLICATION_FACTORY, javax.faces.application.ApplicationFactory.class); factoryClasses.put(VISIT_CONTEXT_FACTORY, javax.faces.component.visit.VisitContextFactory.class); factoryClasses.put(EXCEPTION_HANDLER_FACTORY, javax.faces.context.ExceptionHandlerFactory.class); factoryClasses.put(EXTERNAL_CONTEXT_FACTORY, javax.faces.context.ExternalContextFactory.class); factoryClasses.put(FACES_CONTEXT_FACTORY, javax.faces.context.FacesContextFactory.class); factoryClasses.put(FLASH_FACTORY, javax.faces.context.FlashFactory.class); factoryClasses.put(PARTIAL_VIEW_CONTEXT_FACTORY, javax.faces.context.PartialViewContextFactory.class); factoryClasses.put(LIFECYCLE_FACTORY, javax.faces.lifecycle.LifecycleFactory.class); factoryClasses.put(RENDER_KIT_FACTORY, javax.faces.render.RenderKitFactory.class); factoryClasses.put(VIEW_DECLARATION_LANGUAGE_FACTORY, javax.faces.view.ViewDeclarationLanguageFactory.class); factoryClasses.put(FACELET_CACHE_FACTORY, javax.faces.view.facelets.FaceletCacheFactory.class); factoryClasses.put(TAG_HANDLER_DELEGATE_FACTORY, javax.faces.view.facelets.TagHandlerDelegateFactory.class); factoryClasses.put(FLOW_HANDLER_FACTORY, javax.faces.flow.FlowHandlerFactory.class); LOGGER = Logger.getLogger("javax.faces", "javax.faces.LogStrings"); } // --------------------------------------------------------- Public Methods /** *

Create (if * necessary) and return a per-web-application instance of the * appropriate implementation class for the specified JavaServer * Faces factory class, based on the discovery algorithm described * in the class description.

* *

The standard injectionProvider and wrappers * in JSF all implement the interface {@link FacesWrapper}. If the * returned Object is an implementation of one of the * standard injectionProvider, it must be legal to cast it to an instance of * FacesWrapper and call {@link * FacesWrapper#getWrapped} on the instance.

* * @param factoryName Fully qualified name of the JavaServer Faces factory * for which an implementation instance is requested * @throws FacesException if the web application class loader * cannot be identified * @throws FacesException if an instance of the configured factory * implementation class cannot be loaded * @throws FacesException if an instance of the configured factory * implementation class cannot be instantiated * @throws IllegalArgumentException if factoryName does not * identify a standard JavaServer Faces factory name * @throws IllegalStateException if there is no configured factory * implementation class for the specified factory name * @throws NullPointerException if factoryname * is null */ public static Object getFactory(String factoryName) throws FacesException { validateFactoryName(factoryName); // Identify the web application class loader ClassLoader classLoader = getClassLoader(); FactoryManager manager = FACTORIES_CACHE.getApplicationFactoryManager(classLoader); return manager.getFactory(classLoader, factoryName); } /** *

This method will store the argument * factoryName/implName mapping in such a way that * {@link #getFactory} will find this mapping when searching for a * match.

*

*

This method has no effect if getFactory() has * already been called looking for a factory for this * factoryName.

*

*

This method can be used by implementations to store a factory * mapping while parsing the Faces configuration file

* * @throws IllegalArgumentException if factoryName does not * identify a standard JavaServer Faces factory name * @throws NullPointerException if factoryname * is null */ public static void setFactory(String factoryName, String implName) { validateFactoryName(factoryName); // Identify the web application class loader ClassLoader classLoader = getClassLoader(); FactoryManager manager = FACTORIES_CACHE.getApplicationFactoryManager(classLoader); manager.addFactory(factoryName, implName); } /** *

Release any * references to factory instances associated with the class loader * for the calling web application. This method must be called during of * web application shutdown.

* * @throws FacesException if the web application class loader * cannot be identified */ public static void releaseFactories() throws FacesException { // Identify the web application class loader ClassLoader cl = getClassLoader(); if (!FACTORIES_CACHE.applicationMap.isEmpty()) { FactoryManager fm = FACTORIES_CACHE.getApplicationFactoryManager(cl); InjectionProvider provider = fm.getInjectionProvider(); if (null != provider) { Collection factories = null; for (Map.Entry entry : FACTORIES_CACHE.applicationMap.entrySet()) { factories = entry.getValue().getFactories(); for (Object curFactory : factories) { try { provider.invokePreDestroy(curFactory); } catch (Exception ex) { if (LOGGER.isLoggable(Level.SEVERE)) { String message = MessageFormat.format("Unable to invoke @PreDestroy annotated methods on {0}.", curFactory); LOGGER.log(Level.SEVERE, message, ex); } } } } } else { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Unable to call @PreDestroy annotated methods because no InjectionProvider can be found. Does this container implement the Mojarra Injection SPI?"); } } } FACTORIES_CACHE.removeApplicationFactoryManager(cl); } // -------------------------------------------------------- Private Methods /** *

Identify and return the class loader that is associated with the * calling web application.

* * @throws FacesException if the web application class loader * cannot be identified */ 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); } /** *

Load and return an instance of the specified implementation * class using the following algorithm.

*

*

    *

    *

  1. If the argument implementations list has * more than one element, or exactly one element, interpret the * last element in the list to be the fully qualified class name of * a class implementing factoryName. Instantiate that * class and save it for return. If the * implementations list has only one element, skip * this step.

  2. *

    *

  3. Look for a resource called * /META-INF/services/<factoryName>. If found, * interpret it as a properties file, and read out the first entry. * Interpret the first entry as a fully qualify class name of a * class that implements factoryName. If we have an * instantiated factory from the previous step and the * implementing class has a one arg constructor of the type for * factoryName, instantiate it, passing the * instantiated factory from the previous step. If there is no one * arg constructor, just instantiate the zero arg constructor. Save * the newly instantiated factory for return, replacing the * instantiated factory from the previous step.

  4. *

    *

  5. Treat each remaining element in the * implementations list as a fully qualified class name * of a class implementing factoryName. If the current * element has a one arg constructor of the type for * factoryName, instantiate it, passing the * instantiated factory from the previous or step iteration. If * there is no one arg constructor, just instantiate the zero arg * constructor, replacing the instantiated factory from the previous * step or iteration.

  6. *

    *

  7. Return the saved factory

  8. *

    *

* * @param classLoader Class loader for the web application that will * be loading the implementation class * @param implementations A List of implementations for a given * factory class. * @throws FacesException if the specified implementation class * cannot be loaded * @throws FacesException if an instance of the specified implementation * class cannot be instantiated */ private static Object getImplementationInstance(ClassLoader classLoader, String factoryName, List implementations) throws FacesException { Object result = null; String curImplClass; int len; // step 1. if (null != implementations && (1 < (len = implementations.size()) || 1 == len)) { curImplClass = (String) implementations.remove(len - 1); // since this is the hard coded implementation default, // there is no preceding implementation, so don't bother // with a non-zero-arg ctor. result = getImplGivenPreviousImpl(classLoader, factoryName, curImplClass, null); } // step 2. List fromServices = getImplNameFromServices(classLoader, factoryName); if (fromServices != null) { for (String name : fromServices) { result = getImplGivenPreviousImpl(classLoader, factoryName, name, result); } } // step 3. if (null != implementations) { for (len = (implementations.size() - 1); 0 <= len; len--) { curImplClass = (String) implementations.remove(len); result = getImplGivenPreviousImpl(classLoader, factoryName, curImplClass, result); } } return result; } /** *

Perform the logic to get the implementation class for the * second step of {@link FactoryFinder#getImplementationInstance(ClassLoader, String, java.util.List)}.

*/ private static List getImplNameFromServices(ClassLoader classLoader, String factoryName) { // Check for a services definition List result = null; String resourceName = "META-INF/services/" + factoryName; InputStream stream; BufferedReader reader = null; try { Enumeration e = classLoader.getResources(resourceName); while (e.hasMoreElements()) { URL url = e.nextElement(); URLConnection conn = url.openConnection(); conn.setUseCaches(false); stream = conn.getInputStream(); if (stream != null) { // Deal with systems whose native encoding is possibly // different from the way that the services entry was created try { reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); if (result == null) { result = new ArrayList(3); } result.add(reader.readLine()); } catch (UnsupportedEncodingException uee) { // The DM_DEFAULT_ENCODING warning is acceptable here // because we explicitly *want* to use the Java runtime's // default encoding. reader = new BufferedReader(new InputStreamReader(stream)); } finally { if (reader != null) { reader.close(); reader = null; } if (stream != null) { stream.close(); //noinspection UnusedAssignment stream = null; } } } } } catch (IOException e) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, e.toString(), e); } } catch (SecurityException e) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, e.toString(), e); } } return result; } /** *

Implement the decorator pattern for the factory * implementation.

*

*

If previousImpl is non-null and the * class named by the argument implName has a one arg * contstructor of type factoryName, instantiate it, * passing previousImpl to the constructor.

*

*

Otherwise, we just instantiate and return * implName.

* * @param classLoader the ClassLoader from which to load the class * @param factoryName the fully qualified class name of the factory. * @param implName the fully qualified class name of a class that * implements the factory. * @param previousImpl if non-null, the factory * instance to be passed to the constructor of the new factory. */ private static Object getImplGivenPreviousImpl(ClassLoader classLoader, String factoryName, String implName, Object previousImpl) { Class clazz; Class factoryClass = null; Class[] getCtorArg; Object[] newInstanceArgs = new Object[1]; Constructor ctor; Object result = null; InjectionProvider provider = null; // if we have a previousImpl and the appropriate one arg ctor. if ((null != previousImpl) && (null != (factoryClass = getFactoryClass(factoryName)))) { try { clazz = Class.forName(implName, false, classLoader); getCtorArg = new Class[1]; getCtorArg[0] = factoryClass; ctor = clazz.getConstructor(getCtorArg); newInstanceArgs[0] = previousImpl; result = ctor.newInstance(newInstanceArgs); FactoryManager fm = FACTORIES_CACHE.getApplicationFactoryManager(classLoader); provider = fm.getInjectionProvider(); if (null != provider) { provider.inject(result); provider.invokePostConstruct(result); } else { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Unable to inject {0} because no InjectionProvider can be found. Does this container implement the Mojarra Injection SPI?", result); } } } catch (NoSuchMethodException nsme) { // fall through to "zero-arg-ctor" case factoryClass = null; } catch (Exception e) { throw new FacesException(implName, e); } } if (null == previousImpl || null == factoryClass) { // we have either no previousImpl or no appropriate one arg // ctor. try { clazz = Class.forName(implName, false, classLoader); // since this is the hard coded implementation default, // there is no preceding implementation, so don't bother // with a non-zero-arg ctor. result = clazz.newInstance(); } catch (Exception e) { throw new FacesException(implName, e); } } return result; } /** * @return the java.lang.Class for the argument * factory. */ private static Class getFactoryClass(String factoryClassName) { return factoryClasses.get(factoryClassName); } /** * Ensure the provided factory name is valid. */ private static void validateFactoryName(String factoryName) { if (factoryName == null) { throw new NullPointerException(); } if (Arrays.binarySearch(FACTORY_NAMES, factoryName) < 0) { throw new IllegalArgumentException(factoryName); } } private static void reInitializeFactoryManager() { FACTORIES_CACHE.resetSpecialInitializationCaseFlags(); } // ----------------------------------------------------------- Inner Classes /** * Managed the mappings between a web application and its configured * injectionProvider. */ private static final class FactoryManagerCache { private ConcurrentMap applicationMap = new ConcurrentHashMap(); private AtomicBoolean logNullFacesContext = new AtomicBoolean(false); private AtomicBoolean logNonNullFacesContext = new AtomicBoolean(false); // ------------------------------------------------------ Public Methods private Object getFallbackFactory(ClassLoader cl, FactoryManager brokenFactoryManager, String factoryName) { Object result = null; for (Map.Entry cur : applicationMap.entrySet()) { if (cur.getKey().getClassLoader().equals(cl) && !cur.getValue().equals(brokenFactoryManager)) { result = cur.getValue().getFactory(cl, factoryName); if (null != result) { break; } } } return result; } private FactoryManager getApplicationFactoryManager(ClassLoader cl) { FactoryManager result = getApplicationFactoryManager(cl, true); return result; } private FactoryManager getApplicationFactoryManager(ClassLoader cl, boolean create) { FacesContext facesContext = FacesContext.getCurrentInstance(); boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext); FactoryManagerCacheKey key = new FactoryManagerCacheKey(facesContext, cl, applicationMap); FactoryManager result = applicationMap.get(key); FactoryManager toCopy = null; if (result == null && create) { boolean createNewFactoryManagerInstance = false; if (isSpecialInitializationCase) { // We need to obtain a reference to the correct // FactoryManager. Iterate through the data structure // containing all FactoryManager instances for this VM. FactoryManagerCacheKey curKey; boolean classLoadersMatchButContextsDoNotMatch = false; boolean foundNoMatchInApplicationMap = true; for (Map.Entry cur : applicationMap.entrySet()) { curKey = cur.getKey(); // If the current FactoryManager is for a // the same ClassLoader as the current ClassLoader... if (curKey.getClassLoader().equals(cl)) { foundNoMatchInApplicationMap = false; // Check the other descriminator for the // key: the context. // If the context objects of the keys are // both non-null and non-equal, then *do* // create a new FactoryManager instance. if ((null != key.getContext() && null != curKey.getContext()) && (!key.getContext().equals(curKey.getContext()))) { classLoadersMatchButContextsDoNotMatch = true; toCopy = cur.getValue(); } else { // Otherwise, use this FactoryManager // instance. result = cur.getValue(); } break; } } // We must create a new FactoryManager if there was no match // at all found in the applicationMap, or a match was found // and the match is safe to use in this web app createNewFactoryManagerInstance = foundNoMatchInApplicationMap || (null == result && classLoadersMatchButContextsDoNotMatch); } else { createNewFactoryManagerInstance = true; } if (createNewFactoryManagerInstance) { FactoryManager newResult; if (null != toCopy) { newResult = new FactoryManager(toCopy); } else { newResult = new FactoryManager(); } result = applicationMap.putIfAbsent(key, newResult); result = (null != result) ? result : newResult; } } return result; } /** * This method is used to detect the following special initialization case. * IF no FactoryManager can be found for key, * AND this call to getApplicationFactoryManager() *does* have a current FacesContext * BUT a previous call to getApplicationFactoryManager *did not* have a current FacesContext * * @param facesContext the current FacesContext for this request * @return true if the current execution falls into the special initialization case. */ private boolean detectSpecialInitializationCase(FacesContext facesContext) { boolean result = false; if (null == facesContext) { logNullFacesContext.compareAndSet(false, true); } else { logNonNullFacesContext.compareAndSet(false, true); } result = logNullFacesContext.get() && logNonNullFacesContext.get(); return result; } public void removeApplicationFactoryManager(ClassLoader cl) { FactoryManager fm = this.getApplicationFactoryManager(cl, false); if (null != fm) { fm.clearInjectionProvider(); } FacesContext facesContext = FacesContext.getCurrentInstance(); boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext); FactoryManagerCacheKey key = new FactoryManagerCacheKey(facesContext, cl, applicationMap); applicationMap.remove(key); if (isSpecialInitializationCase) { logNullFacesContext.set(false); logNonNullFacesContext.set(false); } } public void resetSpecialInitializationCaseFlags() { logNullFacesContext.set(false); logNonNullFacesContext.set(false); } } // END FactoryCache private static final class FactoryManagerCacheKey { private ClassLoader cl; private Long marker; private Object context; private static final String KEY = FactoryFinder.class.getName() + "." + FactoryManagerCacheKey.class.getSimpleName(); public FactoryManagerCacheKey(FacesContext facesContext, ClassLoader cl, Map factoryMap) { this.cl = cl; boolean resolveValueFromFactoryMap = false; if (null == facesContext) { resolveValueFromFactoryMap = true; } else { ExternalContext extContext = facesContext.getExternalContext(); context = extContext.getContext(); if (null == context) { resolveValueFromFactoryMap = true; } else { Map appMap = extContext.getApplicationMap(); Long val = (Long) appMap.get(KEY); if (null == val) { marker = new Long(System.currentTimeMillis()); appMap.put(KEY, marker); } else { marker = val; } } } if (resolveValueFromFactoryMap) { // We don't have a FacesContext. // Our only recourse is to inspect the keys of the // factoryMap and see if any of them has a classloader // equal to our argument cl. Set keys = factoryMap.keySet(); FactoryManagerCacheKey match = null; for (FactoryManagerCacheKey cur : keys) { if (this.cl.equals(cur.cl)) { if (null != cur && null != match) { LOGGER.log(Level.WARNING, "Multiple JSF Applications found on same ClassLoader. Unable to safely determine which FactoryManager instance to use. Defaulting to first match."); break; } match = cur; } } if (null != match) { this.marker = match.marker; } } } public ClassLoader getClassLoader() { return cl; } public Object getContext() { return context; } private FactoryManagerCacheKey() {} @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final FactoryManagerCacheKey other = (FactoryManagerCacheKey) obj; if (this.cl != other.cl && (this.cl == null || !this.cl.equals(other.cl))) { return false; } if (this.marker != other.marker && (this.marker == null || !this.marker.equals(other.marker))) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 97 * hash + (this.cl != null ? this.cl.hashCode() : 0); hash = 97 * hash + (this.marker != null ? this.marker.hashCode() : 0); return hash; } } /** * Maintains the injectionProvider for a single web application. */ private static final class FactoryManager { private final Map factories; private final Map> savedFactoryNames; private final ReentrantReadWriteLock lock; private static final String INJECTION_PROVIDER_KEY = FactoryFinder.class.getPackage().getName() + "INJECTION_PROVIDER_KEY"; // -------------------------------------------------------- Consturctors public FactoryManager() { lock = new ReentrantReadWriteLock(true); factories = new HashMap(); savedFactoryNames = new HashMap>(); for (String name : FACTORY_NAMES) { factories.put(name, new ArrayList(4)); } copyInjectionProviderFromFacesContext(); } public FactoryManager(FactoryManager toCopy) { lock = new ReentrantReadWriteLock(true); factories = new HashMap(); savedFactoryNames = new HashMap>(); factories.putAll(toCopy.savedFactoryNames); copyInjectionProviderFromFacesContext(); } private void copyInjectionProviderFromFacesContext() { InjectionProvider injectionProvider = null; FacesContext context = FacesContext.getCurrentInstance(); if (null != context) { injectionProvider = (InjectionProvider) context.getAttributes().get("com.sun.faces.config.ConfigManager_INJECTION_PROVIDER_TASK"); } if (null != injectionProvider) { factories.put(INJECTION_PROVIDER_KEY, injectionProvider); } else { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Unable to obtain InjectionProvider from init time FacesContext. Does this container implement the Mojarra Injection SPI?"); } } } // ------------------------------------------------------ Public Methods public Collection getFactories() { return factories.values(); } public void addFactory(String factoryName, String implementation) { Object result = factories.get(factoryName); lock.writeLock().lock(); try { if (result instanceof List) { TypedCollections.dynamicallyCastList((List) result, String.class).add(0, implementation); } } finally { lock.writeLock().unlock(); } } InjectionProvider getInjectionProvider() { InjectionProvider result = (InjectionProvider) factories.get(INJECTION_PROVIDER_KEY); return result; } void clearInjectionProvider() { factories.remove(INJECTION_PROVIDER_KEY); } public Object getFactory(ClassLoader cl, String factoryName) { Object factoryOrList; lock.readLock().lock(); try { factoryOrList = factories.get(factoryName); if (!(factoryOrList instanceof List)) { return factoryOrList; } } finally { lock.readLock().unlock(); } // factory hasn't been constructed lock.writeLock().lock(); try { // double check the current value. Another thread // may have completed the initialization by the time // this thread entered this block factoryOrList = factories.get(factoryName); if (!(factoryOrList instanceof List)) { return factoryOrList; } savedFactoryNames.put(factoryName, new ArrayList((List)factoryOrList)); Object factory = getImplementationInstance(cl, factoryName, (List) factoryOrList); if (factory == null) { ResourceBundle rb = LOGGER.getResourceBundle(); String message = rb.getString("severe.no_factory"); message = MessageFormat.format(message, factoryName); if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, message); } factory = FACTORIES_CACHE.getFallbackFactory(cl, this, factoryName); if (null == factory) { message = rb.getString("severe.no_factory_backup_failed"); message = MessageFormat.format(message, factoryName); throw new IllegalStateException(message); } } // Record and return the new instance factories.put(factoryName, factory); return (factory); } finally { lock.writeLock().unlock(); } } } // END FactoryManager }