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 }