1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.util;
24  
25  import com.liferay.portal.kernel.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.List;
34  
35  /**
36   * <a href="AggregateClassLoader.java.html"><b><i>View Source</i></b></a>
37   *
38   * @author Brian Wing Shun Chan
39   * @author Michael C. Han
40   */
41  public class AggregateClassLoader extends ClassLoader {
42  
43      public static ClassLoader getAggregateClassLoader(
44          ClassLoader parentClassLoader, ClassLoader[] classLoaders) {
45  
46          if ((classLoaders == null) || (classLoaders.length == 0)) {
47              return null;
48          }
49  
50          if (classLoaders.length == 1) {
51              return classLoaders[0];
52          }
53  
54          AggregateClassLoader aggregateClassLoader = new AggregateClassLoader(
55              parentClassLoader);
56  
57          for (ClassLoader classLoader : classLoaders) {
58              aggregateClassLoader.addClassLoader(classLoader);
59          }
60  
61          return aggregateClassLoader;
62      }
63  
64      public static ClassLoader getAggregateClassLoader(
65          ClassLoader[] classLoaders) {
66  
67          if ((classLoaders == null) || (classLoaders.length == 0)) {
68              return null;
69          }
70  
71          return getAggregateClassLoader(classLoaders[0], classLoaders);
72      }
73  
74      public AggregateClassLoader(ClassLoader classLoader) {
75          super(classLoader);
76      }
77  
78      public void addClassLoader(ClassLoader classLoader) {
79          if (_classLoaders.contains(classLoader)) {
80              return;
81          }
82  
83          if ((classLoader instanceof AggregateClassLoader) &&
84              (classLoader.getParent().equals(getParent()))){
85  
86              AggregateClassLoader aggregateClassLoader =
87                  (AggregateClassLoader)classLoader;
88  
89              for (ClassLoader curClassLoader :
90                      aggregateClassLoader.getClassLoaders()) {
91  
92                  addClassLoader(curClassLoader);
93              }
94          }
95          else {
96              if (classLoader instanceof ClassLoaderWrapper) {
97                  _classLoaders.add((ClassLoaderWrapper)classLoader);
98              }
99              else {
100                 _classLoaders.add(new ClassLoaderWrapper(classLoader));
101             }
102         }
103     }
104 
105     public void addClassLoader(ClassLoader... classLoaders) {
106         for (ClassLoader classLoader : classLoaders) {
107             addClassLoader(classLoader);
108         }
109     }
110 
111     public void addClassLoader(Collection<ClassLoader> classLoaders) {
112         for (ClassLoader classLoader : classLoaders) {
113             addClassLoader(classLoader);
114         }
115     }
116 
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 (_classLoaders.equals(aggregateClassLoader._classLoaders) &&
129             (((getParent() == null) &&
130               (aggregateClassLoader.getParent() == null)) ||
131              ((getParent() != null) &&
132               (getParent().equals(aggregateClassLoader.getParent()))))) {
133 
134             return true;
135         }
136 
137         return false;
138     }
139 
140     public List<ClassLoaderWrapper> getClassLoaders() {
141         return _classLoaders;
142     }
143 
144     public int hashCode() {
145         if (_classLoaders != null) {
146             return _classLoaders.hashCode();
147         }
148         else {
149             return 0;
150         }
151     }
152 
153     protected Class<?> findClass(String name) throws ClassNotFoundException {
154         for (ClassLoaderWrapper classLoader : _classLoaders) {
155             try {
156                 return classLoader.findClass(name);
157             }
158             catch (ClassNotFoundException cnfe) {
159             }
160         }
161 
162         throw new ClassNotFoundException("Unable to find class " + name);
163     }
164 
165     protected Class<?> loadClass(String name, boolean resolve)
166         throws ClassNotFoundException {
167 
168         Class<?> loadedClass = null;
169 
170         for (ClassLoaderWrapper classLoader : _classLoaders) {
171             try {
172                 loadedClass = classLoader.loadClass(name, resolve);
173 
174                 break;
175             }
176             catch (ClassNotFoundException cnfe) {
177             }
178         }
179 
180         if (loadedClass == null) {
181             loadedClass = super.loadClass(name, resolve);
182         }
183         else if (resolve) {
184             resolveClass(loadedClass);
185         }
186 
187         return loadedClass;
188     }
189 
190     private static Log _log = LogFactoryUtil.getLog(AggregateClassLoader.class);
191 
192     private List<ClassLoaderWrapper> _classLoaders =
193         new ArrayList<ClassLoaderWrapper>();
194 
195     // The following wrapper is key since we need access to the findClass
196     // method. An aggregate needs to be able to call the parent's findClass
197     // method. However, since this findClass method is normally protected, we
198     // must use reflection to access.
199 
200     private static class ClassLoaderWrapper extends ClassLoader {
201 
202         public ClassLoaderWrapper(ClassLoader classLoader) {
203             super(classLoader);
204         }
205 
206         public boolean equals(Object obj) {
207             if (!(obj instanceof ClassLoader)) {
208                 return false;
209             }
210 
211             return getParent().equals(obj);
212         }
213 
214         public Class<?> findClass(String name) throws ClassNotFoundException {
215             try {
216                 return (Class<?>)_findClassMethod.invoke(getParent(), name);
217             }
218             catch (InvocationTargetException ite) {
219                 throw new ClassNotFoundException(
220                     "Unable to find class " + name, ite.getTargetException());
221             }
222             catch (Exception e) {
223                 throw new ClassNotFoundException(
224                     "Unable to find class " + name, e);
225             }
226         }
227 
228         public Class<?> loadClass(String name, boolean resolve)
229             throws ClassNotFoundException {
230 
231             return super.loadClass(name, resolve);
232         }
233 
234         private static Method _findClassMethod;
235 
236         static {
237             try {
238                 _findClassMethod = ClassLoader.class.getDeclaredMethod(
239                     "findClass", String.class);
240 
241                 _findClassMethod.setAccessible(true);
242             }
243             catch (NoSuchMethodException nsme) {
244                 if (_log.isErrorEnabled()) {
245                     _log.error("Unable to locate findClass method", nsme);
246                 }
247             }
248         }
249 
250     }
251 
252 }