1 /*
2 * $Id: FactoryFinder.java,v 1.37 2007/05/21 21:23:29 rlubke Exp $
3 */
4
5 /*
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7 *
8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
9 *
10 * The contents of this file are subject to the terms of either the GNU
11 * General Public License Version 2 only ("GPL") or the Common Development
12 * and Distribution License("CDDL") (collectively, the "License"). You
13 * may not use this file except in compliance with the License. You can obtain
14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
16 * language governing permissions and limitations under the License.
17 *
18 * When distributing the software, include this License Header Notice in each
19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
20 * Sun designates this particular file as subject to the "Classpath" exception
21 * as provided by Sun in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the License
23 * Header, with the fields enclosed by brackets [] replaced by your own
24 * identifying information: "Portions Copyrighted [year]
25 * [name of copyright owner]"
26 *
27 * Contributor(s):
28 *
29 * If you wish your version of this file to be governed by only the CDDL or
30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
31 * elects to include this software in this distribution under the [CDDL or GPL
32 * Version 2] license." If you don't indicate a single choice of license, a
33 * recipient has the option to distribute your version of this file under
34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
35 * its licensees as provided above. However, if you add GPL Version 2 code
36 * and therefore, elected the GPL Version 2 license, then the option applies
37 * only if the new code is made subject to such option by the copyright
38 * holder.
39 */
40
41 package javax.faces;
42
43
44 import java.io.BufferedReader;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.io.IOException;
48 import java.io.UnsupportedEncodingException;
49 import java.text.MessageFormat;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.ResourceBundle;
55 import java.util.Arrays;
56 import java.util.Enumeration;
57 import java.util.concurrent.ConcurrentMap;
58 import java.util.concurrent.ConcurrentHashMap;
59 import java.util.concurrent.Callable;
60 import java.util.concurrent.FutureTask;
61 import java.util.concurrent.Future;
62 import java.util.concurrent.CancellationException;
63 import java.util.concurrent.ExecutionException;
64 import java.util.concurrent.locks.ReentrantReadWriteLock;
65 import java.util.logging.Logger;
66 import java.util.logging.Level;
67 import java.lang.reflect.Constructor;
68 import java.net.URL;
69 import java.net.URLConnection;
70
71
72 /**
73 * <p><strong class="changed_modified_2_0">FactoryFinder</strong>
74 * implements the standard discovery algorithm for all factory objects
75 * specified in the JavaServer Faces APIs. For a given factory class
76 * name, a corresponding implementation class is searched for based on
77 * the following algorithm. Items are listed in order of decreasing
78 * search precedence:</p>
79
80 * <ul>
81
82 * <li><p>If the JavaServer Faces configuration file bundled into the
83 * <code>WEB-INF</code> directory of the webapp contains a
84 * <code>factory</code> entry of the given factory class name, that
85 * factory is used.<p></li>
86
87 * <li><p>If the JavaServer Faces configuration files named by the
88 * <code>javax.faces.CONFIG_FILES</code> <code>ServletContext</code> init
89 * parameter contain any <code>factory</code> entries of the given
90 * factory class name, those factories are used, with the last one taking
91 * precedence.</p></li>
92
93 * <li><p>If there are any JavaServer Faces configuration files bundled
94 * into the <code>META-INF</code> directory of any jars on the
95 * <code>ServletContext</code>'s resource paths, the
96 * <code>factory</code> entries of the given factory class name in those
97 * files are used, with the last one taking precedence.</p></li>
98
99 * <li><p>If a <code>META-INF/services/{factory-class-name}</code>
100 * resource is visible to the web application class loader for the
101 * calling application (typically as a result of being present in the
102 * manifest of a JAR file), its first line is read and assumed to be the
103 * name of the factory implementation class to use.</p></li>
104
105 * <li><p>If none of the above steps yield a match, the JavaServer Faces
106 * implementation specific class is used.</p></li>
107
108 * </ul>
109
110 * <p>If any of the factories found on any of the steps above happen to
111 * have a one-argument constructor, with argument the type being the
112 * abstract factory class, that constructor is invoked, and the previous
113 * match is passed to the constructor. For example, say the container
114 * vendor provided an implementation of {@link
115 * javax.faces.context.FacesContextFactory}, and identified it in
116 * <code>META-INF/services/javax.faces.context.FacesContextFactory</code>
117 * in a jar on the webapp ClassLoader. Also say this implementation
118 * provided by the container vendor had a one argument constructor that
119 * took a <code>FacesContextFactory</code> instance. The
120 * <code>FactoryFinder</code> system would call that one-argument
121 * constructor, passing the implementation of
122 * <code>FacesContextFactory</code> provided by the JavaServer Faces
123 * implementation.</p>
124
125 * <p>If a Factory implementation does not provide a proper one-argument
126 * constructor, it must provide a zero-arguments constructor in order to
127 * be successfully instantiated.</p>
128
129 * <p>Once the name of the factory implementation class is located, the
130 * web application class loader for the calling application is requested
131 * to load this class, and a corresponding instance of the class will be
132 * created. A side effect of this rule is that each web application
133 * will receive its own instance of each factory class, whether the
134 * JavaServer Faces implementation is included within the web
135 * application or is made visible through the container's facilities for
136 * shared libraries.</p>
137 */
138
139 public final class FactoryFinder {
140
141 // ----------------------------------------------------------- Constructors
142
143
144 /**
145 * Package-private constructor to disable instantiation of this class.
146 */
147 FactoryFinder() {
148 }
149
150 // ----------------------------------------------------- Manifest Constants
151
152
153 /**
154 * <p>The property name for the
155 * {@link javax.faces.application.ApplicationFactory} class name.</p>
156 */
157 public final static String APPLICATION_FACTORY =
158 "javax.faces.application.ApplicationFactory";
159
160 /**
161 * <p class="changed_added_2_0">The property name for the {@link
162 * javax.faces.context.ExceptionHandlerFactory} class name.</p>
163 */
164 public final static String EXCEPTION_HANDLER_FACTORY =
165 "javax.faces.context.ExceptionHandlerFactory";
166
167 /**
168 * <p class="changed_added_2_0">The property name for the {@link
169 * javax.faces.context.ExternalContextFactory} class name.</p>
170 */
171 public final static String EXTERNAL_CONTEXT_FACTORY =
172 "javax.faces.context.ExternalContextFactory";
173
174 /**
175 * <p>The property name for the
176 * {@link javax.faces.context.FacesContextFactory} class name.</p>
177 */
178 public final static String FACES_CONTEXT_FACTORY =
179 "javax.faces.context.FacesContextFactory";
180
181 /**
182 * <p class="changed_added_2_0">The property name for the {@link
183 * javax.faces.context.PartialViewContextFactory} class name.</p>
184 */
185 public final static String PARTIAL_VIEW_CONTEXT_FACTORY =
186 "javax.faces.context.PartialViewContextFactory";
187
188 /**
189 * <p class="changed_added_2_0">The property name for the {@link
190 * javax.faces.component.visit.VisitContextFactory} class name.</p>
191 */
192 public final static String VISIT_CONTEXT_FACTORY =
193 "javax.faces.component.visit.VisitContextFactory";
194
195 /**
196 * <p>The property name for the
197 * {@link javax.faces.lifecycle.LifecycleFactory} class name.</p>
198 */
199 public final static String LIFECYCLE_FACTORY =
200 "javax.faces.lifecycle.LifecycleFactory";
201
202 /**
203 * <p>The property name for the
204 * {@link javax.faces.render.RenderKitFactory} class name.</p>
205 */
206 public final static String RENDER_KIT_FACTORY =
207 "javax.faces.render.RenderKitFactory";
208
209 /**
210 * <p class="changed_added_2_0">The property name for the {@link
211 * javax.faces.view.ViewDeclarationLanguage} class name.</p>
212 */
213 public final static String VIEW_DECLARATION_LANGUAGE_FACTORY =
214 "javax.faces.view.ViewDeclarationLanguageFactory";
215
216 /**
217 * <p class="changed_added_2_0">The property name for the {@link
218 * javax.faces.view.facelets.TagHandlerDelegate} class name.</p>
219 */
220 public final static String TAG_HANDLER_DELEGATE_FACTORY =
221 "javax.faces.view.facelets.TagHandlerDelegateFactory";
222
223 // ------------------------------------------------------- Static Variables
224
225 private static final FactoryManagerCache FACTORIES_CACHE =
226 new FactoryManagerCache();
227
228
229 /**
230 * <p>The set of JavaServer Faces factory classes for which the factory
231 * discovery mechanism is supported. The entries in this list must be
232 * alphabetically ordered according to the entire string, not just
233 * the last part!</p>
234 */
235 private static final String[] FACTORY_NAMES = {
236 APPLICATION_FACTORY,
237 VISIT_CONTEXT_FACTORY,
238 EXCEPTION_HANDLER_FACTORY,
239 EXTERNAL_CONTEXT_FACTORY,
240 FACES_CONTEXT_FACTORY,
241 LIFECYCLE_FACTORY,
242 VIEW_DECLARATION_LANGUAGE_FACTORY,
243 PARTIAL_VIEW_CONTEXT_FACTORY,
244 RENDER_KIT_FACTORY,
245 TAG_HANDLER_DELEGATE_FACTORY
246
247 };
248
249 /**
250 * <p>Map of Class instances for the our factory names.</p>
251 */
252 private static Map<String, Class> factoryClasses = null;
253
254 private static final Logger LOGGER =
255 Logger.getLogger("javax.faces", "javax.faces.LogStrings");
256
257 // Ensure the factory names are sorted.
258 //
259 static {
260 Arrays.sort(FACTORY_NAMES);
261 }
262
263
264 // --------------------------------------------------------- Public Methods
265
266
267 /**
268 * <p><span class="changed_modified_2_0">Create</span> (if
269 * necessary) and return a per-web-application instance of the
270 * appropriate implementation class for the specified JavaServer
271 * Faces factory class, based on the discovery algorithm described
272 * in the class description.</p>
273 *
274 * <p class="changed_added_2_0">The standard factories and wrappers
275 * in JSF all implement the interface {@link FacesWrapper}. If the
276 * returned <code>Object</code> is an implementation of one of the
277 * standard factories, it must be legal to cast it to an instance of
278 * <code>FacesWrapper</code> and call {@link
279 * FacesWrapper#getWrapped} on the instance.</p>
280 *
281 * @param factoryName Fully qualified name of the JavaServer Faces factory
282 * for which an implementation instance is requested
283 * @throws FacesException if the web application class loader
284 * cannot be identified
285 * @throws FacesException if an instance of the configured factory
286 * implementation class cannot be loaded
287 * @throws FacesException if an instance of the configured factory
288 * implementation class cannot be instantiated
289 * @throws IllegalArgumentException if <code>factoryName</code> does not
290 * identify a standard JavaServer Faces factory name
291 * @throws IllegalStateException if there is no configured factory
292 * implementation class for the specified factory name
293 * @throws NullPointerException if <code>factoryname</code>
294 * is null
295 */
296 public static Object getFactory(String factoryName)
297 throws FacesException {
298
299 validateFactoryName(factoryName);
300
301 // Identify the web application class loader
302 ClassLoader classLoader = getClassLoader();
303
304 FactoryManager manager =
305 FACTORIES_CACHE.getApplicationFactoryManager(classLoader);
306 return manager.getFactory(classLoader, factoryName);
307
308 }
309
310 /**
311 * <p>This method will store the argument
312 * <code>factoryName/implName</code> mapping in such a way that
313 * {@link #getFactory} will find this mapping when searching for a
314 * match.</p>
315 * <p/>
316 * <p>This method has no effect if <code>getFactory()</code> has
317 * already been called looking for a factory for this
318 * <code>factoryName</code>.</p>
319 * <p/>
320 * <p>This method can be used by implementations to store a factory
321 * mapping while parsing the Faces configuration file</p>
322 *
323 * @throws IllegalArgumentException if <code>factoryName</code> does not
324 * identify a standard JavaServer Faces factory name
325 * @throws NullPointerException if <code>factoryname</code>
326 * is null
327 */
328 public static void setFactory(String factoryName,
329 String implName) {
330
331 validateFactoryName(factoryName);
332
333 // Identify the web application class loader
334 ClassLoader classLoader = getClassLoader();
335
336 FactoryManager manager =
337 FACTORIES_CACHE.getApplicationFactoryManager(classLoader);
338 manager.addFactory(factoryName, implName);
339
340 }
341
342
343 /**
344 * <p><span class="changed_modified_2_0">Release</span> any
345 * references to factory instances associated with the class loader
346 * for the calling web application. <span
347 * class="changed_modified_2_0">This method must be called during of
348 * web application shutdown.</span></p>
349 *
350 * @throws FacesException if the web application class loader
351 * cannot be identified
352 */
353 public static void releaseFactories() throws FacesException {
354
355 // Identify the web application class loader
356 ClassLoader cl = getClassLoader();
357
358 FACTORIES_CACHE.removeApplicationFactoryManager(cl);
359
360 }
361
362
363 // -------------------------------------------------------- Private Methods
364
365
366 /**
367 * <p>Identify and return the class loader that is associated with the
368 * calling web application.</p>
369 *
370 * @throws FacesException if the web application class loader
371 * cannot be identified
372 */
373 private static ClassLoader getClassLoader() throws FacesException {
374
375 // J2EE 1.3 (and later) containers are required to make the
376 // web application class loader visible through the context
377 // class loader of the current thread.
378 ClassLoader cl = Thread.currentThread().getContextClassLoader();
379 if (cl == null) {
380 throw new FacesException("getContextClassLoader");
381 }
382 return (cl);
383
384 }
385
386
387 /**
388 * <p>Load and return an instance of the specified implementation
389 * class using the following algorithm.</p>
390 * <p/>
391 * <ol>
392 * <p/>
393 * <li><p>If the argument <code>implementations</code> list has
394 * more than one element, or exactly one element, interpret the
395 * last element in the list to be the fully qualified class name of
396 * a class implementing <code>factoryName</code>. Instantiate that
397 * class and save it for return. If the
398 * <code>implementations</code> list has only one element, skip
399 * this step.</p></li>
400 * <p/>
401 * <li><p>Look for a resource called
402 * <code>/META-INF/services/<factoryName></code>. If found,
403 * interpret it as a properties file, and read out the first entry.
404 * Interpret the first entry as a fully qualify class name of a
405 * class that implements <code>factoryName</code>. If we have an
406 * instantiated factory from the previous step <em>and</em> the
407 * implementing class has a one arg constructor of the type for
408 * <code>factoryName</code>, instantiate it, passing the
409 * instantiated factory from the previous step. If there is no one
410 * arg constructor, just instantiate the zero arg constructor. Save
411 * the newly instantiated factory for return, replacing the
412 * instantiated factory from the previous step.</p></li>
413 * <p/>
414 * <li><p>Treat each remaining element in the
415 * <code>implementations</code> list as a fully qualified class name
416 * of a class implementing <code>factoryName</code>. If the current
417 * element has a one arg constructor of the type for
418 * <code>factoryName</code>, instantiate it, passing the
419 * instantiated factory from the previous or step iteration. If
420 * there is no one arg constructor, just instantiate the zero arg
421 * constructor, replacing the instantiated factory from the previous
422 * step or iteration.</p></li>
423 * <p/>
424 * <li><p>Return the saved factory</p></li>
425 * <p/>
426 * </ol>
427 *
428 * @param classLoader Class loader for the web application that will
429 * be loading the implementation class
430 * @param implementations A List of implementations for a given
431 * factory class.
432 * @throws FacesException if the specified implementation class
433 * cannot be loaded
434 * @throws FacesException if an instance of the specified implementation
435 * class cannot be instantiated
436 */
437 private static Object getImplementationInstance(ClassLoader classLoader,
438 String factoryName,
439 List implementations)
440 throws FacesException {
441
442 Object result = null;
443 String curImplClass;
444 int len;
445
446 // step 1.
447 if (null != implementations &&
448 (1 < (len = implementations.size()) || 1 == len)) {
449 curImplClass = (String) implementations.remove(len - 1);
450 // since this is the hard coded implementation default,
451 // there is no preceding implementation, so don't bother
452 // with a non-zero-arg ctor.
453 result = getImplGivenPreviousImpl(classLoader, factoryName,
454 curImplClass, null);
455 }
456
457 // step 2.
458 List<String> fromServices = getImplNameFromServices(classLoader, factoryName);
459 if (fromServices != null) {
460 for (String name : fromServices) {
461 result = getImplGivenPreviousImpl(classLoader,
462 factoryName,
463 name,
464 result);
465 }
466 }
467
468 // step 3.
469 if (null != implementations) {
470 for (len = (implementations.size() - 1); 0 <= len; len--) {
471 curImplClass = (String) implementations.remove(len);
472 result = getImplGivenPreviousImpl(classLoader, factoryName,
473 curImplClass, result);
474 }
475 }
476
477 return result;
478
479 }
480
481
482 /**
483 * <p>Perform the logic to get the implementation class for the
484 * second step of {@link FactoryFinder#getImplementationInstance(ClassLoader, String, java.util.List)}.</p>
485 */
486 private static List<String> getImplNameFromServices(ClassLoader classLoader,
487 String factoryName) {
488
489 // Check for a services definition
490 List<String> result = null;
491 String resourceName = "META-INF/services/" + factoryName;
492 InputStream stream;
493 BufferedReader reader = null;
494 try {
495 Enumeration<URL> e = classLoader.getResources(resourceName);
496 while (e.hasMoreElements()) {
497 URL url = e.nextElement();
498 URLConnection conn = url.openConnection();
499 conn.setUseCaches(false);
500 stream = conn.getInputStream();
501 if (stream != null) {
502 // Deal with systems whose native encoding is possibly
503 // different from the way that the services entry was created
504 try {
505 reader =
506 new BufferedReader(new InputStreamReader(stream,
507 "UTF-8"));
508 if (result == null) {
509 result = new ArrayList<String>(3);
510 }
511 result.add(reader.readLine());
512 } catch (UnsupportedEncodingException uee) {
513 reader =
514 new BufferedReader(new InputStreamReader(stream));
515 } finally {
516 if (reader != null) {
517 reader.close();
518 reader = null;
519 }
520 if (stream != null) {
521 stream.close();
522 //noinspection UnusedAssignment
523 stream = null;
524 }
525 }
526
527 }
528 }
529 } catch (IOException e) {
530 if (LOGGER.isLoggable(Level.SEVERE)) {
531 LOGGER.log(Level.SEVERE,
532 e.toString(),
533 e);
534 }
535 } catch (SecurityException e) {
536 if (LOGGER.isLoggable(Level.SEVERE)) {
537 LOGGER.log(Level.SEVERE,
538 e.toString(),
539 e);
540 }
541 }
542 return result;
543
544 }
545
546
547 /**
548 * <p>Implement the decorator pattern for the factory
549 * implementation.</p>
550 * <p/>
551 * <p>If <code>previousImpl</code> is non-<code>null</code> and the
552 * class named by the argument <code>implName</code> has a one arg
553 * contstructor of type <code>factoryName</code>, instantiate it,
554 * passing previousImpl to the constructor.</p>
555 * <p/>
556 * <p>Otherwise, we just instantiate and return
557 * <code>implName</code>.</p>
558 *
559 * @param classLoader the ClassLoader from which to load the class
560 * @param factoryName the fully qualified class name of the factory.
561 * @param implName the fully qualified class name of a class that
562 * implements the factory.
563 * @param previousImpl if non-<code>null</code>, the factory
564 * instance to be passed to the constructor of the new factory.
565 */
566 private static Object getImplGivenPreviousImpl(ClassLoader classLoader,
567 String factoryName,
568 String implName,
569 Object previousImpl) {
570 Class clazz;
571 Class factoryClass = null;
572 Class[] getCtorArg;
573 Object[] newInstanceArgs = new Object[1];
574 Constructor ctor;
575 Object result = null;
576
577 // if we have a previousImpl and the appropriate one arg ctor.
578 if ((null != previousImpl) &&
579 (null != (factoryClass = getFactoryClass(factoryName)))) {
580 try {
581 clazz = Class.forName(implName, false, classLoader);
582 getCtorArg = new Class[1];
583 getCtorArg[0] = factoryClass;
584 ctor = clazz.getConstructor(getCtorArg);
585 newInstanceArgs[0] = previousImpl;
586 result = ctor.newInstance(newInstanceArgs);
587 }
588 catch (NoSuchMethodException nsme) {
589 // fall through to "zero-arg-ctor" case
590 factoryClass = null;
591 }
592 catch (Exception e) {
593 throw new FacesException(implName, e);
594 }
595 }
596 if (null == previousImpl || null == factoryClass) {
597 // we have either no previousImpl or no appropriate one arg
598 // ctor.
599 try {
600 clazz = Class.forName(implName, false, classLoader);
601 // since this is the hard coded implementation default,
602 // there is no preceding implementation, so don't bother
603 // with a non-zero-arg ctor.
604 result = clazz.newInstance();
605 } catch (Exception e) {
606 throw new FacesException(implName, e);
607 }
608 }
609 return result;
610
611 }
612
613
614 /**
615 * @return the <code>java.lang.Class</code> for the argument
616 * factory.
617 */
618 private static Class getFactoryClass(String factoryClassName) {
619
620 if (null == factoryClasses) {
621 factoryClasses = new HashMap<String, Class>(FACTORY_NAMES.length);
622 factoryClasses.put(APPLICATION_FACTORY,
623 javax.faces.application.ApplicationFactory.class);
624 factoryClasses.put(EXCEPTION_HANDLER_FACTORY,
625 javax.faces.context.ExceptionHandlerFactory.class);
626 factoryClasses.put(EXTERNAL_CONTEXT_FACTORY,
627 javax.faces.context.ExternalContextFactory.class);
628 factoryClasses.put(FACES_CONTEXT_FACTORY,
629 javax.faces.context.FacesContextFactory.class);
630 factoryClasses.put(VISIT_CONTEXT_FACTORY,
631 javax.faces.component.visit.VisitContextFactory.class);
632 factoryClasses.put(LIFECYCLE_FACTORY,
633 javax.faces.lifecycle.LifecycleFactory.class);
634 factoryClasses.put(PARTIAL_VIEW_CONTEXT_FACTORY,
635 javax.faces.context.PartialViewContextFactory.class);
636 factoryClasses.put(RENDER_KIT_FACTORY,
637 javax.faces.render.RenderKitFactory.class);
638 factoryClasses.put(VIEW_DECLARATION_LANGUAGE_FACTORY,
639 javax.faces.view.ViewDeclarationLanguageFactory.class);
640 factoryClasses.put(TAG_HANDLER_DELEGATE_FACTORY,
641 javax.faces.view.facelets.TagHandlerDelegateFactory.class);
642 }
643 return factoryClasses.get(factoryClassName);
644
645 }
646
647
648 /**
649 * Ensure the provided factory name is valid.
650 */
651 private static void validateFactoryName(String factoryName) {
652
653 if (factoryName == null) {
654 throw new NullPointerException();
655 }
656 if (Arrays.binarySearch(FACTORY_NAMES, factoryName) < 0) {
657 throw new IllegalArgumentException(factoryName);
658 }
659
660 }
661
662
663 // ----------------------------------------------------------- Inner Classes
664
665
666 /**
667 * Managed the mappings between a web application and its configured
668 * factories.
669 */
670 private static final class FactoryManagerCache {
671
672 private ConcurrentMap<ClassLoader,Future<FactoryManager>> applicationMap =
673 new ConcurrentHashMap<ClassLoader, Future<FactoryManager>>();
674
675
676 // ------------------------------------------------------ Public Methods
677
678
679 private FactoryManager getApplicationFactoryManager(ClassLoader cl) {
680
681 while (true) {
682 Future<FactoryManager> factories = applicationMap.get(cl);
683 if (factories == null) {
684 Callable<FactoryManager> callable =
685 new Callable<FactoryManager>() {
686 public FactoryManager call()
687 throws Exception {
688 return new FactoryManager();
689 }
690 };
691
692 FutureTask<FactoryManager> ft =
693 new FutureTask<FactoryManager>(callable);
694 factories = applicationMap.putIfAbsent(cl, ft);
695 if (factories == null) {
696 factories = ft;
697 ft.run();
698 }
699 }
700
701 try {
702 return factories.get();
703 } catch (CancellationException ce) {
704 if (LOGGER.isLoggable(Level.FINEST)) {
705 LOGGER.log(Level.FINEST,
706 ce.toString(),
707 ce);
708 }
709 applicationMap.remove(cl);
710 } catch (InterruptedException ie) {
711 if (LOGGER.isLoggable(Level.FINEST)) {
712 LOGGER.log(Level.FINEST,
713 ie.toString(),
714 ie);
715 }
716 applicationMap.remove(cl);
717 } catch (ExecutionException ee) {
718 throw new FacesException(ee);
719 }
720
721 }
722
723 }
724
725
726 public void removeApplicationFactoryManager(ClassLoader cl) {
727
728 applicationMap.remove(cl);
729
730 }
731
732 } // END FactoryCache
733
734
735 /**
736 * Maintains the factories for a single web application.
737 */
738 private static final class FactoryManager {
739
740 private final Map<String,Object> factories;
741 private final ReentrantReadWriteLock lock;
742
743
744 // -------------------------------------------------------- Consturctors
745
746
747 public FactoryManager() {
748 factories = new HashMap<String,Object>();
749 for (String name : FACTORY_NAMES) {
750 factories.put(name, new ArrayList(4));
751 }
752 lock = new ReentrantReadWriteLock(true);
753 }
754
755
756 // ------------------------------------------------------ Public Methods
757
758
759 public void addFactory(String factoryName, String implementation) {
760
761 Object result = factories.get(factoryName);
762 lock.writeLock().lock();
763 try {
764 if (result instanceof List) {
765 TypedCollections.dynamicallyCastList((List) result, String.class).add(0, implementation);
766 }
767 } finally {
768 lock.writeLock().unlock();
769 }
770 }
771
772
773 public Object getFactory(ClassLoader cl, String factoryName) {
774
775 Object factoryOrList;
776 lock.readLock().lock();
777 try {
778 factoryOrList = factories.get(factoryName);
779 if (!(factoryOrList instanceof List)) {
780 return factoryOrList;
781 }
782 } finally {
783 lock.readLock().unlock();
784 }
785
786 // factory hasn't been constructed
787 lock.writeLock().lock();
788 try {
789 // double check the current value. Another thread
790 // may have completed the initialization by the time
791 // this thread entered this block
792 factoryOrList = factories.get(factoryName);
793 if (!(factoryOrList instanceof List)) {
794 return factoryOrList;
795 }
796 Object factory = getImplementationInstance(cl,
797 factoryName,
798 (List) factoryOrList);
799
800 if (factory == null) {
801 ResourceBundle rb = LOGGER.getResourceBundle();
802 String message = rb.getString("severe.no_factory");
803 message = MessageFormat.format(message, factoryName);
804 throw new IllegalStateException(message);
805 }
806
807 // Record and return the new instance
808 factories.put(factoryName, factory);
809 return (factory);
810 } finally {
811 lock.writeLock().unlock();
812 }
813 }
814
815 } // END FactoryManager
816
817
818 }