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 public abstract class FactoryFinderProviderFactory 126 { 127 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 Class clazz = ClassUtils.classForName("javax.faces.FactoryFinder"); 151 Field field = clazz.getDeclaredField("initialized"); 152 field.setAccessible(true); 153 154 if (field.getBoolean(null)) 155 { 156 Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName()); 157 if (log.isLoggable(Level.WARNING)) 158 { 159 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 } 167 else 168 { 169 FactoryFinderProviderFactory.instance = instance; 170 } 171 172 field.setBoolean(null, false); 173 } 174 catch (Exception e) 175 { 176 // No Op 177 Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName()); 178 if (log.isLoggable(Level.FINE)) 179 { 180 log.log(Level.FINE, "Cannot access field _initialized" 181 + "from FactoryFinder ", e); 182 } 183 } 184 } 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 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 }