001 /* 002 $Id: RootLoader.java,v 1.6 2005/09/06 08:19:32 hmeling Exp $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package org.codehaus.groovy.tools; 047 048 import java.io.IOException; 049 import java.net.URL; 050 import java.net.URLClassLoader; 051 import java.util.Enumeration; 052 053 /** 054 * This ClassLoader should be used as root of class loaders. Any 055 * RootLoader does have it's own classpath. When searching for a 056 * class or resource this classpath will be used. Parent 057 * Classloaders are ignored first. If a class or resource 058 * can't be found in the classpath of the RootLoader, then parent is 059 * checked. 060 * 061 * <b>Note:</b> this is very against the normal behavior of 062 * classloaders. Normal is to frist check parent and then look in 063 * the ressources you gave this classloader. 064 * 065 * It's possible to add urls to the classpath at runtime through 066 * @see #addURL(URL) 067 * 068 * <b>Why using RootLoader?</b> 069 * If you have to load classes with multiple classloaders and a 070 * classloader does know a class which depends on a class only 071 * a child of this loader does know, then you won't be able to 072 * load the class. To load the class the child is not allowed 073 * to redirect it's search for the class to the parent first. 074 * That way the child can load the class. If the child does not 075 * have all classes to do this, this fails of course. 076 * 077 * For example: 078 * 079 * <pre> 080 * parentLoader (has classpath: a.jar;c.jar) 081 * | 082 * | 083 * childLoader (has classpath: a.jar;b.jar;c.jar) 084 * </pre> 085 * 086 * class C (from c.jar) extends B (from b.jar) 087 * 088 * childLoader.find("C") 089 * --> parentLoader does know C.class, try to load it 090 * --> to load C.class it has to load B.class 091 * --> parentLoader is unable to find B.class in a.jar or c.jar 092 * --> NoClassDefFoundException! 093 * 094 * if childLoader had tried to load the class by itself, there 095 * would be no problem. Changing childLoader to be a RootLoader 096 * instance will solve that problem. 097 * 098 * @author Jochen Theodorou 099 */ 100 public class RootLoader extends ClassLoader { 101 102 private ClassLoader parent; 103 private InnerLoader inner; 104 105 private class InnerLoader extends URLClassLoader { 106 public InnerLoader(URL[] urls) { 107 super(urls,null); 108 } 109 public void addPathEntry(URL url) { 110 addURL(url); 111 } 112 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 113 try { 114 return super.loadClass(name, resolve); 115 } catch (ClassNotFoundException cnfe) { 116 return RootLoader.this.loadClassByName(name,true,resolve); 117 } 118 } 119 } 120 121 /** 122 * constructs a new RootLoader without classpath 123 * @param parent the parent Loader 124 */ 125 private RootLoader(ClassLoader parent) { 126 super(parent); 127 } 128 129 /** 130 * constructs a new RootLoader with a parent loader and an 131 * array of URLs as classpath 132 */ 133 public RootLoader(URL[] urls, ClassLoader parent) { 134 this(parent); 135 inner = new InnerLoader(urls); 136 } 137 138 /** 139 * constructs a new RootLoader with a @see LoaderConfiguration 140 * object which holds the classpath 141 */ 142 public RootLoader(LoaderConfiguration lc) { 143 this(RootLoader.class.getClassLoader()); 144 Thread.currentThread().setContextClassLoader(this); 145 inner = new InnerLoader(lc.getClassPathUrls()); 146 } 147 148 /** 149 * loads a class using the name of the class 150 */ 151 protected synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException { 152 return loadClassByName(name,false,resolve); 153 } 154 155 /** 156 * method to avoid endless loops 157 */ 158 private Class loadClassByName(String name, boolean ignoreInner, boolean resolve) throws ClassNotFoundException { 159 // if the searched class can't be found in inner, then try the 160 // old behavior which searches in parent first 161 if (!ignoreInner) { 162 try { 163 return inner.loadClass(name); 164 } catch (ClassNotFoundException cnfe) { 165 // fall through 166 } 167 } 168 return super.loadClass(name,true); 169 } 170 171 /** 172 * returns the URL of a resource, or null if it is not found 173 */ 174 public URL getResource(String name) { 175 URL url = inner.getResource(name); 176 url = super.getResource(name); 177 return url; 178 } 179 180 /** 181 * returns an Enumeration of all found ressources. Resources found 182 * in the classpath of this loader are at the beginning of the 183 * returned enumeration 184 */ 185 protected Enumeration findResources(String name) throws IOException { 186 final Enumeration enum1 = inner.findResources(name); 187 final Enumeration enum2 = super.findResources(name); 188 return new Enumeration() { 189 public boolean hasMoreElements() { 190 return enum1.hasMoreElements() || enum2.hasMoreElements(); 191 } 192 public Object nextElement() { 193 if (enum1.hasMoreElements()) return enum1.nextElement(); 194 if (enum2.hasMoreElements()) return enum2.nextElement(); 195 return null; 196 } 197 }; 198 } 199 200 /** 201 * adds an url to the classpath of this classloader 202 */ 203 public void addURL(URL url) { 204 inner.addPathEntry(url); 205 } 206 207 /** 208 * returns all classpath entries of this classloader 209 */ 210 public URL[] getURLs() { 211 return inner.getURLs(); 212 } 213 }