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.io.unsync.UnsyncByteArrayInputStream;
018    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    
022    import java.io.ObjectInputStream;
023    import java.io.ObjectOutputStream;
024    
025    import java.lang.reflect.InvocationTargetException;
026    import java.lang.reflect.Method;
027    
028    import java.util.ArrayList;
029    import java.util.List;
030    
031    /**
032     * @author Brian Wing Shun Chan
033     */
034    public class ClassLoaderProxy {
035    
036            public ClassLoaderProxy(Object obj, ClassLoader classLoader) {
037                    this(obj, obj.getClass().getName(), classLoader);
038            }
039    
040            public ClassLoaderProxy(
041                    Object obj, String className, ClassLoader classLoader) {
042    
043                    _obj = obj;
044                    _className = className;
045                    _classLoader = classLoader;
046            }
047    
048            public ClassLoader getClassLoader() {
049                    return _classLoader;
050            }
051    
052            public String getClassName() {
053                    return _className;
054            }
055    
056            public Object invoke(MethodHandler methodHandler) throws Throwable {
057                    Thread currentThread = Thread.currentThread();
058    
059                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
060    
061                    try {
062                            currentThread.setContextClassLoader(_classLoader);
063    
064                            return _invoke(methodHandler);
065                    }
066                    catch (InvocationTargetException ite) {
067                            throw translateThrowable(ite.getCause(), contextClassLoader);
068                    }
069                    catch (Throwable t) {
070                            _log.error(t, t);
071    
072                            throw t;
073                    }
074                    finally {
075                            currentThread.setContextClassLoader(contextClassLoader);
076                    }
077            }
078    
079            /**
080             * @deprecated As of 6.1.0
081             */
082            @Deprecated
083            public Object invoke(String methodName, Object[] args) throws Throwable {
084                    Thread currentThread = Thread.currentThread();
085    
086                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
087    
088                    try {
089                            currentThread.setContextClassLoader(_classLoader);
090    
091                            Class<?> clazz = Class.forName(_className, true, _classLoader);
092    
093                            List<Class<?>> parameterTypes = new ArrayList<>();
094    
095                            for (int i = 0; i < args.length; i++) {
096                                    Object arg = args[i];
097    
098                                    Class<?> argClazz = arg.getClass();
099    
100                                    Class<?> argClass = Class.forName(
101                                            argClazz.getName(), true, _classLoader);
102    
103                                    if (ClassUtil.isSubclass(argClass, PrimitiveWrapper.class)) {
104                                            MethodKey methodKey = new MethodKey(argClass, "getValue");
105    
106                                            Method method = methodKey.getMethod();
107    
108                                            args[i] = method.invoke(arg, (Object[])null);
109    
110                                            argClass = (Class<?>)argClass.getField("TYPE").get(arg);
111                                    }
112    
113                                    if (ClassUtil.isSubclass(argClass, NullWrapper.class)) {
114                                            NullWrapper nullWrapper = (NullWrapper)arg;
115    
116                                            argClass = Class.forName(
117                                                    nullWrapper.getClassName(), true, _classLoader);
118    
119                                            args[i] = null;
120                                    }
121    
122                                    parameterTypes.add(argClass);
123                            }
124    
125                            Method method = null;
126    
127                            try {
128                                    method = clazz.getMethod(
129                                            methodName,
130                                            parameterTypes.toArray(new Class[parameterTypes.size()]));
131                            }
132                            catch (NoSuchMethodException nsme) {
133                                    Method[] methods = ((Class<?>)clazz).getMethods();
134    
135                                    for (int i = 0; i < methods.length; i++) {
136                                            Class<?>[] methodParameterTypes =
137                                                    methods[i].getParameterTypes();
138    
139                                            if (methods[i].getName().equals(methodName) &&
140                                                    (methodParameterTypes.length ==
141                                                            parameterTypes.size())) {
142    
143                                                    boolean correctParams = true;
144    
145                                                    for (int j = 0; j < parameterTypes.size(); j++) {
146                                                            Class<?> a = parameterTypes.get(j);
147                                                            Class<?> b = methodParameterTypes[j];
148    
149                                                            if (!ClassUtil.isSubclass(a, b)) {
150                                                                    correctParams = false;
151    
152                                                                    break;
153                                                            }
154                                                    }
155    
156                                                    if (correctParams) {
157                                                            method = methods[i];
158    
159                                                            break;
160                                                    }
161                                            }
162                                    }
163    
164                                    if (method == null) {
165                                            throw nsme;
166                                    }
167                            }
168    
169                            return method.invoke(_obj, args);
170                    }
171                    catch (InvocationTargetException ite) {
172                            throw translateThrowable(ite.getCause(), contextClassLoader);
173                    }
174                    catch (Throwable t) {
175                            _log.error(t, t);
176    
177                            throw t;
178                    }
179                    finally {
180                            currentThread.setContextClassLoader(contextClassLoader);
181                    }
182            }
183    
184            protected Throwable translateThrowable(
185                    Throwable throwable, ClassLoader contextClassLoader) {
186    
187                    try {
188                            UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
189                                    new UnsyncByteArrayOutputStream();
190    
191                            try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(
192                                            unsyncByteArrayOutputStream)) {
193    
194                                    objectOutputStream.writeObject(throwable);
195    
196                                    objectOutputStream.flush();
197                            }
198    
199                            UnsyncByteArrayInputStream unsyncByteArrayInputStream =
200                                    new UnsyncByteArrayInputStream(
201                                            unsyncByteArrayOutputStream.unsafeGetByteArray(), 0,
202                                            unsyncByteArrayOutputStream.size());
203    
204                            try (ObjectInputStream objectInputStream =
205                                            new ClassLoaderObjectInputStream(
206                                                    unsyncByteArrayInputStream, contextClassLoader)) {
207    
208                                    return (Throwable)objectInputStream.readObject();
209                            }
210                    }
211                    catch (Throwable throwable2) {
212                            _log.error(throwable2, throwable2);
213    
214                            return throwable2;
215                    }
216            }
217    
218            private Object _invoke(MethodHandler methodHandler) throws Exception {
219                    try {
220                            return methodHandler.invoke(_obj);
221                    }
222                    catch (NoSuchMethodException nsme) {
223                            MethodKey methodKey = methodHandler.getMethodKey();
224    
225                            String name = methodKey.getMethodName();
226    
227                            Class<?>[] parameterTypes = methodKey.getParameterTypes();
228    
229                            Class<?> clazz = Class.forName(_className, true, _classLoader);
230    
231                            for (Method method : clazz.getMethods()) {
232                                    String curName = method.getName();
233                                    Class<?>[] curParameterTypes = method.getParameterTypes();
234    
235                                    if (!curName.equals(name) ||
236                                            (curParameterTypes.length != parameterTypes.length)) {
237    
238                                            continue;
239                                    }
240    
241                                    boolean correctParams = true;
242    
243                                    for (int j = 0; j < parameterTypes.length; j++) {
244                                            Class<?> a = parameterTypes[j];
245                                            Class<?> b = curParameterTypes[j];
246    
247                                            if (!ClassUtil.isSubclass(a, b.getName())) {
248                                                    correctParams = false;
249    
250                                                    break;
251                                            }
252                                    }
253    
254                                    if (correctParams) {
255                                            return method.invoke(_obj, methodHandler.getArguments());
256                                    }
257                            }
258    
259                            throw nsme;
260                    }
261            }
262    
263            private static final Log _log = LogFactoryUtil.getLog(
264                    ClassLoaderProxy.class);
265    
266            private final ClassLoader _classLoader;
267            private final String _className;
268            private final Object _obj;
269    
270    }