View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces;
20  
21  
22  import javax.faces.application.ApplicationFactory;
23  import javax.faces.context.FacesContextFactory;
24  import javax.faces.lifecycle.LifecycleFactory;
25  import javax.faces.render.RenderKitFactory;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.InvocationTargetException;
28  import java.util.*;
29  
30  /**
31   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
32   *
33   * @author Manfred Geiler (latest modification by $Author: skitching $)
34   * @version $Revision: 676298 $ $Date: 2008-07-13 05:31:48 -0500 (Sun, 13 Jul 2008) $
35   */
36  public final class FactoryFinder
37  {
38      public static final String APPLICATION_FACTORY = "javax.faces.application.ApplicationFactory";
39      public static final String FACES_CONTEXT_FACTORY = "javax.faces.context.FacesContextFactory";
40      public static final String LIFECYCLE_FACTORY = "javax.faces.lifecycle.LifecycleFactory";
41      public static final String RENDER_KIT_FACTORY = "javax.faces.render.RenderKitFactory";
42  
43      /**
44       * used as a monitor for itself and _factories.
45       * Maps in this map are used as monitors for themselves and the corresponding maps in _factories.
46       */
47      private static Map<ClassLoader, Map> _registeredFactoryNames = new HashMap<ClassLoader, Map>();
48      /**
49       * Maps from classLoader to another map, the container (i.e. Tomcat) will create a class loader for
50       * each web app that it controls (typically anyway) and that class loader is used as the key.
51       *
52       * The secondary map maps the factory name (i.e. FactoryFinder.APPLICATION_FACTORY) to actual instances
53       * that are created via getFactory. The instances will be of the class specified in the setFactory method
54       * for the factory name, i.e. FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY, MyFactory.class).
55       */
56      private static Map<ClassLoader, Map> _factories = new HashMap<ClassLoader, Map>();
57  
58      private static final Set<String> VALID_FACTORY_NAMES = new HashSet<String>();
59      private static final Map<String, Class> ABSTRACT_FACTORY_CLASSES = new HashMap<String, Class>();
60      static {
61          VALID_FACTORY_NAMES.add(APPLICATION_FACTORY);
62          VALID_FACTORY_NAMES.add(FACES_CONTEXT_FACTORY);
63          VALID_FACTORY_NAMES.add(LIFECYCLE_FACTORY);
64          VALID_FACTORY_NAMES.add(RENDER_KIT_FACTORY);
65  
66          ABSTRACT_FACTORY_CLASSES.put(APPLICATION_FACTORY, ApplicationFactory.class);
67          ABSTRACT_FACTORY_CLASSES.put(FACES_CONTEXT_FACTORY, FacesContextFactory.class);
68          ABSTRACT_FACTORY_CLASSES.put(LIFECYCLE_FACTORY, LifecycleFactory.class);
69          ABSTRACT_FACTORY_CLASSES.put(RENDER_KIT_FACTORY, RenderKitFactory.class);
70      }
71  
72  
73    // avoid instantiation
74    FactoryFinder() {
75    }
76  
77    public static Object getFactory(String factoryName)
78              throws FacesException
79      {
80          if(factoryName == null)
81              throw new NullPointerException("factoryName may not be null");
82  
83          ClassLoader classLoader = getClassLoader();
84  
85          //This code must be synchronized because this could cause a problem when
86          //using update feature each time of myfaces (org.apache.myfaces.CONFIG_REFRESH_PERIOD)
87          //In this moment, a concurrency problem could happen
88          Map factoryClassNames = null;
89          Map<String, Object> factoryMap = null;
90          
91          synchronized(_registeredFactoryNames)
92          {
93              factoryClassNames = _registeredFactoryNames.get(classLoader);
94  
95              if (factoryClassNames == null)
96              {
97                  String message = "No Factories configured for this Application. This happens if the faces-initialization "+
98                      "does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application " +
99                      "and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!" +
100                     "\nIf you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which "+
101                     "do not support registering context-listeners via TLD files and " +
102                     "a context listener is not setup in your web.xml.\n" +
103                     "A typical config looks like this;\n<listener>\n" +
104                     "  <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>\n" +
105                     "</listener>\n";
106                 throw new IllegalStateException(message);
107             }
108 
109             if (! factoryClassNames.containsKey(factoryName))
110             {
111                 throw new IllegalArgumentException("no factory " + factoryName + " configured for this application.");
112             }
113 
114             factoryMap = _factories.get(classLoader);
115 
116             if (factoryMap == null)
117             {
118                 factoryMap = new HashMap<String, Object>();
119                 _factories.put(classLoader, factoryMap);
120             }
121         }
122         
123         List classNames = null;
124         Object factory = null;
125         synchronized (factoryClassNames)
126         {
127             factory = factoryMap.get(factoryName);
128             if (factory != null)
129             {
130                 return factory;
131             }
132             classNames = (List) factoryClassNames.get(factoryName);
133         }
134         
135         //release lock while calling out
136         factory = newFactoryInstance(ABSTRACT_FACTORY_CLASSES.get(factoryName), classNames.iterator(), classLoader);
137         
138         synchronized (factoryClassNames)
139         {
140             //check if someone else already installed the factory
141             if (factoryMap.get(factoryName) == null)
142             {
143                 factoryMap.put(factoryName, factory);
144             }            
145         }
146         return factory;
147     }
148 
149 
150     private static Object newFactoryInstance(Class interfaceClass, Iterator classNamesIterator, ClassLoader classLoader)
151     {
152         try
153         {
154             Object current = null;
155 
156             while (classNamesIterator.hasNext())
157             {
158                 String implClassName = (String) classNamesIterator.next();
159                 Class implClass = classLoader.loadClass(implClassName);
160 
161                 // check, if class is of expected interface type
162                 if (!interfaceClass.isAssignableFrom(implClass))
163                 {
164                     throw new IllegalArgumentException("Class " + implClassName + " is no " + interfaceClass.getName());
165                 }
166 
167                 if (current == null)
168                 {
169                     // nothing to decorate
170                     current = implClass.newInstance();
171                 } else
172                 {
173                     // let's check if class supports the decorator pattern
174                     try
175                     {
176                         Constructor delegationConstructor = implClass.getConstructor(new Class[]{interfaceClass});
177                         // impl class supports decorator pattern,
178                         try
179                         {
180                             // create new decorator wrapping current
181                             current = delegationConstructor.newInstance(new Object[]{current});
182                         } catch (InstantiationException e)
183                         {
184                             throw new FacesException(e);
185                         } catch (IllegalAccessException e)
186                         {
187                             throw new FacesException(e);
188                         } catch (InvocationTargetException e)
189                         {
190                             throw new FacesException(e);
191                         }
192                     } catch (NoSuchMethodException e)
193                     {
194                         // no decorator pattern support
195                         current = implClass.newInstance();
196                     }
197                 }
198             }
199 
200             return current;
201         } catch (ClassNotFoundException e)
202         {
203             throw new FacesException(e);
204         } catch (InstantiationException e)
205         {
206             throw new FacesException(e);
207         } catch (IllegalAccessException e)
208         {
209             throw new FacesException(e);
210         }
211     }
212 
213 
214     public static void setFactory(String factoryName,
215                                   String implName)
216     {
217         checkFactoryName(factoryName);
218 
219         ClassLoader classLoader = getClassLoader();
220         Map<String, List> factoryClassNames = null;
221         synchronized(_registeredFactoryNames)
222         {
223             Map factories = _factories.get(classLoader);
224 
225             if (factories != null && factories.containsKey(factoryName)) {
226                 // Javadoc says ... This method has no effect if getFactory() has already been
227                 // called looking for a factory for this factoryName.
228                 return;
229             }
230 
231             factoryClassNames = _registeredFactoryNames.get(classLoader);
232 
233             if (factoryClassNames == null)
234             {
235                 factoryClassNames = new HashMap<String, List>();
236                 _registeredFactoryNames.put(classLoader, factoryClassNames);
237             }
238         }
239         synchronized (factoryClassNames)
240         {
241             List<String> classNameList = factoryClassNames.get(factoryName);
242 
243             if (classNameList == null) 
244             {
245                 classNameList = new ArrayList<String>();
246                 factoryClassNames.put(factoryName, classNameList);
247             }
248             classNameList.add(implName);
249         }
250     }
251 
252 
253     public static void releaseFactories()
254             throws FacesException
255     {
256         ClassLoader classLoader = getClassLoader();
257 
258         //This code must be synchronized
259         synchronized(_registeredFactoryNames)
260         {
261             _factories.remove(classLoader);            
262             
263             // _registeredFactoryNames has as value type Map<String,List> and this must
264             //be cleaned before release (for gc).
265             Map factoryClassNames = (Map) _registeredFactoryNames.get(classLoader);
266             if (factoryClassNames != null) factoryClassNames.clear();
267             _registeredFactoryNames.remove(classLoader);
268         }
269     }
270 
271     private static void checkFactoryName(String factoryName)
272     {
273         if (! VALID_FACTORY_NAMES.contains(factoryName))
274         {
275             throw new IllegalArgumentException("factoryName '" + factoryName + "'");
276         }
277     }
278 
279 
280     private static ClassLoader getClassLoader()
281     {
282         try
283         {
284             ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
285             if (classLoader == null)
286             {
287                 throw new FacesException("web application class loader cannot be identified", null);
288             }
289             return classLoader;
290         }
291         catch (Exception e)
292         {
293             throw new FacesException("web application class loader cannot be identified", e);
294         }
295     }
296 }