All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.CurrentThreadToServletContext Maven / Gradle / Ivy

There is a newer version: 3.1.0.SP02
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package javax.faces;

import static com.sun.faces.util.Util.coalesce;
import static com.sun.faces.util.Util.getContextClassLoader2;
import static com.sun.faces.util.Util.isAnyNull;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.identityHashCode;
import static java.util.logging.Level.WARNING;

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.Logger;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

final class CurrentThreadToServletContext {
    
    private static final Logger LOGGER = Logger.getLogger("javax.faces", "javax.faces.LogStrings");
    
    // 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 = new ServletContextFacesContextFactory();
    
    ConcurrentMap factoryFinderMap = new ConcurrentHashMap<>();
    private AtomicBoolean logNullFacesContext = new AtomicBoolean();
    private AtomicBoolean logNonNullFacesContext = new AtomicBoolean();


    
    // ------------------------------------------------------ Public Methods


    FactoryFinderInstance getFactoryFinder() {
        return getFactoryFinder(getContextClassLoader2(), true);
    }

    FactoryFinderInstance getFactoryFinder(boolean create) {
        return getFactoryFinder(getContextClassLoader2(), create);
    }

    private FactoryFinderInstance getFactoryFinder(ClassLoader classLoader, boolean create) {
       
        FacesContext facesContext = servletContextFacesContextFactory.getFacesContextWithoutServletContextLookup();
       
        boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext);
        FactoryFinderCacheKey key = new FactoryFinderCacheKey(facesContext, classLoader, factoryFinderMap);
        FactoryFinderInstance factoryFinder = factoryFinderMap.get(key);
        FactoryFinderInstance toCopy = null;
        
        if (factoryFinder == null && create) {
            boolean createNewFactoryFinderInstance = 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.
                boolean classLoadersMatchButContextsDoNotMatch = false;
                boolean foundNoMatchInApplicationMap = true;
                
                for (Map.Entry cur : factoryFinderMap.entrySet()) {
                    FactoryFinderCacheKey curKey = cur.getKey();

                    // If the current FactoryFinderInstance is for the same ClassLoader as the current ClassLoader...
                    if (curKey.getClassLoader().equals(classLoader)) {
                        foundNoMatchInApplicationMap = false;
                        
                        // Check the other discriminator 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 (!isAnyNull(key.getContext(), curKey.getContext()) && !key.getContext().equals(curKey.getContext())) {
                            classLoadersMatchButContextsDoNotMatch = true;
                            toCopy = cur.getValue();
                        } else {
                            // Otherwise, use this FactoryFinderInstance instance.
                            factoryFinder = 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
                createNewFactoryFinderInstance = foundNoMatchInApplicationMap || (factoryFinder == null && classLoadersMatchButContextsDoNotMatch);
            } else {
                createNewFactoryFinderInstance = true;
            }
            
            if (createNewFactoryFinderInstance) {
                FactoryFinderInstance newResult;
                if (toCopy != null) {
                    newResult = new FactoryFinderInstance(facesContext, toCopy);
                } else {
                    newResult = new FactoryFinderInstance(facesContext);
                }
                
                factoryFinder = coalesce(factoryFinderMap.putIfAbsent(key, newResult), newResult);
            }
        }
        
        return factoryFinder;
    }
    
    Object getFallbackFactory(FactoryFinderInstance brokenFactoryManager, String factoryName) {
        
        ClassLoader classLoader = getContextClassLoader2();
        for (Map.Entry cur : factoryFinderMap.entrySet()) {
            if (cur.getKey().getClassLoader().equals(classLoader) && !cur.getValue().equals(brokenFactoryManager)) {
                Object factory = cur.getValue().getFactory(factoryName);
                if (factory != null) {
                    return factory;
                }
            }
        }
        
        return null;
    }

    /**
     * Uses the FactoryManagerCacheKey system to find the ServletContext associated with the current
     * ClassLoader, if any.
     */
    Object getServletContextForCurrentClassLoader() {
        return new FactoryFinderCacheKey(null, getContextClassLoader2(), factoryFinderMap).getContext();
    }

