| /** |
| * |
| * Copyright 2005 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.axis2.util; |
| |
| import java.beans.Introspector; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.ObjectStreamClass; |
| import java.lang.reflect.Field; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.net.URLStreamHandlerFactory; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.logging.LogFactory; |
| |
| /** |
| * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class |
| * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced |
| * with a operation that checks each parent in order. This getParent method of this class will always return null, |
| * which may be interpreted by the calling code to mean that this class loader is a direct child of the system class |
| * loader. |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public class MultiParentClassLoader extends URLClassLoader { |
| private final ClassLoader[] parents; |
| private final boolean inverseClassLoading; |
| private final String[] hiddenClasses; |
| private final String[] nonOverridableClasses; |
| private final String[] hiddenResources; |
| private final String[] nonOverridableResources; |
| private boolean destroyed = false; |
| |
| /** |
| * Creates a named class loader with no parents. |
| * |
| * @param urls the urls from which this class loader will classes and resources |
| */ |
| public MultiParentClassLoader(URL[] urls) { |
| super(urls); |
| parents = new ClassLoader[]{ClassLoader.getSystemClassLoader()}; |
| inverseClassLoading = false; |
| hiddenClasses = new String[0]; |
| nonOverridableClasses = new String[0]; |
| hiddenResources = new String[0]; |
| nonOverridableResources = new String[0]; |
| } |
| |
| |
| /** |
| * Creates a named class loader as a child of the specified parent. |
| * |
| * @param urls the urls from which this class loader will classes and resources |
| * @param parent the parent of this class loader |
| */ |
| public MultiParentClassLoader(URL[] urls, ClassLoader parent) { |
| this(urls, new ClassLoader[]{parent}); |
| } |
| |
| public MultiParentClassLoader(URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { |
| this(urls, new ClassLoader[]{parent}, inverseClassLoading, hiddenClasses, nonOverridableClasses); |
| } |
| |
| /** |
| * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory |
| * for accessing the urls.. |
| * |
| * @param urls the urls from which this class loader will classes and resources |
| * @param parent the parent of this class loader |
| * @param factory the URLStreamHandlerFactory used to access the urls |
| */ |
| public MultiParentClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { |
| this(urls, new ClassLoader[]{parent}, factory); |
| } |
| |
| /** |
| * Creates a named class loader as a child of the specified parents. |
| * |
| * @param urls the urls from which this class loader will classes and resources |
| * @param parents the parents of this class loader |
| */ |
| public MultiParentClassLoader(URL[] urls, ClassLoader[] parents) { |
| super(urls); |
| this.parents = copyParents(parents); |
| inverseClassLoading = false; |
| hiddenClasses = new String[0]; |
| nonOverridableClasses = new String[0]; |
| hiddenResources = new String[0]; |
| nonOverridableResources = new String[0]; |
| } |
| |
| public MultiParentClassLoader(URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) { |
| this(urls, parents, inverseClassLoading, (String[]) hiddenClasses.toArray(new String[hiddenClasses.size()]), (String[]) nonOverridableClasses.toArray(new String[nonOverridableClasses.size()])); |
| } |
| |
| public MultiParentClassLoader(URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { |
| super(urls); |
| this.parents = copyParents(parents); |
| this.inverseClassLoading = inverseClassLoading; |
| this.hiddenClasses = hiddenClasses; |
| this.nonOverridableClasses = nonOverridableClasses; |
| hiddenResources = toResources(hiddenClasses); |
| nonOverridableResources = toResources(nonOverridableClasses); |
| } |
| |
| private String[] toResources(String[] classes) { |
| String[] resources = new String[classes.length]; |
| for (int i = 0; i < classes.length; i++) { |
| String className = classes[i]; |
| resources[i] = className.replace('.', '/'); |
| } |
| return resources; |
| } |
| |
| /** |
| * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory |
| * for accessing the urls.. |
| * |
| * @param urls the urls from which this class loader will classes and resources |
| * @param parents the parents of this class loader |
| * @param factory the URLStreamHandlerFactory used to access the urls |
| */ |
| public MultiParentClassLoader(URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) { |
| super(urls, null, factory); |
| this.parents = copyParents(parents); |
| inverseClassLoading = false; |
| hiddenClasses = new String[0]; |
| nonOverridableClasses = new String[0]; |
| hiddenResources = new String[0]; |
| nonOverridableResources = new String[0]; |
| } |
| |
| private static ClassLoader[] copyParents(ClassLoader[] parents) { |
| ClassLoader[] newParentsArray = new ClassLoader[parents.length]; |
| for (int i = 0; i < parents.length; i++) { |
| ClassLoader parent = parents[i]; |
| if (parent == null) { |
| throw new RuntimeException("parent[" + i + "] is null"); |
| } |
| newParentsArray[i] = parent; |
| } |
| return newParentsArray; |
| } |
| |
| /** |
| * Gets the parents of this class loader. |
| * |
| * @return the parents of this class loader |
| */ |
| public ClassLoader[] getParents() { |
| return parents; |
| } |
| |
| public void addURL(URL url) { |
| // todo this needs a security check |
| super.addURL(url); |
| } |
| |
| protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { |
| // |
| // Check if class is in the loaded classes cache |
| // |
| Class cachedClass = findLoadedClass(name); |
| if (cachedClass != null) { |
| return resolveClass(cachedClass, resolve); |
| } |
| |
| // |
| // if we are using inverse class loading, check local urls first |
| // |
| if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) { |
| try { |
| Class clazz = findClass(name); |
| return resolveClass(clazz, resolve); |
| } catch (ClassNotFoundException ignored) { |
| } |
| } |
| |
| // |
| // Check parent class loaders |
| // |
| if (!isHiddenClass(name)) { |
| for (int i = 0; i < parents.length; i++) { |
| ClassLoader parent = parents[i]; |
| try { |
| Class clazz = parent.loadClass(name); |
| return resolveClass(clazz, resolve); |
| } catch (ClassNotFoundException ignored) { |
| // this parent didn't have the class; try the next one |
| } |
| } |
| } |
| |
| // |
| // if we are not using inverse class loading, check local urls now |
| // |
| // don't worry about excluding non-overridable classes here... we |
| // have alredy checked he parent and the parent didn't have the |
| // class, so we can override now |
| if (!isDestroyed()) { |
| try { |
| Class clazz = findClass(name); |
| return resolveClass(clazz, resolve); |
| } catch (ClassNotFoundException ignored) { |
| } |
| } |
| |
| throw new ClassNotFoundException(name); |
| } |
| |
| private boolean isNonOverridableClass(String name) { |
| for (int i = 0; i < nonOverridableClasses.length; i++) { |
| if (name.startsWith(nonOverridableClasses[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean isHiddenClass(String name) { |
| for (int i = 0; i < hiddenClasses.length; i++) { |
| if (name.startsWith(hiddenClasses[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Class resolveClass(Class clazz, boolean resolve) { |
| if (resolve) { |
| resolveClass(clazz); |
| } |
| return clazz; |
| } |
| |
| public URL getResource(String name) { |
| if (isDestroyed()) { |
| return null; |
| } |
| |
| // |
| // if we are using inverse class loading, check local urls first |
| // |
| if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) { |
| URL url = findResource(name); |
| if (url != null) { |
| return url; |
| } |
| } |
| |
| // |
| // Check parent class loaders |
| // |
| if (!isHiddenResource(name)) { |
| for (int i = 0; i < parents.length; i++) { |
| ClassLoader parent = parents[i]; |
| URL url = parent.getResource(name); |
| if (url != null) { |
| return url; |
| } |
| } |
| } |
| |
| // |
| // if we are not using inverse class loading, check local urls now |
| // |
| // don't worry about excluding non-overridable resources here... we |
| // have alredy checked he parent and the parent didn't have the |
| // resource, so we can override now |
| if (!isDestroyed()) { |
| // parents didn't have the resource; attempt to load it from my urls |
| return findResource(name); |
| } |
| |
| return null; |
| } |
| |
| public Enumeration findResources(String name) throws IOException { |
| if (isDestroyed()) { |
| return Collections.enumeration(Collections.EMPTY_SET); |
| } |
| |
| List resources = new ArrayList(); |
| |
| // |
| // if we are using inverse class loading, add the resources from local urls first |
| // |
| if (inverseClassLoading && !isDestroyed()) { |
| List myResources = Collections.list(super.findResources(name)); |
| resources.addAll(myResources); |
| } |
| |
| // |
| // Add parent resources |
| // |
| for (int i = 0; i < parents.length; i++) { |
| ClassLoader parent = parents[i]; |
| List parentResources = Collections.list(parent.getResources(name)); |
| resources.addAll(parentResources); |
| } |
| |
| // |
| // if we are not using inverse class loading, add the resources from local urls now |
| // |
| if (!inverseClassLoading && !isDestroyed()) { |
| List myResources = Collections.list(super.findResources(name)); |
| resources.addAll(myResources); |
| } |
| |
| return Collections.enumeration(resources); |
| } |
| |
| private boolean isNonOverridableResource(String name) { |
| for (int i = 0; i < nonOverridableResources.length; i++) { |
| if (name.startsWith(nonOverridableResources[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean isHiddenResource(String name) { |
| for (int i = 0; i < hiddenResources.length; i++) { |
| if (name.startsWith(hiddenResources[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public String toString() { |
| return "[" + getClass().getName() + "]"; |
| } |
| |
| public synchronized boolean isDestroyed() { |
| return destroyed; |
| } |
| |
| public void destroy() { |
| synchronized(this) { |
| if (destroyed) return; |
| destroyed = true; |
| } |
| |
| LogFactory.release(this); |
| // clearSoftCache(ObjectInputStream.class, "subclassAudits"); |
| // clearSoftCache(ObjectOutputStream.class, "subclassAudits"); |
| // clearSoftCache(ObjectStreamClass.class, "localDescs"); |
| // clearSoftCache(ObjectStreamClass.class, "reflectors"); |
| |
| // The beanInfoCache in java.beans.Introspector will hold on to Classes which |
| // it has introspected. If we don't flush the cache, we may run out of |
| // Permanent Generation space. |
| Introspector.flushCaches(); |
| } |
| |
| // private static final Object lock = new Object(); |
| // private static boolean clearSoftCacheFailed = false; |
| // |
| // private static void clearSoftCache(Class clazz, String fieldName) { |
| // Map cache = null; |
| // try { |
| // Field f = clazz.getDeclaredField(fieldName); |
| // f.setAccessible(true); |
| // cache = (Map) f.get(null); |
| // } catch (Throwable e) { |
| // synchronized (lock) { |
| // if (!clearSoftCacheFailed) { |
| // clearSoftCacheFailed = true; |
| // LogFactory.getLog(ConfigurationClassLoader.class).error("Unable to clear SoftCache field " + fieldName + " in class " + clazz); |
| // } |
| // } |
| // } |
| // |
| // if (cache != null) { |
| // synchronized (cache) { |
| // cache.clear(); |
| // } |
| // } |
| // } |
| |
| } |