📄 adaptiveclassloader.java
字号:
/*
* Copyright (c) 1997-1999 The Java Apache Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Java Apache
* Project for use in the Apache JServ servlet engine project
* <http://java.apache.org/>."
*
* 4. The names "Apache JServ", "Apache JServ Servlet Engine" and
* "Java Apache Project" must not be used to endorse or promote products
* derived from this software without prior written permission.
*
* 5. Products derived from this software may not be called "Apache JServ"
* nor may "Apache" nor "Apache JServ" appear in their names without
* prior written permission of the Java Apache Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Java Apache
* Project for use in the Apache JServ servlet engine project
* <http://java.apache.org/>."
*
* THIS SOFTWARE IS PROVIDED BY THE JAVA APACHE PROJECT "AS IS" AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JAVA APACHE PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Java Apache Group. For more information
* on the Java Apache Project and the Apache JServ Servlet Engine project,
* please see <http://java.apache.org/>.
*
*/
package org.apache.tomcat.loader;
//package org.apache.java.lang;
import java.io.*;
import java.lang.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import java.security.*;
/**
* A class loader that loads classes from directories and/or zip-format
* file such as JAR file. It tracks the modification time of the classes
* it loads to permit reloading through re-instantiation.
* <P>
* When the classloader reports its creator that one of the classes it
* has loaded has changed on disk, it should discard the classloader
* and create a new instance using <CODE>reinstantiate</CODE>.
* The classes are then reloaded into the new classloader as required.
*
* <P>The classloader can also load resources, which are a means
* for packaging application data such as images within a jar file
* or directory.
*
* <P>The classloader always first tries to load classes and resources
* from the system, and uses it's own path if that fails. This is also
* done if an empty repository is passed at construction.
*
* <P><B>How autoreload works:</B></P>
*
* <P>The Java VM considers two classes the same if they have the same
* fully-qualified name <B>and</B> if they were loaded from the same
* <CODE>ClassLoader</CODE>.
*
* <P>There is no way for a classloader to 'undefine' a class once it
* has been loaded. However, the servlet engine can discard a
* classloader and the classes it contains, causing the
*
* <P>The <CODE>JServServletManager</CODE> creates a new instance of
* the classloader each time it detects that any of the loaded classes
* have changed.
*
* <P>Before terminating, all servlets are destroyed.
*
* <P>According to the Java Language Specification (JLS), classes may
* be garbage-collected when there are no longer any instances of that
* class and the <CODE>java.lang.Class</CODE> object is finalizable.
* It is intended that this be the case when a <CODE>JServClassLoader</CODE>
* is discarded.
*
* <P>Many VM releases did not implement class garbage collection
* properly. In such a VM, the memory usage will continue to grow if
* autoreloading is enable. Running the VM with
* <CODE>-verbosegc</CODE> (or the corresponding option for
* non-Javasoft VMs) may give some debugging information.
*
* <P>It is important that the <CODE>destroy</CODE> method be
* implemented properly, as servlets may be destroyed and
* reinitialized several times in the life of a VM.
*
* @author Francis J. Lacoste
* @author Martin Pool
* @author Jim Heintz
* @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
* @version $Revision: 1.9.2.4 $ $Date: 2001/07/23 21:49:08 $
* @see java.lang.ClassLoader
*/
public class AdaptiveClassLoader extends ClassLoader {
private static final int debug=0;
/**
* Instance of the SecurityManager installed.
*/
static protected SecurityManager sm;
/**
* Generation counter, incremented for each classloader as they are
* created.
*/
static private int generationCounter = 0;
/**
* Generation number of the classloader, used to distinguish between
* different instances.
*/
private int generation;
/**
* Cache of the loaded classes. This contains ClassCacheEntry keyed
* by class names.
*/
protected Hashtable cache;
/**
* The classpath which this classloader searches for class definitions.
* Each element of the vector should be either a directory, a .zip
* file, or a .jar file.
* <p>
* It may be empty when only system classes are controlled.
*/
protected Vector repository;
/**
* A parent class loader for delegation of finding a class definition.
* JDK 1.2 contains parent class loaders as part of java.lang.ClassLoader, the parent
* being passed to a constructor, and retreived with getParent() method. For JDK 1.1
* compatibility, we'll duplicate the 1.2 private member var.
*/
protected ClassLoader parent;
/**
* Private class used to maintain information about the classes that
* we loaded.
*/
private static class ClassCacheEntry {
/**
* The actual loaded class
*/
Class loadedClass;
/**
* The file from which this class was loaded; or null if
* it was loaded from the system.
*/
File origin;
/**
* The time at which the class was loaded from the origin
* file, in ms since the epoch.
*/
long lastModified;
/**
* Check whether this class was loaded from the system.
*/
public boolean isSystemClass() {
return origin == null;
}
}
//------------------------------------------------------- Constructors
/**
* Creates a new class loader that will load classes from specified
* class repositories, delegating first to the passed parent for definitions.
*
* @param classRepository An set of File classes indicating
* directories and/or zip/jar files. It may be empty when
* only system classes are loaded.
* @param theParent A containing class loader for initial delegation of class
* definition serach.
* @throw java.lang.IllegalArgumentException if the objects contained
* in the vector are not a file instance or the file is not
* a valid directory or a zip/jar file.
*/
public AdaptiveClassLoader() {
// Create the cache of loaded classes
cache = new Hashtable();
}
public void setRepository( Vector classRepository )
throws IllegalArgumentException
{
// Verify that all the repository are valid.
Enumeration e = classRepository.elements();
while(e.hasMoreElements()) {
ClassRepository cp = (ClassRepository) e.nextElement();
File file;
String[] files;
int i;
// Check to see if element is a File instance.
try {
file = cp.getFile();
} catch (ClassCastException objectIsNotFile) {
throw new IllegalArgumentException("Object " + cp
+ "is not a valid \"File\" instance");
}
// org.apache.java.io.XXX
files=SimpleFileFilter.fileOrFiles(file);
if (files!=null)
{
for (i=0;i<files.length;i++)
{
file=new File(files[i]);
// Check to see if we have proper access.
if (!file.exists()) {
throw new IllegalArgumentException("Repository "
+ file.getAbsolutePath() + " doesn't exist!");
} else if (!file.canRead()) {
throw new IllegalArgumentException(
"Do not have read access for file "
+ file.getAbsolutePath());
}
// Check that it is a directory or zip/jar file
if (!(file.isDirectory() || isZipOrJarArchive(file))) {
throw new IllegalArgumentException(
file.getAbsolutePath()
+ " is not a directory or zip/jar file"
+ " or if it's a zip/jar file then it is corrupted.");
}
}
}
}
// Store the class repository for use
this.repository = classRepository;
// Increment and store generation counter
this.generation = generationCounter++;
}
public void setParent( ClassLoader p ) {
parent=p;
}
void log( String s ) {
System.out.println("AdaptiveClassLoader: " + s );
}
//------------------------------------------------------- Methods
/**
* Test if a file is a ZIP or JAR archive.
*
* @param file the file to be tested.
* @return true if the file is a ZIP/JAR archive, false otherwise.
*/
private boolean isZipOrJarArchive(File file) {
boolean isArchive = true;
ZipFile zipFile = null;
try {
zipFile = new ZipFile(file);
} catch (ZipException zipCurrupted) {
isArchive = false;
} catch (IOException anyIOError) {
isArchive = false;
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException ignored) {}
}
}
return isArchive;
}
/**
* Check to see if a given class should be reloaded because of a
* modification to the original class.
*
* @param className The name of the class to check for modification.
*/
public synchronized boolean shouldReload(String classname) {
return checkExpired( classname );
}
protected boolean checkExpired(String classname ) {
ClassCacheEntry entry = (ClassCacheEntry) cache.get(classname);
if (entry == null) {
// class wasn't even loaded
return false;
} else if (entry.isSystemClass()) {
// System classes cannot be reloaded
return false;
} else {
return (entry.origin.lastModified() != entry.lastModified);
}
}
/**
* Check whether the classloader should be reinstantiated.
* <P>
* The classloader must be replaced if there is any class whose
* origin file has changed since it was last loaded.
*/
public synchronized boolean shouldReload() {
// Check whether any class has changed
Enumeration e = cache.elements();
while (e.hasMoreElements()) {
ClassCacheEntry entry = (ClassCacheEntry) e.nextElement();
if( entry.loadedClass==null )
continue;
if( debug>5 )
log( "cache entry: " + entry.loadedClass.getName());
if (entry.isSystemClass()) continue;
// XXX: Because we want the classloader to be an accurate
// reflection of the contents of the repository, we also
// reload if a class origin file is now missing. This
// probably makes things a bit more fragile, but is OK in
// a servlet development situation. <mbp@pharos.com.au>
long msOrigin = entry.origin.lastModified();
if (msOrigin == 0) {
// class no longer exists
return true;
}
if (msOrigin != entry.lastModified) {
// class is modified
return true;
}
}
// No changes, no need to reload
return false;
}
/**
* Re-instantiate this class loader.
* <p>
* This method creates a new instance
* of the class loader that will load classes form the same path
* as this one.
*/
public AdaptiveClassLoader reinstantiate() {
AdaptiveClassLoader cl=new AdaptiveClassLoader();
cl.setParent(parent);
cl.setRepository(repository);
return cl;
}
//------------------------------------ Implementation of Classloader
/*
* XXX: The javadoc for java.lang.ClassLoader says that the
* ClassLoader should cache classes so that it can handle repeated
* requests for the same class. On the other hand, the JLS seems
* to imply that each classloader is only asked to load each class
* once. Is this a contradiction?
*
* Perhaps the second call only applies to classes which have been
* garbage-collected?
*/
/**
* Resolves the specified name to a Class. The method loadClass()
* is called by the virtual machine. As an abstract method,
* loadClass() must be defined in a subclass of ClassLoader.
*
* @param name the name of the desired Class.
* @param resolve true if the Class needs to be resolved;
* false if the virtual machine just wants to determine
* whether the class exists or not
* @return the resulting Class.
* @exception ClassNotFoundException if the class loader cannot
* find a the requested class.
*/
protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
if( debug>0) log( "loadClass() " + name);
// The class object that will be returned.
Class c = null;
// Use the cached value, if this class is already loaded into
// this classloader.
ClassCacheEntry entry = (ClassCacheEntry) cache.get(name);
if (entry != null) {
if( debug>0) log( "Found in cache " + name);
// Class found in our cache
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -