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  import java.lang.reflect.Constructor;
22  import java.lang.reflect.InvocationTargetException;
23  import java.security.AccessController;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import javax.faces.application.ApplicationFactory;
33  import javax.faces.component.visit.VisitContextFactory;
34  import javax.faces.context.ExceptionHandlerFactory;
35  import javax.faces.context.ExternalContextFactory;
36  import javax.faces.context.FacesContextFactory;
37  import javax.faces.context.PartialViewContextFactory;
38  import javax.faces.lifecycle.LifecycleFactory;
39  import javax.faces.render.RenderKitFactory;
40  import javax.faces.view.ViewDeclarationLanguageFactory;
41  import javax.faces.view.facelets.TagHandlerDelegateFactory;
42  
43  /**
44   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
45   * 
46   * @author Manfred Geiler (latest modification by $Author: bommel $)
47   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
48   */
49  public final class FactoryFinder
50  {
51      public static final String APPLICATION_FACTORY = "javax.faces.application.ApplicationFactory";
52      public static final String EXCEPTION_HANDLER_FACTORY = "javax.faces.context.ExceptionHandlerFactory";
53      public static final String EXTERNAL_CONTEXT_FACTORY = "javax.faces.context.ExternalContextFactory";
54      public static final String FACES_CONTEXT_FACTORY = "javax.faces.context.FacesContextFactory";
55      public static final String LIFECYCLE_FACTORY = "javax.faces.lifecycle.LifecycleFactory";
56      public static final String PARTIAL_VIEW_CONTEXT_FACTORY = "javax.faces.context.PartialViewContextFactory";
57      public static final String RENDER_KIT_FACTORY = "javax.faces.render.RenderKitFactory";
58      public static final String TAG_HANDLER_DELEGATE_FACTORY = "javax.faces.view.facelets.TagHandlerDelegateFactory";
59      public static final String VIEW_DECLARATION_LANGUAGE_FACTORY = "javax.faces.view.ViewDeclarationLanguageFactory";
60      public static final String VISIT_CONTEXT_FACTORY = "javax.faces.component.visit.VisitContextFactory";
61  
62      /**
63       * used as a monitor for itself and _factories. Maps in this map are used as monitors for themselves and the
64       * corresponding maps in _factories.
65       */
66      private static Map<ClassLoader, Map<String, List<String>>> _registeredFactoryNames = new HashMap<ClassLoader, Map<String, List<String>>>();
67  
68      /**
69       * Maps from classLoader to another map, the container (i.e. Tomcat) will create a class loader for each web app
70       * that it controls (typically anyway) and that class loader is used as the key.
71       * 
72       * The secondary map maps the factory name (i.e. FactoryFinder.APPLICATION_FACTORY) to actual instances that are
73       * created via getFactory. The instances will be of the class specified in the setFactory method for the factory
74       * name, i.e. FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY, MyFactory.class).
75       */
76      private static Map<ClassLoader, Map<String, Object>> _factories = new HashMap<ClassLoader, Map<String, Object>>();
77  
78      private static final Set<String> VALID_FACTORY_NAMES = new HashSet<String>();
79      private static final Map<String, Class<?>> ABSTRACT_FACTORY_CLASSES = new HashMap<String, Class<?>>();
80      private static final ClassLoader myFacesClassLoader;
81  
82      static
83      {
84          VALID_FACTORY_NAMES.add(APPLICATION_FACTORY);
85          VALID_FACTORY_NAMES.add(EXCEPTION_HANDLER_FACTORY);
86          VALID_FACTORY_NAMES.add(EXTERNAL_CONTEXT_FACTORY);
87          VALID_FACTORY_NAMES.add(FACES_CONTEXT_FACTORY);
88          VALID_FACTORY_NAMES.add(LIFECYCLE_FACTORY);
89          VALID_FACTORY_NAMES.add(PARTIAL_VIEW_CONTEXT_FACTORY);
90          VALID_FACTORY_NAMES.add(RENDER_KIT_FACTORY);
91          VALID_FACTORY_NAMES.add(TAG_HANDLER_DELEGATE_FACTORY);
92          VALID_FACTORY_NAMES.add(VIEW_DECLARATION_LANGUAGE_FACTORY);
93          VALID_FACTORY_NAMES.add(VISIT_CONTEXT_FACTORY);
94          
95          ABSTRACT_FACTORY_CLASSES.put(APPLICATION_FACTORY, ApplicationFactory.class);
96          ABSTRACT_FACTORY_CLASSES.put(EXCEPTION_HANDLER_FACTORY, ExceptionHandlerFactory.class);
97          ABSTRACT_FACTORY_CLASSES.put(EXTERNAL_CONTEXT_FACTORY, ExternalContextFactory.class);
98          ABSTRACT_FACTORY_CLASSES.put(FACES_CONTEXT_FACTORY, FacesContextFactory.class);
99          ABSTRACT_FACTORY_CLASSES.put(LIFECYCLE_FACTORY, LifecycleFactory.class);
100         ABSTRACT_FACTORY_CLASSES.put(PARTIAL_VIEW_CONTEXT_FACTORY, PartialViewContextFactory.class);
101         ABSTRACT_FACTORY_CLASSES.put(RENDER_KIT_FACTORY, RenderKitFactory.class);
102         ABSTRACT_FACTORY_CLASSES.put(TAG_HANDLER_DELEGATE_FACTORY, TagHandlerDelegateFactory.class);
103         ABSTRACT_FACTORY_CLASSES.put(VIEW_DECLARATION_LANGUAGE_FACTORY, ViewDeclarationLanguageFactory.class);
104         ABSTRACT_FACTORY_CLASSES.put(VISIT_CONTEXT_FACTORY, VisitContextFactory.class);
105         try
106         {
107             ClassLoader classLoader;
108             if (System.getSecurityManager() != null) {
109                 classLoader = (ClassLoader) AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() {
110                     public Object run() {
111                         return FactoryFinder.class.getClassLoader();
112                     }
113                 });
114             }
115             else {
116                 classLoader = FactoryFinder.class.getClassLoader();
117             }
118 
119             if (classLoader == null)
120             {
121                 throw new FacesException("jsf api class loader cannot be identified", null);
122             }
123             myFacesClassLoader = classLoader;
124         }
125         catch (Exception e)
126         {
127             throw new FacesException("jsf api class loader cannot be identified", e);
128         }
129     }
130 
131     // ~ Start FactoryFinderProvider Support ------------------------------------------------------------------------------------
132     
133     private static Object _factoryFinderProviderFactoryInstance;
134     
135     private static volatile boolean _initialized = false;
136     
137     private static void initializeFactoryFinderProviderFactory()
138     {
139         if (!_initialized)
140         {
141             _factoryFinderProviderFactoryInstance = _FactoryFinderProviderFactory.getInstance();
142             _initialized = true;
143         }
144     }
145 
146     // ~ End FactoryFinderProvider Support ------------------------------------------------------------------------------------
147 
148     // avoid instantiation
149     FactoryFinder()
150     {
151     }
152 
153     /**
154      * <p>
155      * Create (if necessary) and return a per-web-application instance of the appropriate implementation class for the
156      * specified JavaServer Faces factory class, based on the discovery algorithm described in the class description.
157      * </p>
158      * 
159      * <p>
160      * The standard factories and wrappers in JSF all implement the interface {@link FacesWrapper}. If the returned
161      * <code>Object</code> is an implementation of one of the standard factories, it must be legal to cast it to an
162      * instance of <code>FacesWrapper</code> and call {@link FacesWrapper#getWrapped()} on the instance.
163      * </p>
164      * 
165      * @param factoryName
166      *            Fully qualified name of the JavaServer Faces factory for which an implementation instance is requested
167      * 
168      * @return A per-web-application instance of the appropriate implementation class for the specified JavaServer Faces
169      *         factory class
170      * 
171      * @throws FacesException
172      *             if the web application class loader cannot be identified
173      * @throws FacesException
174      *             if an instance of the configured factory implementation class cannot be loaded
175      * @throws FacesException
176      *             if an instance of the configured factory implementation class cannot be instantiated
177      * @throws IllegalArgumentException
178      *             if <code>factoryname</code> does not identify a standard JavaServer Faces factory name
179      * @throws IllegalStateException
180      *             if there is no configured factory implementation class for the specified factory name
181      * @throws NullPointerException
182      *             if <code>factoryname</code> is null
183      */
184     public static Object getFactory(String factoryName) throws FacesException
185     {
186         initializeFactoryFinderProviderFactory();
187         
188         if (_factoryFinderProviderFactoryInstance == null)
189         {
190             // Do the typical stuff
191             return _getFactory(factoryName);
192         }
193         else
194         {
195             try
196             {
197                 //Obtain the FactoryFinderProvider instance for this context.
198                 Object ffp = _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD.invoke(
199                             _factoryFinderProviderFactoryInstance, null);
200                 
201                 //Call getFactory method and pass the params
202                 return _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_GET_FACTORY_METHOD.invoke(ffp, factoryName);
203             } catch (InvocationTargetException e) {
204                 Throwable targetException = e.getCause();
205                 if (targetException instanceof NullPointerException) {
206                     throw (NullPointerException) targetException;
207                 } else if (targetException instanceof FacesException) {
208                     throw (FacesException) targetException;
209                 } else if (targetException instanceof IllegalArgumentException) {
210                     throw (IllegalArgumentException) targetException;
211                 } else if (targetException instanceof IllegalStateException) {
212                     throw (IllegalStateException) targetException;
213                 } else if (targetException == null) {
214                     throw new FacesException(e);
215                 } else {
216                     throw new FacesException(targetException);
217                 }
218             } catch (Exception e) {
219                 //No Op
220                 throw new FacesException(e);
221             }
222         }
223     }
224 
225     private static Object _getFactory(String factoryName) throws FacesException
226     {
227         if (factoryName == null)
228         {
229             throw new NullPointerException("factoryName may not be null");
230         }
231 
232         ClassLoader classLoader = getClassLoader();
233 
234         // This code must be synchronized because this could cause a problem when
235         // using update feature each time of myfaces (org.apache.myfaces.CONFIG_REFRESH_PERIOD)
236         // In this moment, a concurrency problem could happen
237         Map<String, List<String>> factoryClassNames = null;
238         Map<String, Object> factoryMap = null;
239 
240         synchronized (_registeredFactoryNames)
241         {
242             factoryClassNames = _registeredFactoryNames.get(classLoader);
243 
244             if (factoryClassNames == null)
245             {
246                 String message = "No Factories configured for this Application. This happens if the faces-initialization "
247                         + "does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application "
248                         + "and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!"
249                         + "\nIf you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which "
250                         + "do not support registering context-listeners via TLD files and "
251                         + "a context listener is not setup in your web.xml.\n"
252                         + "A typical config looks like this;\n<listener>\n"
253                         + "  <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>\n"
254                         + "</listener>\n";
255                 throw new IllegalStateException(message);
256             }
257 
258             if (!factoryClassNames.containsKey(factoryName))
259             {
260                 throw new IllegalArgumentException("no factory " + factoryName + " configured for this application.");
261             }
262 
263             factoryMap = _factories.get(classLoader);
264 
265             if (factoryMap == null)
266             {
267                 factoryMap = new HashMap<String, Object>();
268                 _factories.put(classLoader, factoryMap);
269             }
270         }
271 
272         List<String> classNames;
273         Object factory;
274         synchronized (factoryClassNames)
275         {
276             factory = factoryMap.get(factoryName);
277             if (factory != null)
278             {
279                 return factory;
280             }
281 
282             classNames = factoryClassNames.get(factoryName);
283         }
284 
285         // release lock while calling out
286         factory = newFactoryInstance(ABSTRACT_FACTORY_CLASSES.get(factoryName), classNames.iterator(), classLoader);
287 
288         synchronized (factoryClassNames)
289         {
290             // check if someone else already installed the factory
291             if (factoryMap.get(factoryName) == null)
292             {
293                 factoryMap.put(factoryName, factory);
294             }
295         }
296 
297         return factory;
298     }
299 
300     private static Object newFactoryInstance(Class<?> interfaceClass, Iterator<String> classNamesIterator,
301                                              ClassLoader classLoader)
302     {
303         try
304         {
305             Object current = null;
306 
307             while (classNamesIterator.hasNext())
308             {
309                 String implClassName = classNamesIterator.next();
310                 Class<?> implClass = null;
311                 try {
312                     implClass = classLoader.loadClass(implClassName);
313                 } catch (ClassNotFoundException e) {
314                     implClass = myFacesClassLoader.loadClass(implClassName);
315                 }
316 
317                 // check, if class is of expected interface type
318                 if (!interfaceClass.isAssignableFrom(implClass))
319                 {
320                     throw new IllegalArgumentException("Class " + implClassName + " is no " + interfaceClass.getName());
321                 }
322 
323                 if (current == null)
324                 {
325                     // nothing to decorate
326                     current = implClass.newInstance();
327                 }
328                 else
329                 {
330                     // let's check if class supports the decorator pattern
331                     try
332                     {
333                         Constructor<?> delegationConstructor = implClass.getConstructor(new Class[] { interfaceClass });
334                         // impl class supports decorator pattern,
335                         try
336                         {
337                             // create new decorator wrapping current
338                             current = delegationConstructor.newInstance(new Object[] { current });
339                         }
340                         catch (InstantiationException e)
341                         {
342                             throw new FacesException(e);
343                         }
344                         catch (IllegalAccessException e)
345                         {
346                             throw new FacesException(e);
347                         }
348                         catch (InvocationTargetException e)
349                         {
350                             throw new FacesException(e);
351                         }
352                     }
353                     catch (NoSuchMethodException e)
354                     {
355                         // no decorator pattern support
356                         current = implClass.newInstance();
357                     }
358                 }
359             }
360 
361             return current;
362         }
363         catch (ClassNotFoundException e)
364         {
365             throw new FacesException(e);
366         }
367         catch (InstantiationException e)
368         {
369             throw new FacesException(e);
370         }
371         catch (IllegalAccessException e)
372         {
373             throw new FacesException(e);
374         }
375     }
376 
377     public static void setFactory(String factoryName, String implName)
378     {
379         initializeFactoryFinderProviderFactory();
380         
381         if (_factoryFinderProviderFactoryInstance == null)
382         {
383             // Do the typical stuff
384             _setFactory(factoryName, implName);
385         }
386         else
387         {
388             try
389             {
390                 //Obtain the FactoryFinderProvider instance for this context.
391                 Object ffp = _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD.invoke(
392                         _factoryFinderProviderFactoryInstance,null);
393                 
394                 //Call getFactory method and pass the params
395                 _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_SET_FACTORY_METHOD.invoke(ffp, factoryName, implName);
396             } catch (InvocationTargetException e) {
397                 Throwable targetException = e.getCause();
398                 if (targetException instanceof NullPointerException) {
399                     throw (NullPointerException) targetException;
400                 } else if (targetException instanceof FacesException) {
401                     throw (FacesException) targetException;
402                 } else if (targetException instanceof IllegalArgumentException) {
403                     throw (IllegalArgumentException) targetException;
404                 } else if (targetException == null) {
405                     throw new FacesException(e);
406                 } else {
407                     throw new FacesException(targetException);
408                 }
409             } catch (Exception e) {
410                 //No Op
411                 throw new FacesException(e);
412             }
413             
414         }
415     }
416 
417     private static void _setFactory(String factoryName, String implName)
418     {
419         checkFactoryName(factoryName);
420 
421         ClassLoader classLoader = getClassLoader();
422         Map<String, List<String>> factoryClassNames = null;
423         synchronized (_registeredFactoryNames)
424         {
425             Map<String, Object> factories = _factories.get(classLoader);
426 
427             if (factories != null && factories.containsKey(factoryName))
428             {
429                 // Javadoc says ... This method has no effect if getFactory() has already been
430                 // called looking for a factory for this factoryName.
431                 return;
432             }
433 
434             factoryClassNames = _registeredFactoryNames.get(classLoader);
435 
436             if (factoryClassNames == null)
437             {
438                 factoryClassNames = new HashMap<String, List<String>>();
439                 _registeredFactoryNames.put(classLoader, factoryClassNames);
440             }
441         }
442 
443         synchronized (factoryClassNames)
444         {
445             List<String> classNameList = factoryClassNames.get(factoryName);
446 
447             if (classNameList == null)
448             {
449                 classNameList = new ArrayList<String>();
450                 factoryClassNames.put(factoryName, classNameList);
451             }
452 
453             classNameList.add(implName);
454         }
455     }
456 
457     public static void releaseFactories() throws FacesException
458     {
459         initializeFactoryFinderProviderFactory();
460         
461         if (_factoryFinderProviderFactoryInstance == null)
462         {
463             // Do the typical stuff
464             _releaseFactories();
465         }
466         else
467         {
468             try
469             {
470                 //Obtain the FactoryFinderProvider instance for this context.
471                 Object ffp = _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD.invoke(
472                         _factoryFinderProviderFactoryInstance, null);
473                 
474                 //Call getFactory method and pass the params
475                 _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_RELEASE_FACTORIES_METHOD.invoke(ffp, null);
476             } catch (InvocationTargetException e) {
477                 Throwable targetException = e.getCause();
478                 if (targetException instanceof FacesException) {
479                     throw (FacesException) targetException;
480                 } else if (targetException == null) {
481                     throw new FacesException(e);
482                 } else {
483                     throw new FacesException(targetException);
484                 }
485             } catch (Exception e) {
486                 //No Op
487                 throw new FacesException(e);
488             }
489             
490         }
491     }
492 
493     private static void _releaseFactories() throws FacesException
494     {
495         ClassLoader classLoader = getClassLoader();
496 
497         // This code must be synchronized
498         synchronized (_registeredFactoryNames)
499         {
500             _factories.remove(classLoader);
501 
502             // _registeredFactoryNames has as value type Map<String,List> and this must
503             // be cleaned before release (for gc).
504             Map<String, List<String>> factoryClassNames = _registeredFactoryNames.get(classLoader);
505             if (factoryClassNames != null)
506             {
507                 factoryClassNames.clear();
508             }
509 
510             _registeredFactoryNames.remove(classLoader);
511         }
512     }
513 
514     private static void checkFactoryName(String factoryName)
515     {
516         if (!VALID_FACTORY_NAMES.contains(factoryName))
517         {
518             throw new IllegalArgumentException("factoryName '" + factoryName + "'");
519         }
520     }
521 
522     private static ClassLoader getClassLoader()
523     {
524         try
525         {
526             ClassLoader classLoader = null;
527             if (System.getSecurityManager() != null) {                
528                 classLoader = (ClassLoader) AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() {
529                     public Object run() {
530                         return Thread.currentThread().getContextClassLoader();
531                     }
532                 });
533             }
534             else {
535                 classLoader = Thread.currentThread().getContextClassLoader();
536             }
537             
538             if (classLoader == null)
539             {
540                 throw new FacesException("web application class loader cannot be identified", null);
541             }
542             return classLoader;
543         }
544         catch (Exception e)
545         {
546             throw new FacesException("web application class loader cannot be identified", e);
547         }
548     }
549 }