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 log.log(Level.WARNING,
159 "Called FactoryFinderProviderFactory.setFactory after " +
160 "initialized FactoryFinder (first call to getFactory() or setFactory()). " +
161 "This method should be called before " +
162 "any 'web context' is initialized in the current 'classloader context'. " +
163 "By that reason it will not be changed.");
164 }
165 else
166 {
167 _instance = instance;
168 }
169
170 field.setBoolean(null, false);
171 }
172 catch (Exception e)
173 {
174 // No Op
175 Logger log = Logger.getLogger(FactoryFinderProviderFactory.class.getName());
176 if (log.isLoggable(Level.FINE))
177 log.log(Level.FINE, "Cannot access field _initialized"
178 + "from FactoryFinder ", e);
179 }
180 }
181
182 /**
183 * Retrieve the installed instance of this class to be used by
184 * {@link javax.faces.FactoryFinder}. If no factory is set, return null
185 *
186 * @return
187 */
188 public static FactoryFinderProviderFactory getInstance()
189 {
190 return _instance;
191 }
192
193 /**
194 * Provide the FactoryFinderProvider to be used to resolve factories.
195 * Subclasses must implement this method.
196 *
197 * @return
198 */
199 public abstract FactoryFinderProvider getFactoryFinderProvider();
200 }