001: /*
002: * Copyright 2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package javax.faces;
017:
018: import javax.faces.application.ApplicationFactory;
019: import javax.faces.context.FacesContextFactory;
020: import javax.faces.lifecycle.LifecycleFactory;
021: import javax.faces.render.RenderKitFactory;
022: import java.lang.reflect.Constructor;
023: import java.lang.reflect.InvocationTargetException;
024: import java.util.*;
025:
026: /**
027: * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
028: *
029: * @author Manfred Geiler (latest modification by $Author: mbr $)
030: * @version $Revision: 512227 $ $Date: 2007-02-27 13:25:16 +0100 (Di, 27 Feb 2007) $
031: */
032: public final class FactoryFinder {
033: public static final String APPLICATION_FACTORY = "javax.faces.application.ApplicationFactory";
034: public static final String FACES_CONTEXT_FACTORY = "javax.faces.context.FacesContextFactory";
035: public static final String LIFECYCLE_FACTORY = "javax.faces.lifecycle.LifecycleFactory";
036: public static final String RENDER_KIT_FACTORY = "javax.faces.render.RenderKitFactory";
037:
038: private static Map<ClassLoader, Map> _registeredFactoryNames = new HashMap<ClassLoader, Map>();
039: /**
040: * Maps from classLoader to another map, the container (i.e. Tomcat) will create a class loader for
041: * each web app that it controls (typically anyway) and that class loader is used as the key.
042: *
043: * The secondary map maps the factory name (i.e. FactoryFinder.APPLICATION_FACTORY) to actual instances
044: * that are created via getFactory. The instances will be of the class specified in the setFactory method
045: * for the factory name, i.e. FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY, MyFactory.class).
046: */
047: private static Map<ClassLoader, Map> _factories = new HashMap<ClassLoader, Map>();
048:
049: private static final Set<String> VALID_FACTORY_NAMES = new HashSet<String>();
050: private static final Map<String, Class> ABSTRACT_FACTORY_CLASSES = new HashMap<String, Class>();
051: static {
052: VALID_FACTORY_NAMES.add(APPLICATION_FACTORY);
053: VALID_FACTORY_NAMES.add(FACES_CONTEXT_FACTORY);
054: VALID_FACTORY_NAMES.add(LIFECYCLE_FACTORY);
055: VALID_FACTORY_NAMES.add(RENDER_KIT_FACTORY);
056:
057: ABSTRACT_FACTORY_CLASSES.put(APPLICATION_FACTORY,
058: ApplicationFactory.class);
059: ABSTRACT_FACTORY_CLASSES.put(FACES_CONTEXT_FACTORY,
060: FacesContextFactory.class);
061: ABSTRACT_FACTORY_CLASSES.put(LIFECYCLE_FACTORY,
062: LifecycleFactory.class);
063: ABSTRACT_FACTORY_CLASSES.put(RENDER_KIT_FACTORY,
064: RenderKitFactory.class);
065: }
066:
067: // avoid instantiation
068: FactoryFinder() {
069: }
070:
071: public static Object getFactory(String factoryName)
072: throws FacesException {
073: if (factoryName == null)
074: throw new NullPointerException(
075: "factoryName may not be null");
076:
077: ClassLoader classLoader = getClassLoader();
078: Map factoryClassNames = _registeredFactoryNames
079: .get(classLoader);
080:
081: if (factoryClassNames == null) {
082: String message = "No Factories configured for this Application. This happens if the faces-initialization "
083: + "does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application "
084: + "and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!"
085: + "\nIf you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which "
086: + "do not support registering context-listeners via TLD files and "
087: + "a context listener is not setup in your web.xml.\n"
088: + "A typical config looks like this;\n<listener>\n"
089: + " <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>\n"
090: + "</listener>\n";
091: throw new IllegalStateException(message);
092: }
093:
094: if (!factoryClassNames.containsKey(factoryName)) {
095: throw new IllegalArgumentException("no factory "
096: + factoryName + " configured for this application.");
097: }
098:
099: Map<String, Object> factoryMap = _factories.get(classLoader);
100:
101: if (factoryMap == null) {
102: factoryMap = new HashMap<String, Object>();
103: _factories.put(classLoader, factoryMap);
104: }
105: Object factory = factoryMap.get(factoryName);
106:
107: if (factory == null) {
108: List classNames = (List) factoryClassNames.get(factoryName);
109: factory = newFactoryInstance(ABSTRACT_FACTORY_CLASSES
110: .get(factoryName), classNames.iterator(),
111: classLoader);
112: factoryMap.put(factoryName, factory);
113: }
114: return factory;
115: }
116:
117: private static Object newFactoryInstance(Class interfaceClass,
118: Iterator classNamesIterator, ClassLoader classLoader) {
119: try {
120: Object current = null;
121:
122: while (classNamesIterator.hasNext()) {
123: String implClassName = (String) classNamesIterator
124: .next();
125: Class implClass = classLoader.loadClass(implClassName);
126:
127: // check, if class is of expected interface type
128: if (!interfaceClass.isAssignableFrom(implClass)) {
129: throw new IllegalArgumentException("Class "
130: + implClassName + " is no "
131: + interfaceClass.getName());
132: }
133:
134: if (current == null) {
135: // nothing to decorate
136: current = implClass.newInstance();
137: } else {
138: // let's check if class supports the decorator pattern
139: try {
140: Constructor delegationConstructor = implClass
141: .getConstructor(new Class[] { interfaceClass });
142: // impl class supports decorator pattern,
143: try {
144: // create new decorator wrapping current
145: current = delegationConstructor
146: .newInstance(new Object[] { current });
147: } catch (InstantiationException e) {
148: throw new FacesException(e);
149: } catch (IllegalAccessException e) {
150: throw new FacesException(e);
151: } catch (InvocationTargetException e) {
152: throw new FacesException(e);
153: }
154: } catch (NoSuchMethodException e) {
155: // no decorator pattern support
156: current = implClass.newInstance();
157: }
158: }
159: }
160:
161: return current;
162: } catch (ClassNotFoundException e) {
163: throw new FacesException(e);
164: } catch (InstantiationException e) {
165: throw new FacesException(e);
166: } catch (IllegalAccessException e) {
167: throw new FacesException(e);
168: }
169: }
170:
171: public static void setFactory(String factoryName, String implName) {
172: checkFactoryName(factoryName);
173:
174: ClassLoader classLoader = getClassLoader();
175: synchronized (_registeredFactoryNames) {
176: Map factories = _factories.get(classLoader);
177:
178: if (factories != null && factories.containsKey(factoryName)) {
179: // Javadoc says ... This method has no effect if getFactory() has already been
180: // called looking for a factory for this factoryName.
181: return;
182: }
183:
184: Map<String, List> factoryClassNames = _registeredFactoryNames
185: .get(classLoader);
186:
187: if (factoryClassNames == null) {
188: factoryClassNames = new HashMap<String, List>();
189: _registeredFactoryNames.put(classLoader,
190: factoryClassNames);
191: }
192:
193: List<String> classNameList = factoryClassNames
194: .get(factoryName);
195:
196: if (classNameList == null) {
197: classNameList = new ArrayList<String>();
198: factoryClassNames.put(factoryName, classNameList);
199: }
200: classNameList.add(implName);
201: }
202: }
203:
204: public static void releaseFactories() throws FacesException {
205: ClassLoader classLoader = getClassLoader();
206: _factories.remove(classLoader);
207: }
208:
209: private static void checkFactoryName(String factoryName) {
210: if (!VALID_FACTORY_NAMES.contains(factoryName)) {
211: throw new IllegalArgumentException("factoryName '"
212: + factoryName + "'");
213: }
214: }
215:
216: private static ClassLoader getClassLoader() {
217: try {
218: ClassLoader classLoader = Thread.currentThread()
219: .getContextClassLoader();
220: if (classLoader == null) {
221: throw new FacesException(
222: "web application class loader cannot be identified",
223: null);
224: }
225: return classLoader;
226: } catch (Exception e) {
227: throw new FacesException(
228: "web application class loader cannot be identified",
229: e);
230: }
231: }
232: }
|