001    /**
002     * Copyright (c) 2000-present 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.concurrent.ConcurrentReferenceKeyHashMap;
018    import com.liferay.portal.kernel.concurrent.ConcurrentReferenceValueHashMap;
019    import com.liferay.portal.kernel.memory.FinalizeManager;
020    
021    import java.lang.reflect.Constructor;
022    import java.lang.reflect.Field;
023    import java.lang.reflect.InvocationHandler;
024    import java.lang.reflect.Proxy;
025    
026    import java.util.concurrent.ConcurrentMap;
027    
028    /**
029     * @author Shuyang Zhou
030     */
031    public class ProxyUtil {
032    
033            public static InvocationHandler getInvocationHandler(Object proxy) {
034                    if (!isProxyClass(proxy.getClass())) {
035                            throw new IllegalArgumentException("Not a proxy instance");
036                    }
037    
038                    try {
039                            return (InvocationHandler)_invocationHandlerField.get(proxy);
040                    }
041                    catch (Exception e) {
042                            throw new IllegalArgumentException(e);
043                    }
044            }
045    
046            public static Class<?> getProxyClass(
047                    ClassLoader classLoader, Class<?>... interfaceClasses) {
048    
049                    ConcurrentMap<LookupKey, Class<?>> classReferences =
050                            _classReferences.get(classLoader);
051    
052                    if (classReferences == null) {
053                            classReferences = new ConcurrentReferenceValueHashMap<>(
054                                    FinalizeManager.WEAK_REFERENCE_FACTORY);
055    
056                            ConcurrentMap<LookupKey, Class<?>> oldClassReferences =
057                                    _classReferences.putIfAbsent(classLoader, classReferences);
058    
059                            if (oldClassReferences != null) {
060                                    classReferences = oldClassReferences;
061                            }
062                    }
063    
064                    LookupKey lookupKey = new LookupKey(interfaceClasses);
065    
066                    Class<?> clazz = classReferences.get(lookupKey);
067    
068                    if (clazz == null) {
069                            synchronized(classReferences) {
070                                    clazz = classReferences.get(lookupKey);
071    
072                                    if (clazz == null) {
073                                            clazz = Proxy.getProxyClass(classLoader, interfaceClasses);
074    
075                                            classReferences.put(lookupKey, clazz);
076                                    }
077                            }
078                    }
079    
080                    Constructor<?> constructor = null;
081    
082                    try {
083                            constructor = clazz.getConstructor(_argumentsClazz);
084    
085                            constructor.setAccessible(true);
086                    }
087                    catch (Exception e) {
088                            throw new InternalError(e.toString());
089                    }
090    
091                    _constructors.putIfAbsent(clazz, constructor);
092    
093                    return clazz;
094            }
095    
096            public static boolean isProxyClass(Class<?> clazz) {
097                    if (clazz == null) {
098                            throw new NullPointerException();
099                    }
100    
101                    return _constructors.containsKey(clazz);
102            }
103    
104            public static Object newProxyInstance(
105                    ClassLoader classLoader, Class<?>[] interfaces,
106                    InvocationHandler invocationHandler) {
107    
108                    Constructor<?> constructor = _constructors.get(
109                            getProxyClass(classLoader, interfaces));
110    
111                    try {
112                            return constructor.newInstance(new Object[] {invocationHandler});
113                    }
114                    catch (Exception e) {
115                            throw new InternalError(e.toString());
116                    }
117            }
118    
119            private static final Class<?>[] _argumentsClazz = {InvocationHandler.class};
120            private static final ConcurrentMap
121                    <ClassLoader, ConcurrentMap<LookupKey, Class<?>>> _classReferences =
122                            new ConcurrentReferenceKeyHashMap<>(
123                                    FinalizeManager.WEAK_REFERENCE_FACTORY);
124            private static final ConcurrentMap<Class<?>, Constructor<?>> _constructors =
125                    new ConcurrentReferenceKeyHashMap<>(
126                            new ConcurrentReferenceValueHashMap<Class<?>, Constructor<?>>(
127                                    FinalizeManager.WEAK_REFERENCE_FACTORY),
128                            FinalizeManager.WEAK_REFERENCE_FACTORY);
129            private static final Field _invocationHandlerField;
130    
131            static {
132                    try {
133                            _invocationHandlerField = ReflectionUtil.getDeclaredField(
134                                    Proxy.class, "h");
135                    }
136                    catch (Exception e) {
137                            throw new ExceptionInInitializerError(e);
138                    }
139            }
140    
141            private static class LookupKey {
142    
143                    public LookupKey(Class<?>[] interfaces) {
144                            _interfaces = interfaces;
145    
146                            int hashCode = 0;
147    
148                            for (Class<?> clazz : interfaces) {
149                                    hashCode = HashUtil.hash(hashCode, clazz.getName());
150                            }
151    
152                            _hashCode = hashCode;
153                    }
154    
155                    @Override
156                    public boolean equals(Object obj) {
157                            LookupKey lookupKey = (LookupKey)obj;
158    
159                            if (_interfaces.length != lookupKey._interfaces.length) {
160                                    return false;
161                            }
162    
163                            for (int i = 0; i < _interfaces.length; i++) {
164                                    if (_interfaces[i] != lookupKey._interfaces[i]) {
165                                            return false;
166                                    }
167                            }
168    
169                            return true;
170                    }
171    
172                    @Override
173                    public int hashCode() {
174                            return _hashCode;
175                    }
176    
177                    private final int _hashCode;
178                    private final Class<?>[] _interfaces;
179    
180            }
181    
182    }