1 /* 2 * $Header: /u/cvs/Projects/EnhydraOrg/enhydra3x/Enhydra/modules/Tomcat/src/share/org/apache/tomcat/util/Attic/NetworkClassLoader.java,v 1.2.2.1.2.1 2001/02/09 09:53:01 markd Exp $ 3 * $Revision: 1.2.2.1.2.1 $ 4 * $Date: 2001/02/09 09:53:01 $ 5 * 6 * ==================================================================== 7 * 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 1999 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, if 26 * any, must include the following acknowlegement: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowlegement may appear in the software itself, 30 * if and wherever such third-party acknowlegements normally appear. 31 * 32 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 33 * Foundation" must not be used to endorse or promote products derived 34 * from this software without prior written permission. For written 35 * permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache" 38 * nor may "Apache" appear in their names without prior written 39 * permission of the Apache Group. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>. 59 * 60 * [Additional notices, if required by prior licensing conditions] 61 * 62 */ 63 64 package org.apache.tomcat.util; 65 66 import java.util.zip; 67 import java.net; 68 import java.util; 69 import java.io; 70 71 /** 72 * The correct name for this class should be URLClassLoader. 73 * But there is already a class by that name in JDK1.2. 74 * 75 * I have had quite a few problems with URLClassLoader in 76 * past, so I ended up writing this ClassLoader. I found that 77 * the Java 2's URLClassLoader, does not close the Jar file once 78 * opened. It is a pretty good optimization step, but if you 79 * modify the class in the jar file, it does not pick it up. Some 80 * operating systems may not let you modify the jar file while it is 81 * still open. IMHO, it does make sense to close the jar file 82 * after you are done reading the class data. But this approach may not 83 * get you the performance of the URLClassLoader, but it works in all 84 * cases and also runs on JDK1.1. I have enhanced this class loader 85 * to read all the zip/jar entries once & cache the data, so that 86 * there is no overhead of opening/closing jar file to pick up 87 * each entry. 88 * 89 * 90 * @author Harish Prabandham 91 */ 92 public class NetworkClassLoader extends ClassLoader { 93 private ClassLoader parent = null; // parent classloader 94 private Hashtable classCache = new Hashtable(); 95 private Hashtable urlset = new Hashtable(); 96 97 /** 98 * Creates a new instance of the class loader. 99 * @param delegate/parent class loader. 100 */ 101 public NetworkClassLoader(ClassLoader parent) { 102 setParent(parent); 103 } 104 105 /** 106 * Sets the parent/delegate class loader. 107 * @param delegate/parent class loader. 108 */ 109 protected final void setParent(ClassLoader parent) { 110 this.parent = parent; 111 } 112 113 /** 114 * Adds the given URL to this class loader. If the URL 115 * ends with "/", then it is assumed to be a directory 116 * otherwise, it is assumed to be a zip/jar file. If the 117 * same URL is added again, the URL is re-opened and this 118 * zip/jar file is used for serving any future class requests. 119 * @param URL where to look for the classes. 120 */ 121 public synchronized void addURL(URL url) { 122 // System.out.println("Adding url: " + url); 123 if(!urlset.containsKey(url)) { 124 try { 125 urlset.put(url, new URLResourceReader(url)); 126 }catch(IOException ioe){ 127 // Probably a bad url... 128 } 129 } else { 130 // remove the old one & add a new one... 131 try{ 132 URLResourceReader newu = new URLResourceReader(url); 133 URLResourceReader oldu = (URLResourceReader) urlset.get(url); 134 oldu.close(); 135 urlset.remove(url); 136 urlset.put(url, newu); 137 } catch (IOException ioe) { 138 } 139 } 140 } 141 142 /** 143 * @return An enumeration of URLs where this class loader 144 * looks for classes. 145 */ 146 public Enumeration getURLs() { 147 return urlset.keys(); 148 } 149 150 private byte[] loadResource(URL url, String resourceName) 151 throws IOException { 152 URLResourceReader urr = (URLResourceReader) urlset.get(url); 153 if(urr != null) { 154 return urr.getResource(resourceName); 155 } 156 157 return null; 158 } 159 160 private byte[] loadResource(String resource) { 161 byte[] barray = null; 162 for(Enumeration e = urlset.keys(); e.hasMoreElements();) { 163 URL url = (URL) e.nextElement(); 164 165 try { 166 barray = loadResource(url, resource); 167 } catch(Exception ex) { 168 } finally { 169 if(barray != null) 170 break; 171 } 172 } 173 174 return barray; 175 } 176 177 private byte[] loadClassData(String classname) { 178 String resourceName = classname.replace('.', '/') + ".class"; 179 return loadResource(resourceName); 180 } 181 182 /** 183 * @return The resource as the input stream if such a resource 184 * exists, otherwise returns null. 185 */ 186 public InputStream getResourceAsStream(String name) { 187 InputStream istream = null; 188 189 // Algorithm: 190 // 191 // 1. first check the system path for the resource 192 // 2. next check the delegate/parent class loader for the resource 193 // 3. then attempt to get the resource from the url set. 194 // 195 196 // Lets check the system path for the resource. 197 istream = getSystemResourceAsStream(name); 198 if(istream != null) 199 return istream; 200 201 // Lets check the parent/delegate class loader for the resource. 202 if(parent != null) { 203 istream = parent.getResourceAsStream(name); 204 if(istream != null) 205 return istream; 206 } 207 208 // Lets load it ourselves. 209 byte[] data = loadResource(name); 210 if(data != null) { 211 istream = new ByteArrayInputStream(data); 212 } 213 214 return istream; 215 } 216 217 /** 218 * java.lang.ClassLoader's defineClass method is final, so the 219 * its subclasses cannot override this method. But, this class 220 * calls this method in the loadClass() instead. 221 * @param The name of the class without ".class" extension. 222 * @param The class data bytes. 223 * @return The class object. 224 */ 225 protected Class defineClass(String classname, byte[] classdata) { 226 return defineClass(classname, classdata, 0, classdata.length); 227 } 228 229 protected synchronized Class loadClass(String name, boolean resolve) 230 throws ClassNotFoundException { 231 Class c = null; 232 233 // Algorithm: (Please do not change the order; unless you 234 // have a good reason to do so). 235 // 236 // 1. first check the system class loader. 237 // 2. next check the delegate/parent class loader. 238 // 3. next check the class cache 239 // 4. then attempt to load classes from the URL set. 240 // 241 242 // Lets see if the class is in system class loader. 243 try { 244 c = findSystemClass(name); 245 }catch(ClassNotFoundException cnfe) { 246 }finally { 247 if(c != null) 248 return c; 249 } 250 251 // Lets see if the class is in parent class loader. 252 try { 253 if(parent != null) 254 c = parent.loadClass(name); 255 }catch(ClassNotFoundException cnfe) { 256 }finally { 257 if(c != null) 258 return c; 259 } 260 261 // Lets see if the class is in the cache.. 262 c = (Class) classCache.get(name); 263 264 if(c != null) 265 return c; 266 267 268 // Lets see if we find the class all by ourselves. 269 byte[] data = loadClassData(name); 270 271 if(data != null) { 272 // we did !! 273 c = defineClass(name, data); 274 classCache.put(name, c); 275 if(resolve) 276 resolveClass(c); 277 } else { 278 // We are out of luck at this point... 279 throw new ClassNotFoundException(name); 280 } 281 282 return c; 283 } 284 285 /** 286 * This method resets this ClassLoader's state. It completely 287 * removes all the URLs and classes in this class loader cache. 288 */ 289 protected final void clear() { 290 urlset.clear(); 291 classCache.clear(); 292 } 293 294 /** 295 * This method resets this ClassLoader's state and resets the 296 * references for garbage collection. 297 */ 298 protected void finalize() throws Throwable { 299 // Cleanup real well. Otherwise, this can be 300 // a major source of memory leaks... 301 302 // remove all the urls & class entries. 303 clear(); 304 305 parent = null; 306 urlset = null; 307 classCache = null; 308 } 309 310 311 // Added for enhydra: 312 313 /** 314 * @see ClassLoader#findResource(String) 315 */ 316 protected URL findResource(String name) { 317 for(Enumeration e = urlset.keys(); e.hasMoreElements();) { 318 URL url = (URL) e.nextElement(); 319 320 try { 321 URL realURL = new URL(url.getProtocol(), url.getHost(), 322 url.getFile() + name); 323 File urlFile = new File(realURL.getFile()); 324 if (urlFile.exists()) { 325 return realURL; 326 } 327 } catch (MalformedURLException except) { 328 // Ignore invalid CLASSPATH entries 329 } 330 } 331 332 return null; 333 } 334 335 } 336 337 338 339 340 341 342 343 344 345 346 347