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 Sun's Implementation of the JSF 1.2 Specification.

There is a newer version: 2.1
Show newest version
/*
 * $Id: FactoryFinder.java,v 1.37.4.1 2008/06/09 18:32:11 rlubke Exp $
 */

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. 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.html
 * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [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 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.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Future;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
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;


/**
 * 

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 private static final FactoryManagerCache FACTORIES_CACHE = new FactoryManagerCache(); /** *

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(); 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 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(); 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) { 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; // 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); } 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) { 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); } /** * 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); } } // ----------------------------------------------------------- Inner Classes /** * Managed the mappings between a web application and its configured * factories. */ private static final class FactoryManagerCache { private ConcurrentMap> applicationMap = new ConcurrentHashMap>(); // ------------------------------------------------------ Public Methods private FactoryManager getApplicationFactoryManager(ClassLoader cl) { while (true) { Future factories = applicationMap.get(cl); if (factories == null) { Callable callable = new Callable() { public FactoryManager call() throws Exception { return new FactoryManager(); } }; FutureTask ft = new FutureTask(callable); factories = applicationMap.putIfAbsent(cl, ft); if (factories == null) { factories = ft; ft.run(); } } try { return factories.get(); } catch (CancellationException ce) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, ce.toString(), ce); } applicationMap.remove(cl); } catch (InterruptedException ie) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, ie.toString(), ie); } applicationMap.remove(cl); } catch (ExecutionException ee) { throw new FacesException(ee); } } } public void removeApplicationFactoryManager(ClassLoader cl) { applicationMap.remove(cl); } } // END FactoryCache /** * Maintains the factories for a single web application. */ private static final class FactoryManager { private final Map factories; private final ReentrantReadWriteLock lock; // -------------------------------------------------------- Consturctors public FactoryManager() { factories = new HashMap(); for (String name : FACTORY_NAMES) { factories.put(name, new ArrayList(4)); } lock = new ReentrantReadWriteLock(true); } // ------------------------------------------------------ Public Methods 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(); } } 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; } 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); throw new IllegalStateException(message); } // Record and return the new instance factories.put(factoryName, factory); return (factory); } finally { lock.writeLock().unlock(); } } } // END FactoryManager }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy