001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.classloader;
018
019 import java.io.IOException;
020 import java.net.URL;
021 import java.net.URLStreamHandlerFactory;
022 import java.util.ArrayList;
023 import java.util.Arrays;
024 import java.util.Collection;
025 import java.util.Collections;
026 import java.util.Enumeration;
027 import java.util.List;
028
029 /**
030 * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class
031 * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced
032 * with a operation that checks each parent in order. This getParent method of this class will always return null,
033 * which may be interperated by the calling code to mean that this class loader is a direct child of the system class
034 * loader.
035 *
036 * @author Dain Sundstrom
037 * @version $Id: MultiParentClassLoader.java 437551 2006-08-28 06:14:47Z adc $
038 * @since 2.0
039 */
040 public class MultiParentClassLoader extends NamedClassLoader {
041 private final ClassLoader[] parents;
042 private final boolean inverseClassLoading;
043 private final String[] hiddenClasses;
044 private final String[] nonOverridableClasses;
045 private final String[] hiddenResources;
046 private final String[] nonOverridableResources;
047
048 /**
049 * Creates a named class loader with no parents.
050 * @param name the name of this class loader
051 * @param urls the urls from which this class loader will classes and resources
052 */
053 public MultiParentClassLoader(String name, URL[] urls) {
054 this(name, urls, ClassLoader.getSystemClassLoader());
055 }
056
057 /**
058 * Creates a named class loader as a child of the specified parent.
059 * @param name the name of this class loader
060 * @param urls the urls from which this class loader will classes and resources
061 * @param parent the parent of this class loader
062 */
063 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent) {
064 this(name, urls, new ClassLoader[] {parent});
065 }
066
067 /**
068 * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
069 * for accessing the urls..
070 * @param name the name of this class loader
071 * @param urls the urls from which this class loader will classes and resources
072 * @param parent the parent of this class loader
073 * @param factory the URLStreamHandlerFactory used to access the urls
074 */
075 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
076 this(name, urls, new ClassLoader[] {parent}, factory);
077 }
078
079 /**
080 * Creates a named class loader as a child of the specified parents.
081 * @param name the name of this class loader
082 * @param urls the urls from which this class loader will classes and resources
083 * @param parents the parents of this class loader
084 */
085 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents) {
086 this(name, urls, parents, false, new String[0], new String[0]);
087 }
088
089 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
090 this(name, urls, new ClassLoader[]{parent}, inverseClassLoading, hiddenClasses, nonOverridableClasses);
091 }
092
093 /**
094 * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory
095 * for accessing the urls..
096 * @param name the name of this class loader
097 * @param urls the urls from which this class loader will classes and resources
098 * @param parents the parents of this class loader
099 * @param factory the URLStreamHandlerFactory used to access the urls
100 */
101 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) {
102 super(name, urls, null, factory);
103 this.parents = copyParents(parents);
104 this.inverseClassLoading = false;
105 this.hiddenClasses = new String[0];
106 this.nonOverridableClasses = new String[0];
107 this.hiddenResources = new String[0];
108 this.nonOverridableResources = new String[0];
109 }
110
111 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) {
112 this(name, urls, parents, inverseClassLoading, (String[]) hiddenClasses.toArray(new String[hiddenClasses.size()]), (String[]) nonOverridableClasses.toArray(new String[nonOverridableClasses.size()]));
113 }
114
115 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
116 super(name, urls);
117 this.parents = copyParents(parents);
118 this.inverseClassLoading = inverseClassLoading;
119 this.hiddenClasses = hiddenClasses;
120 this.nonOverridableClasses = nonOverridableClasses;
121 hiddenResources = toResources(hiddenClasses);
122 nonOverridableResources = toResources(nonOverridableClasses);
123 }
124
125 private static String[] toResources(String[] classes) {
126 String[] resources = new String[classes.length];
127 for (int i = 0; i < classes.length; i++) {
128 String className = classes[i];
129 resources[i] = className.replace('.', '/');
130 }
131 return resources;
132 }
133
134 private static ClassLoader[] copyParents(ClassLoader[] parents) {
135 ClassLoader[] newParentsArray = new ClassLoader[parents.length];
136 for (int i = 0; i < parents.length; i++) {
137 ClassLoader parent = parents[i];
138 if (parent == null) {
139 throw new NullPointerException("parent[" + i + "] is null");
140 }
141 newParentsArray[i] = parent;
142 }
143 return newParentsArray;
144 }
145
146 /**
147 * Gets the parents of this class loader.
148 * @return the parents of this class loader
149 */
150 public ClassLoader[] getParents() {
151 return parents;
152 }
153
154 /**
155 * {@inheritDoc}
156 */
157 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
158 //
159 // Check if class is in the loaded classes cache
160 //
161 Class cachedClass = findLoadedClass(name);
162 if (cachedClass != null) {
163 return resolveClass(cachedClass, resolve);
164 }
165
166 //
167 // if we are using inverse class loading, check local urls first
168 //
169 if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) {
170 try {
171 Class clazz = findClass(name);
172 return resolveClass(clazz, resolve);
173 } catch (ClassNotFoundException ignored) {
174 }
175 }
176
177 //
178 // Check parent class loaders
179 //
180 if (!isHiddenClass(name)) {
181 for (int i = 0; i < parents.length; i++) {
182 ClassLoader parent = parents[i];
183 try {
184 Class clazz = parent.loadClass(name);
185 return resolveClass(clazz, resolve);
186 } catch (ClassNotFoundException ignored) {
187 // this parent didn't have the class; try the next one
188 }
189 }
190 }
191
192 //
193 // if we are not using inverse class loading, check local urls now
194 //
195 // don't worry about excluding non-overridable classes here... we
196 // have alredy checked he parent and the parent didn't have the
197 // class, so we can override now
198 if (!isDestroyed()) {
199 try {
200 Class clazz = findClass(name);
201 return resolveClass(clazz, resolve);
202 } catch (ClassNotFoundException ignored) {
203 }
204 }
205
206 throw new ClassNotFoundException(name + " in classloader " + name);
207 }
208
209 private boolean isNonOverridableClass(String name) {
210 for (int i = 0; i < nonOverridableClasses.length; i++) {
211 if (name.startsWith(nonOverridableClasses[i])) {
212 return true;
213 }
214 }
215 return false;
216 }
217
218 private boolean isHiddenClass(String name) {
219 for (int i = 0; i < hiddenClasses.length; i++) {
220 if (name.startsWith(hiddenClasses[i])) {
221 return true;
222 }
223 }
224 return false;
225 }
226
227 private Class resolveClass(Class clazz, boolean resolve) {
228 if (resolve) {
229 resolveClass(clazz);
230 }
231 return clazz;
232 }
233
234 /**
235 * {@inheritDoc}
236 */
237 public URL getResource(String name) {
238 if (isDestroyed()) {
239 return null;
240 }
241
242 //
243 // if we are using inverse class loading, check local urls first
244 //
245 if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) {
246 URL url = findResource(name);
247 if (url != null) {
248 return url;
249 }
250 }
251
252 //
253 // Check parent class loaders
254 //
255 if (!isHiddenResource(name)) {
256 for (int i = 0; i < parents.length; i++) {
257 ClassLoader parent = parents[i];
258 URL url = parent.getResource(name);
259 if (url != null) {
260 return url;
261 }
262 }
263 }
264
265 //
266 // if we are not using inverse class loading, check local urls now
267 //
268 // don't worry about excluding non-overridable resources here... we
269 // have alredy checked he parent and the parent didn't have the
270 // resource, so we can override now
271 if (!isDestroyed()) {
272 // parents didn't have the resource; attempt to load it from my urls
273 return findResource(name);
274 }
275
276 return null;
277 }
278
279 /**
280 * {@inheritDoc}
281 */
282 public Enumeration findResources(String name) throws IOException {
283 if (isDestroyed()) {
284 return Collections.enumeration(Collections.EMPTY_SET);
285 }
286
287 List resources = new ArrayList();
288
289 //
290 // if we are using inverse class loading, add the resources from local urls first
291 //
292 if (inverseClassLoading && !isDestroyed()) {
293 List myResources = Collections.list(super.findResources(name));
294 resources.addAll(myResources);
295 }
296
297 //
298 // Add parent resources
299 //
300 for (int i = 0; i < parents.length; i++) {
301 ClassLoader parent = parents[i];
302 List parentResources = Collections.list(parent.getResources(name));
303 resources.addAll(parentResources);
304 }
305
306 //
307 // if we are not using inverse class loading, add the resources from local urls now
308 //
309 if (!inverseClassLoading && !isDestroyed()) {
310 List myResources = Collections.list(super.findResources(name));
311 resources.addAll(myResources);
312 }
313
314 return Collections.enumeration(resources);
315 }
316
317 private boolean isNonOverridableResource(String name) {
318 for (int i = 0; i < nonOverridableResources.length; i++) {
319 if (name.startsWith(nonOverridableResources[i])) {
320 return true;
321 }
322 }
323 return false;
324 }
325
326 private boolean isHiddenResource(String name) {
327 for (int i = 0; i < hiddenResources.length; i++) {
328 if (name.startsWith(hiddenResources[i])) {
329 return true;
330 }
331 }
332 return false;
333 }
334
335 /**
336 * {@inheritDoc}
337 */
338 public String toString() {
339 return "[" + getClass().getName() + ":" +
340 " name=" + getName() +
341 " urls=" + Arrays.asList(getURLs()) +
342 " parents=" + Arrays.asList(parents) +
343 "]";
344 }
345 }