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 javax.faces.application.ApplicationFactory;
22  import javax.faces.component.visit.VisitContextFactory;
23  import javax.faces.context.ExceptionHandlerFactory;
24  import javax.faces.context.ExternalContextFactory;
25  import javax.faces.context.FacesContext;
26  import javax.faces.context.FacesContextFactory;
27  import javax.faces.context.FlashFactory;
28  import javax.faces.context.PartialViewContextFactory;
29  import javax.faces.flow.FlowHandlerFactory;
30  import javax.faces.lifecycle.ClientWindowFactory;
31  import javax.faces.lifecycle.LifecycleFactory;
32  import javax.faces.render.RenderKitFactory;
33  import javax.faces.view.ViewDeclarationLanguageFactory;
34  import javax.faces.view.facelets.FaceletCacheFactory;
35  import javax.faces.view.facelets.TagHandlerDelegateFactory;
36  import java.lang.reflect.Constructor;
37  import java.lang.reflect.InvocationTargetException;
38  import java.lang.reflect.Method;
39  import java.security.AccessController;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.Iterator;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Set;
47  import java.util.concurrent.CopyOnWriteArrayList;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  
51  /**
52   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
53   */
54  public final class FactoryFinder
55  {
56      public static final String APPLICATION_FACTORY = "javax.faces.application.ApplicationFactory";
57      public static final String EXCEPTION_HANDLER_FACTORY = "javax.faces.context.ExceptionHandlerFactory";
58      public static final String EXTERNAL_CONTEXT_FACTORY = "javax.faces.context.ExternalContextFactory";
59      public static final String FACES_CONTEXT_FACTORY = "javax.faces.context.FacesContextFactory";
60      public static final String LIFECYCLE_FACTORY = "javax.faces.lifecycle.LifecycleFactory";
61      public static final String PARTIAL_VIEW_CONTEXT_FACTORY = "javax.faces.context.PartialViewContextFactory";
62      public static final String RENDER_KIT_FACTORY = "javax.faces.render.RenderKitFactory";
63      public static final String TAG_HANDLER_DELEGATE_FACTORY = "javax.faces.view.facelets.TagHandlerDelegateFactory";
64      public static final String VIEW_DECLARATION_LANGUAGE_FACTORY = "javax.faces.view.ViewDeclarationLanguageFactory";
65      public static final String VISIT_CONTEXT_FACTORY = "javax.faces.component.visit.VisitContextFactory";
66      public static final String FACELET_CACHE_FACTORY = "javax.faces.view.facelets.FaceletCacheFactory";
67      public static final String FLASH_FACTORY = "javax.faces.context.FlashFactory";
68      public static final String FLOW_HANDLER_FACTORY = "javax.faces.flow.FlowHandlerFactory";
69      public static final String CLIENT_WINDOW_FACTORY = "javax.faces.lifecycle.ClientWindowFactory";
70  
71      /**
72       * used as a monitor for itself and _factories. Maps in this map are used as monitors for themselves and the
73       * corresponding maps in _factories.
74       */
75      private static Map<ClassLoader, Map<String, List<String>>> registeredFactoryNames
76              = new HashMap<ClassLoader, Map<String, List<String>>>();
77  
78      /**
79       * Maps from classLoader to another map, the container (i.e. Tomcat) will create a class loader for each web app
80       * that it controls (typically anyway) and that class loader is used as the key.
81       * 
82       * The secondary map maps the factory name (i.e. FactoryFinder.APPLICATION_FACTORY) to actual instances that are
83       * created via getFactory. The instances will be of the class specified in the setFactory method for the factory
84       * name, i.e. FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY, MyFactory.class).
85       */
86      private static Map<ClassLoader, Map<String, Object>> factories
87              = new HashMap<ClassLoader, Map<String, Object>>();
88  
89      private static final Set<String> VALID_FACTORY_NAMES = new HashSet<String>();
90      private static final Map<String, Class<?>> ABSTRACT_FACTORY_CLASSES = new HashMap<String, Class<?>>();
91      private static final ClassLoader MYFACES_CLASSLOADER;
92      
93      private static final String INJECTION_PROVIDER_INSTANCE = "oam.spi.INJECTION_PROVIDER_KEY";
94      private static final String INJECTED_BEAN_STORAGE_KEY = "org.apache.myfaces.spi.BEAN_ENTRY_STORAGE";
95      private static final String BEAN_ENTRY_CLASS_NAME = "org.apache.myfaces.cdi.dependent.BeanEntry";
96  
97      private static final Logger LOGGER = Logger.getLogger(FactoryFinder.class.getName());
98  
99      static
100     {
101         VALID_FACTORY_NAMES.add(APPLICATION_FACTORY);
102         VALID_FACTORY_NAMES.add(EXCEPTION_HANDLER_FACTORY);
103         VALID_FACTORY_NAMES.add(EXTERNAL_CONTEXT_FACTORY);
104         VALID_FACTORY_NAMES.add(FACES_CONTEXT_FACTORY);
105         VALID_FACTORY_NAMES.add(LIFECYCLE_FACTORY);
106         VALID_FACTORY_NAMES.add(PARTIAL_VIEW_CONTEXT_FACTORY);
107         VALID_FACTORY_NAMES.add(RENDER_KIT_FACTORY);
108         VALID_FACTORY_NAMES.add(TAG_HANDLER_DELEGATE_FACTORY);
109         VALID_FACTORY_NAMES.add(VIEW_DECLARATION_LANGUAGE_FACTORY);
110         VALID_FACTORY_NAMES.add(VISIT_CONTEXT_FACTORY);
111         VALID_FACTORY_NAMES.add(FACELET_CACHE_FACTORY);
112         VALID_FACTORY_NAMES.add(FLASH_FACTORY);
113         VALID_FACTORY_NAMES.add(FLOW_HANDLER_FACTORY);
114         VALID_FACTORY_NAMES.add(CLIENT_WINDOW_FACTORY);
115         
116         ABSTRACT_FACTORY_CLASSES.put(APPLICATION_FACTORY, ApplicationFactory.class);
117         ABSTRACT_FACTORY_CLASSES.put(EXCEPTION_HANDLER_FACTORY, ExceptionHandlerFactory.class);
118         ABSTRACT_FACTORY_CLASSES.put(EXTERNAL_CONTEXT_FACTORY, ExternalContextFactory.class);
119         ABSTRACT_FACTORY_CLASSES.put(FACES_CONTEXT_FACTORY, FacesContextFactory.class);
120         ABSTRACT_FACTORY_CLASSES.put(LIFECYCLE_FACTORY, LifecycleFactory.class);
121         ABSTRACT_FACTORY_CLASSES.put(PARTIAL_VIEW_CONTEXT_FACTORY, PartialViewContextFactory.class);
122         ABSTRACT_FACTORY_CLASSES.put(RENDER_KIT_FACTORY, RenderKitFactory.class);
123         ABSTRACT_FACTORY_CLASSES.put(TAG_HANDLER_DELEGATE_FACTORY, TagHandlerDelegateFactory.class);
124         ABSTRACT_FACTORY_CLASSES.put(VIEW_DECLARATION_LANGUAGE_FACTORY, ViewDeclarationLanguageFactory.class);
125         ABSTRACT_FACTORY_CLASSES.put(VISIT_CONTEXT_FACTORY, VisitContextFactory.class);
126         ABSTRACT_FACTORY_CLASSES.put(FACELET_CACHE_FACTORY, FaceletCacheFactory.class);
127         ABSTRACT_FACTORY_CLASSES.put(FLASH_FACTORY, FlashFactory.class);
128         ABSTRACT_FACTORY_CLASSES.put(FLOW_HANDLER_FACTORY, FlowHandlerFactory.class);
129         ABSTRACT_FACTORY_CLASSES.put(CLIENT_WINDOW_FACTORY, ClientWindowFactory.class);
130         try
131         {
132             ClassLoader classLoader;
133             if (System.getSecurityManager() != null)
134             {
135                 classLoader = (ClassLoader) AccessController.doPrivileged(new java.security.PrivilegedExceptionAction()
136                 {
137                     public Object run()
138                     {
139                         return FactoryFinder.class.getClassLoader();
140                     }
141                 });
142             }
143             else
144             {
145                 classLoader = FactoryFinder.class.getClassLoader();
146             }
147 
148             if (classLoader == null)
149             {
150                 throw new FacesException("jsf api class loader cannot be identified", null);
151             }
152             MYFACES_CLASSLOADER = classLoader;
153         }
154         catch (Exception e)
155         {
156             throw new FacesException("jsf api class loader cannot be identified", e);
157         }
158     }
159 
160     // ~ Start FactoryFinderProvider Support
161     
162     private static Object factoryFinderProviderFactoryInstance;
163     
164     private static volatile boolean initialized = false;
165     
166     private static void initializeFactoryFinderProviderFactory()
167     {
168         if (!initialized)
169         {
170             factoryFinderProviderFactoryInstance = _FactoryFinderProviderFactory.getInstance();
171             initialized = true;
172         }
173     }
174 
175     // ~ End FactoryFinderProvider Support
176 
177     // avoid instantiation
178     FactoryFinder()
179     {
180     }
181 
182     /**
183      * <p>
184      * Create (if necessary) and return a per-web-application instance of the appropriate implementation class for the
185      * specified JavaServer Faces factory class, based on the discovery algorithm described in the class description.
186      * </p>
187      * 
188      * <p>
189      * The standard factories and wrappers in JSF all implement the interface {@link FacesWrapper}. If the returned
190      * <code>Object</code> is an implementation of one of the standard factories, it must be legal to cast it to an
191      * instance of <code>FacesWrapper</code> and call {@link FacesWrapper#getWrapped()} on the instance.
192      * </p>
193      * 
194      * @param factoryName
195      *            Fully qualified name of the JavaServer Faces factory for which an implementation instance is requested
196      * 
197      * @return A per-web-application instance of the appropriate implementation class for the specified JavaServer Faces
198      *         factory class
199      * 
200      * @throws FacesException
201      *             if the web application class loader cannot be identified
202      * @throws FacesException
203      *             if an instance of the configured factory implementation class cannot be loaded
204      * @throws FacesException
205      *             if an instance of the configured factory implementation class cannot be instantiated
206      * @throws IllegalArgumentException
207      *             if <code>factoryname</code> does not identify a standard JavaServer Faces factory name
208      * @throws IllegalStateException
209      *             if there is no configured factory implementation class for the specified factory name
210      * @throws NullPointerException
211      *             if <code>factoryname</code> is null
212      */
213     public static Object getFactory(String factoryName) throws FacesException
214     {
215         if (factoryName == null)
216         {
217             throw new NullPointerException("factoryName may not be null");
218         }
219         
220         initializeFactoryFinderProviderFactory();
221         
222         if (factoryFinderProviderFactoryInstance == null)
223         {
224             // Do the typical stuff
225             return _getFactory(factoryName);
226         }
227         else
228         {
229             try
230             {
231                 //Obtain the FactoryFinderProvider instance for this context.
232                 Object ffp = _FactoryFinderProviderFactory
233                         .FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD
234                         .invoke(factoryFinderProviderFactoryInstance, null);
235                 
236                 //Call getFactory method and pass the params
237                 return _FactoryFinderProviderFactory
238                         .FACTORY_FINDER_PROVIDER_GET_FACTORY_METHOD.invoke(ffp, factoryName);
239             }
240             catch (InvocationTargetException e)
241             {
242                 Throwable targetException = e.getCause();
243                 if (targetException instanceof NullPointerException)
244                 {
245                     throw (NullPointerException) targetException;
246                 }
247                 else if (targetException instanceof FacesException)
248                 {
249                     throw (FacesException) targetException;
250                 }
251                 else if (targetException instanceof IllegalArgumentException)
252                 {
253                     throw (IllegalArgumentException) targetException;
254                 }
255                 else if (targetException instanceof IllegalStateException)
256                 {
257                     throw (IllegalStateException) targetException;
258                 }
259                 else if (targetException == null)
260                 {
261                     throw new FacesException(e);
262                 }
263                 else
264                 {
265                     throw new FacesException(targetException);
266                 }
267             }
268             catch (Exception e)
269             {
270                 //No Op
271                 throw new FacesException(e);
272             }
273         }
274     }
275 
276     private static Object _getFactory(String factoryName) throws FacesException
277     {
278         ClassLoader classLoader = getClassLoader();
279 
280         // This code must be synchronized because this could cause a problem when
281         // using update feature each time of myfaces (org.apache.myfaces.CONFIG_REFRESH_PERIOD)
282         // In this moment, a concurrency problem could happen
283         Map<String, List<String>> factoryClassNames = null;
284         Map<String, Object> factoryMap = null;
285 
286         synchronized (registeredFactoryNames)
287         {
288             factoryClassNames = registeredFactoryNames.get(classLoader);
289 
290             if (factoryClassNames == null)
291             {
292                 String message
293                         = "No Factories configured for this Application. This happens if the faces-initialization "
294                         + "does not work at all - make sure that you properly include all configuration "
295                         + "settings necessary for a basic faces application "
296                         + "and that all the necessary libs are included. Also check the logging output of your "
297                         + "web application and your container for any exceptions!"
298                         + "\nIf you did that and find nothing, the mistake might be due to the fact "
299                         + "that you use some special web-containers which "
300                         + "do not support registering context-listeners via TLD files and "
301                         + "a context listener is not setup in your web.xml.\n"
302                         + "A typical config looks like this;\n<listener>\n"
303                         + "  <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>\n"
304                         + "</listener>\n";
305                 throw new IllegalStateException(message);
306             }
307 
308             if (!factoryClassNames.containsKey(factoryName))
309             {
310                 throw new IllegalArgumentException("no factory " + factoryName + " configured for this application.");
311             }
312 
313             factoryMap = factories.get(classLoader);
314 
315             if (factoryMap == null)
316             {
317                 factoryMap = new HashMap<String, Object>();
318                 factories.put(classLoader, factoryMap);
319             }
320         }
321 
322         List beanEntryStorage;
323 
324         synchronized (factoryClassNames)
325         {
326             beanEntryStorage = (List)factoryMap.get(INJECTED_BEAN_STORAGE_KEY);
327 
328             if (beanEntryStorage == null)
329             {
330                 beanEntryStorage = new CopyOnWriteArrayList();
331                 factoryMap.put(INJECTED_BEAN_STORAGE_KEY, beanEntryStorage);
332             }
333         }
334 
335         List<String> classNames;
336         Object factory;
337         Object injectionProvider;
338         synchronized (factoryClassNames)
339         {
340             factory = factoryMap.get(factoryName);
341             if (factory != null)
342             {
343                 return factory;
344             }
345 
346             classNames = factoryClassNames.get(factoryName);
347             
348             injectionProvider = factoryMap.get(INJECTION_PROVIDER_INSTANCE);
349         }
350 
351         if (injectionProvider == null)
352         {
353             injectionProvider = getInjectionProvider();
354             synchronized (factoryClassNames)
355             {
356                 factoryMap.put(INJECTION_PROVIDER_INSTANCE, injectionProvider);
357             }
358         }
359 
360         // release lock while calling out
361         factory = newFactoryInstance(ABSTRACT_FACTORY_CLASSES.get(factoryName), 
362             classNames.iterator(), classLoader, injectionProvider, beanEntryStorage);
363 
364         synchronized (factoryClassNames)
365         {
366             // check if someone else already installed the factory
367             if (factoryMap.get(factoryName) == null)
368             {
369                 factoryMap.put(factoryName, factory);
370             }
371         }
372 
373         return factory;
374     }
375     
376     private static Object getInjectionProvider()
377     {
378         try
379         {
380             // Remember the first call in a webapp over FactoryFinder.getFactory(...) comes in the 
381             // initialization block, so there is a startup FacesContext active and
382             // also a valid startup ExternalContext. Note after that, we need to cache
383             // the injection provider for the classloader, because in a normal
384             // request there is no active FacesContext in the moment and this call will
385             // surely fail.
386             FacesContext facesContext = FacesContext.getCurrentInstance();
387             if (facesContext != null)
388             {
389                 Object injectionProviderFactory =
390                     _FactoryFinderProviderFactory.INJECTION_PROVIDER_FACTORY_GET_INSTANCE_METHOD
391                         .invoke(_FactoryFinderProviderFactory.INJECTION_PROVIDER_CLASS);
392                 Object injectionProvider = 
393                     _FactoryFinderProviderFactory.INJECTION_PROVIDER_FACTORY_GET_INJECTION_PROVIDER_METHOD
394                         .invoke(injectionProviderFactory, facesContext.getExternalContext());
395                 return injectionProvider;
396             }
397         }
398         catch (Exception e)
399         {
400         }
401         return null;
402     }
403     
404     private static void injectAndPostConstruct(Object injectionProvider, Object instance, List injectedBeanStorage)
405     {
406         if (injectionProvider != null)
407         {
408             try
409             {
410                 Object creationMetaData = _FactoryFinderProviderFactory.INJECTION_PROVIDER_INJECT_METHOD.invoke(
411                     injectionProvider, instance);
412 
413                 addBeanEntry(instance, creationMetaData, injectedBeanStorage);
414 
415                 _FactoryFinderProviderFactory.INJECTION_PROVIDER_POST_CONSTRUCT_METHOD.invoke(
416                     injectionProvider, instance, creationMetaData);
417             }
418             catch (Exception ex)
419             {
420                 throw new FacesException(ex);
421             }
422         }
423     }
424     
425     private static void preDestroy(Object injectionProvider, Object beanEntry)
426     {
427         if (injectionProvider != null)
428         {
429             try
430             {
431                 _FactoryFinderProviderFactory.INJECTION_PROVIDER_PRE_DESTROY_METHOD.invoke(
432                     injectionProvider, getInstance(beanEntry), getCreationMetaData(beanEntry));
433             }
434             catch (Exception ex)
435             {
436                 throw new FacesException(ex);
437             }
438         }
439     }
440 
441     private static Object getInstance(Object beanEntry)
442     {
443         try
444         {
445             Method getterMethod = getMethod(beanEntry, "getInstance");
446             return getterMethod.invoke(beanEntry);
447         }
448         catch (Exception e)
449         {
450             throw new IllegalStateException(e);
451         }
452     }
453 
454     private static Object getCreationMetaData(Object beanEntry)
455     {
456         try
457         {
458             Method getterMethod = getMethod(beanEntry, "getCreationMetaData");
459             return getterMethod.invoke(beanEntry);
460         }
461         catch (Exception e)
462         {
463             throw new IllegalStateException(e);
464         }
465     }
466 
467     private static Method getMethod(Object beanEntry, String methodName) throws NoSuchMethodException
468     {
469         return beanEntry.getClass().getDeclaredMethod(methodName);
470     }
471 
472     private static void addBeanEntry(Object instance, Object creationMetaData, List injectedBeanStorage)
473     {
474         try
475         {
476             Class<?> beanEntryClass = _FactoryFinderProviderFactory.classForName(BEAN_ENTRY_CLASS_NAME);
477             Constructor beanEntryConstructor = beanEntryClass.getDeclaredConstructor(Object.class, Object.class);
478 
479             Object result = beanEntryConstructor.newInstance(instance, creationMetaData);
480             injectedBeanStorage.add(result);
481         }
482         catch (Exception e)
483         {
484             throw new RuntimeException(e);
485         }
486     }
487 
488     private static Object newFactoryInstance(Class<?> interfaceClass, Iterator<String> classNamesIterator,
489                                              ClassLoader classLoader, Object injectionProvider,
490                                              List injectedBeanStorage)
491     {
492         try
493         {
494             Object current = null;
495             
496             while (classNamesIterator.hasNext())
497             {
498                 String implClassName = classNamesIterator.next();
499                 Class<?> implClass = null;
500                 try
501                 {
502                     implClass = classLoader.loadClass(implClassName);
503                 }
504                 catch (ClassNotFoundException e)
505                 {
506                     implClass = MYFACES_CLASSLOADER.loadClass(implClassName);
507                 }
508 
509                 // check, if class is of expected interface type
510                 if (!interfaceClass.isAssignableFrom(implClass))
511                 {
512                     throw new IllegalArgumentException("Class " + implClassName + " is no " + interfaceClass.getName());
513                 }
514 
515                 if (current == null)
516                 {
517                     // nothing to decorate
518                     current = implClass.newInstance();
519                     injectAndPostConstruct(injectionProvider, current, injectedBeanStorage);
520                 }
521                 else
522                 {
523                     // let's check if class supports the decorator pattern
524                     try
525                     {
526                         Constructor<?> delegationConstructor = implClass.getConstructor(new Class[] { interfaceClass });
527                         // impl class supports decorator pattern,
528                         try
529                         {
530                             // create new decorator wrapping current
531                             current = delegationConstructor.newInstance(new Object[] { current });
532                             injectAndPostConstruct(injectionProvider, current, injectedBeanStorage);
533                         }
534                         catch (InstantiationException e)
535                         {
536                             throw new FacesException(e);
537                         }
538                         catch (IllegalAccessException e)
539                         {
540                             throw new FacesException(e);
541                         }
542                         catch (InvocationTargetException e)
543                         {
544                             throw new FacesException(e);
545                         }
546                     }
547                     catch (NoSuchMethodException e)
548                     {
549                         // no decorator pattern support
550                         current = implClass.newInstance();
551                         injectAndPostConstruct(injectionProvider, current, injectedBeanStorage);
552                     }
553                 }
554             }
555 
556             return current;
557         }
558         catch (ClassNotFoundException e)
559         {
560             throw new FacesException(e);
561         }
562         catch (InstantiationException e)
563         {
564             throw new FacesException(e);
565         }
566         catch (IllegalAccessException e)
567         {
568             throw new FacesException(e);
569         }
570     }
571 
572     public static void setFactory(String factoryName, String implName)
573     {
574         if (factoryName == null)
575         {
576             throw new NullPointerException("factoryName may not be null");
577         }
578         
579         initializeFactoryFinderProviderFactory();
580         
581         if (factoryFinderProviderFactoryInstance == null)
582         {
583             // Do the typical stuff
584             _setFactory(factoryName, implName);
585         }
586         else
587         {
588             try
589             {
590                 //Obtain the FactoryFinderProvider instance for this context.
591                 Object ffp = _FactoryFinderProviderFactory
592                         .FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD
593                         .invoke(factoryFinderProviderFactoryInstance, null);
594                 
595                 //Call getFactory method and pass the params
596                 _FactoryFinderProviderFactory
597                         .FACTORY_FINDER_PROVIDER_SET_FACTORY_METHOD.invoke(ffp, factoryName, implName);
598             }
599             catch (InvocationTargetException e)
600             {
601                 Throwable targetException = e.getCause();
602                 if (targetException instanceof NullPointerException)
603                 {
604                     throw (NullPointerException) targetException;
605                 }
606                 else if (targetException instanceof FacesException)
607                 {
608                     throw (FacesException) targetException;
609                 }
610                 else if (targetException instanceof IllegalArgumentException)
611                 {
612                     throw (IllegalArgumentException) targetException;
613                 }
614                 else if (targetException == null)
615                 {
616                     throw new FacesException(e);
617                 }
618                 else
619                 {
620                     throw new FacesException(targetException);
621                 }
622             }
623             catch (Exception e)
624             {
625                 //No Op
626                 throw new FacesException(e);
627             }
628             
629         }
630     }
631 
632     private static void _setFactory(String factoryName, String implName)
633     {
634         checkFactoryName(factoryName);
635 
636         ClassLoader classLoader = getClassLoader();
637         Map<String, List<String>> factoryClassNames = null;
638         synchronized (registeredFactoryNames)
639         {
640             Map<String, Object> factories = FactoryFinder.factories.get(classLoader);
641 
642             if (factories != null && factories.containsKey(factoryName))
643             {
644                 // Javadoc says ... This method has no effect if getFactory() has already been
645                 // called looking for a factory for this factoryName.
646                 return;
647             }
648 
649             factoryClassNames = registeredFactoryNames.get(classLoader);
650 
651             if (factoryClassNames == null)
652             {
653                 factoryClassNames = new HashMap<String, List<String>>();
654                 registeredFactoryNames.put(classLoader, factoryClassNames);
655             }
656         }
657 
658         synchronized (factoryClassNames)
659         {
660             List<String> classNameList = factoryClassNames.get(factoryName);
661 
662             if (classNameList == null)
663             {
664                 classNameList = new ArrayList<String>();
665                 factoryClassNames.put(factoryName, classNameList);
666             }
667 
668             classNameList.add(implName);
669         }
670     }
671 
672     public static void releaseFactories() throws FacesException
673     {
674         initializeFactoryFinderProviderFactory();
675         
676         if (factoryFinderProviderFactoryInstance == null)
677         {
678             // Do the typical stuff
679             _releaseFactories();
680         }
681         else
682         {
683             try
684             {
685                 //Obtain the FactoryFinderProvider instance for this context.
686                 Object ffp = _FactoryFinderProviderFactory
687                         .FACTORY_FINDER_PROVIDER_FACTORY_GET_FACTORY_FINDER_METHOD
688                         .invoke(factoryFinderProviderFactoryInstance, null);
689                 
690                 //Call getFactory method and pass the params
691                 _FactoryFinderProviderFactory.FACTORY_FINDER_PROVIDER_RELEASE_FACTORIES_METHOD.invoke(ffp, null);
692             }
693             catch (InvocationTargetException e)
694             {
695                 Throwable targetException = e.getCause();
696                 if (targetException instanceof FacesException)
697                 {
698                     throw (FacesException) targetException;
699                 }
700                 else if (targetException == null)
701                 {
702                     throw new FacesException(e);
703                 }
704                 else
705                 {
706                     throw new FacesException(targetException);
707                 }
708             }
709             catch (Exception e)
710             {
711                 //No Op
712                 throw new FacesException(e);
713             }
714             
715         }
716     }
717 
718     private static void _releaseFactories() throws FacesException
719     {
720         ClassLoader classLoader = getClassLoader();
721 
722         Map<String, Object> factoryMap;
723         // This code must be synchronized
724         synchronized (registeredFactoryNames)
725         {
726             factoryMap = factories.remove(classLoader);
727 
728             // _registeredFactoryNames has as value type Map<String,List> and this must
729             // be cleaned before release (for gc).
730             Map<String, List<String>> factoryClassNames = registeredFactoryNames.get(classLoader);
731             if (factoryClassNames != null)
732             {
733                 factoryClassNames.clear();
734             }
735 
736             registeredFactoryNames.remove(classLoader);
737         }
738 
739         if (factoryMap != null)
740         {
741             Object injectionProvider = factoryMap.remove(INJECTION_PROVIDER_INSTANCE);
742             if (injectionProvider != null)
743             {
744                 List injectedBeanStorage = (List)factoryMap.get(INJECTED_BEAN_STORAGE_KEY);
745 
746                 FacesException firstException = null;
747                 for (Object entry : injectedBeanStorage)
748                 {
749                     try
750                     {
751                         preDestroy(injectionProvider, entry);
752                     }
753                     catch (FacesException e)
754                     {
755                         LOGGER.log(Level.SEVERE, "#preDestroy failed", e);
756 
757                         if (firstException == null)
758                         {
759                             firstException = e; //all preDestroy callbacks need to get invoked
760                         }
761                     }
762                 }
763                 injectedBeanStorage.clear();
764 
765                 if (firstException != null)
766                 {
767                     throw firstException;
768                 }
769             }
770         }
771     }
772 
773     private static void checkFactoryName(String factoryName)
774     {
775         if (!VALID_FACTORY_NAMES.contains(factoryName))
776         {
777             throw new IllegalArgumentException("factoryName '" + factoryName + "'");
778         }
779     }
780 
781     private static ClassLoader getClassLoader()
782     {
783         try
784         {
785             ClassLoader classLoader = null;
786             if (System.getSecurityManager() != null)
787             {
788                 classLoader = (ClassLoader) AccessController.doPrivileged(new java.security.PrivilegedExceptionAction()
789                 {
790                     public Object run()
791                     {
792                         return Thread.currentThread().getContextClassLoader();
793                     }
794                 });
795             }
796             else
797             {
798                 classLoader = Thread.currentThread().getContextClassLoader();
799             }
800             
801             if (classLoader == null)
802             {
803                 throw new FacesException("web application class loader cannot be identified", null);
804             }
805             return classLoader;
806         }
807         catch (Exception e)
808         {
809             throw new FacesException("web application class loader cannot be identified", e);
810         }
811     }
812 }