1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  
27  /**
28   * <a href="AggregateClassLoader.java.html"><b><i>View Source</i></b></a>
29   *
30   * @author Brian Wing Shun Chan
31   * @author Michael C. Han
32   */
33  public class AggregateClassLoader extends ClassLoader {
34  
35      public static ClassLoader getAggregateClassLoader(
36          ClassLoader parentClassLoader, ClassLoader[] classLoaders) {
37  
38          if ((classLoaders == null) || (classLoaders.length == 0)) {
39              return null;
40          }
41  
42          if (classLoaders.length == 1) {
43              return classLoaders[0];
44          }
45  
46          AggregateClassLoader aggregateClassLoader = new AggregateClassLoader(
47              parentClassLoader);
48  
49          for (ClassLoader classLoader : classLoaders) {
50              aggregateClassLoader.addClassLoader(classLoader);
51          }
52  
53          return aggregateClassLoader;
54      }
55  
56      public static ClassLoader getAggregateClassLoader(
57          ClassLoader[] classLoaders) {
58  
59          if ((classLoaders == null) || (classLoaders.length == 0)) {
60              return null;
61          }
62  
63          return getAggregateClassLoader(classLoaders[0], classLoaders);
64      }
65  
66      public AggregateClassLoader(ClassLoader classLoader) {
67          super(classLoader);
68      }
69  
70      public void addClassLoader(ClassLoader classLoader) {
71          if (_classLoaders.contains(classLoader)) {
72              return;
73          }
74  
75          if ((classLoader instanceof AggregateClassLoader) &&
76              (classLoader.getParent().equals(getParent()))){
77  
78              AggregateClassLoader aggregateClassLoader =
79                  (AggregateClassLoader)classLoader;
80  
81              for (ClassLoader curClassLoader :
82                      aggregateClassLoader.getClassLoaders()) {
83  
84                  addClassLoader(curClassLoader);
85              }
86          }
87          else {
88              if (classLoader instanceof ClassLoaderWrapper) {
89                  _classLoaders.add((ClassLoaderWrapper)classLoader);
90              }
91              else {
92                  _classLoaders.add(new ClassLoaderWrapper(classLoader));
93              }
94          }
95      }
96  
97      public void addClassLoader(ClassLoader... classLoaders) {
98          for (ClassLoader classLoader : classLoaders) {
99              addClassLoader(classLoader);
100         }
101     }
102 
103     public void addClassLoader(Collection<ClassLoader> classLoaders) {
104         for (ClassLoader classLoader : classLoaders) {
105             addClassLoader(classLoader);
106         }
107     }
108 
109     public boolean equals(Object obj) {
110         if (this == obj) {
111             return true;
112         }
113 
114         if (!(obj instanceof AggregateClassLoader)) {
115             return false;
116         }
117 
118         AggregateClassLoader aggregateClassLoader = (AggregateClassLoader)obj;
119 
120         if (_classLoaders.equals(aggregateClassLoader._classLoaders) &&
121             (((getParent() == null) &&
122               (aggregateClassLoader.getParent() == null)) ||
123              ((getParent() != null) &&
124               (getParent().equals(aggregateClassLoader.getParent()))))) {
125 
126             return true;
127         }
128 
129         return false;
130     }
131 
132     public List<ClassLoaderWrapper> getClassLoaders() {
133         return _classLoaders;
134     }
135 
136     public int hashCode() {
137         if (_classLoaders != null) {
138             return _classLoaders.hashCode();
139         }
140         else {
141             return 0;
142         }
143     }
144 
145     protected Class<?> findClass(String name) throws ClassNotFoundException {
146         for (ClassLoaderWrapper classLoader : _classLoaders) {
147             try {
148                 return classLoader.findClass(name);
149             }
150             catch (ClassNotFoundException cnfe) {
151             }
152         }
153 
154         throw new ClassNotFoundException("Unable to find class " + name);
155     }
156 
157     protected Class<?> loadClass(String name, boolean resolve)
158         throws ClassNotFoundException {
159 
160         Class<?> loadedClass = null;
161 
162         for (ClassLoaderWrapper classLoader : _classLoaders) {
163             try {
164                 loadedClass = classLoader.loadClass(name, resolve);
165 
166                 break;
167             }
168             catch (ClassNotFoundException cnfe) {
169             }
170         }
171 
172         if (loadedClass == null) {
173             loadedClass = super.loadClass(name, resolve);
174         }
175         else if (resolve) {
176             resolveClass(loadedClass);
177         }
178 
179         return loadedClass;
180     }
181 
182     private static Log _log = LogFactoryUtil.getLog(AggregateClassLoader.class);
183 
184     private List<ClassLoaderWrapper> _classLoaders =
185         new ArrayList<ClassLoaderWrapper>();
186 
187     // The following wrapper is key since we need access to the findClass
188     // method. An aggregate needs to be able to call the parent's findClass
189     // method. However, since this findClass method is normally protected, we
190     // must use reflection to access.
191 
192     private static class ClassLoaderWrapper extends ClassLoader {
193 
194         public ClassLoaderWrapper(ClassLoader classLoader) {
195             super(classLoader);
196         }
197 
198         public boolean equals(Object obj) {
199             if (!(obj instanceof ClassLoader)) {
200                 return false;
201             }
202 
203             return getParent().equals(obj);
204         }
205 
206         public Class<?> findClass(String name) throws ClassNotFoundException {
207             try {
208                 return (Class<?>)_findClassMethod.invoke(getParent(), name);
209             }
210             catch (InvocationTargetException ite) {
211                 throw new ClassNotFoundException(
212                     "Unable to find class " + name, ite.getTargetException());
213             }
214             catch (Exception e) {
215                 throw new ClassNotFoundException(
216                     "Unable to find class " + name, e);
217             }
218         }
219 
220         public Class<?> loadClass(String name, boolean resolve)
221             throws ClassNotFoundException {
222 
223             return super.loadClass(name, resolve);
224         }
225 
226         private static Method _findClassMethod;
227 
228         static {
229             try {
230                 _findClassMethod = ClassLoader.class.getDeclaredMethod(
231                     "findClass", String.class);
232 
233                 _findClassMethod.setAccessible(true);
234             }
235             catch (NoSuchMethodException nsme) {
236                 if (_log.isErrorEnabled()) {
237                     _log.error("Unable to locate findClass method", nsme);
238                 }
239             }
240         }
241 
242     }
243 
244 }