001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.classloader;
020:
021: import org.apache.commons.logging.LogFactory;
022:
023: import java.beans.Introspector;
024: import java.io.IOException;
025: import java.net.URL;
026: import java.net.URLClassLoader;
027: import java.net.URLStreamHandlerFactory;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.Collections;
031: import java.util.Enumeration;
032: import java.util.List;
033:
034: /**
035: * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class
036: * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced
037: * with a operation that checks each parent in order. This getParent method of this class will always return null,
038: * which may be interpreted by the calling code to mean that this class loader is a direct child of the system class
039: * loader.
040: *
041: * @version $Rev$ $Date$
042: */
043: public class MultiParentClassLoader extends URLClassLoader {
044: private final ClassLoader[] parents;
045: private final boolean inverseClassLoading;
046: private final String[] hiddenClasses;
047: private final String[] nonOverridableClasses;
048: private final String[] hiddenResources;
049: private final String[] nonOverridableResources;
050: private boolean destroyed = false;
051:
052: /**
053: * Creates a named class loader with no parents.
054: *
055: * @param urls the urls from which this class loader will classes and resources
056: */
057: public MultiParentClassLoader(URL[] urls) {
058: super (urls);
059: parents = new ClassLoader[] { ClassLoader
060: .getSystemClassLoader() };
061: inverseClassLoading = false;
062: hiddenClasses = new String[0];
063: nonOverridableClasses = new String[0];
064: hiddenResources = new String[0];
065: nonOverridableResources = new String[0];
066: }
067:
068: /**
069: * Creates a named class loader as a child of the specified parent.
070: *
071: * @param urls the urls from which this class loader will classes and resources
072: * @param parent the parent of this class loader
073: */
074: public MultiParentClassLoader(URL[] urls, ClassLoader parent) {
075: this (urls, new ClassLoader[] { parent });
076: }
077:
078: public MultiParentClassLoader(URL[] urls, ClassLoader parent,
079: boolean inverseClassLoading, String[] hiddenClasses,
080: String[] nonOverridableClasses) {
081: this (urls, new ClassLoader[] { parent }, inverseClassLoading,
082: hiddenClasses, nonOverridableClasses);
083: }
084:
085: /**
086: * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
087: * for accessing the urls..
088: *
089: * @param urls the urls from which this class loader will classes and resources
090: * @param parent the parent of this class loader
091: * @param factory the URLStreamHandlerFactory used to access the urls
092: */
093: public MultiParentClassLoader(URL[] urls, ClassLoader parent,
094: URLStreamHandlerFactory factory) {
095: this (urls, new ClassLoader[] { parent }, factory);
096: }
097:
098: /**
099: * Creates a named class loader as a child of the specified parents.
100: *
101: * @param urls the urls from which this class loader will classes and resources
102: * @param parents the parents of this class loader
103: */
104: public MultiParentClassLoader(URL[] urls, ClassLoader[] parents) {
105: super (urls);
106: this .parents = copyParents(parents);
107: inverseClassLoading = false;
108: hiddenClasses = new String[0];
109: nonOverridableClasses = new String[0];
110: hiddenResources = new String[0];
111: nonOverridableResources = new String[0];
112: }
113:
114: public MultiParentClassLoader(URL[] urls, ClassLoader[] parents,
115: boolean inverseClassLoading, Collection hiddenClasses,
116: Collection nonOverridableClasses) {
117: this (urls, parents, inverseClassLoading,
118: (String[]) hiddenClasses
119: .toArray(new String[hiddenClasses.size()]),
120: (String[]) nonOverridableClasses
121: .toArray(new String[nonOverridableClasses
122: .size()]));
123: }
124:
125: public MultiParentClassLoader(URL[] urls, ClassLoader[] parents,
126: boolean inverseClassLoading, String[] hiddenClasses,
127: String[] nonOverridableClasses) {
128: super (urls);
129: this .parents = copyParents(parents);
130: this .inverseClassLoading = inverseClassLoading;
131: this .hiddenClasses = hiddenClasses;
132: this .nonOverridableClasses = nonOverridableClasses;
133: hiddenResources = toResources(hiddenClasses);
134: nonOverridableResources = toResources(nonOverridableClasses);
135: }
136:
137: private String[] toResources(String[] classes) {
138: String[] resources = new String[classes.length];
139: for (int i = 0; i < classes.length; i++) {
140: String className = classes[i];
141: resources[i] = className.replace('.', '/');
142: }
143: return resources;
144: }
145:
146: /**
147: * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory
148: * for accessing the urls..
149: *
150: * @param urls the urls from which this class loader will classes and resources
151: * @param parents the parents of this class loader
152: * @param factory the URLStreamHandlerFactory used to access the urls
153: */
154: public MultiParentClassLoader(URL[] urls, ClassLoader[] parents,
155: URLStreamHandlerFactory factory) {
156: super (urls, null, factory);
157: this .parents = copyParents(parents);
158: inverseClassLoading = false;
159: hiddenClasses = new String[0];
160: nonOverridableClasses = new String[0];
161: hiddenResources = new String[0];
162: nonOverridableResources = new String[0];
163: }
164:
165: private static ClassLoader[] copyParents(ClassLoader[] parents) {
166: ClassLoader[] newParentsArray = new ClassLoader[parents.length];
167: for (int i = 0; i < parents.length; i++) {
168: ClassLoader parent = parents[i];
169: if (parent == null) {
170: throw new RuntimeException("parent[" + i + "] is null");
171: }
172: newParentsArray[i] = parent;
173: }
174: return newParentsArray;
175: }
176:
177: /**
178: * Gets the parents of this class loader.
179: *
180: * @return the parents of this class loader
181: */
182: public ClassLoader[] getParents() {
183: return parents;
184: }
185:
186: public void addURL(URL url) {
187: // todo this needs a security check
188: super .addURL(url);
189: }
190:
191: protected synchronized Class loadClass(String name, boolean resolve)
192: throws ClassNotFoundException {
193: //
194: // Check if class is in the loaded classes cache
195: //
196: Class cachedClass = findLoadedClass(name);
197: if (cachedClass != null) {
198: return resolveClass(cachedClass, resolve);
199: }
200:
201: //
202: // if we are using inverse class loading, check local urls first
203: //
204: if (inverseClassLoading && !isDestroyed()
205: && !isNonOverridableClass(name)) {
206: try {
207: Class clazz = findClass(name);
208: return resolveClass(clazz, resolve);
209: } catch (ClassNotFoundException ignored) {
210: }
211: }
212:
213: //
214: // Check parent class loaders
215: //
216: if (!isHiddenClass(name)) {
217: for (int i = 0; i < parents.length; i++) {
218: ClassLoader parent = parents[i];
219: try {
220: Class clazz = parent.loadClass(name);
221: return resolveClass(clazz, resolve);
222: } catch (ClassNotFoundException ignored) {
223: // this parent didn't have the class; try the next one
224: }
225: }
226: }
227:
228: //
229: // if we are not using inverse class loading, check local urls now
230: //
231: // don't worry about excluding non-overridable classes here... we
232: // have alredy checked he parent and the parent didn't have the
233: // class, so we can override now
234: if (!isDestroyed()) {
235: try {
236: Class clazz = findClass(name);
237: return resolveClass(clazz, resolve);
238: } catch (ClassNotFoundException ignored) {
239: }
240: }
241:
242: throw new ClassNotFoundException(name);
243: }
244:
245: private boolean isNonOverridableClass(String name) {
246: for (int i = 0; i < nonOverridableClasses.length; i++) {
247: if (name.startsWith(nonOverridableClasses[i])) {
248: return true;
249: }
250: }
251: return false;
252: }
253:
254: private boolean isHiddenClass(String name) {
255: for (int i = 0; i < hiddenClasses.length; i++) {
256: if (name.startsWith(hiddenClasses[i])) {
257: return true;
258: }
259: }
260: return false;
261: }
262:
263: private Class resolveClass(Class clazz, boolean resolve) {
264: if (resolve) {
265: resolveClass(clazz);
266: }
267: return clazz;
268: }
269:
270: public URL getResource(String name) {
271: if (isDestroyed()) {
272: return null;
273: }
274:
275: //
276: // if we are using inverse class loading, check local urls first
277: //
278: if (inverseClassLoading && !isDestroyed()
279: && !isNonOverridableResource(name)) {
280: URL url = findResource(name);
281: if (url != null) {
282: return url;
283: }
284: }
285:
286: //
287: // Check parent class loaders
288: //
289: if (!isHiddenResource(name)) {
290: for (int i = 0; i < parents.length; i++) {
291: ClassLoader parent = parents[i];
292: URL url = parent.getResource(name);
293: if (url != null) {
294: return url;
295: }
296: }
297: }
298:
299: //
300: // if we are not using inverse class loading, check local urls now
301: //
302: // don't worry about excluding non-overridable resources here... we
303: // have alredy checked he parent and the parent didn't have the
304: // resource, so we can override now
305: if (!isDestroyed()) {
306: // parents didn't have the resource; attempt to load it from my urls
307: return findResource(name);
308: }
309:
310: return null;
311: }
312:
313: public Enumeration findResources(String name) throws IOException {
314: if (isDestroyed()) {
315: return Collections.enumeration(Collections.EMPTY_SET);
316: }
317:
318: List resources = new ArrayList();
319:
320: //
321: // if we are using inverse class loading, add the resources from local urls first
322: //
323: if (inverseClassLoading && !isDestroyed()) {
324: List myResources = Collections.list(super
325: .findResources(name));
326: resources.addAll(myResources);
327: }
328:
329: //
330: // Add parent resources
331: //
332: for (int i = 0; i < parents.length; i++) {
333: ClassLoader parent = parents[i];
334: List parentResources = Collections.list(parent
335: .getResources(name));
336: resources.addAll(parentResources);
337: }
338:
339: //
340: // if we are not using inverse class loading, add the resources from local urls now
341: //
342: if (!inverseClassLoading && !isDestroyed()) {
343: List myResources = Collections.list(super
344: .findResources(name));
345: resources.addAll(myResources);
346: }
347:
348: return Collections.enumeration(resources);
349: }
350:
351: private boolean isNonOverridableResource(String name) {
352: for (int i = 0; i < nonOverridableResources.length; i++) {
353: if (name.startsWith(nonOverridableResources[i])) {
354: return true;
355: }
356: }
357: return false;
358: }
359:
360: private boolean isHiddenResource(String name) {
361: for (int i = 0; i < hiddenResources.length; i++) {
362: if (name.startsWith(hiddenResources[i])) {
363: return true;
364: }
365: }
366: return false;
367: }
368:
369: public String toString() {
370: return "[" + getClass().getName() + "]";
371: }
372:
373: public synchronized boolean isDestroyed() {
374: return destroyed;
375: }
376:
377: public void destroy() {
378: synchronized (this ) {
379: if (destroyed) {
380: return;
381: }
382: destroyed = true;
383: }
384:
385: LogFactory.release(this );
386: // clearSoftCache(ObjectInputStream.class, "subclassAudits");
387: // clearSoftCache(ObjectOutputStream.class, "subclassAudits");
388: // clearSoftCache(ObjectStreamClass.class, "localDescs");
389: // clearSoftCache(ObjectStreamClass.class, "reflectors");
390:
391: // The beanInfoCache in java.beans.Introspector will hold on to Classes which
392: // it has introspected. If we don't flush the cache, we may run out of
393: // Permanent Generation space.
394: Introspector.flushCaches();
395: }
396:
397: // private static final Object lock = new Object();
398: // private static boolean clearSoftCacheFailed = false;
399: //
400: // private static void clearSoftCache(Class clazz, String fieldName) {
401: // Map cache = null;
402: // try {
403: // Field f = clazz.getDeclaredField(fieldName);
404: // f.setAccessible(true);
405: // cache = (Map) f.get(null);
406: // } catch (Throwable e) {
407: // synchronized (lock) {
408: // if (!clearSoftCacheFailed) {
409: // clearSoftCacheFailed = true;
410: // LogFactory.getLog(ConfigurationClassLoader.class).error("Unable to clear SoftCache field " + fieldName + " in class " + clazz);
411: // }
412: // }
413: // }
414: //
415: // if (cache != null) {
416: // synchronized (cache) {
417: // cache.clear();
418: // }
419: // }
420: // }
421:
422: }
|