Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » loader » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.loader;
   20   
   21   import java.io.ByteArrayInputStream;
   22   import java.io.File;
   23   import java.io.FileOutputStream;
   24   import java.io.FilePermission;
   25   import java.io.IOException;
   26   import java.io.InputStream;
   27   import java.lang.reflect.Field;
   28   import java.lang.reflect.Modifier;
   29   import java.net.MalformedURLException;
   30   import java.net.URL;
   31   import java.net.URLClassLoader;
   32   import java.security.AccessControlException;
   33   import java.security.AccessController;
   34   import java.security.CodeSource;
   35   import java.security.Permission;
   36   import java.security.PermissionCollection;
   37   import java.security.Policy;
   38   import java.security.PrivilegedAction;
   39   import java.sql.Driver;
   40   import java.sql.DriverManager;
   41   import java.sql.SQLException;
   42   import java.util.ArrayList;
   43   import java.util.Enumeration;
   44   import java.util.HashMap;
   45   import java.util.Iterator;
   46   import java.util.Vector;
   47   import java.util.jar.Attributes;
   48   import java.util.jar.JarEntry;
   49   import java.util.jar.JarFile;
   50   import java.util.jar.Manifest;
   51   import java.util.jar.Attributes.Name;
   52   
   53   import javax.naming.NameClassPair;
   54   import javax.naming.NamingEnumeration;
   55   import javax.naming.NamingException;
   56   import javax.naming.directory.DirContext;
   57   
   58   import org.apache.catalina.Lifecycle;
   59   import org.apache.catalina.LifecycleException;
   60   import org.apache.catalina.LifecycleListener;
   61   import org.apache.catalina.util.StringManager;
   62   import org.apache.naming.JndiPermission;
   63   import org.apache.naming.resources.Resource;
   64   import org.apache.naming.resources.ResourceAttributes;
   65   import org.apache.tomcat.util.IntrospectionUtils;
   66   
   67   /**
   68    * Specialized web application class loader.
   69    * <p>
   70    * This class loader is a full reimplementation of the 
   71    * <code>URLClassLoader</code> from the JDK. It is desinged to be fully
   72    * compatible with a normal <code>URLClassLoader</code>, although its internal
   73    * behavior may be completely different.
   74    * <p>
   75    * <strong>IMPLEMENTATION NOTE</strong> - This class loader faithfully follows 
   76    * the delegation model recommended in the specification. The system class 
   77    * loader will be queried first, then the local repositories, and only then 
   78    * delegation to the parent class loader will occur. This allows the web 
   79    * application to override any shared class except the classes from J2SE.
   80    * Special handling is provided from the JAXP XML parser interfaces, the JNDI
   81    * interfaces, and the classes from the servlet API, which are never loaded 
   82    * from the webapp repository.
   83    * <p>
   84    * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper 
   85    * compilation technology, any repository which contains classes from 
   86    * the servlet API will be ignored by the class loader.
   87    * <p>
   88    * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
   89    * URLs which include the full JAR URL when a class is loaded from a JAR file,
   90    * which allows setting security permission at the class level, even when a
   91    * class is contained inside a JAR.
   92    * <p>
   93    * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
   94    * the order they are added via the initial constructor and/or any subsequent
   95    * calls to <code>addRepository()</code> or <code>addJar()</code>.
   96    * <p>
   97    * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
   98    * security is made unless a security manager is present.
   99    *
  100    * @author Remy Maucherat
  101    * @author Craig R. McClanahan
  102    * @version $Revision: 604974 $ $Date: 2007-12-17 20:36:44 +0100 (lun., 17 déc. 2007) $
  103    */
  104   public class WebappClassLoader
  105       extends URLClassLoader
  106       implements Reloader, Lifecycle
  107    {
  108   
  109       protected static org.apache.juli.logging.Log log=
  110           org.apache.juli.logging.LogFactory.getLog( WebappClassLoader.class );
  111   
  112       public static final boolean ENABLE_CLEAR_REFERENCES = 
  113           Boolean.valueOf(System.getProperty("org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES", "true")).booleanValue();
  114       
  115       protected class PrivilegedFindResource
  116           implements PrivilegedAction {
  117   
  118           protected File file;
  119           protected String path;
  120   
  121           PrivilegedFindResource(File file, String path) {
  122               this.file = file;
  123               this.path = path;
  124           }
  125   
  126           public Object run() {
  127               return findResourceInternal(file, path);
  128           }
  129   
  130       }
  131   
  132   
  133       // ------------------------------------------------------- Static Variables
  134   
  135   
  136       /**
  137        * The set of trigger classes that will cause a proposed repository not
  138        * to be added if this class is visible to the class loader that loaded
  139        * this factory class.  Typically, trigger classes will be listed for
  140        * components that have been integrated into the JDK for later versions,
  141        * but where the corresponding JAR files are required to run on
  142        * earlier versions.
  143        */
  144       protected static final String[] triggers = {
  145           "javax.servlet.Servlet"                     // Servlet API
  146       };
  147   
  148   
  149       /**
  150        * Set of package names which are not allowed to be loaded from a webapp
  151        * class loader without delegating first.
  152        */
  153       protected static final String[] packageTriggers = {
  154       };
  155   
  156   
  157       /**
  158        * The string manager for this package.
  159        */
  160       protected static final StringManager sm =
  161           StringManager.getManager(Constants.Package);
  162   
  163       
  164       /**
  165        * Use anti JAR locking code, which does URL rerouting when accessing
  166        * resources.
  167        */
  168       boolean antiJARLocking = false; 
  169       
  170   
  171       // ----------------------------------------------------------- Constructors
  172   
  173   
  174       /**
  175        * Construct a new ClassLoader with no defined repositories and no
  176        * parent ClassLoader.
  177        */
  178       public WebappClassLoader() {
  179   
  180           super(new URL[0]);
  181           this.parent = getParent();
  182           system = getSystemClassLoader();
  183           securityManager = System.getSecurityManager();
  184   
  185           if (securityManager != null) {
  186               refreshPolicy();
  187           }
  188   
  189       }
  190   
  191   
  192       /**
  193        * Construct a new ClassLoader with no defined repositories and no
  194        * parent ClassLoader.
  195        */
  196       public WebappClassLoader(ClassLoader parent) {
  197   
  198           super(new URL[0], parent);
  199                   
  200           this.parent = getParent();
  201           
  202           system = getSystemClassLoader();
  203           securityManager = System.getSecurityManager();
  204   
  205           if (securityManager != null) {
  206               refreshPolicy();
  207           }
  208       }
  209   
  210   
  211       // ----------------------------------------------------- Instance Variables
  212   
  213   
  214       /**
  215        * Associated directory context giving access to the resources in this
  216        * webapp.
  217        */
  218       protected DirContext resources = null;
  219   
  220   
  221       /**
  222        * The cache of ResourceEntry for classes and resources we have loaded,
  223        * keyed by resource name.
  224        */
  225       protected HashMap resourceEntries = new HashMap();
  226   
  227   
  228       /**
  229        * The list of not found resources.
  230        */
  231       protected HashMap notFoundResources = new HashMap();
  232   
  233   
  234       /**
  235        * Should this class loader delegate to the parent class loader
  236        * <strong>before</strong> searching its own repositories (i.e. the
  237        * usual Java2 delegation model)?  If set to <code>false</code>,
  238        * this class loader will search its own repositories first, and
  239        * delegate to the parent only if the class or resource is not
  240        * found locally.
  241        */
  242       protected boolean delegate = false;
  243   
  244   
  245       /**
  246        * Last time a JAR was accessed.
  247        */
  248       protected long lastJarAccessed = 0L;
  249   
  250   
  251       /**
  252        * The list of local repositories, in the order they should be searched
  253        * for locally loaded classes or resources.
  254        */
  255       protected String[] repositories = new String[0];
  256   
  257   
  258        /**
  259         * Repositories URLs, used to cache the result of getURLs.
  260         */
  261        protected URL[] repositoryURLs = null;
  262   
  263   
  264       /**
  265        * Repositories translated as path in the work directory (for Jasper
  266        * originally), but which is used to generate fake URLs should getURLs be
  267        * called.
  268        */
  269       protected File[] files = new File[0];
  270   
  271   
  272       /**
  273        * The list of JARs, in the order they should be searched
  274        * for locally loaded classes or resources.
  275        */
  276       protected JarFile[] jarFiles = new JarFile[0];
  277   
  278   
  279       /**
  280        * The list of JARs, in the order they should be searched
  281        * for locally loaded classes or resources.
  282        */
  283       protected File[] jarRealFiles = new File[0];
  284   
  285   
  286       /**
  287        * The path which will be monitored for added Jar files.
  288        */
  289       protected String jarPath = null;
  290   
  291   
  292       /**
  293        * The list of JARs, in the order they should be searched
  294        * for locally loaded classes or resources.
  295        */
  296       protected String[] jarNames = new String[0];
  297   
  298   
  299       /**
  300        * The list of JARs last modified dates, in the order they should be
  301        * searched for locally loaded classes or resources.
  302        */
  303       protected long[] lastModifiedDates = new long[0];
  304   
  305   
  306       /**
  307        * The list of resources which should be checked when checking for
  308        * modifications.
  309        */
  310       protected String[] paths = new String[0];
  311   
  312   
  313       /**
  314        * A list of read File and Jndi Permission's required if this loader
  315        * is for a web application context.
  316        */
  317       protected ArrayList permissionList = new ArrayList();
  318   
  319   
  320       /**
  321        * Path where resources loaded from JARs will be extracted.
  322        */
  323       protected File loaderDir = null;
  324   
  325   
  326       /**
  327        * The PermissionCollection for each CodeSource for a web
  328        * application context.
  329        */
  330       protected HashMap loaderPC = new HashMap();
  331   
  332   
  333       /**
  334        * Instance of the SecurityManager installed.
  335        */
  336       protected SecurityManager securityManager = null;
  337   
  338   
  339       /**
  340        * The parent class loader.
  341        */
  342       protected ClassLoader parent = null;
  343   
  344   
  345       /**
  346        * The system class loader.
  347        */
  348       protected ClassLoader system = null;
  349   
  350   
  351       /**
  352        * Has this component been started?
  353        */
  354       protected boolean started = false;
  355   
  356   
  357       /**
  358        * Has external repositories.
  359        */
  360       protected boolean hasExternalRepositories = false;
  361   
  362       /**
  363        * need conversion for properties files
  364        */
  365       protected boolean needConvert = false;
  366   
  367   
  368       /**
  369        * All permission.
  370        */
  371       protected Permission allPermission = new java.security.AllPermission();
  372   
  373   
  374       // ------------------------------------------------------------- Properties
  375   
  376   
  377       /**
  378        * Get associated resources.
  379        */
  380       public DirContext getResources() {
  381   
  382           return this.resources;
  383   
  384       }
  385   
  386   
  387       /**
  388        * Set associated resources.
  389        */
  390       public void setResources(DirContext resources) {
  391   
  392           this.resources = resources;
  393   
  394       }
  395   
  396   
  397       /**
  398        * Return the "delegate first" flag for this class loader.
  399        */
  400       public boolean getDelegate() {
  401   
  402           return (this.delegate);
  403   
  404       }
  405   
  406   
  407       /**
  408        * Set the "delegate first" flag for this class loader.
  409        *
  410        * @param delegate The new "delegate first" flag
  411        */
  412       public void setDelegate(boolean delegate) {
  413   
  414           this.delegate = delegate;
  415   
  416       }
  417   
  418   
  419       /**
  420        * @return Returns the antiJARLocking.
  421        */
  422       public boolean getAntiJARLocking() {
  423           return antiJARLocking;
  424       }
  425       
  426       
  427       /**
  428        * @param antiJARLocking The antiJARLocking to set.
  429        */
  430       public void setAntiJARLocking(boolean antiJARLocking) {
  431           this.antiJARLocking = antiJARLocking;
  432       }
  433   
  434       
  435       /**
  436        * If there is a Java SecurityManager create a read FilePermission
  437        * or JndiPermission for the file directory path.
  438        *
  439        * @param path file directory path
  440        */
  441       public void addPermission(String path) {
  442           if (path == null) {
  443               return;
  444           }
  445   
  446           if (securityManager != null) {
  447               Permission permission = null;
  448               if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
  449                   if (!path.endsWith("/")) {
  450                       path = path + "/";
  451                   }
  452                   permission = new JndiPermission(path + "*");
  453                   addPermission(permission);
  454               } else {
  455                   if (!path.endsWith(File.separator)) {
  456                       permission = new FilePermission(path, "read");
  457                       addPermission(permission);
  458                       path = path + File.separator;
  459                   }
  460                   permission = new FilePermission(path + "-", "read");
  461                   addPermission(permission);
  462               }
  463           }
  464       }
  465   
  466   
  467       /**
  468        * If there is a Java SecurityManager create a read FilePermission
  469        * or JndiPermission for URL.
  470        *
  471        * @param url URL for a file or directory on local system
  472        */
  473       public void addPermission(URL url) {
  474           if (url != null) {
  475               addPermission(url.toString());
  476           }
  477       }
  478   
  479   
  480       /**
  481        * If there is a Java SecurityManager create a Permission.
  482        *
  483        * @param permission The permission
  484        */
  485       public void addPermission(Permission permission) {
  486           if ((securityManager != null) && (permission != null)) {
  487               permissionList.add(permission);
  488           }
  489       }
  490   
  491   
  492       /**
  493        * Return the JAR path.
  494        */
  495       public String getJarPath() {
  496   
  497           return this.jarPath;
  498   
  499       }
  500   
  501   
  502       /**
  503        * Change the Jar path.
  504        */
  505       public void setJarPath(String jarPath) {
  506   
  507           this.jarPath = jarPath;
  508   
  509       }
  510   
  511   
  512       /**
  513        * Change the work directory.
  514        */
  515       public void setWorkDir(File workDir) {
  516           this.loaderDir = new File(workDir, "loader");
  517       }
  518   
  519        /**
  520         * Utility method for use in subclasses.
  521         * Must be called before Lifecycle methods to have any effect.
  522         */
  523        protected void setParentClassLoader(ClassLoader pcl) {
  524            parent = pcl;
  525        }
  526   
  527       // ------------------------------------------------------- Reloader Methods
  528   
  529   
  530       /**
  531        * Add a new repository to the set of places this ClassLoader can look for
  532        * classes to be loaded.
  533        *
  534        * @param repository Name of a source of classes to be loaded, such as a
  535        *  directory pathname, a JAR file pathname, or a ZIP file pathname
  536        *
  537        * @exception IllegalArgumentException if the specified repository is
  538        *  invalid or does not exist
  539        */
  540       public void addRepository(String repository) {
  541   
  542           // Ignore any of the standard repositories, as they are set up using
  543           // either addJar or addRepository
  544           if (repository.startsWith("/WEB-INF/lib")
  545               || repository.startsWith("/WEB-INF/classes"))
  546               return;
  547   
  548           // Add this repository to our underlying class loader
  549           try {
  550               URL url = new URL(repository);
  551               super.addURL(url);
  552               hasExternalRepositories = true;
  553               repositoryURLs = null;
  554           } catch (MalformedURLException e) {
  555               IllegalArgumentException iae = new IllegalArgumentException
  556                   ("Invalid repository: " + repository); 
  557               iae.initCause(e);
  558               throw iae;
  559           }
  560   
  561       }
  562   
  563   
  564       /**
  565        * Add a new repository to the set of places this ClassLoader can look for
  566        * classes to be loaded.
  567        *
  568        * @param repository Name of a source of classes to be loaded, such as a
  569        *  directory pathname, a JAR file pathname, or a ZIP file pathname
  570        *
  571        * @exception IllegalArgumentException if the specified repository is
  572        *  invalid or does not exist
  573        */
  574       synchronized void addRepository(String repository, File file) {
  575   
  576           // Note : There should be only one (of course), but I think we should
  577           // keep this a bit generic
  578   
  579           if (repository == null)
  580               return;
  581   
  582           if (log.isDebugEnabled())
  583               log.debug("addRepository(" + repository + ")");
  584   
  585           int i;
  586   
  587           // Add this repository to our internal list
  588           String[] result = new String[repositories.length + 1];
  589           for (i = 0; i < repositories.length; i++) {
  590               result[i] = repositories[i];
  591           }
  592           result[repositories.length] = repository;
  593           repositories = result;
  594   
  595           // Add the file to the list
  596           File[] result2 = new File[files.length + 1];
  597           for (i = 0; i < files.length; i++) {
  598               result2[i] = files[i];
  599           }
  600           result2[files.length] = file;
  601           files = result2;
  602   
  603       }
  604   
  605   
  606       synchronized void addJar(String jar, JarFile jarFile, File file)
  607           throws IOException {
  608   
  609           if (jar == null)
  610               return;
  611           if (jarFile == null)
  612               return;
  613           if (file == null)
  614               return;
  615   
  616           if (log.isDebugEnabled())
  617               log.debug("addJar(" + jar + ")");
  618   
  619           int i;
  620   
  621           if ((jarPath != null) && (jar.startsWith(jarPath))) {
  622   
  623               String jarName = jar.substring(jarPath.length());
  624               while (jarName.startsWith("/"))
  625                   jarName = jarName.substring(1);
  626   
  627               String[] result = new String[jarNames.length + 1];
  628               for (i = 0; i < jarNames.length; i++) {
  629                   result[i] = jarNames[i];
  630               }
  631               result[jarNames.length] = jarName;
  632               jarNames = result;
  633   
  634           }
  635   
  636           try {
  637   
  638               // Register the JAR for tracking
  639   
  640               long lastModified =
  641                   ((ResourceAttributes) resources.getAttributes(jar))
  642                   .getLastModified();
  643   
  644               String[] result = new String[paths.length + 1];
  645               for (i = 0; i < paths.length; i++) {
  646                   result[i] = paths[i];
  647               }
  648               result[paths.length] = jar;
  649               paths = result;
  650   
  651               long[] result3 = new long[lastModifiedDates.length + 1];
  652               for (i = 0; i < lastModifiedDates.length; i++) {
  653                   result3[i] = lastModifiedDates[i];
  654               }
  655               result3[lastModifiedDates.length] = lastModified;
  656               lastModifiedDates = result3;
  657   
  658           } catch (NamingException e) {
  659               // Ignore
  660           }
  661   
  662           // If the JAR currently contains invalid classes, don't actually use it
  663           // for classloading
  664           if (!validateJarFile(file))
  665               return;
  666   
  667           JarFile[] result2 = new JarFile[jarFiles.length + 1];
  668           for (i = 0; i < jarFiles.length; i++) {
  669               result2[i] = jarFiles[i];
  670           }
  671           result2[jarFiles.length] = jarFile;
  672           jarFiles = result2;
  673   
  674           // Add the file to the list
  675           File[] result4 = new File[jarRealFiles.length + 1];
  676           for (i = 0; i < jarRealFiles.length; i++) {
  677               result4[i] = jarRealFiles[i];
  678           }
  679           result4[jarRealFiles.length] = file;
  680           jarRealFiles = result4;
  681       }
  682   
  683   
  684       /**
  685        * Return a String array of the current repositories for this class
  686        * loader.  If there are no repositories, a zero-length array is
  687        * returned.For security reason, returns a clone of the Array (since 
  688        * String are immutable).
  689        */
  690       public String[] findRepositories() {
  691   
  692           return ((String[])repositories.clone());
  693   
  694       }
  695   
  696   
  697       /**
  698        * Have one or more classes or resources been modified so that a reload
  699        * is appropriate?
  700        */
  701       public boolean modified() {
  702   
  703           if (log.isDebugEnabled())
  704               log.debug("modified()");
  705   
  706           // Checking for modified loaded resources
  707           int length = paths.length;
  708   
  709           // A rare race condition can occur in the updates of the two arrays
  710           // It's totally ok if the latest class added is not checked (it will
  711           // be checked the next time
  712           int length2 = lastModifiedDates.length;
  713           if (length > length2)
  714               length = length2;
  715   
  716           for (int i = 0; i < length; i++) {
  717               try {
  718                   long lastModified =
  719                       ((ResourceAttributes) resources.getAttributes(paths[i]))
  720                       .getLastModified();
  721                   if (lastModified != lastModifiedDates[i]) {
  722                       if( log.isDebugEnabled() ) 
  723                           log.debug("  Resource '" + paths[i]
  724                                     + "' was modified; Date is now: "
  725                                     + new java.util.Date(lastModified) + " Was: "
  726                                     + new java.util.Date(lastModifiedDates[i]));
  727                       return (true);
  728                   }
  729               } catch (NamingException e) {
  730                   log.error("    Resource '" + paths[i] + "' is missing");
  731                   return (true);
  732               }
  733           }
  734   
  735           length = jarNames.length;
  736   
  737           // Check if JARs have been added or removed
  738           if (getJarPath() != null) {
  739   
  740               try {
  741                   NamingEnumeration enumeration = resources.listBindings(getJarPath());
  742                   int i = 0;
  743                   while (enumeration.hasMoreElements() && (i < length)) {
  744                       NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
  745                       String name = ncPair.getName();
  746                       // Ignore non JARs present in the lib folder
  747                       if (!name.endsWith(".jar"))
  748                           continue;
  749                       if (!name.equals(jarNames[i])) {
  750                           // Missing JAR
  751                           log.info("    Additional JARs have been added : '" 
  752                                    + name + "'");
  753                           return (true);
  754                       }
  755                       i++;
  756                   }
  757                   if (enumeration.hasMoreElements()) {
  758                       while (enumeration.hasMoreElements()) {
  759                           NameClassPair ncPair = 
  760                               (NameClassPair) enumeration.nextElement();
  761                           String name = ncPair.getName();
  762                           // Additional non-JAR files are allowed
  763                           if (name.endsWith(".jar")) {
  764                               // There was more JARs
  765                               log.info("    Additional JARs have been added");
  766                               return (true);
  767                           }
  768                       }
  769                   } else if (i < jarNames.length) {
  770                       // There was less JARs
  771                       log.info("    Additional JARs have been added");
  772                       return (true);
  773                   }
  774               } catch (NamingException e) {
  775                   if (log.isDebugEnabled())
  776                       log.debug("    Failed tracking modifications of '"
  777                           + getJarPath() + "'");
  778               } catch (ClassCastException e) {
  779                   log.error("    Failed tracking modifications of '"
  780                             + getJarPath() + "' : " + e.getMessage());
  781               }
  782   
  783           }
  784   
  785           // No classes have been modified
  786           return (false);
  787   
  788       }
  789   
  790   
  791       /**
  792        * Render a String representation of this object.
  793        */
  794       public String toString() {
  795   
  796           StringBuffer sb = new StringBuffer("WebappClassLoader\r\n");
  797           sb.append("  delegate: ");
  798           sb.append(delegate);
  799           sb.append("\r\n");
  800           sb.append("  repositories:\r\n");
  801           if (repositories != null) {
  802               for (int i = 0; i < repositories.length; i++) {
  803                   sb.append("    ");
  804                   sb.append(repositories[i]);
  805                   sb.append("\r\n");
  806               }
  807           }
  808           if (this.parent != null) {
  809               sb.append("----------> Parent Classloader:\r\n");
  810               sb.append(this.parent.toString());
  811               sb.append("\r\n");
  812           }
  813           return (sb.toString());
  814   
  815       }
  816   
  817   
  818       // ---------------------------------------------------- ClassLoader Methods
  819   
  820   
  821        /**
  822         * Add the specified URL to the classloader.
  823         */
  824        protected void addURL(URL url) {
  825            super.addURL(url);
  826            hasExternalRepositories = true;
  827            repositoryURLs = null;
  828        }
  829   
  830   
  831       /**
  832        * Find the specified class in our local repositories, if possible.  If
  833        * not found, throw <code>ClassNotFoundException</code>.
  834        *
  835        * @param name Name of the class to be loaded
  836        *
  837        * @exception ClassNotFoundException if the class was not found
  838        */
  839       public Class findClass(String name) throws ClassNotFoundException {
  840   
  841           if (log.isDebugEnabled())
  842               log.debug("    findClass(" + name + ")");
  843   
  844           // Cannot load anything from local repositories if class loader is stopped
  845           if (!started) {
  846               throw new ClassNotFoundException(name);
  847           }
  848   
  849           // (1) Permission to define this class when using a SecurityManager
  850           if (securityManager != null) {
  851               int i = name.lastIndexOf('.');
  852               if (i >= 0) {
  853                   try {
  854                       if (log.isTraceEnabled())
  855                           log.trace("      securityManager.checkPackageDefinition");
  856                       securityManager.checkPackageDefinition(name.substring(0,i));
  857                   } catch (Exception se) {
  858                       if (log.isTraceEnabled())
  859                           log.trace("      -->Exception-->ClassNotFoundException", se);
  860                       throw new ClassNotFoundException(name, se);
  861                   }
  862               }
  863           }
  864   
  865           // Ask our superclass to locate this class, if possible
  866           // (throws ClassNotFoundException if it is not found)
  867           Class clazz = null;
  868           try {
  869               if (log.isTraceEnabled())
  870                   log.trace("      findClassInternal(" + name + ")");
  871               try {
  872                   clazz = findClassInternal(name);
  873               } catch(ClassNotFoundException cnfe) {
  874                   if (!hasExternalRepositories) {
  875                       throw cnfe;
  876                   }
  877               } catch(AccessControlException ace) {
  878                   throw new ClassNotFoundException(name, ace);
  879               } catch (RuntimeException e) {
  880                   if (log.isTraceEnabled())
  881                       log.trace("      -->RuntimeException Rethrown", e);
  882                   throw e;
  883               }
  884               if ((clazz == null) && hasExternalRepositories) {
  885                   try {
  886                       synchronized (this) {
  887                           clazz = super.findClass(name);
  888                       }
  889                   } catch(AccessControlException ace) {
  890                       throw new ClassNotFoundException(name, ace);
  891                   } catch (RuntimeException e) {
  892                       if (log.isTraceEnabled())
  893                           log.trace("      -->RuntimeException Rethrown", e);
  894                       throw e;
  895                   }
  896               }
  897               if (clazz == null) {
  898                   if (log.isDebugEnabled())
  899                       log.debug("    --> Returning ClassNotFoundException");
  900                   throw new ClassNotFoundException(name);
  901               }
  902           } catch (ClassNotFoundException e) {
  903               if (log.isTraceEnabled())
  904                   log.trace("    --> Passing on ClassNotFoundException");
  905               throw e;
  906           }
  907   
  908           // Return the class we have located
  909           if (log.isTraceEnabled())
  910               log.debug("      Returning class " + clazz);
  911           if ((log.isTraceEnabled()) && (clazz != null))
  912               log.debug("      Loaded by " + clazz.getClassLoader());
  913           return (clazz);
  914   
  915       }
  916   
  917   
  918       /**
  919        * Find the specified resource in our local repository, and return a
  920        * <code>URL</code> refering to it, or <code>null</code> if this resource
  921        * cannot be found.
  922        *
  923        * @param name Name of the resource to be found
  924        */
  925       public URL findResource(final String name) {
  926   
  927           if (log.isDebugEnabled())
  928               log.debug("    findResource(" + name + ")");
  929   
  930           URL url = null;
  931   
  932           ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
  933           if (entry == null) {
  934               entry = findResourceInternal(name, name);
  935           }
  936           if (entry != null) {
  937               url = entry.source;
  938           }
  939   
  940           if ((url == null) && hasExternalRepositories)
  941               url = super.findResource(name);
  942   
  943           if (log.isDebugEnabled()) {
  944               if (url != null)
  945                   log.debug("    --> Returning '" + url.toString() + "'");
  946               else
  947                   log.debug("    --> Resource not found, returning null");
  948           }
  949           return (url);
  950   
  951       }
  952   
  953   
  954       /**
  955        * Return an enumeration of <code>URLs</code> representing all of the
  956        * resources with the given name.  If no resources with this name are
  957        * found, return an empty enumeration.
  958        *
  959        * @param name Name of the resources to be found
  960        *
  961        * @exception IOException if an input/output error occurs
  962        */
  963       public Enumeration findResources(String name) throws IOException {
  964   
  965           if (log.isDebugEnabled())
  966               log.debug("    findResources(" + name + ")");
  967   
  968           Vector result = new Vector();
  969   
  970           int jarFilesLength = jarFiles.length;
  971           int repositoriesLength = repositories.length;
  972   
  973           int i;
  974   
  975           // Looking at the repositories
  976           for (i = 0; i < repositoriesLength; i++) {
  977               try {
  978                   String fullPath = repositories[i] + name;
  979                   resources.lookup(fullPath);
  980                   // Note : Not getting an exception here means the resource was
  981                   // found
  982                   try {
  983                       result.addElement(getURI(new File(files[i], name)));
  984                   } catch (MalformedURLException e) {
  985                       // Ignore
  986                   }
  987               } catch (NamingException e) {
  988               }
  989           }
  990   
  991           // Looking at the JAR files
  992           synchronized (jarFiles) {
  993               if (openJARs()) {
  994                   for (i = 0; i < jarFilesLength; i++) {
  995                       JarEntry jarEntry = jarFiles[i].getJarEntry(name);
  996                       if (jarEntry != null) {
  997                           try {
  998                               String jarFakeUrl = getURI(jarRealFiles[i]).toString();
  999                               jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
 1000                               result.addElement(new URL(jarFakeUrl));
 1001                           } catch (MalformedURLException e) {
 1002                               // Ignore
 1003                           }
 1004                       }
 1005                   }
 1006               }
 1007           }
 1008   
 1009           // Adding the results of a call to the superclass
 1010           if (hasExternalRepositories) {
 1011   
 1012               Enumeration otherResourcePaths = super.findResources(name);
 1013   
 1014               while (otherResourcePaths.hasMoreElements()) {
 1015                   result.addElement(otherResourcePaths.nextElement());
 1016               }
 1017   
 1018           }
 1019   
 1020           return result.elements();
 1021   
 1022       }
 1023   
 1024   
 1025       /**
 1026        * Find the resource with the given name.  A resource is some data
 1027        * (images, audio, text, etc.) that can be accessed by class code in a
 1028        * way that is independent of the location of the code.  The name of a
 1029        * resource is a "/"-separated path name that identifies the resource.
 1030        * If the resource cannot be found, return <code>null</code>.
 1031        * <p>
 1032        * This method searches according to the following algorithm, returning
 1033        * as soon as it finds the appropriate URL.  If the resource cannot be
 1034        * found, returns <code>null</code>.
 1035        * <ul>
 1036        * <li>If the <code>delegate</code> property is set to <code>true</code>,
 1037        *     call the <code>getResource()</code> method of the parent class
 1038        *     loader, if any.</li>
 1039        * <li>Call <code>findResource()</code> to find this resource in our
 1040        *     locally defined repositories.</li>
 1041        * <li>Call the <code>getResource()</code> method of the parent class
 1042        *     loader, if any.</li>
 1043        * </ul>
 1044        *
 1045        * @param name Name of the resource to return a URL for
 1046        */
 1047       public URL getResource(String name) {
 1048   
 1049           if (log.isDebugEnabled())
 1050               log.debug("getResource(" + name + ")");
 1051           URL url = null;
 1052   
 1053           // (1) Delegate to parent if requested
 1054           if (delegate) {
 1055               if (log.isDebugEnabled())
 1056                   log.debug("  Delegating to parent classloader " + parent);
 1057               ClassLoader loader = parent;
 1058               if (loader == null)
 1059                   loader = system;
 1060               url = loader.getResource(name);
 1061               if (url != null) {
 1062                   if (log.isDebugEnabled())
 1063                       log.debug("  --> Returning '" + url.toString() + "'");
 1064                   return (url);
 1065               }
 1066           }
 1067   
 1068           // (2) Search local repositories
 1069           url = findResource(name);
 1070           if (url != null) {
 1071               // Locating the repository for special handling in the case 
 1072               // of a JAR
 1073               if (antiJARLocking) {
 1074                   ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
 1075                   try {
 1076                       String repository = entry.codeBase.toString();
 1077                       if ((repository.endsWith(".jar")) 
 1078                               && (!(name.endsWith(".class")))) {
 1079                           // Copy binary content to the work directory if not present
 1080                           File resourceFile = new File(loaderDir, name);
 1081                           url = getURI(resourceFile);
 1082                       }
 1083                   } catch (Exception e) {
 1084                       // Ignore
 1085                   }
 1086               }
 1087               if (log.isDebugEnabled())
 1088                   log.debug("  --> Returning '" + url.toString() + "'");
 1089               return (url);
 1090           }
 1091   
 1092           // (3) Delegate to parent unconditionally if not already attempted
 1093           if( !delegate ) {
 1094               ClassLoader loader = parent;
 1095               if (loader == null)
 1096                   loader = system;
 1097               url = loader.getResource(name);
 1098               if (url != null) {
 1099                   if (log.isDebugEnabled())
 1100                       log.debug("  --> Returning '" + url.toString() + "'");
 1101                   return (url);
 1102               }
 1103           }
 1104   
 1105           // (4) Resource was not found
 1106           if (log.isDebugEnabled())
 1107               log.debug("  --> Resource not found, returning null");
 1108           return (null);
 1109   
 1110       }
 1111   
 1112   
 1113       /**
 1114        * Find the resource with the given name, and return an input stream
 1115        * that can be used for reading it.  The search order is as described
 1116        * for <code>getResource()</code>, after checking to see if the resource
 1117        * data has been previously cached.  If the resource cannot be found,
 1118        * return <code>null</code>.
 1119        *
 1120        * @param name Name of the resource to return an input stream for
 1121        */
 1122       public InputStream getResourceAsStream(String name) {
 1123   
 1124           if (log.isDebugEnabled())
 1125               log.debug("getResourceAsStream(" + name + ")");
 1126           InputStream stream = null;
 1127   
 1128           // (0) Check for a cached copy of this resource
 1129           stream = findLoadedResource(name);
 1130           if (stream != null) {
 1131               if (log.isDebugEnabled())
 1132                   log.debug("  --> Returning stream from cache");
 1133               return (stream);
 1134           }
 1135   
 1136           // (1) Delegate to parent if requested
 1137           if (delegate) {
 1138               if (log.isDebugEnabled())
 1139                   log.debug("  Delegating to parent classloader " + parent);
 1140               ClassLoader loader = parent;
 1141               if (loader == null)
 1142                   loader = system;
 1143               stream = loader.getResourceAsStream(name);
 1144               if (stream != null) {
 1145                   // FIXME - cache???
 1146                   if (log.isDebugEnabled())
 1147                       log.debug("  --> Returning stream from parent");
 1148                   return (stream);
 1149               }
 1150           }
 1151   
 1152           // (2) Search local repositories
 1153           if (log.isDebugEnabled())
 1154               log.debug("  Searching local repositories");
 1155           URL url = findResource(name);
 1156           if (url != null) {
 1157               // FIXME - cache???
 1158               if (log.isDebugEnabled())
 1159                   log.debug("  --> Returning stream from local");
 1160               stream = findLoadedResource(name);
 1161               try {
 1162                   if (hasExternalRepositories && (stream == null))
 1163                       stream = url.openStream();
 1164               } catch (IOException e) {
 1165                   ; // Ignore
 1166               }
 1167               if (stream != null)
 1168                   return (stream);
 1169           }
 1170   
 1171           // (3) Delegate to parent unconditionally
 1172           if (!delegate) {
 1173               if (log.isDebugEnabled())
 1174                   log.debug("  Delegating to parent classloader unconditionally " + parent);
 1175               ClassLoader loader = parent;
 1176               if (loader == null)
 1177                   loader = system;
 1178               stream = loader.getResourceAsStream(name);
 1179               if (stream != null) {
 1180                   // FIXME - cache???
 1181                   if (log.isDebugEnabled())
 1182                       log.debug("  --> Returning stream from parent");
 1183                   return (stream);
 1184               }
 1185           }
 1186   
 1187           // (4) Resource was not found
 1188           if (log.isDebugEnabled())
 1189               log.debug("  --> Resource not found, returning null");
 1190           return (null);
 1191   
 1192       }
 1193   
 1194   
 1195       /**
 1196        * Load the class with the specified name.  This method searches for
 1197        * classes in the same manner as <code>loadClass(String, boolean)</code>
 1198        * with <code>false</code> as the second argument.
 1199        *
 1200        * @param name Name of the class to be loaded
 1201        *
 1202        * @exception ClassNotFoundException if the class was not found
 1203        */
 1204       public Class loadClass(String name) throws ClassNotFoundException {
 1205   
 1206           return (loadClass(name, false));
 1207   
 1208       }
 1209   
 1210   
 1211       /**
 1212        * Load the class with the specified name, searching using the following
 1213        * algorithm until it finds and returns the class.  If the class cannot
 1214        * be found, returns <code>ClassNotFoundException</code>.
 1215        * <ul>
 1216        * <li>Call <code>findLoadedClass(String)</code> to check if the
 1217        *     class has already been loaded.  If it has, the same
 1218        *     <code>Class</code> object is returned.</li>
 1219        * <li>If the <code>delegate</code> property is set to <code>true</code>,
 1220        *     call the <code>loadClass()</code> method of the parent class
 1221        *     loader, if any.</li>
 1222        * <li>Call <code>findClass()</code> to find this class in our locally
 1223        *     defined repositories.</li>
 1224        * <li>Call the <code>loadClass()</code> method of our parent
 1225        *     class loader, if any.</li>
 1226        * </ul>
 1227        * If the class was found using the above steps, and the
 1228        * <code>resolve</code> flag is <code>true</code>, this method will then
 1229        * call <code>resolveClass(Class)</code> on the resulting Class object.
 1230        *
 1231        * @param name Name of the class to be loaded
 1232        * @param resolve If <code>true</code> then resolve the class
 1233        *
 1234        * @exception ClassNotFoundException if the class was not found
 1235        */
 1236       public Class loadClass(String name, boolean resolve)
 1237           throws ClassNotFoundException {
 1238   
 1239           if (log.isDebugEnabled())
 1240               log.debug("loadClass(" + name + ", " + resolve + ")");
 1241           Class clazz = null;
 1242   
 1243           // Log access to stopped classloader
 1244           if (!started) {
 1245               try {
 1246                   throw new IllegalStateException();
 1247               } catch (IllegalStateException e) {
 1248                   log.info(sm.getString("webappClassLoader.stopped", name), e);
 1249               }
 1250           }
 1251   
 1252           // (0) Check our previously loaded local class cache
 1253           clazz = findLoadedClass0(name);
 1254           if (clazz != null) {
 1255               if (log.isDebugEnabled())
 1256                   log.debug("  Returning class from cache");
 1257               if (resolve)
 1258                   resolveClass(clazz);
 1259               return (clazz);
 1260           }
 1261   
 1262           // (0.1) Check our previously loaded class cache
 1263           clazz = findLoadedClass(name);
 1264           if (clazz != null) {
 1265               if (log.isDebugEnabled())
 1266                   log.debug("  Returning class from cache");
 1267               if (resolve)
 1268                   resolveClass(clazz);
 1269               return (clazz);
 1270           }
 1271   
 1272           // (0.2) Try loading the class with the system class loader, to prevent
 1273           //       the webapp from overriding J2SE classes
 1274           try {
 1275               clazz = system.loadClass(name);
 1276               if (clazz != null) {
 1277                   if (resolve)
 1278                       resolveClass(clazz);
 1279                   return (clazz);
 1280               }
 1281           } catch (ClassNotFoundException e) {
 1282               // Ignore
 1283           }
 1284   
 1285           // (0.5) Permission to access this class when using a SecurityManager
 1286           if (securityManager != null) {
 1287               int i = name.lastIndexOf('.');
 1288               if (i >= 0) {
 1289                   try {
 1290                       securityManager.checkPackageAccess(name.substring(0,i));
 1291                   } catch (SecurityException se) {
 1292                       String error = "Security Violation, attempt to use " +
 1293                           "Restricted Class: " + name;
 1294                       log.info(error, se);
 1295                       throw new ClassNotFoundException(error, se);
 1296                   }
 1297               }
 1298           }
 1299   
 1300           boolean delegateLoad = delegate || filter(name);
 1301   
 1302           // (1) Delegate to our parent if requested
 1303           if (delegateLoad) {
 1304               if (log.isDebugEnabled())
 1305                   log.debug("  Delegating to parent classloader1 " + parent);
 1306               ClassLoader loader = parent;
 1307               if (loader == null)
 1308                   loader = system;
 1309               try {
 1310                   clazz = loader.loadClass(name);
 1311                   if (clazz != null) {
 1312                       if (log.isDebugEnabled())
 1313                           log.debug("  Loading class from parent");
 1314                       if (resolve)
 1315                           resolveClass(clazz);
 1316                       return (clazz);
 1317                   }
 1318               } catch (ClassNotFoundException e) {
 1319                   ;
 1320               }
 1321           }
 1322   
 1323           // (2) Search local repositories
 1324           if (log.isDebugEnabled())
 1325               log.debug("  Searching local repositories");
 1326           try {
 1327               clazz = findClass(name);
 1328               if (clazz != null) {
 1329                   if (log.isDebugEnabled())
 1330                       log.debug("  Loading class from local repository");
 1331                   if (resolve)
 1332                       resolveClass(clazz);
 1333                   return (clazz);
 1334               }
 1335           } catch (ClassNotFoundException e) {
 1336               ;
 1337           }
 1338   
 1339           // (3) Delegate to parent unconditionally
 1340           if (!delegateLoad) {
 1341               if (log.isDebugEnabled())
 1342                   log.debug("  Delegating to parent classloader at end: " + parent);
 1343               ClassLoader loader = parent;
 1344               if (loader == null)
 1345                   loader = system;
 1346               try {
 1347                   clazz = loader.loadClass(name);
 1348                   if (clazz != null) {
 1349                       if (log.isDebugEnabled())
 1350                           log.debug("  Loading class from parent");
 1351                       if (resolve)
 1352                           resolveClass(clazz);
 1353                       return (clazz);
 1354                   }
 1355               } catch (ClassNotFoundException e) {
 1356                   ;
 1357               }
 1358           }
 1359   
 1360           throw new ClassNotFoundException(name);
 1361       }
 1362   
 1363   
 1364       /**
 1365        * Get the Permissions for a CodeSource.  If this instance
 1366        * of WebappClassLoader is for a web application context,
 1367        * add read FilePermission or JndiPermissions for the base
 1368        * directory (if unpacked),
 1369        * the context URL, and jar file resources.
 1370        *
 1371        * @param codeSource where the code was loaded from
 1372        * @return PermissionCollection for CodeSource
 1373        */
 1374       protected PermissionCollection getPermissions(CodeSource codeSource) {
 1375   
 1376           String codeUrl = codeSource.getLocation().toString();
 1377           PermissionCollection pc;
 1378           if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
 1379               pc = super.getPermissions(codeSource);
 1380               if (pc != null) {
 1381                   Iterator perms = permissionList.iterator();
 1382                   while (perms.hasNext()) {
 1383                       Permission p = (Permission)perms.next();
 1384                       pc.add(p);
 1385                   }
 1386                   loaderPC.put(codeUrl,pc);
 1387               }
 1388           }
 1389           return (pc);
 1390   
 1391       }
 1392   
 1393   
 1394       /**
 1395        * Returns the search path of URLs for loading classes and resources.
 1396        * This includes the original list of URLs specified to the constructor,
 1397        * along with any URLs subsequently appended by the addURL() method.
 1398        * @return the search path of URLs for loading classes and resources.
 1399        */
 1400       public URL[] getURLs() {
 1401   
 1402           if (repositoryURLs != null) {
 1403               return repositoryURLs;
 1404           }
 1405   
 1406           URL[] external = super.getURLs();
 1407   
 1408           int filesLength = files.length;
 1409           int jarFilesLength = jarRealFiles.length;
 1410           int length = filesLength + jarFilesLength + external.length;
 1411           int i;
 1412   
 1413           try {
 1414   
 1415               URL[] urls = new URL[length];
 1416               for (i = 0; i < length; i++) {
 1417                   if (i < filesLength) {
 1418                       urls[i] = getURL(files[i], true);
 1419                   } else if (i < filesLength + jarFilesLength) {
 1420                       urls[i] = getURL(jarRealFiles[i - filesLength], true);
 1421                   } else {
 1422                       urls[i] = external[i - filesLength - jarFilesLength];
 1423                   }
 1424               }
 1425   
 1426               repositoryURLs = urls;
 1427   
 1428           } catch (MalformedURLException e) {
 1429               repositoryURLs = new URL[0];
 1430           }
 1431   
 1432           return repositoryURLs;
 1433   
 1434       }
 1435   
 1436   
 1437       // ------------------------------------------------------ Lifecycle Methods
 1438   
 1439   
 1440       /**
 1441        * Add a lifecycle event listener to this component.
 1442        *
 1443        * @param listener The listener to add
 1444        */
 1445       public void addLifecycleListener(LifecycleListener listener) {
 1446       }
 1447   
 1448   
 1449       /**
 1450        * Get the lifecycle listeners associated with this lifecycle. If this 
 1451        * Lifecycle has no listeners registered, a zero-length array is returned.
 1452        */
 1453       public LifecycleListener[] findLifecycleListeners() {
 1454           return new LifecycleListener[0];
 1455       }
 1456   
 1457   
 1458       /**
 1459        * Remove a lifecycle event listener from this component.
 1460        *
 1461        * @param listener The listener to remove
 1462        */
 1463       public void removeLifecycleListener(LifecycleListener listener) {
 1464       }
 1465   
 1466   
 1467       /**
 1468        * Start the class loader.
 1469        *
 1470        * @exception LifecycleException if a lifecycle error occurs
 1471        */
 1472       public void start() throws LifecycleException {
 1473   
 1474           started = true;
 1475           String encoding = null;
 1476           try {
 1477               encoding = System.getProperty("file.encoding");
 1478           } catch (Exception e) {
 1479               return;
 1480           }
 1481           if (encoding.indexOf("EBCDIC")!=-1) {
 1482               needConvert = true;
 1483           }
 1484   
 1485       }
 1486   
 1487   
 1488       /**
 1489        * Stop the class loader.
 1490        *
 1491        * @exception LifecycleException if a lifecycle error occurs
 1492        */
 1493       public void stop() throws LifecycleException {
 1494   
 1495           // Clearing references should be done before setting started to
 1496           // false, due to possible side effects
 1497           clearReferences();
 1498   
 1499           started = false;
 1500   
 1501           int length = files.length;
 1502           for (int i = 0; i < length; i++) {
 1503               files[i] = null;
 1504           }
 1505   
 1506           length = jarFiles.length;
 1507           for (int i = 0; i < length; i++) {
 1508               try {
 1509                   if (jarFiles[i] != null) {
 1510                       jarFiles[i].close();
 1511                   }
 1512               } catch (IOException e) {
 1513                   // Ignore
 1514               }
 1515               jarFiles[i] = null;
 1516           }
 1517   
 1518           notFoundResources.clear();
 1519           resourceEntries.clear();
 1520           resources = null;
 1521           repositories = null;
 1522           repositoryURLs = null;
 1523           files = null;
 1524           jarFiles = null;
 1525           jarRealFiles = null;
 1526           jarPath = null;
 1527           jarNames = null;
 1528           lastModifiedDates = null;
 1529           paths = null;
 1530           hasExternalRepositories = false;
 1531           parent = null;
 1532   
 1533           permissionList.clear();
 1534           loaderPC.clear();
 1535   
 1536           if (loaderDir != null) {
 1537               deleteDir(loaderDir);
 1538           }
 1539   
 1540       }
 1541   
 1542   
 1543       /**
 1544        * Used to periodically signal to the classloader to release 
 1545        * JAR resources.
 1546        */
 1547       public void closeJARs(boolean force) {
 1548           if (jarFiles.length > 0) {
 1549                   synchronized (jarFiles) {
 1550                       if (force || (System.currentTimeMillis() 
 1551                                     > (lastJarAccessed + 90000))) {
 1552                           for (int i = 0; i < jarFiles.length; i++) {
 1553                               try {
 1554                                   if (jarFiles[i] != null) {
 1555                                       jarFiles[i].close();
 1556                                       jarFiles[i] = null;
 1557                                   }
 1558                               } catch (IOException e) {
 1559                                   if (log.isDebugEnabled()) {
 1560                                       log.debug("Failed to close JAR", e);
 1561                                   }
 1562                               }
 1563                           }
 1564                       }
 1565                   }
 1566           }
 1567       }
 1568   
 1569   
 1570       // ------------------------------------------------------ Protected Methods
 1571   
 1572       
 1573       /**
 1574        * Clear references.
 1575        */
 1576       protected void clearReferences() {
 1577   
 1578           // Unregister any JDBC drivers loaded by this classloader
 1579           Enumeration drivers = DriverManager.getDrivers();
 1580           while (drivers.hasMoreElements()) {
 1581               Driver driver = (Driver) drivers.nextElement();
 1582               if (driver.getClass().getClassLoader() == this) {
 1583                   try {
 1584                       DriverManager.deregisterDriver(driver);
 1585                   } catch (SQLException e) {
 1586                       log.warn("SQL driver deregistration failed", e);
 1587                   }
 1588               }
 1589           }
 1590           
 1591           // Null out any static or final fields from loaded classes,
 1592           // as a workaround for apparent garbage collection bugs
 1593           if (ENABLE_CLEAR_REFERENCES) {
 1594               Iterator loadedClasses = ((HashMap) resourceEntries.clone()).values().iterator();
 1595               while (loadedClasses.hasNext()) {
 1596                   ResourceEntry entry = (ResourceEntry) loadedClasses.next();
 1597                   if (entry.loadedClass != null) {
 1598                       Class clazz = entry.loadedClass;
 1599                       try {
 1600                           Field[] fields = clazz.getDeclaredFields();
 1601                           for (int i = 0; i < fields.length; i++) {
 1602                               Field field = fields[i];
 1603                               int mods = field.getModifiers();
 1604                               if (field.getType().isPrimitive() 
 1605                                       || (field.getName().indexOf("$") != -1)) {
 1606                                   continue;
 1607                               }
 1608                               if (Modifier.isStatic(mods)) {
 1609                                   try {
 1610                                       field.setAccessible(true);
 1611                                       if (Modifier.isFinal(mods)) {
 1612                                           if (!((field.getType().getName().startsWith("java."))
 1613                                                   || (field.getType().getName().startsWith("javax.")))) {
 1614                                               nullInstance(field.get(null));
 1615                                           }
 1616                                       } else {
 1617                                           field.set(null, null);
 1618                                           if (log.isDebugEnabled()) {
 1619                                               log.debug("Set field " + field.getName() 
 1620                                                       + " to null in class " + clazz.getName());
 1621                                           }
 1622                                       }
 1623                                   } catch (Throwable t) {
 1624                                       if (log.isDebugEnabled()) {
 1625                                           log.debug("Could not set field " + field.getName() 
 1626                                                   + " to null in class " + clazz.getName(), t);
 1627                                       }
 1628                                   }
 1629                               }
 1630                           }
 1631                       } catch (Throwable t) {
 1632                           if (log.isDebugEnabled()) {
 1633                               log.debug("Could not clean fields for class " + clazz.getName(), t);
 1634                           }
 1635                       }
 1636                   }
 1637               }
 1638           }
 1639           
 1640            // Clear the IntrospectionUtils cache.
 1641           IntrospectionUtils.clear();
 1642           
 1643           // Clear the classloader reference in common-logging
 1644           org.apache.juli.logging.LogFactory.release(this);
 1645           
 1646           // Clear the classloader reference in the VM's bean introspector
 1647           java.beans.Introspector.flushCaches();
 1648   
 1649       }
 1650   
 1651   
 1652       protected void nullInstance(Object instance) {
 1653           if (instance == null) {
 1654               return;
 1655           }
 1656           Field[] fields = instance.getClass().getDeclaredFields();
 1657           for (int i = 0; i < fields.length; i++) {
 1658               Field field = fields[i];
 1659               int mods = field.getModifiers();
 1660               if (field.getType().isPrimitive() 
 1661                       || (field.getName().indexOf("$") != -1)) {
 1662                   continue;
 1663               }
 1664               try {
 1665                   field.setAccessible(true);
 1666                   if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
 1667                       // Doing something recursively is too risky
 1668                       continue;
 1669                   } else {
 1670                       Object value = field.get(instance);
 1671                       if (null != value) {
 1672                           Class valueClass = value.getClass();
 1673                           if (!loadedByThisOrChild(valueClass)) {
 1674                               if (log.isDebugEnabled()) {
 1675                                   log.debug("Not setting field " + field.getName() +
 1676                                           " to null in object of class " + 
 1677                                           instance.getClass().getName() +
 1678                                           " because the referenced object was of type " +
 1679                                           valueClass.getName() + 
 1680                                           " which was not loaded by this WebappClassLoader.");
 1681                               }
 1682                           } else {
 1683                               field.set(instance, null);
 1684                               if (log.isDebugEnabled()) {
 1685                                   log.debug("Set field " + field.getName() 
 1686                                           + " to null in class " + instance.getClass().getName());
 1687                               }
 1688                           }
 1689                       }
 1690                   }
 1691               } catch (Throwable t) {
 1692                   if (log.isDebugEnabled()) {
 1693                       log.debug("Could not set field " + field.getName() 
 1694                               + " to null in object instance of class " 
 1695                               + instance.getClass().getName(), t);
 1696                   }
 1697               }
 1698           }
 1699       }
 1700   
 1701   
 1702       /**
 1703        * Determine whether a class was loaded by this class loader or one of
 1704        * its child class loaders.
 1705        */
 1706       protected boolean loadedByThisOrChild(Class clazz)
 1707       {
 1708           boolean result = false;
 1709           for (ClassLoader classLoader = clazz.getClassLoader();
 1710                   null != classLoader; classLoader = classLoader.getParent()) {
 1711               if (classLoader.equals(this)) {
 1712                   result = true;
 1713                   break;
 1714               }
 1715           }
 1716           return result;
 1717       }    
 1718   
 1719   
 1720       /**
 1721        * Used to periodically signal to the classloader to release JAR resources.
 1722        */
 1723       protected boolean openJARs() {
 1724           if (started && (jarFiles.length > 0)) {
 1725               lastJarAccessed = System.currentTimeMillis();
 1726               if (jarFiles[0] == null) {
 1727                   for (int i = 0; i < jarFiles.length; i++) {
 1728                       try {
 1729                           jarFiles[i] = new JarFile(jarRealFiles[i]);
 1730                       } catch (IOException e) {
 1731                           if (log.isDebugEnabled()) {
 1732                               log.debug("Failed to open JAR", e);
 1733                           }
 1734                           return false;
 1735                       }
 1736                   }
 1737               }
 1738           }
 1739           return true;
 1740       }
 1741   
 1742   
 1743       /**
 1744        * Find specified class in local repositories.
 1745        *
 1746        * @return the loaded class, or null if the class isn't found
 1747        */
 1748       protected Class findClassInternal(String name)
 1749           throws ClassNotFoundException {
 1750   
 1751           if (!validate(name))
 1752               throw new ClassNotFoundException(name);
 1753   
 1754           String tempPath = name.replace('.', '/');
 1755           String classPath = tempPath + ".class";
 1756   
 1757           ResourceEntry entry = null;
 1758   
 1759           entry = findResourceInternal(name, classPath);
 1760   
 1761           if (entry == null)
 1762               throw new ClassNotFoundException(name);
 1763   
 1764           Class clazz = entry.loadedClass;
 1765           if (clazz != null)
 1766               return clazz;
 1767   
 1768           synchronized (this) {
 1769               if (entry.binaryContent == null && entry.loadedClass == null)
 1770                   throw new ClassNotFoundException(name);
 1771   
 1772               // Looking up the package
 1773               String packageName = null;
 1774               int pos = name.lastIndexOf('.');
 1775               if (pos != -1)
 1776                   packageName = name.substring(0, pos);
 1777           
 1778               Package pkg = null;
 1779           
 1780               if (packageName != null) {
 1781                   pkg = getPackage(packageName);
 1782                   // Define the package (if null)
 1783                   if (pkg == null) {
 1784                       try {
 1785                           if (entry.manifest == null) {
 1786                               definePackage(packageName, null, null, null, null,
 1787                                       null, null, null);
 1788                           } else {
 1789                               definePackage(packageName, entry.manifest,
 1790                                       entry.codeBase);
 1791                           }
 1792                       } catch (IllegalArgumentException e) {
 1793                           // Ignore: normal error due to dual definition of package
 1794                       }
 1795                       pkg = getPackage(packageName);
 1796                   }
 1797               }
 1798       
 1799               if (securityManager != null) {
 1800   
 1801                   // Checking sealing
 1802                   if (pkg != null) {
 1803                       boolean sealCheck = true;
 1804                       if (pkg.isSealed()) {
 1805                           sealCheck = pkg.isSealed(entry.codeBase);
 1806                       } else {
 1807                           sealCheck = (entry.manifest == null)
 1808                               || !isPackageSealed(packageName, entry.manifest);
 1809                       }
 1810                       if (!sealCheck)
 1811                           throw new SecurityException
 1812                               ("Sealing violation loading " + name + " : Package "
 1813                                + packageName + " is sealed.");
 1814                   }
 1815       
 1816               }
 1817   
 1818               if (entry.loadedClass == null) {
 1819                   clazz = defineClass(name, entry.binaryContent, 0,
 1820                           entry.binaryContent.length, 
 1821                           new CodeSource(entry.codeBase, entry.certificates));
 1822                   entry.loadedClass = clazz;
 1823                   entry.binaryContent = null;
 1824                   entry.source = null;
 1825                   entry.codeBase = null;
 1826                   entry.manifest = null;
 1827                   entry.certificates = null;
 1828               } else {
 1829                   clazz = entry.loadedClass;
 1830               }
 1831           }
 1832           
 1833           return clazz;
 1834   
 1835       }
 1836   
 1837       /**
 1838        * Find specified resource in local repositories. This block
 1839        * will execute under an AccessControl.doPrivilege block.
 1840        *
 1841        * @return the loaded resource, or null if the resource isn't found
 1842        */
 1843       protected ResourceEntry findResourceInternal(File file, String path){
 1844           ResourceEntry entry = new ResourceEntry();
 1845           try {
 1846               entry.source = getURI(new File(file, path));
 1847               entry.codeBase = getURL(new File(file, path), false);
 1848           } catch (MalformedURLException e) {
 1849               return null;
 1850           }   
 1851           return entry;
 1852       }
 1853       
 1854   
 1855       /**
 1856        * Find specified resource in local repositories.
 1857        *
 1858        * @return the loaded resource, or null if the resource isn't found
 1859        */
 1860       protected ResourceEntry findResourceInternal(String name, String path) {
 1861   
 1862           if (!started) {
 1863               log.info(sm.getString("webappClassLoader.stopped", name));
 1864               return null;
 1865           }
 1866   
 1867           if ((name == null) || (path == null))
 1868               return null;
 1869   
 1870           ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
 1871           if (entry != null)
 1872               return entry;
 1873   
 1874           int contentLength = -1;
 1875           InputStream binaryStream = null;
 1876   
 1877           int jarFilesLength = jarFiles.length;
 1878           int repositoriesLength = repositories.length;
 1879   
 1880           int i;
 1881   
 1882           Resource resource = null;
 1883   
 1884           boolean fileNeedConvert = false;
 1885   
 1886           for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
 1887               try {
 1888   
 1889                   String fullPath = repositories[i] + path;
 1890   
 1891                   Object lookupResult = resources.lookup(fullPath);
 1892                   if (lookupResult instanceof Resource) {
 1893                       resource = (Resource) lookupResult;
 1894                   }
 1895   
 1896                   // Note : Not getting an exception here means the resource was
 1897                   // found
 1898                    if (securityManager != null) {
 1899                       PrivilegedAction dp =
 1900                           new PrivilegedFindResource(files[i], path);
 1901                       entry = (ResourceEntry)AccessController.doPrivileged(dp);
 1902                    } else {
 1903                       entry = findResourceInternal(files[i], path);
 1904                    }
 1905   
 1906                   ResourceAttributes attributes =
 1907                       (ResourceAttributes) resources.getAttributes(fullPath);
 1908                   contentLength = (int) attributes.getContentLength();
 1909                   entry.lastModified = attributes.getLastModified();
 1910   
 1911                   if (resource != null) {
 1912   
 1913   
 1914                       try {
 1915                           binaryStream = resource.streamContent();
 1916                       } catch (IOException e) {
 1917                           return null;
 1918                       }
 1919   
 1920                       if (needConvert) {
 1921                           if (path.endsWith(".properties")) {
 1922                               fileNeedConvert = true;
 1923                           }
 1924                       }
 1925   
 1926                       // Register the full path for modification checking
 1927                       // Note: Only syncing on a 'constant' object is needed
 1928                       synchronized (allPermission) {
 1929   
 1930                           int j;
 1931   
 1932                           long[] result2 = 
 1933                               new long[lastModifiedDates.length + 1];
 1934                           for (j = 0; j < lastModifiedDates.length; j++) {
 1935                               result2[j] = lastModifiedDates[j];
 1936                           }
 1937                           result2[lastModifiedDates.length] = entry.lastModified;
 1938                           lastModifiedDates = result2;
 1939   
 1940                           String[] result = new String[paths.length + 1];
 1941                           for (j = 0; j < paths.length; j++) {
 1942                               result[j] = paths[j];
 1943                           }
 1944                           result[paths.length] = fullPath;
 1945                           paths = result;
 1946   
 1947                       }
 1948   
 1949                   }
 1950   
 1951               } catch (NamingException e) {
 1952               }
 1953           }
 1954   
 1955           if ((entry == null) && (notFoundResources.containsKey(name)))
 1956               return null;
 1957   
 1958           JarEntry jarEntry = null;
 1959   
 1960           synchronized (jarFiles) {
 1961   
 1962               if (!openJARs()) {
 1963                   return null;
 1964               }
 1965               for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
 1966   
 1967                   jarEntry = jarFiles[i].getJarEntry(path);
 1968   
 1969                   if (jarEntry != null) {
 1970   
 1971                       entry = new ResourceEntry();
 1972                       try {
 1973                           entry.codeBase = getURL(jarRealFiles[i], false);
 1974                           String jarFakeUrl = getURI(jarRealFiles[i]).toString();
 1975                           jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
 1976                           entry.source = new URL(jarFakeUrl);
 1977                           entry.lastModified = jarRealFiles[i].lastModified();
 1978                       } catch (MalformedURLException e) {
 1979                           return null;
 1980                       }
 1981                       contentLength = (int) jarEntry.getSize();
 1982                       try {
 1983                           entry.manifest = jarFiles[i].getManifest();
 1984                           binaryStream = jarFiles[i].getInputStream(jarEntry);
 1985                       } catch (IOException e) {
 1986                           return null;
 1987                       }
 1988   
 1989                       // Extract resources contained in JAR to the workdir
 1990                       if (antiJARLocking && !(path.endsWith(".class"))) {
 1991                           byte[] buf = new byte[1024];
 1992                           File resourceFile = new File
 1993                               (loaderDir, jarEntry.getName());
 1994                           if (!resourceFile.exists()) {
 1995                               Enumeration entries = jarFiles[i].entries();
 1996                               while (entries.hasMoreElements()) {
 1997                                   JarEntry jarEntry2 = 
 1998                                       (JarEntry) entries.nextElement();
 1999                                   if (!(jarEntry2.isDirectory()) 
 2000                                       && (!jarEntry2.getName().endsWith
 2001                                           (".class"))) {
 2002                                       resourceFile = new File
 2003                                           (loaderDir, jarEntry2.getName());
 2004                                       resourceFile.getParentFile().mkdirs();
 2005                                       FileOutputStream os = null;
 2006                                       InputStream is = null;
 2007                                       try {
 2008                                           is = jarFiles[i].getInputStream
 2009                                               (jarEntry2);
 2010                                           os = new FileOutputStream
 2011                                               (resourceFile);
 2012                                           while (true) {
 2013                                               int n = is.read(buf);
 2014                                               if (n <= 0) {
 2015                                                   break;
 2016                                               }
 2017                                               os.write(buf, 0, n);
 2018                                           }
 2019                                       } catch (IOException e) {
 2020                                           // Ignore
 2021                                       } finally {
 2022                                           try {
 2023                                               if (is != null) {
 2024                                                   is.close();
 2025                                               }
 2026                                           } catch (IOException e) {
 2027                                           }
 2028                                           try {
 2029                                               if (os != null) {
 2030                                                   os.close();
 2031                                               }
 2032                                           } catch (IOException e) {
 2033                                           }
 2034                                       }
 2035                                   }
 2036                               }
 2037                           }
 2038                       }
 2039   
 2040                   }
 2041   
 2042               }
 2043   
 2044               if (entry == null) {
 2045                   synchronized (notFoundResources) {
 2046                       notFoundResources.put(name, name);
 2047                   }
 2048                   return null;
 2049               }
 2050   
 2051               if (binaryStream != null) {
 2052   
 2053                   byte[] binaryContent = new byte[contentLength];
 2054   
 2055                   int pos = 0;
 2056                   try {
 2057   
 2058                       while (true) {
 2059                           int n = binaryStream.read(binaryContent, pos,
 2060                                                     binaryContent.length - pos);
 2061                           if (n <= 0)
 2062                               break;
 2063                           pos += n;
 2064                       }
 2065                       binaryStream.close();
 2066                   } catch (IOException e) {
 2067                       e.printStackTrace();
 2068                       return null;
 2069                   } catch (Exception e) {
 2070                       e.printStackTrace();
 2071                       return null;
 2072                   }
 2073   
 2074                   if (fileNeedConvert) {
 2075                       String str = new String(binaryContent,0,pos);
 2076                       try {
 2077                           binaryContent = str.getBytes("UTF-8");
 2078                       } catch (Exception e) {
 2079                           return null;
 2080                       }
 2081                   }
 2082                   entry.binaryContent = binaryContent;
 2083   
 2084                   // The certificates are only available after the JarEntry 
 2085                   // associated input stream has been fully read
 2086                   if (jarEntry != null) {
 2087                       entry.certificates = jarEntry.getCertificates();
 2088                   }
 2089   
 2090               }
 2091   
 2092           }
 2093   
 2094           // Add the entry in the local resource repository
 2095           synchronized (resourceEntries) {
 2096               // Ensures that all the threads which may be in a race to load
 2097               // a particular class all end up with the same ResourceEntry
 2098               // instance
 2099               ResourceEntry entry2 = (ResourceEntry) resourceEntries.get(name);
 2100               if (entry2 == null) {
 2101                   resourceEntries.put(name, entry);
 2102               } else {
 2103                   entry = entry2;
 2104               }
 2105           }
 2106   
 2107           return entry;
 2108   
 2109       }
 2110   
 2111   
 2112       /**
 2113        * Returns true if the specified package name is sealed according to the
 2114        * given manifest.
 2115        */
 2116       protected boolean isPackageSealed(String name, Manifest man) {
 2117   
 2118           String path = name.replace('.', '/') + '/';
 2119           Attributes attr = man.getAttributes(path); 
 2120           String sealed = null;
 2121           if (attr != null) {
 2122               sealed = attr.getValue(Name.SEALED);
 2123           }
 2124           if (sealed == null) {
 2125               if ((attr = man.getMainAttributes()) != null) {
 2126                   sealed = attr.getValue(Name.SEALED);
 2127               }
 2128           }
 2129           return "true".equalsIgnoreCase(sealed);
 2130   
 2131       }
 2132   
 2133   
 2134       /**
 2135        * Finds the resource with the given name if it has previously been
 2136        * loaded and cached by this class loader, and return an input stream
 2137        * to the resource data.  If this resource has not been cached, return
 2138        * <code>null</code>.
 2139        *
 2140        * @param name Name of the resource to return
 2141        */
 2142       protected InputStream findLoadedResource(String name) {
 2143   
 2144           ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
 2145           if (entry != null) {
 2146               if (entry.binaryContent != null)
 2147                   return new ByteArrayInputStream(entry.binaryContent);
 2148           }
 2149           return (null);
 2150   
 2151       }
 2152   
 2153   
 2154       /**
 2155        * Finds the class with the given name if it has previously been
 2156        * loaded and cached by this class loader, and return the Class object.
 2157        * If this class has not been cached, return <code>null</code>.
 2158        *
 2159        * @param name Name of the resource to return
 2160        */
 2161       protected Class findLoadedClass0(String name) {
 2162   
 2163           ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
 2164           if (entry != null) {
 2165               return entry.loadedClass;
 2166           }
 2167           return (null);  // FIXME - findLoadedResource()
 2168   
 2169       }
 2170   
 2171   
 2172       /**
 2173        * Refresh the system policy file, to pick up eventual changes.
 2174        */
 2175       protected void refreshPolicy() {
 2176   
 2177           try {
 2178               // The policy file may have been modified to adjust 
 2179               // permissions, so we're reloading it when loading or 
 2180               // reloading a Context
 2181               Policy policy = Policy.getPolicy();
 2182               policy.refresh();
 2183           } catch (AccessControlException e) {
 2184               // Some policy files may restrict this, even for the core,
 2185               // so this exception is ignored
 2186           }
 2187   
 2188       }
 2189   
 2190   
 2191       /**
 2192        * Filter classes.
 2193        * 
 2194        * @param name class name
 2195        * @return true if the class should be filtered
 2196        */
 2197       protected boolean filter(String name) {
 2198   
 2199           if (name == null)
 2200               return false;
 2201   
 2202           // Looking up the package
 2203           String packageName = null;
 2204           int pos = name.lastIndexOf('.');
 2205           if (pos != -1)
 2206               packageName = name.substring(0, pos);
 2207           else
 2208               return false;
 2209   
 2210           for (int i = 0; i < packageTriggers.length; i++) {
 2211               if (packageName.startsWith(packageTriggers[i]))
 2212                   return true;
 2213           }
 2214   
 2215           return false;
 2216   
 2217       }
 2218   
 2219   
 2220       /**
 2221        * Validate a classname. As per SRV.9.7.2, we must restict loading of 
 2222        * classes from J2SE (java.*) and classes of the servlet API 
 2223        * (javax.servlet.*). That should enhance robustness and prevent a number
 2224        * of user error (where an older version of servlet.jar would be present
 2225        * in /WEB-INF/lib).
 2226        * 
 2227        * @param name class name
 2228        * @return true if the name is valid
 2229        */
 2230       protected boolean validate(String name) {
 2231   
 2232           if (name == null)
 2233               return false;
 2234           if (name.startsWith("java."))
 2235               return false;
 2236   
 2237           return true;
 2238   
 2239       }
 2240   
 2241   
 2242       /**
 2243        * Check the specified JAR file, and return <code>true</code> if it does
 2244        * not contain any of the trigger classes.
 2245        *
 2246        * @param jarfile The JAR file to be checked
 2247        *
 2248        * @exception IOException if an input/output error occurs
 2249        */
 2250       protected boolean validateJarFile(File jarfile)
 2251           throws IOException {
 2252   
 2253           if (triggers == null)
 2254               return (true);
 2255           JarFile jarFile = new JarFile(jarfile);
 2256           for (int i = 0; i < triggers.length; i++) {
 2257               Class clazz = null;
 2258               try {
 2259                   if (parent != null) {
 2260                       clazz = parent.loadClass(triggers[i]);
 2261                   } else {
 2262                       clazz = Class.forName(triggers[i]);
 2263                   }
 2264               } catch (Throwable t) {
 2265                   clazz = null;
 2266               }
 2267               if (clazz == null)
 2268                   continue;
 2269               String name = triggers[i].replace('.', '/') + ".class";
 2270               if (log.isDebugEnabled())
 2271                   log.debug(" Checking for " + name);
 2272               JarEntry jarEntry = jarFile.getJarEntry(name);
 2273               if (jarEntry != null) {
 2274                   log.info("validateJarFile(" + jarfile + 
 2275                       ") - jar not loaded. See Servlet Spec 2.3, "
 2276                       + "section 9.7.2. Offending class: " + name);
 2277                   jarFile.close();
 2278                   return (false);
 2279               }
 2280           }
 2281           jarFile.close();
 2282           return (true);
 2283   
 2284       }
 2285   
 2286   
 2287       /**
 2288        * Get URL.
 2289        */
 2290       protected URL getURL(File file, boolean encoded)
 2291           throws MalformedURLException {
 2292   
 2293           File realFile = file;
 2294           try {
 2295               realFile = realFile.getCanonicalFile();
 2296           } catch (IOException e) {
 2297               // Ignore
 2298           }
 2299           if(encoded) {
 2300               return getURI(realFile);
 2301           } else {
 2302               return realFile.toURL();
 2303           }
 2304   
 2305       }
 2306   
 2307   
 2308       /**
 2309        * Get URL.
 2310        */
 2311       protected URL getURI(File file)
 2312           throws MalformedURLException {
 2313   
 2314   
 2315           File realFile = file;
 2316           try {
 2317               realFile = realFile.getCanonicalFile();
 2318           } catch (IOException e) {
 2319               // Ignore
 2320           }
 2321           return realFile.toURI().toURL();
 2322   
 2323       }
 2324   
 2325   
 2326       /**
 2327        * Delete the specified directory, including all of its contents and
 2328        * subdirectories recursively.
 2329        *
 2330        * @param dir File object representing the directory to be deleted
 2331        */
 2332       protected static void deleteDir(File dir) {
 2333   
 2334           String files[] = dir.list();
 2335           if (files == null) {
 2336               files = new String[0];
 2337           }
 2338           for (int i = 0; i < files.length; i++) {
 2339               File file = new File(dir, files[i]);
 2340               if (file.isDirectory()) {
 2341                   deleteDir(file);
 2342               } else {
 2343                   file.delete();
 2344               }
 2345           }
 2346           dir.delete();
 2347   
 2348       }
 2349   
 2350   
 2351   }
 2352   

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » loader » [javadoc | source]