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

javax.faces.FactoryFinder Maven / Gradle / Ivy

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.1 Specification.

There is a newer version: 2.1
Show newest version
/*
 * $Id: FactoryFinder.java,v 1.35 2007/01/29 22:29:08 rlubke Exp $
 */

/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at
 * https://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2005 Sun Microsystems Inc. All Rights Reserved
 */

package javax.faces;


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.logging.Logger;
import java.lang.reflect.Constructor;


/**
 * 

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 * factories 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 result 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 factories 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.FacesContextFactory} class name.

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

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"; // ------------------------------------------------------- Static Variables /** *

Keys are web application class loaders. Values are factory * maps for each web application.

*

*

For the nested map, the keys are the factoryName, which must * be one of the *_FACTORY constants above. Values are * one of:

*

*

    *

    *

  1. the actual factory class, if {@link getFactory} has been * called before on this factoryName

  2. *

    *

  3. An ArrayList of Strings * representing the configured implementations of for the * factoryName.

  4. *

    *

*/ @SuppressWarnings({"CollectionWithoutInitialCapacity"}) private static final Map> applicationMaps = new HashMap>(); /** *

The set of JavaServer Faces factory classes for which the factory * discovery mechanism is supported.

*/ private static final String[] FACTORY_NAMES = { APPLICATION_FACTORY, FACES_CONTEXT_FACTORY, LIFECYCLE_FACTORY, RENDER_KIT_FACTORY }; /** *

Map of Class instances for the our factory names.

*/ private static Map factoryClasses = null; private static final Logger 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.

* * @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(); synchronized (applicationMaps) { Map appMap = getApplicationMap(); // assert(null != appMap); Object factory; Object factoryOrList = appMap.get(factoryName); // If this factory has been retrieved before if (factoryOrList != null && (!(factoryOrList instanceof List))) { // just return it. return (factoryOrList); } // else, this factory has not been retrieved before; let's // find it. 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); } // Record and return the new instance appMap.put(factoryName, factory); return (factory); } } /** *

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); Object previouslySetFactories; Map appMap; synchronized (applicationMaps) { appMap = getApplicationMap(); // assert(null != appMap); // Has set or get been called on this factoryName before? if (null != (previouslySetFactories = appMap.get(factoryName))) { // Yes. Has get been called on this factoryName before? // If previouslySetFactories is not a List, get has been // called. if (!(previouslySetFactories instanceof List)) { // take no action. return; } // get has not been called, previouslySetFactories is a // List } else { // No. Create a List for this FactoryName. //noinspection CollectionWithoutInitialCapacity previouslySetFactories = new ArrayList(); appMap.put(factoryName, previouslySetFactories); } // Put this at the beginning of the list. (TypedCollections.dynamicallyCastList((List) previouslySetFactories, String.class)).add(0, implName); } } /** *

Release any references to factory instances associated with the * class loader for the calling web application. This method should be * called as apart of web application shutdown in a container where the * JavaServer Faces API classes are part of the container itself, rather * than being included inside the web application.

* * @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(); // Release any and all factory instances corresponding to this // class loader synchronized (applicationMaps) { HashMap map = (HashMap) applicationMaps.get(cl); if (map != null) { map.clear(); applicationMaps.remove(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. if (null != (curImplClass = getImplNameFromServices(classLoader, factoryName))) { result = getImplGivenPreviousImpl(classLoader, factoryName, curImplClass, 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 getImplementationInstance}.

*/ private static String getImplNameFromServices(ClassLoader classLoader, String factoryName) { // Check for a services definition String result = null; BufferedReader reader = null; String resourceName = "META-INF/services/" + factoryName; InputStream stream = null; try { stream = classLoader.getResourceAsStream(resourceName); 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")); } catch (UnsupportedEncodingException e) { reader = new BufferedReader(new InputStreamReader(stream)); } result = reader.readLine(); } } catch (IOException e) { } catch (SecurityException e) { } finally { if (reader != null) { try { reader.close(); } catch (Throwable t) { ; } //noinspection UnusedAssignment reader = null; } if (stream != null) { try { stream.close(); } catch (Throwable t) { ; } //noinspection UnusedAssignment stream = null; } } 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; // if we have a previousImpl and the appropriate one arg ctor. if ((null != previousImpl) && (null != (factoryClass = getFactoryClass(classLoader, 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); } 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(ClassLoader classLoader, String factoryClassName) { if (null == factoryClasses) { factoryClasses = new HashMap(FACTORY_NAMES.length); factoryClasses.put(APPLICATION_FACTORY, javax.faces.application.ApplicationFactory.class); factoryClasses.put(FACES_CONTEXT_FACTORY, javax.faces.context.FacesContextFactory.class); factoryClasses.put(LIFECYCLE_FACTORY, javax.faces.lifecycle.LifecycleFactory.class); factoryClasses.put(RENDER_KIT_FACTORY, javax.faces.render.RenderKitFactory.class); } return factoryClasses.get(factoryClassName); } /** *

This method must only be called from within a synchronized * block for the {@link #applicationMaps} ivar.

*/ private static Map getApplicationMap() { // Identify the web application class loader ClassLoader classLoader = getClassLoader(); Map result ; // Return any previously instantiated factory instance (of the // specified name) for this web application result = applicationMaps.get(classLoader); if (result == null) { //noinspection CollectionWithoutInitialCapacity result = new HashMap(); applicationMaps.put(classLoader, result); } return result; } private static void validateFactoryName(String factoryName) { // Validate the requested factory name if (factoryName == null) { throw new NullPointerException(); } boolean found = false; for (int i = 0; i < FACTORY_NAMES.length; i++) { if (factoryName.equals(FACTORY_NAMES[i])) { found = true; break; } } if (!found) { throw new IllegalArgumentException(factoryName); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy