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.exception.LoggedExceptionInInitializerError;
018    import com.liferay.portal.kernel.memory.EqualityWeakReference;
019    
020    import java.io.IOException;
021    
022    import java.lang.ref.WeakReference;
023    import java.lang.reflect.InvocationTargetException;
024    import java.lang.reflect.Method;
025    
026    import java.net.URL;
027    
028    import java.util.ArrayList;
029    import java.util.Collection;
030    import java.util.Collections;
031    import java.util.Enumeration;
032    import java.util.Iterator;
033    import java.util.List;
034    
035    /**
036     * @author Brian Wing Shun Chan
037     * @author Michael C. Han
038     * @author Shuyang Zhou
039     */
040    public class AggregateClassLoader extends ClassLoader {
041    
042            public static ClassLoader getAggregateClassLoader(
043                    ClassLoader parentClassLoader, ClassLoader... classLoaders) {
044    
045                    if (ArrayUtil.isEmpty(classLoaders)) {
046                            return parentClassLoader;
047                    }
048    
049                    AggregateClassLoader aggregateClassLoader = null;
050    
051                    if (parentClassLoader instanceof AggregateClassLoader) {
052                            aggregateClassLoader = (AggregateClassLoader)parentClassLoader;
053                    }
054                    else {
055                            aggregateClassLoader = new AggregateClassLoader(parentClassLoader);
056                    }
057    
058                    for (ClassLoader classLoader : classLoaders) {
059                            aggregateClassLoader.addClassLoader(classLoader);
060                    }
061    
062                    return aggregateClassLoader;
063            }
064    
065            public static ClassLoader getAggregateClassLoader(
066                    ClassLoader[] classLoaders) {
067    
068                    if (ArrayUtil.isEmpty(classLoaders)) {
069                            return null;
070                    }
071    
072                    return getAggregateClassLoader(classLoaders[0], classLoaders);
073            }
074    
075            public AggregateClassLoader(ClassLoader classLoader) {
076                    _parentClassLoaderReference = new WeakReference<>(classLoader);
077            }
078    
079            public void addClassLoader(ClassLoader classLoader) {
080                    List<ClassLoader> classLoaders = getClassLoaders();
081    
082                    if (classLoaders.contains(classLoader)) {
083                            return;
084                    }
085    
086                    if ((classLoader instanceof AggregateClassLoader) &&
087                            classLoader.getParent().equals(getParent())) {
088    
089                            AggregateClassLoader aggregateClassLoader =
090                                    (AggregateClassLoader)classLoader;
091    
092                            for (ClassLoader curClassLoader :
093                                            aggregateClassLoader.getClassLoaders()) {
094    
095                                    addClassLoader(curClassLoader);
096                            }
097                    }
098                    else {
099                            _classLoaderReferences.add(
100                                    new EqualityWeakReference<>(classLoader));
101                    }
102            }
103    
104            public void addClassLoader(ClassLoader... classLoaders) {
105                    for (ClassLoader classLoader : classLoaders) {
106                            addClassLoader(classLoader);
107                    }
108            }
109    
110            public void addClassLoader(Collection<ClassLoader> classLoaders) {
111                    for (ClassLoader classLoader : classLoaders) {
112                            addClassLoader(classLoader);
113                    }
114            }
115    
116            @Override
117            public boolean equals(Object obj) {
118                    if (this == obj) {
119                            return true;
120                    }
121    
122                    if (!(obj instanceof AggregateClassLoader)) {
123                            return false;
124                    }
125    
126                    AggregateClassLoader aggregateClassLoader = (AggregateClassLoader)obj;
127    
128                    if (_classLoaderReferences.equals(
129                                    aggregateClassLoader._classLoaderReferences) &&
130                            (((getParent() == null) &&
131                              (aggregateClassLoader.getParent() == null)) ||
132                             ((getParent() != null) &&
133                              getParent().equals(aggregateClassLoader.getParent())))) {
134    
135                            return true;
136                    }
137    
138                    return false;
139            }
140    
141            public List<ClassLoader> getClassLoaders() {
142                    List<ClassLoader> classLoaders = new ArrayList<>(
143                            _classLoaderReferences.size());
144    
145                    Iterator<EqualityWeakReference<ClassLoader>> itr =
146                            _classLoaderReferences.iterator();
147    
148                    while (itr.hasNext()) {
149                            WeakReference<ClassLoader> weakReference = itr.next();
150    
151                            ClassLoader classLoader = weakReference.get();
152    
153                            if (classLoader == null) {
154                                    itr.remove();
155                            }
156                            else {
157                                    classLoaders.add(classLoader);
158                            }
159                    }
160    
161                    return classLoaders;
162            }
163    
164            @Override
165            public URL getResource(String name) {
166                    for (ClassLoader classLoader : getClassLoaders()) {
167                            URL url = _getResource(classLoader, name);
168    
169                            if (url != null) {
170                                    return url;
171                            }
172                    }
173    
174                    ClassLoader parentClassLoader = _parentClassLoaderReference.get();
175    
176                    if (parentClassLoader == null) {
177                            return null;
178                    }
179    
180                    return parentClassLoader.getResource(name);
181            }
182    
183            @Override
184            public Enumeration<URL> getResources(String name) throws IOException {
185                    List<URL> urls = new ArrayList<>();
186    
187                    for (ClassLoader classLoader : getClassLoaders()) {
188                            urls.addAll(Collections.list(_getResources(classLoader, name)));
189                    }
190    
191                    ClassLoader parentClassLoader = _parentClassLoaderReference.get();
192    
193                    if (parentClassLoader != null) {
194                            urls.addAll(
195                                    Collections.list(_getResources(parentClassLoader, name)));
196                    }
197    
198                    return Collections.enumeration(urls);
199            }
200    
201            @Override
202            public int hashCode() {
203                    if (_classLoaderReferences != null) {
204                            return _classLoaderReferences.hashCode();
205                    }
206                    else {
207                            return 0;
208                    }
209            }
210    
211            @Override
212            protected Class<?> findClass(String name) throws ClassNotFoundException {
213                    for (ClassLoader classLoader : getClassLoaders()) {
214                            try {
215                                    return _findClass(classLoader, name);
216                            }
217                            catch (ClassNotFoundException cnfe) {
218                            }
219                    }
220    
221                    throw new ClassNotFoundException("Unable to find class " + name);
222            }
223    
224            @Override
225            protected synchronized Class<?> loadClass(String name, boolean resolve)
226                    throws ClassNotFoundException {
227    
228                    Class<?> loadedClass = null;
229    
230                    for (ClassLoader classLoader : getClassLoaders()) {
231                            try {
232                                    loadedClass = _loadClass(classLoader, name, resolve);
233    
234                                    break;
235                            }
236                            catch (ClassNotFoundException cnfe) {
237                            }
238                    }
239    
240                    if (loadedClass == null) {
241                            ClassLoader parentClassLoader = _parentClassLoaderReference.get();
242    
243                            if (parentClassLoader == null) {
244                                    throw new ClassNotFoundException(
245                                            "Parent class loader has been garbage collected");
246                            }
247    
248                            loadedClass = _loadClass(parentClassLoader, name, resolve);
249                    }
250                    else if (resolve) {
251                            resolveClass(loadedClass);
252                    }
253    
254                    return loadedClass;
255            }
256    
257            private static Class<?> _findClass(ClassLoader classLoader, String name)
258                    throws ClassNotFoundException {
259    
260                    try {
261                            return (Class<?>) _FIND_CLASS_METHOD.invoke(classLoader, name);
262                    }
263                    catch (InvocationTargetException ite) {
264                            throw new ClassNotFoundException(
265                                    "Unable to find class " + name, ite.getTargetException());
266                    }
267                    catch (Exception e) {
268                            throw new ClassNotFoundException("Unable to find class " + name, e);
269                    }
270            }
271    
272            private static URL _getResource(ClassLoader classLoader, String name) {
273                    try {
274                            return (URL)_GET_RESOURCE_METHOD.invoke(classLoader, name);
275                    }
276                    catch (InvocationTargetException ite) {
277                            return null;
278                    }
279                    catch (Exception e) {
280                            return null;
281                    }
282            }
283    
284            private static Enumeration<URL> _getResources(
285                            ClassLoader classLoader, String name)
286                    throws IOException {
287    
288                    try {
289                            return (Enumeration<URL>)_GET_RESOURCES_METHOD.invoke(
290                                    classLoader, name);
291                    }
292                    catch (InvocationTargetException ite) {
293                            Throwable t = ite.getTargetException();
294    
295                            throw new IOException(t);
296                    }
297                    catch (Exception e) {
298                            throw new IOException(e);
299                    }
300            }
301    
302            private static Class<?> _loadClass(
303                            ClassLoader classLoader, String name, boolean resolve)
304                    throws ClassNotFoundException {
305    
306                    try {
307                            return (Class<?>)_LOAD_CLASS_METHOD.invoke(
308                                    classLoader, name, resolve);
309                    }
310                    catch (InvocationTargetException ite) {
311                            throw new ClassNotFoundException(
312                                    "Unable to load class " + name, ite.getTargetException());
313                    }
314                    catch (Exception e) {
315                            throw new ClassNotFoundException("Unable to load class " + name, e);
316                    }
317            }
318    
319            private static final Method _FIND_CLASS_METHOD;
320    
321            private static final Method _GET_RESOURCE_METHOD;
322    
323            private static final Method _GET_RESOURCES_METHOD;
324    
325            private static final Method _LOAD_CLASS_METHOD;
326    
327            static {
328                    try {
329                            _FIND_CLASS_METHOD = ReflectionUtil.getDeclaredMethod(
330                                    ClassLoader.class, "findClass", String.class);
331    
332                            _GET_RESOURCE_METHOD = ReflectionUtil.getDeclaredMethod(
333                                    ClassLoader.class, "getResource", String.class);
334    
335                            _GET_RESOURCES_METHOD = ReflectionUtil.getDeclaredMethod(
336                                    ClassLoader.class, "getResources", String.class);
337    
338                            _LOAD_CLASS_METHOD = ReflectionUtil.getDeclaredMethod(
339                                    ClassLoader.class, "loadClass", String.class, boolean.class);
340                    }
341                    catch (Exception e) {
342                            throw new LoggedExceptionInInitializerError(e);
343                    }
344            }
345    
346            private final List<EqualityWeakReference<ClassLoader>>
347                    _classLoaderReferences = new ArrayList<>();
348            private final WeakReference<ClassLoader> _parentClassLoaderReference;
349    
350    }