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
018package org.apache.commons.proxy2.impl;
019
020import java.lang.ref.Reference;
021import java.lang.ref.WeakReference;
022import java.util.Arrays;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Set;
027import java.util.WeakHashMap;
028
029/**
030 * A cache for storing implementation classes for proxies based on a specific type of {@link ProxyClassGenerator}. A
031 * proxy class cache ensures that there is only one class for every {@link ProxyClassGenerator}/{@link ClassLoader}
032 * /proxy class array combination.
033 * 
034 * @since 1.0
035 */
036public class ProxyClassCache
037{
038
039    //******************************************************************************************************************
040    // Fields
041    //******************************************************************************************************************
042
043    private final Map<ClassLoader, Map<Set<Class<?>>, WeakReference<Class<?>>>> loaderToClassCache
044        = new WeakHashMap<ClassLoader, Map<Set<Class<?>>, WeakReference<Class<?>>>>();
045    private final ProxyClassGenerator proxyClassGenerator;
046
047    //******************************************************************************************************************
048    // Constructors
049    //******************************************************************************************************************
050
051    /**
052     * Create a new ProxyClassCache instance.
053     * 
054     * @param proxyClassGenerator
055     */
056    public ProxyClassCache(ProxyClassGenerator proxyClassGenerator)
057    {
058        this.proxyClassGenerator = proxyClassGenerator;
059    }
060
061    //******************************************************************************************************************
062    // Other Methods
063    //******************************************************************************************************************
064
065    private Map<Set<Class<?>>, WeakReference<Class<?>>> getClassCache(ClassLoader classLoader)
066    {
067        Map<Set<Class<?>>, WeakReference<Class<?>>> cache = loaderToClassCache.get(classLoader);
068        if (cache == null)
069        {
070            cache = new HashMap<Set<Class<?>>, WeakReference<Class<?>>>();
071            loaderToClassCache.put(classLoader, cache);
072        }
073        return cache;
074    }
075
076    private Set<Class<?>> toClassCacheKey(Class<?>[] proxyClasses)
077    {
078        return new HashSet<Class<?>>(Arrays.asList(proxyClasses));
079    }
080
081    /**
082     * Returns the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and
083     * array of proxy classes.
084     * 
085     * @param classLoader
086     *            the classloader
087     * @param proxyClasses
088     *            the proxy classes
089     * @return the proxy class generated by the {@link ProxyClassGenerator} using the specified {@link ClassLoader} and
090     *         array of proxy classes
091     */
092    public synchronized Class<?> getProxyClass(ClassLoader classLoader, Class<?>[] proxyClasses)
093    {
094        final Map<Set<Class<?>>, WeakReference<Class<?>>> classCache = getClassCache(classLoader);
095        final Set<Class<?>> key = toClassCacheKey(proxyClasses);
096        Class<?> proxyClass;
097        Reference<Class<?>> proxyClassReference = classCache.get(key);
098        if (proxyClassReference == null)
099        {
100            proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);
101            classCache.put(key, new WeakReference<Class<?>>(proxyClass));
102        }
103        else
104        {
105            synchronized (proxyClassReference)
106            {
107                proxyClass = proxyClassReference.get();
108                if (proxyClass == null)
109                {
110                    proxyClass = proxyClassGenerator.generateProxyClass(classLoader, proxyClasses);
111                    classCache.put(key, new WeakReference<Class<?>>(proxyClass));
112                }
113            }
114        }
115        return proxyClass;
116    }
117}