001    /**
002     * Copyright (c) 2000-2011 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.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Method;
022    
023    import java.util.ArrayList;
024    import java.util.Collection;
025    import java.util.List;
026    
027    /**
028     * @author Brian Wing Shun Chan
029     * @author Michael C. Han
030     */
031    public class AggregateClassLoader extends ClassLoader {
032    
033            public static ClassLoader getAggregateClassLoader(
034                    ClassLoader parentClassLoader, ClassLoader[] classLoaders) {
035    
036                    if ((classLoaders == null) || (classLoaders.length == 0)) {
037                            return null;
038                    }
039    
040                    if (classLoaders.length == 1) {
041                            return classLoaders[0];
042                    }
043    
044                    AggregateClassLoader aggregateClassLoader = new AggregateClassLoader(
045                            parentClassLoader);
046    
047                    for (ClassLoader classLoader : classLoaders) {
048                            aggregateClassLoader.addClassLoader(classLoader);
049                    }
050    
051                    return aggregateClassLoader;
052            }
053    
054            public static ClassLoader getAggregateClassLoader(
055                    ClassLoader[] classLoaders) {
056    
057                    if ((classLoaders == null) || (classLoaders.length == 0)) {
058                            return null;
059                    }
060    
061                    return getAggregateClassLoader(classLoaders[0], classLoaders);
062            }
063    
064            public AggregateClassLoader(ClassLoader classLoader) {
065                    super(classLoader);
066            }
067    
068            public void addClassLoader(ClassLoader classLoader) {
069                    if (_classLoaders.contains(classLoader)) {
070                            return;
071                    }
072    
073                    if ((classLoader instanceof AggregateClassLoader) &&
074                            (classLoader.getParent().equals(getParent()))){
075    
076                            AggregateClassLoader aggregateClassLoader =
077                                    (AggregateClassLoader)classLoader;
078    
079                            for (ClassLoader curClassLoader :
080                                            aggregateClassLoader.getClassLoaders()) {
081    
082                                    addClassLoader(curClassLoader);
083                            }
084                    }
085                    else {
086                            if (classLoader instanceof ClassLoaderWrapper) {
087                                    _classLoaders.add((ClassLoaderWrapper)classLoader);
088                            }
089                            else {
090                                    _classLoaders.add(new ClassLoaderWrapper(classLoader));
091                            }
092                    }
093            }
094    
095            public void addClassLoader(ClassLoader... classLoaders) {
096                    for (ClassLoader classLoader : classLoaders) {
097                            addClassLoader(classLoader);
098                    }
099            }
100    
101            public void addClassLoader(Collection<ClassLoader> classLoaders) {
102                    for (ClassLoader classLoader : classLoaders) {
103                            addClassLoader(classLoader);
104                    }
105            }
106    
107            public boolean equals(Object obj) {
108                    if (this == obj) {
109                            return true;
110                    }
111    
112                    if (!(obj instanceof AggregateClassLoader)) {
113                            return false;
114                    }
115    
116                    AggregateClassLoader aggregateClassLoader = (AggregateClassLoader)obj;
117    
118                    if (_classLoaders.equals(aggregateClassLoader._classLoaders) &&
119                            (((getParent() == null) &&
120                              (aggregateClassLoader.getParent() == null)) ||
121                             ((getParent() != null) &&
122                              (getParent().equals(aggregateClassLoader.getParent()))))) {
123    
124                            return true;
125                    }
126    
127                    return false;
128            }
129    
130            public List<ClassLoaderWrapper> getClassLoaders() {
131                    return _classLoaders;
132            }
133    
134            public int hashCode() {
135                    if (_classLoaders != null) {
136                            return _classLoaders.hashCode();
137                    }
138                    else {
139                            return 0;
140                    }
141            }
142    
143            protected Class<?> findClass(String name) throws ClassNotFoundException {
144                    for (ClassLoaderWrapper classLoader : _classLoaders) {
145                            try {
146                                    return classLoader.findClass(name);
147                            }
148                            catch (ClassNotFoundException cnfe) {
149                            }
150                    }
151    
152                    throw new ClassNotFoundException("Unable to find class " + name);
153            }
154    
155            protected Class<?> loadClass(String name, boolean resolve)
156                    throws ClassNotFoundException {
157    
158                    Class<?> loadedClass = null;
159    
160                    for (ClassLoaderWrapper classLoader : _classLoaders) {
161                            try {
162                                    loadedClass = classLoader.loadClass(name, resolve);
163    
164                                    break;
165                            }
166                            catch (ClassNotFoundException cnfe) {
167                            }
168                    }
169    
170                    if (loadedClass == null) {
171                            loadedClass = super.loadClass(name, resolve);
172                    }
173                    else if (resolve) {
174                            resolveClass(loadedClass);
175                    }
176    
177                    return loadedClass;
178            }
179    
180            private static Log _log = LogFactoryUtil.getLog(AggregateClassLoader.class);
181    
182            private List<ClassLoaderWrapper> _classLoaders =
183                    new ArrayList<ClassLoaderWrapper>();
184    
185            // The following wrapper is key since we need access to the findClass
186            // method. An aggregate needs to be able to call the parent's findClass
187            // method. However, since this findClass method is normally protected, we
188            // must use reflection to access.
189    
190            private static class ClassLoaderWrapper extends ClassLoader {
191    
192                    public ClassLoaderWrapper(ClassLoader classLoader) {
193                            super(classLoader);
194                    }
195    
196                    public boolean equals(Object obj) {
197                            if (!(obj instanceof ClassLoader)) {
198                                    return false;
199                            }
200    
201                            return getParent().equals(obj);
202                    }
203    
204                    public Class<?> findClass(String name) throws ClassNotFoundException {
205                            try {
206                                    return (Class<?>)_findClassMethod.invoke(getParent(), name);
207                            }
208                            catch (InvocationTargetException ite) {
209                                    throw new ClassNotFoundException(
210                                            "Unable to find class " + name, ite.getTargetException());
211                            }
212                            catch (Exception e) {
213                                    throw new ClassNotFoundException(
214                                            "Unable to find class " + name, e);
215                            }
216                    }
217    
218                    public Class<?> loadClass(String name, boolean resolve)
219                            throws ClassNotFoundException {
220    
221                            return super.loadClass(name, resolve);
222                    }
223    
224                    private static Method _findClassMethod;
225    
226                    static {
227                            try {
228                                    _findClassMethod = ClassLoader.class.getDeclaredMethod(
229                                            "findClass", String.class);
230    
231                                    _findClassMethod.setAccessible(true);
232                            }
233                            catch (NoSuchMethodException nsme) {
234                                    if (_log.isErrorEnabled()) {
235                                            _log.error("Unable to locate findClass method", nsme);
236                                    }
237                            }
238                    }
239    
240            }
241    
242    }