javax.faces.CurrentThreadToServletContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javax.faces Show documentation
Show all versions of javax.faces Show documentation
This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.faces;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
// ----------------------------------------------------------- Inner Classes
final class CurrentThreadToServletContext {
ConcurrentMap applicationMap = new ConcurrentHashMap<>();
private AtomicBoolean logNullFacesContext = new AtomicBoolean(false);
private AtomicBoolean logNonNullFacesContext = new AtomicBoolean(false);
// Bug 20458755: This instance provides a method to look up the current FacesContext
// that bypasses the additional check for the InitFacesContext introduced
// by the fix for 20458755
private final ServletContextFacesContextFactory servletContextFacesContextFactory;
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger("javax.faces", "javax.faces.LogStrings");
}
CurrentThreadToServletContext() {
servletContextFacesContextFactory = new ServletContextFacesContextFactory();
}
// ------------------------------------------------------ Public Methods
Object getFallbackFactory(FactoryFinderInstance brokenFactoryManager, String factoryName) {
Object result = null;
ClassLoader cl = getClassLoader();
for (Map.Entry cur : applicationMap.entrySet()) {
if (cur.getKey().getClassLoader().equals(cl) && !cur.getValue().equals(brokenFactoryManager)) {
result = cur.getValue().getFactory(factoryName);
if (null != result) {
break;
}
}
}
return result;
}
FactoryFinderInstance getApplicationFactoryManager() {
ClassLoader cl = getClassLoader();
FactoryFinderInstance result = getApplicationFactoryManager(cl, true);
return result;
}
FactoryFinderInstance getApplicationFactoryManager(boolean create) {
ClassLoader cl = getClassLoader();
FactoryFinderInstance result = getApplicationFactoryManager(cl, create);
return result;
}
private FactoryFinderInstance getApplicationFactoryManager(ClassLoader cl, boolean create) {
FacesContext facesContext = servletContextFacesContextFactory.getFacesContextWithoutServletContextLookup();
boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext);
FactoryManagerCacheKey key = new FactoryManagerCacheKey(facesContext, cl, applicationMap);
FactoryFinderInstance result = applicationMap.get(key);
FactoryFinderInstance toCopy = null;
if (result == null && create) {
boolean createNewFactoryManagerInstance = false;
if (isSpecialInitializationCase) {
// We need to obtain a reference to the correct
// FactoryFinderInstance. Iterate through the data structure
// containing all FactoryFinderInstance instances for this VM.
FactoryManagerCacheKey curKey;
boolean classLoadersMatchButContextsDoNotMatch = false;
boolean foundNoMatchInApplicationMap = true;
for (Map.Entry cur : applicationMap.entrySet()) {
curKey = cur.getKey();
// If the current FactoryFinderInstance is for a
// the same ClassLoader as the current ClassLoader...
if (curKey.getClassLoader().equals(cl)) {
foundNoMatchInApplicationMap = false;
// Check the other descriminator for the
// key: the context.
// If the context objects of the keys are
// both non-null and non-equal, then *do*
// create a new FactoryFinderInstance instance.
if ((null != key.getContext() && null != curKey.getContext()) && (!key.getContext().equals(curKey.getContext()))) {
classLoadersMatchButContextsDoNotMatch = true;
toCopy = cur.getValue();
} else {
// Otherwise, use this FactoryFinderInstance
// instance.
result = cur.getValue();
}
break;
}
}
// We must create a new FactoryFinderInstance if there was no matchingKey
// at all found in the applicationMap, or a matchingKey was found
// and the matchingKey is safe to use in this web app
createNewFactoryManagerInstance = foundNoMatchInApplicationMap || (null == result && classLoadersMatchButContextsDoNotMatch);
} else {
createNewFactoryManagerInstance = true;
}
if (createNewFactoryManagerInstance) {
FactoryFinderInstance newResult;
if (null != toCopy) {
newResult = new FactoryFinderInstance(toCopy);
} else {
newResult = new FactoryFinderInstance();
}
result = applicationMap.putIfAbsent(key, newResult);
result = (null != result) ? result : newResult;
}
}
return result;
}
/*
* Uses the FactoryManagerCacheKey system to find the ServletContext
* associated with the current ClassLoader, if any.
*/
Object getServletContextForCurrentClassLoader() {
Object result;
FactoryManagerCacheKey key = new FactoryManagerCacheKey(null, getClassLoader(), applicationMap);
result = key.getContext();
return result;
}
/**
* This method is used to detect the following special initialization case.
* IF no FactoryFinderInstance can be found for key,
AND this call to getApplicationFactoryFinderInstance() *does* have a currentKeyrent FacesContext
BUT a previous call to getApplicationFactoryFinderInstance *did not* have a currentKeyrent FacesContext
*
* @param facesContext the currentKeyrent FacesContext for this request
* @return true if the currentKeyrent execution falls into the special initialization case.
*/
private boolean detectSpecialInitializationCase(FacesContext facesContext) {
boolean result = false;
if (null == facesContext) {
logNullFacesContext.compareAndSet(false, true);
} else {
logNonNullFacesContext.compareAndSet(false, true);
}
result = logNullFacesContext.get() && logNonNullFacesContext.get();
return result;
}
void removeApplicationFactoryManager() {
ClassLoader cl = getClassLoader();
FactoryFinderInstance fm = this.getApplicationFactoryManager(cl, false);
if (null != fm) {
fm.clearInjectionProvider();
}
FacesContext facesContext = servletContextFacesContextFactory.getFacesContextWithoutServletContextLookup();
boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext);
FactoryManagerCacheKey key = new FactoryManagerCacheKey(facesContext, cl, applicationMap);
applicationMap.remove(key);
if (isSpecialInitializationCase) {
logNullFacesContext.set(false);
logNonNullFacesContext.set(false);
}
}
void resetSpecialInitializationCaseFlags() {
logNullFacesContext.set(false);
logNonNullFacesContext.set(false);
}
/**
* Identify and return the class loader that is associated with the
* calling web application.
*
* @throws FacesException if the web application class loader
* cannot be identified
*/
private ClassLoader getClassLoader() throws FacesException {
// J2EE 1.3 (and later) containers are required to make the
// web application class loader visible through the context
// class loader of the current thread.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
throw new FacesException("getContextClassLoader");
}
return (cl);
}
private static final class FactoryManagerCacheKey {
// The ClassLoader that is active the first time this key
// is created. At startup time, this is assumed to be the
// web app ClassLoader
private ClassLoader cl;
// I marker that disambiguates the case when multiple
// web apps have the same web app ClassLoader but different
// ServletContext instances.
private Long marker;
// The ServletContext corresponding to this marker/ClassLoader pair.
private Object context;
private static final String MARKER_KEY = FactoryFinder.class.getName() + "." +
FactoryManagerCacheKey.class.getSimpleName();
private static final String INIT_TIME_CL_KEY = MARKER_KEY + ".InitTimeCLKey";
//
FactoryManagerCacheKey(FacesContext facesContext, ClassLoader cl,
Map factoryMap) {
ExternalContext extContext = (null != facesContext) ? facesContext.getExternalContext()
: null;
Object servletContext = (null != extContext) ? extContext.getContext() : null;
if (null == facesContext || null == extContext || null == servletContext) {
initFromFactoryMap(cl, factoryMap);
} else {
initFromAppMap(extContext, cl);
}
}
private void initFromFactoryMap(ClassLoader cl,
Map factoryMap) {
// We don't have a FacesContext.
// Our only recourse is to inspect the keys of the
// factoryMap and see if any of them has a classloader
// equal to our argument cl.
Set keys = factoryMap.keySet();
FactoryManagerCacheKey matchingKey = null;
if (keys.isEmpty()) {
this.cl = cl;
this.marker = new Long(System.currentTimeMillis());
} else {
// For each entry in the factoryMap's keySet...
for (FactoryManagerCacheKey currentKey : keys) {
assert(null != currentKey.cl);
ClassLoader matchingCL = findMatchConsideringParentClassLoader(cl, currentKey.cl);
// If there is a match...
if (null != matchingCL) {
// If the match was found on a previous iteration...
if (null != matchingKey) {
// LOGGER.log(Level.WARNING, "Multiple JSF Applications found on same ClassLoader. Unable to safely determine which FactoryManager instance to use. Defaulting to first match.");
break;
}
matchingKey = currentKey;
this.cl = matchingCL;
}
}
if (null != matchingKey) {
this.marker = matchingKey.marker;
this.context = matchingKey.context;
}
}
}
private ClassLoader findMatchConsideringParentClassLoader(ClassLoader argumentCL, ClassLoader currentKeyCL) {
ClassLoader curCL = argumentCL;
// For each ClassLoader in the hierarchy starting
// with the argument ClassLoader...
while (null != curCL) {
// if the ClassLoader at this level in the hierarchy
// is equal to the argument ClassLoader, consider it a matchingKey.
if (curCL.equals(currentKeyCL)) {
return curCL;
} else {
// If it's not a matchingKey, try the parent in the ClassLoader
// hierarchy.
curCL = curCL.getParent();
}
}
return null;
}
private void initFromAppMap(ExternalContext extContext, ClassLoader cl) {
Map appMap = extContext.getApplicationMap();
Long val = (Long) appMap.get(MARKER_KEY);
if (null == val) {
this.marker = new Long(System.currentTimeMillis());
appMap.put(MARKER_KEY, marker);
// If we needed to create a marker, assume that the
// argument CL is safe to treat as the web app
// ClassLoader. This assumption allows us
// to bypass the ClassLoader resolution algorithm
// in resolveToFirstTimeUsedClassLoader() in all cases
// except when the TCCL has been replaced.
appMap.put(INIT_TIME_CL_KEY, new Integer(System.identityHashCode(cl)));
} else {
this.marker = val;
}
this.cl = resolveToFirstTimeUsedClassLoader(cl, extContext);
this.context = extContext.getContext();
}
/*
* Resolve the argument ClassLoader to be the ClassLoader that
* was passed in to the ctor the first time a FactoryManagerCacheKey
* was created for this web app.
*/
private ClassLoader resolveToFirstTimeUsedClassLoader(ClassLoader toResolve, ExternalContext extContext) {
ClassLoader curCL = toResolve;
ClassLoader resolved = null;
Map appMap = extContext.getApplicationMap();
// See if the argument curCL already is the web app class loader
Integer webAppCLHashCode = (Integer) appMap.get(INIT_TIME_CL_KEY);
boolean found = false;
if (null != webAppCLHashCode) {
int toResolveHashCode = System.identityHashCode(curCL);
while (!found && null != curCL) {
found = (toResolveHashCode == webAppCLHashCode);
if (!found) {
curCL = curCL.getParent();
toResolveHashCode = System.identityHashCode(curCL);
}
}
}
resolved = found ? curCL : toResolve;
return resolved;
}
//
ClassLoader getClassLoader() {
return cl;
}
Object getContext() {
return context;
}
private FactoryManagerCacheKey() {}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FactoryManagerCacheKey other = (FactoryManagerCacheKey) obj;
if (this.cl != other.cl && (this.cl == null || !this.cl.equals(other.cl))) {
return false;
}
if (this.marker != other.marker && (this.marker == null || !this.marker.equals(other.marker))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + (this.cl != null ? this.cl.hashCode() : 0);
hash = 97 * hash + (this.marker != null ? this.marker.hashCode() : 0);
return hash;
}
}
} // END FactoryCache