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