001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.discovery.tools;
018    
019    import java.util.HashMap;
020    import java.util.Map;
021    
022    import org.apache.commons.discovery.jdk.JDKHooks;
023    
024    /**
025     * Cache by a 'key' unique to the environment:
026     *
027     * - ClassLoader::groupContext::Object Cache
028     *         Cache : HashMap
029     *         Key   : Thread Context Class Loader (<code>ClassLoader</code>)
030     *         Value : groupContext::SPI Cache (<code>HashMap</code>)
031     *
032     * //- groupContext::Object Cache
033     * //         Cache : HashMap
034     * //         Key   : groupContext (<code>String</code>)
035     * //        Value : <code>Object</code>
036     *
037     * When we 'release', it is expected that the caller of the 'release'
038     * have the same thread context class loader... as that will be used
039     * to identify cached entries to be released.
040     */
041    public class EnvironmentCache {
042    
043        /**
044         * Allows null key, important as default groupContext is null.
045         *
046         * We will manage synchronization directly, so all caches are implemented
047         * as HashMap (unsynchronized).
048         */
049        private static final Map<ClassLoader, Map<String, Object>> root_cache =
050            new HashMap<ClassLoader, Map<String, Object>>();
051    
052        /**
053         * Initial hash size for SPI's, default just seem TO big today..
054         */
055        public static final int smallHashSize = 13;
056    
057        /**
058         * Get object keyed by classLoader.
059         *
060         * @param classLoader The class loader key
061         * @return The SPI name/instance cache
062         */
063        public static synchronized Map<String, Object> get(ClassLoader classLoader) {
064            /*
065             * 'null' (bootstrap/system class loader) thread context class loader
066             * is ok...  Until we learn otherwise.
067             */
068            return root_cache.get(classLoader);
069        }
070    
071        /**
072         * Put service keyed by spi & classLoader.
073         *
074         * @param classLoader The class loader key
075         * @param spis The SPI name/instance cache
076         */
077        public static synchronized void put(ClassLoader classLoader, Map<String, Object> spis) {
078            /*
079             * 'null' (bootstrap/system class loader) thread context class loader
080             * is ok...  Until we learn otherwise.
081             */
082            if (spis != null) {
083                root_cache.put(classLoader, spis);
084            }
085        }
086    
087        /********************** CACHE-MANAGEMENT SUPPORT **********************/
088    
089        /**
090         * Release all internal references to previously created service
091         * instances associated with the current thread context class loader.
092         * The <code>release()</code> method is called for service instances that
093         * implement the <code>Service</code> interface.
094         *
095         * This is useful in environments like servlet containers,
096         * which implement application reloading by throwing away a ClassLoader.
097         * Dangling references to objects in that class loader would prevent
098         * garbage collection.
099         */
100        public static synchronized void release() {
101            /*
102             * 'null' (bootstrap/system class loader) thread context class loader
103             * is ok...  Until we learn otherwise.
104             */
105            root_cache.remove(JDKHooks.getJDKHooks().getThreadContextClassLoader());
106        }
107    
108        /**
109         * Release any internal references to a previously created service
110         * instance associated with the current thread context class loader.
111         * If the SPI instance implements <code>Service</code>, then call
112         * <code>release()</code>.
113         *
114         * @param classLoader The class loader key
115         */
116        public static synchronized void release(ClassLoader classLoader) {
117            /*
118             * 'null' (bootstrap/system class loader) thread context class loader
119             * is ok...  Until we learn otherwise.
120             */
121            root_cache.remove(classLoader);
122        }
123    
124    }