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 */
017package org.apache.commons.discovery.tools;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import 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 */
041public 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}