You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/07/27 21:02:34 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util SimpleClassLoader.java

costin      00/07/27 12:02:33

  Modified:    .        build.xml
  Added:       src/share/org/apache/tomcat/context LoaderInterceptor1.java
               src/share/org/apache/tomcat/util SimpleClassLoader.java
  Log:
  Added SimpleClassLoader to util. This is a general-purpose class loader,
  with nothing specific to tomcat. It tries to provide the same features
  and behavior with the standard URLClassLoader. Use it only in
  JDK1.1 - for 1.2 you can use the real thing.
  
  Added LoaderInterceptor1 - it will configure and use URLClassLoader.
  No reloading supported yet ( it will be added as a separate component ).
  
  Revision  Changes    Path
  1.55      +2 -0      jakarta-tomcat/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/build.xml,v
  retrieving revision 1.54
  retrieving revision 1.55
  diff -u -r1.54 -r1.55
  --- build.xml	2000/07/27 17:55:28	1.54
  +++ build.xml	2000/07/27 19:02:32	1.55
  @@ -72,6 +72,8 @@
              deprecation="off" >
              <exclude name="**/Jdk12Interceptor.java" 
                       unless="jdk12.present"/>
  +           <exclude name="**/LoaderInterceptor1.java" 
  +                    unless="jdk12.present"/>
              <exclude name="**/SetSecurityManager.java" 
                       unless="jdk12.present"/>
              <exclude name="**/EmbededTomcat.java" 
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/context/LoaderInterceptor1.java
  
  Index: LoaderInterceptor1.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  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. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``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 APACHE SOFTWARE FOUNDATION 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 Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.tomcat.context;
  
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.core.Constants;
  import org.apache.tomcat.request.*;
  import org.apache.tomcat.util.*;
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import java.security.*;
  
  import org.apache.tomcat.logging.*;
  import org.apache.tomcat.loader.*;
  
  /**
   * Set class loader based on WEB-INF/classes, lib.
   * Uses the protection domain - if any, so PolicyInterceptor
   * must be called before it.
   *
   * @author costin@dnt.ro
   */
  public class LoaderInterceptor1 extends BaseInterceptor {
      String classLoaderName;
      
      public LoaderInterceptor1() {
      }
  
      public void setClassLoaderName( String name ) {
  	classLoaderName=name;
      }
  
      /** The class paths are added when the context is added
       */
      public void addContext( ContextManager cm, Context context)
  	throws TomcatException
      {
          String base = context.getDocBase();
  
  	// Add "WEB-INF/classes"
  	File dir = new File(base + "/WEB-INF/classes");
  
          // GS, Fix for the jar@lib directory problem.
          // Thanks for Kevin Jones for providing the fix.
          dir = cm.getAbsolute(dir);
  	if( dir.exists() ) {
  	    try {
  		URL url=new URL( "file", null, dir.getAbsolutePath() + "/" );
  		context.addClassPath( url );
  	    } catch( MalformedURLException ex ) {
  	    }
          }
  
          File f = cm.getAbsolute(new File(base + "/WEB-INF/lib"));
  	Vector jars = new Vector();
  	getJars(jars, f);
  
  	for(int i=0; i < jars.size(); ++i) {
  	    String jarfile = (String) jars.elementAt(i);
  	    File jarF=new File(f, jarfile );
  	    File jf=cm.getAbsolute( jarF );
  	    String absPath=jf.getAbsolutePath();
  	    try {
  		URL url=new URL( "file", null, absPath );
  		context.addClassPath( url );
  	    } catch( MalformedURLException ex ) {
  	    }
  	}
      }
  
      /** The class loader is set when the context us initialized
       *  or at reload
       */
      public void contextInit( Context context)
  	throws TomcatException
      {
          ContextManager cm = context.getContextManager();
  	URL urls[]=context.getClassPath();
  	
  	URLClassLoader urlLoader=URLClassLoader.newInstance( urls );
  
  	context.setClassLoader( urlLoader );
      }
  
      private void getJars(Vector v, File f) {
          FilenameFilter jarfilter = new FilenameFilter() {
  		public boolean accept(File dir, String fname) {
  		    if(fname.endsWith(".jar"))
  			return true;
  
  		    return false;
  		}
  	    };
          FilenameFilter dirfilter = new FilenameFilter() {
  		public boolean accept(File dir, String fname) {
  		    File f1 = new File(dir, fname);
  		    if(f1.isDirectory())
  			return true;
  
  		    return false;
  		}
  	    };
  
          if(f.exists() && f.isDirectory() && f.isAbsolute()) {
              String[] jarlist = f.list(jarfilter);
  
              for(int i=0; (jarlist != null) && (i < jarlist.length); ++i) {
                  v.addElement(jarlist[i]);
              }
  
              String[] dirlist = f.list(dirfilter);
  
              for(int i=0; (dirlist != null) && (i < dirlist.length); ++i) {
                  File dir = new File(f, dirlist[i]);
                  getJars(v, dir);
              }
          }
      }
  
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/SimpleClassLoader.java
  
  Index: SimpleClassLoader.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.util;
  
  import java.io.*;
  import java.lang.*;
  import java.net.*;
  import java.text.*;
  import java.util.*;
  import java.util.zip.*;
  import java.security.*;
  
  /**
   * This is a JDK1.1 equivalent of URLClassLoader. It have no dependency on
   * tomcat or any other api - just standard java. 
   *
   * Based on AdaptiveClassLoader from JServ1, with the dependency check
   * and reloading features removed ( and moved to an external component)
   *  This is based on the fact that class loading and dependency checking can
   * be separated and we want to support multiple forms of class loaders.
   *
   * The interface also changed to match URLClassLoader. 
   * This class should be used _only_ with JDK1.1, for JDK1.2 you
   * should use a class loader that is aware of Permissions and the
   * new rules ( sealing, etc )
   *
   * This class loader respects the standard order defined in the ClassLoader
   * documentation - for a different order you can plug in a different
   * class loader ( in the configurations ).
   *
   * Since this class loader will be visible to applications we need
   * to prevent exploits - we'll minimize the public method usage.
   * 
   * The class path can be set only when the object is constructed.
   */
  public class SimpleClassLoader extends ClassLoader {
      private static final int debug=0;
      
      /**
       * 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 URL urls[];
  
      /**
       * 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;
  
      /** Reserved names - this class loader will not allow creation of
  	classes that start with one of those strings.
      */
      protected String reserved[];
  
      SecurityManager sm;
      //------------------------------------------------------- Constructors
  
      public SimpleClassLoader( URL urls[]) {
  	super(); // will check permissions 
  	this.urls=urls;
  	sm=System.getSecurityManager();
      }
  
      public SimpleClassLoader( URL urls[], ClassLoader parent ) {
  	super(); // will check permissions 
  	this.urls=urls;
  	this.parent=parent;
  	sm=System.getSecurityManager();
      }
  
      /** This is the prefered constructor to be used with this class loader
       */
      public SimpleClassLoader( URL urls[], ClassLoader parent,
  				 String reserved[] ) {
  	super(); // will check permissions 
  	this.urls=urls;
  	this.parent=parent;
  	this.reserved=reserved;
  	sm=System.getSecurityManager();
      }
  
      // debug only 
      void log( String s ) {
  	System.out.println("AdaptiveClassLoader1: " + s );
      }
  
      //------------------------------------ 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 + " " + resolve);
  	// The class object that will be returned.
          Class c = null;
  
  	// check if the class was already loaded
  	c = findLoadedClass( name );
  	if (c!= null ) {
  	    if(resolve) resolveClass(c);
  	    return c;
          }
  
  	// Attempt to load the class from the parent class loader
  	// (untrusted case)
  	if (parent != null) {
  	    try {
  		c = parent.loadClass(name);
  		if (c != null) {
  		    if( debug>0) log( "loadClass() from parent " + name);
  		    if (resolve) resolveClass(c);
  		    return c;
  		}
  	    } catch (Exception e) {
  		c = null;
  	    }
  	}
  
          // Attempt to load the class from the system class loader
  	try {
  	    c = findSystemClass(name);
  	    if (c != null) {
  		if( debug>0) log( "loadClass() from system " + name);
  		if (resolve) resolveClass(c);
  		return c;
  	    }
  	} catch (Exception e) {
  	    c = null;
  	}
  	
          // Make sure we can access this class when using a SecurityManager
          if (sm != null) {
              int i = name.lastIndexOf('.');
              if (i >= 0) {
                  sm.checkPackageAccess(name.substring(0,i)); 
                  sm.checkPackageDefinition(name.substring(0,i));
  	    }
  	}
  
  	// make sure the class is not in a "reserved" package.
  	if( reserved!=null ) {
  	    for( int i=0; i<reserved.length; i++ ) {
  		if( name.startsWith( reserved[i] )) {
  		    if( debug>0) log( "reserved: " + name + " " + reserved[i]);
  		    throw new ClassNotFoundException(name);
  		}
  	    }
  	}
  
  	if( urls==null ) 
  	    throw new ClassNotFoundException(name);
  	
          // Translate class name to file name
          String classFileName =
              name.replace('.', '/') + ".class";
  	// It's '/' not File.Separator - at least that's how URLClassLoader
  	// does it and I suppose there are reasons ( costin )
  
  
  	Resource r=doFindResource( classFileName );
  	if( r==null )
  	    throw new ClassNotFoundException(name);
  
  	byte[] classData=null;
  
  	if( r.file != null ) {
  	    InputStream in=null;
              try {
  		in = new FileInputStream(r.file);
                  classData=loadBytesFromStream(in, (int) r.file.length());
              } catch (IOException ioex) {
                  return null;
              } finally {
                  try {
  		    if( in!=null) in.close();
  		} catch( IOException ex ) {}
              }
  	} else if( r.zipEntry != null ) {
  	    try {
                  classData=loadBytesFromStream(r.zipFile.
  					      getInputStream(r.zipEntry),
  					      (int)r.zipEntry.getSize());
              } catch (IOException ioex) {
                  return null;
  	    } finally {
  		try {
  		    r.zipFile.close();
  		} catch ( IOException ignored ) {
  		}
  	    }
  	}
  
  	if (classData != null) {
  	    try {
  		c = defineClass(name, classData, 0, classData.length );
  		if (resolve) resolveClass(c);
  		if( debug>0) log( "loadClass() from local repository " +
  				  name);
  		return c;
  	    } catch(Throwable t ) {
  		t.printStackTrace();
  	    }
  	}
  
          // If not found in any repository
          throw new ClassNotFoundException(name);
      }
  
      /**
       * Find a resource with a given name.  The return is a URL to the
       * resource. Doing a getContent() on the URL may return an Image,
       * an AudioClip,or an InputStream.
       *
       * @param   name    the name of the resource, to be used as is.
       * @return  an URL on the resource, or null if not found.
       */
      public URL getResource(String name) {
          if( debug > 0 ) log( "getResource() " + name );
  
  	URL u = getSystemResource(name);
  	if (u != null) {
  	    return u;
  	}
  
  	Resource r=doFindResource( name );
  
  	if( r==null )
  	    return null;
  
  	// Construct a file://-URL if the repository is a directory
  	if( r.file != null ) { // Build a file:// URL form the file name
  	    try {
  		return new URL("file", null,
  			       r.file.getAbsolutePath());
  	    } catch(java.net.MalformedURLException badurl) {
  		badurl.printStackTrace();
  		return null;
  	    }
  	}
  
  	// a jar:-URL *could* change even between minor releases, but
  	// didn't between JVM's 1.1.6 and 1.3beta. Tested on JVM's from
  	// IBM, Blackdown, Microsoft, Sun @ Windows and Sun @ Solaris
  	if( r.zipEntry != null ) {
  	    try {
  		return new URL("jar:file:" +
  			       r.repository.getPath() + "!/" +
  			       name);
  	    } catch(java.net.MalformedURLException badurl) {
  		badurl.printStackTrace();
  		return null;
  	    } finally {
  		try {
  		    r.zipFile.close();
  		} catch ( IOException ignored ) {
  		}
  	    }
  	}
  	// Not found
          return null;
  
      }
  
      /**
       * Get an InputStream on a given resource.  Will return null if no
       * resource with this name is found.
       * <p>
       * The JServClassLoader translate the resource's name to a file
       * or a zip entry. It looks for the resource in all its repository
       * entry.
       *
       * @see     java.lang.Class#getResourceAsStream(String)
       * @param   name    the name of the resource, to be used as is.
       * @return  an InputStream on the resource, or null if not found.
       */
      public InputStream getResourceAsStream(String name) {
          // Try to load it from the system class
          if( debug > 0 ) log( "getResourceAsStream() " + name );
  	//	InputStream s = getSystemResourceAsStream(name);
  	InputStream s = null;
  
  	// Get this resource from system class loader 
  	s = getSystemResourceAsStream(name);
  
          if (s != null) {
  	    return s;
  	}
  		
  	Resource r=doFindResource( name );
  
  	if( r.file!=null ) {
  	    try {
                  return new FileInputStream(r.file);
              } catch (FileNotFoundException shouldnothappen) {
                  return null;
              }
  	} else if( r.zipEntry != null ) {
  	    // workaround - the better solution is to not close the
  	    // zipfile !!!!
  	    try {
  		byte[] data= loadBytesFromStream(r.zipFile.
  						 getInputStream(r.zipEntry),
  						 (int) r.zipEntry.getSize());
  		if(data != null) {
  		    InputStream istream = new ByteArrayInputStream(data);
  		    return istream;
  		}
  	    } catch(IOException e) {
  	    } finally {
  	    // if we close the zipfile bad things will happen -
  		// we can't read the stream on some VMs
  		if ( r.zipFile != null ) {
  		    try {
  			r.zipFile.close();
  		    } catch ( IOException ignored ) {
  		    }
  		}
  	    }
  	}
  	
          return s;
      }
  
      // -------------------- Private methods --------------------
      /** Private class used to store the result of the search 
       */
      private class Resource {
  	/* Repository used to find the resource */
  	File repository;
  	/* File - if the resource is a file */
  	File file;
  
  	/* Zip file and entry if it's in a jar */
  	ZipEntry zipEntry;
  	ZipFile zipFile;
      }
  
      // common code to find the resource
      private Resource doFindResource( String name ) {
  	Resource r=new Resource();
  	
  	for( int i=0; i<urls.length; i++ ) {
  	    URL cp = urls[i];
              String fileN = cp.getFile();
  	    File file=new File( fileN );
  	    
  	    if (fileN.endsWith("/")) {
                  String fileName = name.replace('/', File.separatorChar);
                  File resFile = new File(file, fileName);
                  if (resFile.exists()) {
  		    r.file=resFile;
  		    r.repository=file;
  		    return r;
                  }
              } else {
                  try {
                      ZipFile zf = new ZipFile(file.getAbsolutePath());
                      ZipEntry ze = zf.getEntry(name);
  
                      if (ze != null) {
  			r.zipEntry=ze;
  			r.zipFile=zf;
  			r.repository=file;
  			return r;
                      }
                  } catch (IOException ioe) {
                      ioe.printStackTrace();
                      return null;
                  }
              }   
          }
  	return null;
      }
      
      /**
       * Loads all the bytes of an InputStream.
       */
      private byte[] loadBytesFromStream(InputStream in, int length)
          throws IOException
      {
          byte[] buf = new byte[length];
          int nRead, count = 0;
  
          while ((length > 0) && ((nRead = in.read(buf,count,length)) != -1)) {
              count += nRead;
              length -= nRead;
          }
          return buf;
      }
  
  }