Coverage Report - org.apache.myfaces.spi.FactoryFinderProviderFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
FactoryFinderProviderFactory
0%
0/19
0%
0/6
2.333
 
 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 org.apache.myfaces.spi;
 20  
 
 21  
 import java.lang.reflect.Field;
 22  
 import java.util.logging.Level;
 23  
 import java.util.logging.Logger;
 24  
 
 25  
 import org.apache.myfaces.shared.util.ClassUtils;
 26  
 
 27  
 /**
 28  
  * <p>{@link javax.faces.FactoryFinder} is a class with three methods:</p>
 29  
  * 
 30  
  * <code>
 31  
  * public final class FactoryFinder
 32  
  * {
 33  
  *    public static Object getFactory(String factoryName) throws FacesException {...}
 34  
  *    public static void setFactory(String factoryName, String implName) {...}
 35  
  *    public static void releaseFactories() throws FacesException {...}
 36  
  * }
 37  
  * </code>
 38  
  * 
 39  
  * <p>The javadoc describe the intention of FactoryFinder class:
 40  
  * </p>
 41  
  * 
 42  
  * <p>"... FactoryFinder implements the standard discovery algorithm for all factory 
 43  
  * objects specified in the JavaServer Faces APIs. For a given factory class name, a 
 44  
  * corresponding implementation class is searched for based on the following 
 45  
  * algorithm...."</p>
 46  
  * 
 47  
  * <p>In few words, this class allows to find JSF factory classes. The necessary 
 48  
  * information to create factory instances is loaded on initialization time, 
 49  
  * but which locations contains such information (for more information see 
 50  
  * JSF 2.0 spec section 11.4.2) (here the only interest is in jsf factories 
 51  
  * initialization information) ?</p>
 52  
  * 
 53  
  * <ul>
 54  
  * <li>Look factories on META-INF/services/[factoryClassName]</li>
 55  
  * <li>Look META-INF/faces-config.xml or META-INF/[prefix].faces-config.xml</li>
 56  
  * <li>Look the files pointed by javax.faces.CONFIG_FILES web config param 
 57  
  *     (note WEB-INF/web.xml is taken into consideration)</li>
 58  
  * <li>Look the applicationFacesConfig on WEB-INF/faces-config.xml</li>
 59  
  * </ul>
 60  
  * 
 61  
  * <p>Based on the previous facts, the first conclusion to take into account arise: 
 62  
  * Configuration information is gathered per "web context". What is a "web context"? 
 63  
  * In simple terms, is the "space" where a web application is deployed. 
 64  
  * Let's suppose an EAR file with two WAR files: a.war and b.war. 
 65  
  * Both contains different "web applications" and when are deployed has 
 66  
  * different "web context", so both can provide different factory configuration, 
 67  
  * because both has different WEB-INF/web.xml and WEB-INF/faces-config.xml files.</p>
 68  
  * 
 69  
  * <p>Now, given a request, how the web container identify a "web context"? 
 70  
  * At start, it receives the request information and based on that it decides 
 71  
  * which web application should process it. After that, it assign to a thread 
 72  
  * from is thread pool to be processed and the control is passed to the proper 
 73  
  * filters/servlets.</p> 
 74  
  * 
 75  
  * <p>So, if there is not a servlet context/portlet context/whatever context, 
 76  
  * how to identify a "web context"? The answer is using the thread, but the one 
 77  
  * who knows how to do that is the web container, not the jsf implementation.</p>
 78  
  * 
 79  
  * <p>The existing problem is caused by a "shortcut" taken to make things easier. 
 80  
  * Instead use the current "thread", it is taken as advantage the fact that each 
 81  
  * web application deployed has a different classloader. That is true for a lot 
 82  
  * of application servers, so the current implementation of FactoryFinder is based 
 83  
  * on that fact too and has worked well since the beginning.</p>
 84  
  * 
 85  
  * <p>Now let's examine in detail how a "single classloader per EAR" option could 
 86  
  * work. If the EAR has two WAR files (a.war and b.war), we have two web context, 
 87  
  * and the initialization code is executed twice. When all FactoryFinder methods 
 88  
  * are called?</p>
 89  
  * 
 90  
  * <ul>
 91  
  * <li>FactoryFinder.setFactory is called on initialization</li>
 92  
  * <li>FactoryFinder.releaseFactories is called on shutdown</li>
 93  
  * <li>FactoryFinder.getFactory is called after initialization configuration is 
 94  
  *     done but before shutdown call to FactoryFinder.setFactory </li>
 95  
  * </ul>
 96  
  * 
 97  
  * <p>Remember all methods of FactoryFinder are static.</p> 
 98  
  * 
 99  
  * <p>One possible solution could be:</p>
 100  
  * 
 101  
  * <ol>
 102  
  * <li>Create a class called FactoryFinderProvider, that has the same three method 
 103  
  *     but in a non static version.</li>
 104  
  * <li>A singleton component is provided that holds the information of the 
 105  
  *     FactoryFinderProviderFactory. This one works per classloader, so the 
 106  
  *     singleton is implemented using an static variable. To configure it, the 
 107  
  *     static method should be called when the "classloader realm" is initialized, 
 108  
  *     before any web context is started (the WAR is deployed). Usually the EAR is 
 109  
  *     started as a single entity, so this should occur when the EAR starts, but 
 110  
  *     before the WAR files are started (or the web context are created). 
 111  
  *     The singleton will be responsible to decide which FactoryFinderProvider 
 112  
  *     should be used, based on the current thread information.</li>
 113  
  * <li>Add utility methods to retrieve the required objects and call the methods 
 114  
  *     using reflection from javax.faces.FactoryFinder</li>
 115  
  * </ol>
 116  
  * 
 117  
  * <p>This class implements the proposed solution. Note by definition, this factory 
 118  
  * cannot be configured using SPI standard algorithm (look for 
 119  
  * META-INF/services/[factory_class_name]).</p>
 120  
  * 
 121  
  * @since 2.0.5
 122  
  * @author Leonardo Uribe
 123  
  *
 124  
  */
 125  0
 public abstract class FactoryFinderProviderFactory
 126  
 {
 127  0
     private static volatile FactoryFinderProviderFactory instance = null;
 128  
     
 129  
     /**
 130  
      * Set the instance to be used by {@link javax.faces.FactoryFinder} to resolve
 131  
      * factories. 
 132  
      * 
 133  
      * <p>This method should be called before any "web context" is initialized in the
 134  
      * current "classloader context". For example, if a EAR file contains two WAR files,
 135  
      * this method should be called before initialize any WAR, since each one requires
 136  
      * a different "web context"</p>
 137  
      * 
 138  
      * @param instance
 139  
      */
 140  
     public static void setInstance(FactoryFinderProviderFactory instance)
 141  
     {
 142  
         
 143  
         
 144  
         // Now we need to make sure the volatile var FactoryFinder._initialized is
 145  
         // set to false, to make sure the right factory is fetched after this method
 146  
         // exists. It is just a fail-safe, because after all if the conditions to make 
 147  
         // this call are met, _initialized should be false.
 148  
         try
 149  
         {
 150  0
             Class clazz = ClassUtils.classForName("javax.faces.FactoryFinder");
 151  0
             Field field = clazz.getDeclaredField("initialized");
 152  0
             field.setAccessible(true);
 153  
             
 154  0
             if (field.getBoolean(null))
 155  
             {
 156  0
                 Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName());
 157  0
                 if (log.isLoggable(Level.WARNING))
 158  
                 {
 159  0
                     log.log(Level.WARNING,
 160  
                             "Called FactoryFinderProviderFactory.setFactory after " +
 161  
                                     "initialized FactoryFinder (first call to getFactory() or setFactory()). " +
 162  
                                     "This method should be called before " +
 163  
                                     "any 'web context' is initialized in the current 'classloader context'. " +
 164  
                                     "By that reason it will not be changed.");
 165  
                 }
 166  0
             }
 167  
             else
 168  
             {
 169  0
                 FactoryFinderProviderFactory.instance = instance;
 170  
             }
 171  
             
 172  0
             field.setBoolean(null, false);
 173  
         }
 174  0
         catch (Exception e)
 175  
         {
 176  
             // No Op
 177  0
             Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName());
 178  0
             if (log.isLoggable(Level.FINE))
 179  
             {
 180  0
                 log.log(Level.FINE, "Cannot access field _initialized"
 181  
                         + "from FactoryFinder ", e);
 182  
             }
 183  0
         }
 184  0
     }
 185  
     
 186  
     /**
 187  
      * Retrieve the installed instance of this class to be used by 
 188  
      * {@link javax.faces.FactoryFinder}. If no factory is set, return null
 189  
      * 
 190  
      * @return
 191  
      */
 192  
     public static FactoryFinderProviderFactory getInstance()
 193  
     {
 194  0
         return instance;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Provide the FactoryFinderProvider to be used to resolve factories.
 199  
      * Subclasses must implement this method. 
 200  
      * 
 201  
      * @return
 202  
      */
 203  
     public abstract FactoryFinderProvider getFactoryFinderProvider();
 204  
 }