    /**
     * 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) {
        if (facesContext == null) {
            logNullFacesContext.compareAndSet(false, true);
        } else {
            logNonNullFacesContext.compareAndSet(false, true);
        }
        
        return logNullFacesContext.get() && logNonNullFacesContext.get();
    }

    void removeFactoryFinder() {
        ClassLoader classLoader = getContextClassLoader2();
        FactoryFinderInstance factoryFinder = getFactoryFinder(classLoader, false);
        if (factoryFinder != null) {
            factoryFinder.clearInjectionProvider();
        }
        
        FacesContext facesContext = servletContextFacesContextFactory.getFacesContextWithoutServletContextLookup();
        boolean isSpecialInitializationCase = detectSpecialInitializationCase(facesContext);
        
        factoryFinderMap.remove(new FactoryFinderCacheKey(facesContext, classLoader, factoryFinderMap));
       
        if (isSpecialInitializationCase) {
            resetSpecialInitializationCaseFlags();
        }
    }

    void resetSpecialInitializationCaseFlags() {
        logNullFacesContext.set(false);
        logNonNullFacesContext.set(false);
    }

    private static final class FactoryFinderCacheKey {
        
        /**
         * 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 classLoader;
        
        /**
         * A 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() + "." + FactoryFinderCacheKey.class.getSimpleName();
        private static final String INIT_TIME_CL_KEY = MARKER_KEY + ".InitTimeCLKey";

        FactoryFinderCacheKey(FacesContext facesContext, ClassLoader classLoaderIn, Map factoryMap) {
            ExternalContext extContext = facesContext != null ? facesContext.getExternalContext() : null;
            Object servletContext = extContext != null ? extContext.getContext() : null;

            if (isAnyNull(facesContext, extContext, servletContext)) {
                initFromFactoryMap(classLoaderIn, factoryMap);
            } else {
                initFromApplicationMap(extContext, classLoaderIn);
            }
        }

        private void initFromFactoryMap(ClassLoader classLoaderIn, Map factoryFinderMap) {
            
            // We don't have a FacesContext.
            //
            // Our only recourse is to inspect the keys of the factoryFinderMap and see if any of them has a classloader
            // equal to our argument classLoader
            
            Set keys = factoryFinderMap.keySet();
            FactoryFinderCacheKey matchingKey = null;

            if (keys.isEmpty()) {
                classLoader = classLoaderIn;
                marker = currentTimeMillis();
            } else {

                // For each entry in the factoryMap's keySet...
                for (FactoryFinderCacheKey currentKey : keys) {
                    
                    ClassLoader matchingClassLoader = findMatchConsideringParentClassLoader(classLoaderIn, currentKey.classLoader);
                    
                    // If there is a match...
                    if (matchingClassLoader != null) {
                        // If the match was found on a previous iteration...
                        if (matchingKey != null) {
                            // LOGGER.log(WARNING,
                            //     "Multiple Jakarta Server Faces Applications found on same ClassLoader.  Unable to safely determine which FactoryFinder instance to use. Defaulting to first match.");
                            break;
                        }
                        matchingKey = currentKey;
                        classLoader = matchingClassLoader;
                    }
                }
                
                if (matchingKey != null) {
                    marker = matchingKey.marker;
                    context = matchingKey.context;
                }
            }
        }

        private ClassLoader findMatchConsideringParentClassLoader(ClassLoader argumentClassLoader, ClassLoader currentKeyCL) {
            ClassLoader currentClassLoader = argumentClassLoader;
            
            // For each ClassLoader in the hierarchy starting with the argument ClassLoader...
            while (currentClassLoader != null) {
                
                // If the ClassLoader at this level in the hierarchy is equal to the argument ClassLoader, 
                // consider it a matchingKey.
                if (currentClassLoader.equals(currentKeyCL)) {
                    return currentClassLoader;
                } else {
                    // If it's not a matchingKey, try the parent in the ClassLoader hierarchy.
                    currentClassLoader = currentClassLoader.getParent();
                }
            }
            
            return null;
        }

        private void initFromApplicationMap(ExternalContext extContext, ClassLoader classLoaderIn) {
            Map applicationMap = extContext.getApplicationMap();

            Long val = (Long) applicationMap.get(MARKER_KEY);
            if (val == null) {
                marker = currentTimeMillis();
                applicationMap.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.
                
                applicationMap.put(INIT_TIME_CL_KEY, identityHashCode(classLoaderIn));

            } else {
                marker = val;
            }
            
            classLoader = resolveToFirstTimeUsedClassLoader(classLoaderIn, extContext);
            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 classLoaderToResolve, ExternalContext extContext) {
            ClassLoader currentClassLoader = classLoaderToResolve;
            Map appMap = extContext.getApplicationMap();

            // See if the argument currentClassLoader already is the web app class loader
            Integer webAppCLHashCode = (Integer) appMap.get(INIT_TIME_CL_KEY);
            boolean found = false;
            if (webAppCLHashCode != null) {
                int toResolveHashCode = identityHashCode(currentClassLoader);
                while (!found && currentClassLoader != null) {
                    found = toResolveHashCode == webAppCLHashCode;
                    if (!found) {
                        currentClassLoader = currentClassLoader.getParent();
                        toResolveHashCode = identityHashCode(currentClassLoader);
                    }
                }
            }

            return found ? currentClassLoader : classLoaderToResolve;
        }

        ClassLoader getClassLoader() {
            return classLoader;
        }

        Object getContext() {
            return context;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            
            if (getClass() != obj.getClass()) {
                return false;
            }
            
            final FactoryFinderCacheKey other = (FactoryFinderCacheKey) obj;
            if (this.classLoader != other.classLoader && (this.classLoader == null || !this.classLoader.equals(other.classLoader))) {
                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 + (classLoader != null ? this.classLoader.hashCode() : 0);
            return 97 * hash + (marker != null ? marker.hashCode() : 0);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy