/* * Copyright 2003-2007 the original author or authors. * * 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.codehaus.groovy.tools; import java.net.URL; import java.net.URLClassLoader; import java.util.Map; import java.util.HashMap; /** * This ClassLoader should be used as root of class loaders. Any * RootLoader does have it's own classpath. When searching for a * class or resource this classpath will be used. Parent * Classloaders are ignored first. If a class or resource * can't be found in the classpath of the RootLoader, then parent is * checked. * <p/> * <b>Note:</b> this is very against the normal behavior of * classloaders. Normal is to first check parent and then look in * the resources you gave this classloader. * <p/> * It's possible to add urls to the classpath at runtime through * {@link <a href="#addURL(URL)">addURL(URL)</a>} * <p/> * <b>Why using RootLoader?</b> * If you have to load classes with multiple classloaders and a * classloader does know a class which depends on a class only * a child of this loader does know, then you won't be able to * load the class. To load the class the child is not allowed * to redirect it's search for the class to the parent first. * That way the child can load the class. If the child does not * have all classes to do this, this fails of course. * <p/> * For example: * <p/> * <pre> * parentLoader (has classpath: a.jar;c.jar) * | * | * childLoader (has classpath: a.jar;b.jar;c.jar) * </pre> * <p/> * class C (from c.jar) extends B (from b.jar) * <p/> * childLoader.find("C") * --> parentLoader does know C.class, try to load it * --> to load C.class it has to load B.class * --> parentLoader is unable to find B.class in a.jar or c.jar * --> NoClassDefFoundException! * <p/> * if childLoader had tried to load the class by itself, there * would be no problem. Changing childLoader to be a RootLoader * instance will solve that problem. * * @author Jochen Theodorou */ public class RootLoader extends URLClassLoader { private Map<String, Class> customClasses = new HashMap<String, Class>(); /** * constructs a new RootLoader without classpath * * @param parent the parent Loader */ private RootLoader(ClassLoader parent) { this(new URL[0], parent); } /** * constructs a new RootLoader with a parent loader and an * array of URLs as classpath */ public RootLoader(URL[] urls, ClassLoader parent) { super(urls, parent); // major hack here...! try{ customClasses.put("org.w3c.dom.Node",super.loadClass("org.w3c.dom.Node",false)); } catch (Exception e) { /* ignore */ } } private static ClassLoader chooseParent() { ClassLoader cl = RootLoader.class.getClassLoader(); if (cl != null) return cl; return ClassLoader.getSystemClassLoader(); } /** * constructs a new RootLoader with a @see LoaderConfiguration * object which holds the classpath */ public RootLoader(LoaderConfiguration lc) { this(chooseParent()); Thread.currentThread().setContextClassLoader(this); URL[] urls = lc.getClassPathUrls(); for (URL url : urls) { addURL(url); } } /** * loads a class using the name of the class */ protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException { Class c = this.findLoadedClass(name); if (c != null) return c; c = (Class) customClasses.get(name); if (c != null) return c; try { c = oldFindClass(name); } catch (ClassNotFoundException cnfe) { // IGNORE } if (c == null) c = super.loadClass(name, resolve); if (resolve) resolveClass(c); return c; } /** * returns the URL of a resource, or null if it is not found */ public URL getResource(String name) { URL url = findResource(name); if (url == null) url = super.getResource(name); return url; } /** * adds an url to the classpath of this classloader */ public void addURL(URL url) { super.addURL(url); } private Class oldFindClass(String name) throws ClassNotFoundException { return super.findClass(name); } protected Class findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } }