001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.memory.EqualityWeakReference;
018    
019    import java.lang.ref.Reference;
020    import java.lang.ref.ReferenceQueue;
021    import java.lang.ref.WeakReference;
022    import java.lang.reflect.Constructor;
023    import java.lang.reflect.Field;
024    import java.lang.reflect.InvocationHandler;
025    import java.lang.reflect.Proxy;
026    
027    import java.util.concurrent.ConcurrentHashMap;
028    import java.util.concurrent.ConcurrentMap;
029    
030    /**
031     * @author Shuyang Zhou
032     */
033    public class ProxyUtil {
034    
035            public static InvocationHandler getInvocationHandler(Object proxy) {
036                    if (!isProxyClass(proxy.getClass())) {
037                            throw new IllegalArgumentException("Not a proxy instance");
038                    }
039    
040                    try {
041                            return (InvocationHandler)_invocationHandlerField.get(proxy);
042                    }
043                    catch (Exception e) {
044                            throw new IllegalArgumentException(e);
045                    }
046            }
047    
048            public static Class<?> getProxyClass(
049                    ClassLoader classLoader, Class<?>... interfaceClasses) {
050    
051                    EqualityWeakReference<ClassLoader> classLoaderReference =
052                            new EqualityWeakReference<ClassLoader>(classLoader);
053    
054                    ConcurrentMap<LookupKey, Reference<Class<?>>> classReferences =
055                            _classReferences.get(classLoaderReference);
056    
057                    if (classReferences == null) {
058                            classReferences =
059                                    new ConcurrentHashMap<LookupKey, Reference<Class<?>>>();
060    
061                            classLoaderReference = new EqualityWeakReference<ClassLoader>(
062                                    classLoader, _classLoaderReferenceQueue);
063    
064                            ConcurrentMap<LookupKey, Reference<Class<?>>> oldClassReferences =
065                                    _classReferences.putIfAbsent(
066                                            classLoaderReference, classReferences);
067    
068                            if (oldClassReferences != null) {
069                                    classReferences = oldClassReferences;
070    
071                                    classLoaderReference.enqueue();
072                            }
073                    }
074    
075                    LookupKey lookupKey = new LookupKey(interfaceClasses);
076    
077                    Reference<Class<?>> classReference = classReferences.get(lookupKey);
078    
079                    Class<?> clazz = null;
080    
081                    if ((classReference == null) ||
082                            ((clazz = classReference.get()) == null)) {
083    
084                            synchronized(classReferences) {
085                                    classReference = classReferences.get(lookupKey);
086    
087                                    if ((classReference == null) ||
088                                            ((clazz = classReference.get()) == null)) {
089    
090                                            clazz = Proxy.getProxyClass(classLoader, interfaceClasses);
091    
092                                            classReferences.put(
093                                                    lookupKey, new WeakReference<Class<?>>(clazz));
094                                    }
095                            }
096                    }
097    
098                    Constructor<?> constructor = null;
099    
100                    try {
101                            constructor = clazz.getConstructor(_argumentsClazz);
102                    }
103                    catch (Exception e) {
104                            throw new InternalError(e.toString());
105                    }
106    
107                    EqualityWeakReference<Class<?>> proxyClassReference =
108                            new EqualityWeakReference<Class<?>>(
109                                    clazz, _proxyClassReferenceQueue);
110    
111                    _constructors.putIfAbsent(proxyClassReference, constructor);
112    
113                    while (true) {
114                            EqualityWeakReference<ClassLoader> staleClassLoaderReference =
115                                    (EqualityWeakReference<ClassLoader>)
116                                            _classLoaderReferenceQueue.poll();
117    
118                            if (staleClassLoaderReference == null) {
119                                    break;
120                            }
121    
122                            _classReferences.remove(staleClassLoaderReference);
123                    }
124    
125                    while (true) {
126                            EqualityWeakReference<Class<?>> staleProxyClassReference =
127                                    (EqualityWeakReference<Class<?>>)
128                                            _proxyClassReferenceQueue.poll();
129    
130                            if (staleProxyClassReference == null) {
131                                    break;
132                            }
133    
134                            _constructors.remove(staleProxyClassReference);
135                    }
136    
137                    return clazz;
138            }
139    
140            public static boolean isProxyClass(Class<?> clazz) {
141                    if (clazz == null) {
142                            throw new NullPointerException();
143                    }
144    
145                    EqualityWeakReference<Class<?>> equalityWeakReference =
146                            new EqualityWeakReference<Class<?>>(clazz);
147    
148                    return _constructors.containsKey(equalityWeakReference);
149            }
150    
151            public static Object newProxyInstance(
152                    ClassLoader classLoader, Class<?>[] interfaces,
153                    InvocationHandler invocationHandler) {
154    
155                    Class<?> clazz = getProxyClass(classLoader, interfaces);
156    
157                    EqualityWeakReference<Class<?>> proxyClassReference =
158                            new EqualityWeakReference<Class<?>>(clazz);
159    
160                    Constructor<?> constructor = _constructors.get(proxyClassReference);
161    
162                    try {
163                            return constructor.newInstance(new Object[] {invocationHandler});
164                    }
165                    catch (Exception e) {
166                            throw new InternalError(e.toString());
167                    }
168            }
169    
170            private static Class<?>[] _argumentsClazz = {InvocationHandler.class};
171            private static ReferenceQueue<ClassLoader> _classLoaderReferenceQueue =
172                    new ReferenceQueue<ClassLoader>();
173            private static ConcurrentMap
174                    <EqualityWeakReference<ClassLoader>,
175                            ConcurrentMap<LookupKey, Reference<Class<?>>>> _classReferences =
176                                    new ConcurrentHashMap<EqualityWeakReference<ClassLoader>,
177                                            ConcurrentMap<LookupKey, Reference<Class<?>>>>();
178            private static ConcurrentMap
179                    <EqualityWeakReference<Class<?>>, Constructor<?>> _constructors =
180                            new ConcurrentHashMap
181                                    <EqualityWeakReference<Class<?>>, Constructor<?>>();
182            private static Field _invocationHandlerField;
183            private static ReferenceQueue<Class<?>> _proxyClassReferenceQueue =
184                    new ReferenceQueue<Class<?>>();
185    
186            private static class LookupKey {
187    
188                    public LookupKey(Class<?>[] interfaces) {
189                            _interfaces = interfaces;
190    
191                            _hashCode = 1;
192    
193                            for (Class<?> clazz : interfaces) {
194                                    String name = clazz.getName();
195    
196                                    _hashCode = HashUtil.hash(_hashCode, name.hashCode());
197                            }
198                    }
199    
200                    @Override
201                    public boolean equals(Object obj) {
202                            LookupKey lookupKey = (LookupKey)obj;
203    
204                            if (_interfaces.length != lookupKey._interfaces.length) {
205                                    return false;
206                            }
207    
208                            for (int i = 0; i < _interfaces.length; i++) {
209                                    if (_interfaces[i] != lookupKey._interfaces[i]) {
210                                            return false;
211                                    }
212                            }
213    
214                            return true;
215                    }
216    
217                    @Override
218                    public int hashCode() {
219                            return _hashCode;
220                    }
221    
222                    private int _hashCode;
223                    private final Class<?>[] _interfaces;
224    
225            }
226    
227            static {
228                    try {
229                            _invocationHandlerField = ReflectionUtil.getDeclaredField(
230                                    Proxy.class, "h");
231                    }
232                    catch (Exception e) {
233                            throw new ExceptionInInitializerError(e);
234                    }
235            }
236    
237    }