Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [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   package org.apache.tools.ant;
   19   
   20   import java.io.ByteArrayOutputStream;
   21   import java.io.File;
   22   import java.io.FileInputStream;
   23   import java.io.IOException;
   24   import java.io.InputStream;
   25   import java.lang.reflect.Constructor;
   26   import java.net.MalformedURLException;
   27   import java.net.URL;
   28   import java.security.CodeSource;
   29   import java.security.ProtectionDomain;
   30   import java.security.cert.Certificate;
   31   import java.util.Collections;
   32   import java.util.Enumeration;
   33   import java.util.HashMap;
   34   import java.util.Hashtable;
   35   import java.util.Map;
   36   import java.util.StringTokenizer;
   37   import java.util.Vector;
   38   import java.util.jar.Attributes;
   39   import java.util.jar.Attributes.Name;
   40   import java.util.jar.JarEntry;
   41   import java.util.jar.JarFile;
   42   import java.util.jar.Manifest;
   43   import org.apache.tools.ant.types.Path;
   44   import org.apache.tools.ant.util.CollectionUtils;
   45   import org.apache.tools.ant.util.FileUtils;
   46   import org.apache.tools.ant.util.JavaEnvUtils;
   47   import org.apache.tools.ant.util.LoaderUtils;
   48   import org.apache.tools.ant.util.ReflectUtil;
   49   import org.apache.tools.ant.util.VectorSet;
   50   import org.apache.tools.ant.launch.Locator;
   51   
   52   /**
   53    * Used to load classes within ant with a different classpath from
   54    * that used to start ant. Note that it is possible to force a class
   55    * into this loader even when that class is on the system classpath by
   56    * using the forceLoadClass method. Any subsequent classes loaded by that
   57    * class will then use this loader rather than the system class loader.
   58    *
   59    * <p>
   60    * Note that this classloader has a feature to allow loading
   61    * in reverse order and for "isolation".
   62    * Due to the fact that a number of
   63    * methods in java.lang.ClassLoader are final (at least
   64    * in java 1.4 getResources) this means that the
   65    * class has to fake the given parent.
   66    * </p>
   67    *
   68    */
   69   public class AntClassLoader extends ClassLoader implements SubBuildListener {
   70   
   71       private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
   72   
   73       /**
   74        * An enumeration of all resources of a given name found within the
   75        * classpath of this class loader. This enumeration is used by the
   76        * ClassLoader.findResources method, which is in
   77        * turn used by the ClassLoader.getResources method.
   78        *
   79        * @see AntClassLoader#findResources(String)
   80        * @see java.lang.ClassLoader#getResources(String)
   81        */
   82       private class ResourceEnumeration implements Enumeration {
   83           /**
   84            * The name of the resource being searched for.
   85            */
   86           private String resourceName;
   87   
   88           /**
   89            * The index of the next classpath element to search.
   90            */
   91           private int pathElementsIndex;
   92   
   93           /**
   94            * The URL of the next resource to return in the enumeration. If this
   95            * field is <code>null</code> then the enumeration has been completed,
   96            * i.e., there are no more elements to return.
   97            */
   98           private URL nextResource;
   99   
  100           /**
  101            * Constructs a new enumeration of resources of the given name found
  102            * within this class loader's classpath.
  103            *
  104            * @param name the name of the resource to search for.
  105            */
  106           ResourceEnumeration(String name) {
  107               this.resourceName = name;
  108               this.pathElementsIndex = 0;
  109               findNextResource();
  110           }
  111   
  112           /**
  113            * Indicates whether there are more elements in the enumeration to
  114            * return.
  115            *
  116            * @return <code>true</code> if there are more elements in the
  117            *         enumeration; <code>false</code> otherwise.
  118            */
  119           public boolean hasMoreElements() {
  120               return (this.nextResource != null);
  121           }
  122   
  123           /**
  124            * Returns the next resource in the enumeration.
  125            *
  126            * @return the next resource in the enumeration
  127            */
  128           public Object nextElement() {
  129               URL ret = this.nextResource;
  130               findNextResource();
  131               return ret;
  132           }
  133   
  134           /**
  135            * Locates the next resource of the correct name in the classpath and
  136            * sets <code>nextResource</code> to the URL of that resource. If no
  137            * more resources can be found, <code>nextResource</code> is set to
  138            * <code>null</code>.
  139            */
  140           private void findNextResource() {
  141               URL url = null;
  142               while ((pathElementsIndex < pathComponents.size()) && (url == null)) {
  143                   try {
  144                       File pathComponent = (File) pathComponents.elementAt(pathElementsIndex);
  145                       url = getResourceURL(pathComponent, this.resourceName);
  146                       pathElementsIndex++;
  147                   } catch (BuildException e) {
  148                       // ignore path elements which are not valid relative to the
  149                       // project
  150                   }
  151               }
  152               this.nextResource = url;
  153           }
  154       }
  155   
  156       /**
  157        * The size of buffers to be used in this classloader.
  158        */
  159       private static final int BUFFER_SIZE = 8192;
  160   
  161       /**
  162        * Number of array elements in a test array of strings
  163        */
  164       private static final int NUMBER_OF_STRINGS = 256;
  165   
  166       /**
  167        * The components of the classpath that the classloader searches
  168        * for classes.
  169        */
  170       private Vector pathComponents  = new VectorSet();
  171   
  172       /**
  173        * The project to which this class loader belongs.
  174        */
  175       private Project project;
  176   
  177       /**
  178        * Indicates whether the parent class loader should be
  179        * consulted before trying to load with this class loader.
  180        */
  181       private boolean parentFirst = true;
  182   
  183       /**
  184        * These are the package roots that are to be loaded by the parent class
  185        * loader regardless of whether the parent class loader is being searched
  186        * first or not.
  187        */
  188       private Vector systemPackages = new Vector();
  189   
  190       /**
  191        * These are the package roots that are to be loaded by this class loader
  192        * regardless of whether the parent class loader is being searched first
  193        * or not.
  194        */
  195       private Vector loaderPackages = new Vector();
  196   
  197       /**
  198        * Whether or not this classloader will ignore the base
  199        * classloader if it can't find a class.
  200        *
  201        * @see #setIsolated(boolean)
  202        */
  203       private boolean ignoreBase = false;
  204   
  205       /**
  206        * The parent class loader, if one is given or can be determined.
  207        */
  208       private ClassLoader parent = null;
  209   
  210       /**
  211        * A hashtable of zip files opened by the classloader (File to JarFile).
  212        */
  213       private Hashtable jarFiles = new Hashtable();
  214   
  215       /** Static map of jar file/time to manifest class-path entries */
  216       private static Map/*<String,String>*/ pathMap = Collections.synchronizedMap(new HashMap());
  217   
  218       /**
  219        * The context loader saved when setting the thread's current
  220        * context loader.
  221        */
  222       private ClassLoader savedContextLoader = null;
  223   
  224       /**
  225        * Whether or not the context loader is currently saved.
  226        */
  227       private boolean isContextLoaderSaved = false;
  228   
  229       /**
  230        * Create an Ant ClassLoader for a given project, with
  231        * a parent classloader and an initial classpath.
  232        * @since Ant 1.7.
  233        * @param parent the parent for this classloader.
  234        * @param project The project to which this classloader is to
  235        *                belong.
  236        * @param classpath The classpath to use to load classes.
  237        */
  238       public AntClassLoader(ClassLoader parent, Project project, Path classpath) {
  239           setParent(parent);
  240           setClassPath(classpath);
  241           setProject(project);
  242       }
  243   
  244       /**
  245        * Create an Ant Class Loader
  246        */
  247       public AntClassLoader() {
  248           setParent(null);
  249       }
  250   
  251       /**
  252        * Creates a classloader for the given project using the classpath given.
  253        *
  254        * @param project The project to which this classloader is to belong.
  255        *                Must not be <code>null</code>.
  256        * @param classpath The classpath to use to load the classes.  This
  257        *                is combined with the system classpath in a manner
  258        *                determined by the value of ${build.sysclasspath}.
  259        *                May be <code>null</code>, in which case no path
  260        *                elements are set up to start with.
  261        */
  262       public AntClassLoader(Project project, Path classpath) {
  263           setParent(null);
  264           setProject(project);
  265           setClassPath(classpath);
  266       }
  267   
  268       /**
  269        * Creates a classloader for the given project using the classpath given.
  270        *
  271        * @param parent The parent classloader to which unsatisfied loading
  272        *               attempts are delegated. May be <code>null</code>,
  273        *               in which case the classloader which loaded this
  274        *               class is used as the parent.
  275        * @param project The project to which this classloader is to belong.
  276        *                Must not be <code>null</code>.
  277        * @param classpath the classpath to use to load the classes.
  278        *                  May be <code>null</code>, in which case no path
  279        *                  elements are set up to start with.
  280        * @param parentFirst If <code>true</code>, indicates that the parent
  281        *                    classloader should be consulted  before trying to
  282        *                    load the a class through this loader.
  283        */
  284       public AntClassLoader(
  285           ClassLoader parent, Project project, Path classpath, boolean parentFirst) {
  286           this(project, classpath);
  287           if (parent != null) {
  288               setParent(parent);
  289           }
  290           setParentFirst(parentFirst);
  291           addJavaLibraries();
  292       }
  293   
  294       /**
  295        * Creates a classloader for the given project using the classpath given.
  296        *
  297        * @param project The project to which this classloader is to belong.
  298        *                Must not be <code>null</code>.
  299        * @param classpath The classpath to use to load the classes. May be
  300        *                  <code>null</code>, in which case no path
  301        *                  elements are set up to start with.
  302        * @param parentFirst If <code>true</code>, indicates that the parent
  303        *                    classloader should be consulted before trying to
  304        *                    load the a class through this loader.
  305        */
  306       public AntClassLoader(Project project, Path classpath, boolean parentFirst) {
  307           this(null, project, classpath, parentFirst);
  308       }
  309   
  310       /**
  311        * Creates an empty class loader. The classloader should be configured
  312        * with path elements to specify where the loader is to look for
  313        * classes.
  314        *
  315        * @param parent The parent classloader to which unsatisfied loading
  316        *               attempts are delegated. May be <code>null</code>,
  317        *               in which case the classloader which loaded this
  318        *               class is used as the parent.
  319        * @param parentFirst If <code>true</code>, indicates that the parent
  320        *                    classloader should be consulted before trying to
  321        *                    load the a class through this loader.
  322        */
  323       public AntClassLoader(ClassLoader parent, boolean parentFirst) {
  324           setParent(parent);
  325           project = null;
  326           this.parentFirst = parentFirst;
  327       }
  328   
  329       /**
  330        * Set the project associated with this class loader
  331        *
  332        * @param project the project instance
  333        */
  334       public void setProject(Project project) {
  335           this.project = project;
  336           if (project != null) {
  337               project.addBuildListener(this);
  338           }
  339       }
  340   
  341       /**
  342        * Set the classpath to search for classes to load. This should not be
  343        * changed once the classloader starts to server classes
  344        *
  345        * @param classpath the search classpath consisting of directories and
  346        *        jar/zip files.
  347        */
  348       public void setClassPath(Path classpath) {
  349           pathComponents.removeAllElements();
  350           if (classpath != null) {
  351               Path actualClasspath = classpath.concatSystemClasspath("ignore");
  352               String[] pathElements = actualClasspath.list();
  353               for (int i = 0; i < pathElements.length; ++i) {
  354                   try {
  355                       addPathElement(pathElements[i]);
  356                   } catch (BuildException e) {
  357                       // ignore path elements which are invalid
  358                       // relative to the project
  359                   }
  360               }
  361           }
  362       }
  363   
  364       /**
  365        * Set the parent for this class loader. This is the class loader to which
  366        * this class loader will delegate to load classes
  367        *
  368        * @param parent the parent class loader.
  369        */
  370       public void setParent(ClassLoader parent) {
  371           this.parent = parent == null ? AntClassLoader.class.getClassLoader() : parent;
  372       }
  373   
  374       /**
  375        * Control whether class lookup is delegated to the parent loader first
  376        * or after this loader. Use with extreme caution. Setting this to
  377        * false violates the class loader hierarchy and can lead to Linkage errors
  378        *
  379        * @param parentFirst if true, delegate initial class search to the parent
  380        *                    classloader.
  381        */
  382       public void setParentFirst(boolean parentFirst) {
  383           this.parentFirst = parentFirst;
  384       }
  385   
  386       /**
  387        * Logs a message through the project object if one has been provided.
  388        *
  389        * @param message The message to log.
  390        *                Should not be <code>null</code>.
  391        *
  392        * @param priority The logging priority of the message.
  393        */
  394       protected void log(String message, int priority) {
  395           if (project != null) {
  396               project.log(message, priority);
  397           }
  398       }
  399   
  400       /**
  401        * Sets the current thread's context loader to this classloader, storing
  402        * the current loader value for later resetting.
  403        */
  404       public void setThreadContextLoader() {
  405           if (isContextLoaderSaved) {
  406               throw new BuildException("Context loader has not been reset");
  407           }
  408           if (LoaderUtils.isContextLoaderAvailable()) {
  409               savedContextLoader = LoaderUtils.getContextClassLoader();
  410               ClassLoader loader = this;
  411               if (project != null && "only".equals(project.getProperty("build.sysclasspath"))) {
  412                   loader = this.getClass().getClassLoader();
  413               }
  414               LoaderUtils.setContextClassLoader(loader);
  415               isContextLoaderSaved = true;
  416           }
  417       }
  418   
  419       /**
  420        * Resets the current thread's context loader to its original value.
  421        */
  422       public void resetThreadContextLoader() {
  423           if (LoaderUtils.isContextLoaderAvailable() && isContextLoaderSaved) {
  424               LoaderUtils.setContextClassLoader(savedContextLoader);
  425               savedContextLoader = null;
  426               isContextLoaderSaved = false;
  427           }
  428       }
  429   
  430   
  431       /**
  432        * Adds an element to the classpath to be searched.
  433        *
  434        * @param pathElement The path element to add. Must not be
  435        *                    <code>null</code>.
  436        *
  437        * @exception BuildException if the given path element cannot be resolved
  438        *                           against the project.
  439        */
  440       public void addPathElement(String pathElement) throws BuildException {
  441           File pathComponent = project != null ? project.resolveFile(pathElement) : new File(
  442                   pathElement);
  443           try {
  444               addPathFile(pathComponent);
  445           } catch (IOException e) {
  446               throw new BuildException(e);
  447           }
  448       }
  449   
  450       /**
  451        * Add a path component.
  452        * This simply adds the file, unlike addPathElement
  453        * it does not open jar files and load files from
  454        * their CLASSPATH entry in the manifest file.
  455        * @param file the jar file or directory to add.
  456        */
  457       public void addPathComponent(File file) {
  458           if (pathComponents.contains(file)) {
  459               return;
  460           }
  461           pathComponents.addElement(file);
  462       }
  463   
  464       /**
  465        * Add a file to the path.
  466        * Reads the manifest, if available, and adds any additional class path jars
  467        * specified in the manifest.
  468        *
  469        * @param pathComponent the file which is to be added to the path for
  470        *                      this class loader
  471        *
  472        * @throws IOException if data needed from the file cannot be read.
  473        */
  474       protected void addPathFile(File pathComponent) throws IOException {
  475           if (!pathComponents.contains(pathComponent)) {
  476               pathComponents.addElement(pathComponent);
  477           }
  478           if (pathComponent.isDirectory()) {
  479               return;
  480           }
  481   
  482           String absPathPlusTimeAndLength = pathComponent.getAbsolutePath()
  483                   + pathComponent.lastModified() + "-" + pathComponent.length();
  484           String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
  485           if (classpath == null) {
  486               JarFile jarFile = null;
  487               try {
  488                   jarFile = new JarFile(pathComponent);
  489                   Manifest manifest = jarFile.getManifest();
  490                   if (manifest == null) {
  491                       return;
  492                   }
  493                   classpath = manifest.getMainAttributes()
  494                       .getValue(Attributes.Name.CLASS_PATH);
  495               } finally {
  496                   if (jarFile != null) {
  497                       jarFile.close();
  498                   }
  499               }
  500               if (classpath == null) {
  501                   classpath = "";
  502               }
  503               pathMap.put(absPathPlusTimeAndLength, classpath);
  504           }
  505   
  506           if (!"".equals(classpath)) {
  507               URL baseURL = FILE_UTILS.getFileURL(pathComponent);
  508               StringTokenizer st = new StringTokenizer(classpath);
  509               while (st.hasMoreTokens()) {
  510                   String classpathElement = st.nextToken();
  511                   URL libraryURL = new URL(baseURL, classpathElement);
  512                   if (!libraryURL.getProtocol().equals("file")) {
  513                       log("Skipping jar library " + classpathElement
  514                               + " since only relative URLs are supported by this" + " loader",
  515                               Project.MSG_VERBOSE);
  516                       continue;
  517                   }
  518                   String decodedPath = Locator.decodeUri(libraryURL.getFile());
  519                   File libraryFile = new File(decodedPath);
  520                   if (libraryFile.exists() && !isInPath(libraryFile)) {
  521                       addPathFile(libraryFile);
  522                   }
  523               }
  524           }
  525       }
  526   
  527       /**
  528        * Returns the classpath this classloader will consult.
  529        *
  530        * @return the classpath used for this classloader, with elements
  531        *         separated by the path separator for the system.
  532        */
  533       public String getClasspath() {
  534           StringBuffer sb = new StringBuffer();
  535           boolean firstPass = true;
  536           Enumeration componentEnum = pathComponents.elements();
  537           while (componentEnum.hasMoreElements()) {
  538               if (!firstPass) {
  539                   sb.append(System.getProperty("path.separator"));
  540               } else {
  541                   firstPass = false;
  542               }
  543               sb.append(((File) componentEnum.nextElement()).getAbsolutePath());
  544           }
  545           return sb.toString();
  546       }
  547   
  548       /**
  549        * Sets whether this classloader should run in isolated mode. In
  550        * isolated mode, classes not found on the given classpath will
  551        * not be referred to the parent class loader but will cause a
  552        * ClassNotFoundException.
  553        *
  554        * @param isolated Whether or not this classloader should run in
  555        *                 isolated mode.
  556        */
  557       public synchronized void setIsolated(boolean isolated) {
  558           ignoreBase = isolated;
  559       }
  560   
  561       /**
  562        * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
  563        * way.
  564        *
  565        * @param theClass The class to initialize.
  566        *                 Must not be <code>null</code>.
  567        *
  568        * @deprecated since 1.6.x.
  569        *             Use Class.forName with initialize=true instead.
  570        */
  571       public static void initializeClass(Class theClass) {
  572           // ***HACK*** We ask the VM to create an instance
  573           // by voluntarily providing illegal arguments to force
  574           // the VM to run the class' static initializer, while
  575           // at the same time not running a valid constructor.
  576   
  577           final Constructor[] cons = theClass.getDeclaredConstructors();
  578           //At least one constructor is guaranteed to be there, but check anyway.
  579           if (cons != null) {
  580               if (cons.length > 0 && cons[0] != null) {
  581                   final String[] strs = new String[NUMBER_OF_STRINGS];
  582                   try {
  583                       cons[0].newInstance((Object[]) strs);
  584                       // Expecting an exception to be thrown by this call:
  585                       // IllegalArgumentException: wrong number of Arguments
  586                   } catch (Exception e) {
  587                       // Ignore - we are interested only in the side
  588                       // effect - that of getting the static initializers
  589                       // invoked.  As we do not want to call a valid
  590                       // constructor to get this side effect, an
  591                       // attempt is made to call a hopefully
  592                       // invalid constructor - come on, nobody
  593                       // would have a constructor that takes in
  594                       // 256 String arguments ;-)
  595                       // (In fact, they can't - according to JVM spec
  596                       // section 4.10, the number of method parameters is limited
  597                       // to 255 by the definition of a method descriptor.
  598                       // Constructors count as methods here.)
  599                   }
  600               }
  601           }
  602       }
  603   
  604       /**
  605        * Adds a package root to the list of packages which must be loaded on the
  606        * parent loader.
  607        *
  608        * All subpackages are also included.
  609        *
  610        * @param packageRoot The root of all packages to be included.
  611        *                    Should not be <code>null</code>.
  612        */
  613       public void addSystemPackageRoot(String packageRoot) {
  614           systemPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
  615       }
  616   
  617       /**
  618        * Adds a package root to the list of packages which must be loaded using
  619        * this loader.
  620        *
  621        * All subpackages are also included.
  622        *
  623        * @param packageRoot The root of all packages to be included.
  624        *                    Should not be <code>null</code>.
  625        */
  626       public void addLoaderPackageRoot(String packageRoot) {
  627           loaderPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
  628       }
  629   
  630       /**
  631        * Loads a class through this class loader even if that class is available
  632        * on the parent classpath.
  633        *
  634        * This ensures that any classes which are loaded by the returned class
  635        * will use this classloader.
  636        *
  637        * @param classname The name of the class to be loaded.
  638        *                  Must not be <code>null</code>.
  639        *
  640        * @return the required Class object
  641        *
  642        * @exception ClassNotFoundException if the requested class does not exist
  643        *                                   on this loader's classpath.
  644        */
  645       public Class forceLoadClass(String classname) throws ClassNotFoundException {
  646           log("force loading " + classname, Project.MSG_DEBUG);
  647   
  648           Class theClass = findLoadedClass(classname);
  649   
  650           if (theClass == null) {
  651               theClass = findClass(classname);
  652           }
  653           return theClass;
  654       }
  655   
  656       /**
  657        * Loads a class through this class loader but defer to the parent class
  658        * loader.
  659        *
  660        * This ensures that instances of the returned class will be compatible
  661        * with instances which have already been loaded on the parent
  662        * loader.
  663        *
  664        * @param classname The name of the class to be loaded.
  665        *                  Must not be <code>null</code>.
  666        *
  667        * @return the required Class object
  668        *
  669        * @exception ClassNotFoundException if the requested class does not exist
  670        * on this loader's classpath.
  671        */
  672       public Class forceLoadSystemClass(String classname) throws ClassNotFoundException {
  673           log("force system loading " + classname, Project.MSG_DEBUG);
  674   
  675           Class theClass = findLoadedClass(classname);
  676   
  677           if (theClass == null) {
  678               theClass = findBaseClass(classname);
  679           }
  680           return theClass;
  681       }
  682   
  683       /**
  684        * Returns a stream to read the requested resource name.
  685        *
  686        * @param name The name of the resource for which a stream is required.
  687        *             Must not be <code>null</code>.
  688        *
  689        * @return a stream to the required resource or <code>null</code> if the
  690        *         resource cannot be found on the loader's classpath.
  691        */
  692       public InputStream getResourceAsStream(String name) {
  693           InputStream resourceStream = null;
  694           if (isParentFirst(name)) {
  695               resourceStream = loadBaseResource(name);
  696           }
  697           if (resourceStream != null) {
  698               log("ResourceStream for " + name
  699                   + " loaded from parent loader", Project.MSG_DEBUG);
  700           } else {
  701               resourceStream = loadResource(name);
  702               if (resourceStream != null) {
  703                   log("ResourceStream for " + name
  704                       + " loaded from ant loader", Project.MSG_DEBUG);
  705               }
  706           }
  707           if (resourceStream == null && !isParentFirst(name)) {
  708               if (ignoreBase) {
  709                   resourceStream = getRootLoader() == null ? null : getRootLoader().getResourceAsStream(name);
  710               } else {
  711                   resourceStream = loadBaseResource(name);
  712               }
  713               if (resourceStream != null) {
  714                   log("ResourceStream for " + name + " loaded from parent loader",
  715                       Project.MSG_DEBUG);
  716               }
  717           }
  718           if (resourceStream == null) {
  719               log("Couldn't load ResourceStream for " + name, Project.MSG_DEBUG);
  720           }
  721           return resourceStream;
  722       }
  723   
  724       /**
  725        * Returns a stream to read the requested resource name from this loader.
  726        *
  727        * @param name The name of the resource for which a stream is required.
  728        *             Must not be <code>null</code>.
  729        *
  730        * @return a stream to the required resource or <code>null</code> if
  731        *         the resource cannot be found on the loader's classpath.
  732        */
  733       private InputStream loadResource(String name) {
  734           // we need to search the components of the path to see if we can
  735           // find the class we want.
  736           InputStream stream = null;
  737   
  738           Enumeration e = pathComponents.elements();
  739           while (e.hasMoreElements() && stream == null) {
  740               File pathComponent = (File) e.nextElement();
  741               stream = getResourceStream(pathComponent, name);
  742           }
  743           return stream;
  744       }
  745   
  746       /**
  747        * Finds a system resource (which should be loaded from the parent
  748        * classloader).
  749        *
  750        * @param name The name of the system resource to load.
  751        *             Must not be <code>null</code>.
  752        *
  753        * @return a stream to the named resource, or <code>null</code> if
  754        *         the resource cannot be found.
  755        */
  756       private InputStream loadBaseResource(String name) {
  757           return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name);
  758       }
  759   
  760       /**
  761        * Returns an inputstream to a given resource in the given file which may
  762        * either be a directory or a zip file.
  763        *
  764        * @param file the file (directory or jar) in which to search for the
  765        *             resource. Must not be <code>null</code>.
  766        * @param resourceName The name of the resource for which a stream is
  767        *                     required. Must not be <code>null</code>.
  768        *
  769        * @return a stream to the required resource or <code>null</code> if
  770        *         the resource cannot be found in the given file.
  771        */
  772       private InputStream getResourceStream(File file, String resourceName) {
  773           try {
  774               JarFile jarFile = (JarFile) jarFiles.get(file);
  775               if (jarFile == null && file.isDirectory()) {
  776                   File resource = new File(file, resourceName);
  777                   if (resource.exists()) {
  778                       return new FileInputStream(resource);
  779                   }
  780               } else {
  781                   if (jarFile == null) {
  782                       if (file.exists()) {
  783                           jarFile = new JarFile(file);
  784                           jarFiles.put(file, jarFile);
  785                       } else {
  786                           return null;
  787                       }
  788                       //to eliminate a race condition, retrieve the entry
  789                       //that is in the hash table under that filename
  790                       jarFile = (JarFile) jarFiles.get(file);
  791                   }
  792                   JarEntry entry = jarFile.getJarEntry(resourceName);
  793                   if (entry != null) {
  794                       return jarFile.getInputStream(entry);
  795                   }
  796               }
  797           } catch (Exception e) {
  798               log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage()
  799                       + " reading resource " + resourceName + " from " + file, Project.MSG_VERBOSE);
  800           }
  801           return null;
  802       }
  803   
  804       /**
  805        * Tests whether or not the parent classloader should be checked for a
  806        * resource before this one. If the resource matches both the "use parent
  807        * classloader first" and the "use this classloader first" lists, the latter
  808        * takes priority.
  809        *
  810        * @param resourceName
  811        *            The name of the resource to check. Must not be
  812        *            <code>null</code>.
  813        *
  814        * @return whether or not the parent classloader should be checked for a
  815        *         resource before this one is.
  816        */
  817       private boolean isParentFirst(String resourceName) {
  818           // default to the global setting and then see
  819           // if this class belongs to a package which has been
  820           // designated to use a specific loader first
  821           // (this one or the parent one)
  822   
  823           // XXX - shouldn't this always return false in isolated mode?
  824   
  825           boolean useParentFirst = parentFirst;
  826   
  827           for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
  828               String packageName = (String) e.nextElement();
  829               if (resourceName.startsWith(packageName)) {
  830                   useParentFirst = true;
  831                   break;
  832               }
  833           }
  834           for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
  835               String packageName = (String) e.nextElement();
  836               if (resourceName.startsWith(packageName)) {
  837                   useParentFirst = false;
  838                   break;
  839               }
  840           }
  841           return useParentFirst;
  842       }
  843   
  844       /**
  845        * Used for isolated resource seaching.
  846        * @return the root classloader of AntClassLoader.
  847        */
  848       private ClassLoader getRootLoader() {
  849           ClassLoader ret = getClass().getClassLoader();
  850           while (ret != null && ret.getParent() != null) {
  851               ret = ret.getParent();
  852           }
  853           return ret;
  854       }
  855   
  856       /**
  857        * Finds the resource with the given name. A resource is
  858        * some data (images, audio, text, etc) that can be accessed by class
  859        * code in a way that is independent of the location of the code.
  860        *
  861        * @param name The name of the resource for which a stream is required.
  862        *             Must not be <code>null</code>.
  863        *
  864        * @return a URL for reading the resource, or <code>null</code> if the
  865        *         resource could not be found or the caller doesn't have
  866        *         adequate privileges to get the resource.
  867        */
  868       public URL getResource(String name) {
  869           // we need to search the components of the path to see if
  870           // we can find the class we want.
  871           URL url = null;
  872           if (isParentFirst(name)) {
  873               url = parent == null ? super.getResource(name) : parent.getResource(name);
  874           }
  875           if (url != null) {
  876               log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
  877           } else {
  878               // try and load from this loader if the parent either didn't find
  879               // it or wasn't consulted.
  880               Enumeration e = pathComponents.elements();
  881               while (e.hasMoreElements() && url == null) {
  882                   File pathComponent = (File) e.nextElement();
  883                   url = getResourceURL(pathComponent, name);
  884                   if (url != null) {
  885                       log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG);
  886                   }
  887               }
  888           }
  889           if (url == null && !isParentFirst(name)) {
  890               // this loader was first but it didn't find it - try the parent
  891               if (ignoreBase) {
  892                   url = getRootLoader() == null ? null : getRootLoader().getResource(name);
  893               } else {
  894                   url = parent == null ? super.getResource(name) : parent.getResource(name);
  895               }
  896               if (url != null) {
  897                   log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
  898               }
  899           }
  900           if (url == null) {
  901               log("Couldn't load Resource " + name, Project.MSG_DEBUG);
  902           }
  903           return url;
  904       }
  905   
  906       /**
  907        * Finds all the resources with the given name. A resource is some
  908        * data (images, audio, text, etc) that can be accessed by class
  909        * code in a way that is independent of the location of the code.
  910        *
  911        * <p>Would override getResources if that wasn't final in Java
  912        * 1.4.</p>
  913        *
  914        * @since Ant 1.8.0
  915        */
  916       public Enumeration/*<URL>*/ getNamedResources(String name)
  917           throws IOException {
  918           return findResources(name, false);
  919       }
  920   
  921       /**
  922        * Returns an enumeration of URLs representing all the resources with the
  923        * given name by searching the class loader's classpath.
  924        *
  925        * @param name The resource name to search for.
  926        *             Must not be <code>null</code>.
  927        * @return an enumeration of URLs for the resources
  928        * @exception IOException if I/O errors occurs (can't happen)
  929        */
  930       protected Enumeration/*<URL>*/ findResources(String name) throws IOException {
  931           return findResources(name, true);
  932       }
  933   
  934       /**
  935        * Returns an enumeration of URLs representing all the resources with the
  936        * given name by searching the class loader's classpath.
  937        *
  938        * @param name The resource name to search for.
  939        *             Must not be <code>null</code>.
  940        * @param parentHasBeenSearched whether ClassLoader.this.parent
  941        * has been searched - will be true if the method is (indirectly)
  942        * called from ClassLoader.getResources
  943        * @return an enumeration of URLs for the resources
  944        * @exception IOException if I/O errors occurs (can't happen)
  945        */
  946       protected Enumeration/*<URL>*/ findResources(String name,
  947                                                    boolean parentHasBeenSearched)
  948           throws IOException {
  949           Enumeration/*<URL>*/ mine = new ResourceEnumeration(name);
  950           Enumeration/*<URL>*/ base;
  951           if (parent != null && (!parentHasBeenSearched || parent != getParent())) {
  952               // Delegate to the parent:
  953               base = parent.getResources(name);
  954               // Note: could cause overlaps in case
  955               // ClassLoader.this.parent has matches and
  956               // parentHasBeenSearched is true
  957           } else {
  958               // ClassLoader.this.parent is already delegated to for example from
  959               // ClassLoader.getResources, no need:
  960               base = new CollectionUtils.EmptyEnumeration();
  961           }
  962           if (isParentFirst(name)) {
  963               // Normal case.
  964               return CollectionUtils.append(base, mine);
  965           }
  966           if (ignoreBase) {
  967               return getRootLoader() == null ? mine : CollectionUtils.append(mine, getRootLoader()
  968                       .getResources(name));
  969           }
  970           // parent last:
  971           return CollectionUtils.append(mine, base);
  972       }
  973   
  974       /**
  975        * Returns the URL of a given resource in the given file which may
  976        * either be a directory or a zip file.
  977        *
  978        * @param file The file (directory or jar) in which to search for
  979        *             the resource. Must not be <code>null</code>.
  980        * @param resourceName The name of the resource for which a stream
  981        *                     is required. Must not be <code>null</code>.
  982        *
  983        * @return a stream to the required resource or <code>null</code> if the
  984        *         resource cannot be found in the given file object.
  985        */
  986       protected URL getResourceURL(File file, String resourceName) {
  987           try {
  988               JarFile jarFile = (JarFile) jarFiles.get(file);
  989               if (jarFile == null && file.isDirectory()) {
  990                   File resource = new File(file, resourceName);
  991   
  992                   if (resource.exists()) {
  993                       try {
  994                           return FILE_UTILS.getFileURL(resource);
  995                       } catch (MalformedURLException ex) {
  996                           return null;
  997                       }
  998                   }
  999               } else {
 1000                   if (jarFile == null) {
 1001                       if (file.exists()) {
 1002                           jarFile = new JarFile(file);
 1003                           jarFiles.put(file, jarFile);
 1004                       } else {
 1005                           return null;
 1006                       }
 1007                       // potential race-condition
 1008                       jarFile = (JarFile) jarFiles.get(file);
 1009                   }
 1010                   JarEntry entry = jarFile.getJarEntry(resourceName);
 1011                   if (entry != null) {
 1012                       try {
 1013                           return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry);
 1014                       } catch (MalformedURLException ex) {
 1015                           return null;
 1016                       }
 1017                   }
 1018               }
 1019           } catch (Exception e) {
 1020               String msg = "Unable to obtain resource from " + file + ": ";
 1021               log(msg + e, Project.MSG_WARN);
 1022               System.err.println(msg);
 1023               e.printStackTrace();
 1024           }
 1025           return null;
 1026       }
 1027   
 1028       /**
 1029        * Loads a class with this class loader.
 1030        *
 1031        * This class attempts to load the class in an order determined by whether
 1032        * or not the class matches the system/loader package lists, with the
 1033        * loader package list taking priority. If the classloader is in isolated
 1034        * mode, failure to load the class in this loader will result in a
 1035        * ClassNotFoundException.
 1036        *
 1037        * @param classname The name of the class to be loaded.
 1038        *                  Must not be <code>null</code>.
 1039        * @param resolve <code>true</code> if all classes upon which this class
 1040        *                depends are to be loaded.
 1041        *
 1042        * @return the required Class object
 1043        *
 1044        * @exception ClassNotFoundException if the requested class does not exist
 1045        * on the system classpath (when not in isolated mode) or this loader's
 1046        * classpath.
 1047        */
 1048       protected synchronized Class loadClass(String classname, boolean resolve)
 1049               throws ClassNotFoundException {
 1050           // 'sync' is needed - otherwise 2 threads can load the same class
 1051           // twice, resulting in LinkageError: duplicated class definition.
 1052           // findLoadedClass avoids that, but without sync it won't work.
 1053   
 1054           Class theClass = findLoadedClass(classname);
 1055           if (theClass != null) {
 1056               return theClass;
 1057           }
 1058           if (isParentFirst(classname)) {
 1059               try {
 1060                   theClass = findBaseClass(classname);
 1061                   log("Class " + classname + " loaded from parent loader " + "(parentFirst)",
 1062                           Project.MSG_DEBUG);
 1063               } catch (ClassNotFoundException cnfe) {
 1064                   theClass = findClass(classname);
 1065                   log("Class " + classname + " loaded from ant loader " + "(parentFirst)",
 1066                           Project.MSG_DEBUG);
 1067               }
 1068           } else {
 1069               try {
 1070                   theClass = findClass(classname);
 1071                   log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG);
 1072               } catch (ClassNotFoundException cnfe) {
 1073                   if (ignoreBase) {
 1074                       throw cnfe;
 1075                   }
 1076                   theClass = findBaseClass(classname);
 1077                   log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG);
 1078               }
 1079           }
 1080           if (resolve) {
 1081               resolveClass(theClass);
 1082           }
 1083           return theClass;
 1084       }
 1085   
 1086       /**
 1087        * Converts the class dot notation to a filesystem equivalent for
 1088        * searching purposes.
 1089        *
 1090        * @param classname The class name in dot format (eg java.lang.Integer).
 1091        *                  Must not be <code>null</code>.
 1092        *
 1093        * @return the classname in filesystem format (eg java/lang/Integer.class)
 1094        */
 1095       private String getClassFilename(String classname) {
 1096           return classname.replace('.', '/') + ".class";
 1097       }
 1098   
 1099       /**
 1100        * Define a class given its bytes
 1101        *
 1102        * @param container the container from which the class data has been read
 1103        *                  may be a directory or a jar/zip file.
 1104        *
 1105        * @param classData the bytecode data for the class
 1106        * @param classname the name of the class
 1107        *
 1108        * @return the Class instance created from the given data
 1109        *
 1110        * @throws IOException if the class data cannot be read.
 1111        */
 1112       protected Class defineClassFromData(File container, byte[] classData, String classname)
 1113               throws IOException {
 1114           definePackage(container, classname);
 1115           ProtectionDomain currentPd = Project.class.getProtectionDomain();
 1116           String classResource = getClassFilename(classname);
 1117           CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container),
 1118                                           getCertificates(container,
 1119                                                           classResource));
 1120           ProtectionDomain classesPd =
 1121               new ProtectionDomain(src, currentPd.getPermissions(),
 1122                                    this,
 1123                                    currentPd.getPrincipals());
 1124           return defineClass(classname, classData, 0, classData.length,
 1125                              classesPd);
 1126       }
 1127   
 1128       /**
 1129        * Define the package information associated with a class.
 1130        *
 1131        * @param container the file containing the class definition.
 1132        * @param className the class name of for which the package information
 1133        *        is to be determined.
 1134        *
 1135        * @exception IOException if the package information cannot be read from the
 1136        *            container.
 1137        */
 1138       protected void definePackage(File container, String className) throws IOException {
 1139           int classIndex = className.lastIndexOf('.');
 1140           if (classIndex == -1) {
 1141               return;
 1142           }
 1143           String packageName = className.substring(0, classIndex);
 1144           if (getPackage(packageName) != null) {
 1145               // already defined
 1146               return;
 1147           }
 1148           // define the package now
 1149           Manifest manifest = getJarManifest(container);
 1150   
 1151           if (manifest == null) {
 1152               definePackage(packageName, null, null, null, null, null, null, null);
 1153           } else {
 1154               definePackage(container, packageName, manifest);
 1155           }
 1156       }
 1157   
 1158       /**
 1159        * Get the manifest from the given jar, if it is indeed a jar and it has a
 1160        * manifest
 1161        *
 1162        * @param container the File from which a manifest is required.
 1163        *
 1164        * @return the jar's manifest or null is the container is not a jar or it
 1165        *         has no manifest.
 1166        *
 1167        * @exception IOException if the manifest cannot be read.
 1168        */
 1169       private Manifest getJarManifest(File container) throws IOException {
 1170           if (container.isDirectory()) {
 1171               return null;
 1172           }
 1173           JarFile jarFile = (JarFile) jarFiles.get(container);
 1174           if (jarFile == null) {
 1175               return null;
 1176           }
 1177           return jarFile.getManifest();
 1178       }
 1179   
 1180       /**
 1181        * Get the certificates for a given jar entry, if it is indeed a jar.
 1182        *
 1183        * @param container the File from which to read the entry
 1184        * @param entry the entry of which the certificates are requested
 1185        *
 1186        * @return the entry's certificates or null is the container is
 1187        *         not a jar or it has no certificates.
 1188        *
 1189        * @exception IOException if the manifest cannot be read.
 1190        */
 1191       private Certificate[] getCertificates(File container, String entry)
 1192           throws IOException {
 1193           if (container.isDirectory()) {
 1194               return null;
 1195           }
 1196           JarFile jarFile = (JarFile) jarFiles.get(container);
 1197           if (jarFile == null) {
 1198               return null;
 1199           }
 1200           JarEntry ent = jarFile.getJarEntry(entry);
 1201           return ent == null ? null : ent.getCertificates();
 1202       }
 1203   
 1204       /**
 1205        * Define the package information when the class comes from a
 1206        * jar with a manifest
 1207        *
 1208        * @param container the jar file containing the manifest
 1209        * @param packageName the name of the package being defined.
 1210        * @param manifest the jar's manifest
 1211        */
 1212       protected void definePackage(File container, String packageName, Manifest manifest) {
 1213           String sectionName = packageName.replace('.', '/') + "/";
 1214   
 1215           String specificationTitle = null;
 1216           String specificationVendor = null;
 1217           String specificationVersion = null;
 1218           String implementationTitle = null;
 1219           String implementationVendor = null;
 1220           String implementationVersion = null;
 1221           String sealedString = null;
 1222           URL sealBase = null;
 1223   
 1224           Attributes sectionAttributes = manifest.getAttributes(sectionName);
 1225           if (sectionAttributes != null) {
 1226               specificationTitle = sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
 1227               specificationVendor = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
 1228               specificationVersion = sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
 1229               implementationTitle = sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
 1230               implementationVendor = sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
 1231               implementationVersion = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
 1232               sealedString = sectionAttributes.getValue(Name.SEALED);
 1233           }
 1234           Attributes mainAttributes = manifest.getMainAttributes();
 1235           if (mainAttributes != null) {
 1236               if (specificationTitle == null) {
 1237                   specificationTitle = mainAttributes.getValue(Name.SPECIFICATION_TITLE);
 1238               }
 1239               if (specificationVendor == null) {
 1240                   specificationVendor = mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
 1241               }
 1242               if (specificationVersion == null) {
 1243                   specificationVersion = mainAttributes.getValue(Name.SPECIFICATION_VERSION);
 1244               }
 1245               if (implementationTitle == null) {
 1246                   implementationTitle = mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
 1247               }
 1248               if (implementationVendor == null) {
 1249                   implementationVendor = mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
 1250               }
 1251               if (implementationVersion == null) {
 1252                   implementationVersion = mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
 1253               }
 1254               if (sealedString == null) {
 1255                   sealedString = mainAttributes.getValue(Name.SEALED);
 1256               }
 1257           }
 1258           if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
 1259               try {
 1260                   sealBase = new URL(FileUtils.getFileUtils().toURI(container.getAbsolutePath()));
 1261               } catch (MalformedURLException e) {
 1262                   // ignore
 1263               }
 1264           }
 1265           definePackage(packageName, specificationTitle, specificationVersion, specificationVendor,
 1266                   implementationTitle, implementationVersion, implementationVendor, sealBase);
 1267       }
 1268   
 1269       /**
 1270        * Reads a class definition from a stream.
 1271        *
 1272        * @param stream The stream from which the class is to be read.
 1273        *               Must not be <code>null</code>.
 1274        * @param classname The name of the class in the stream.
 1275        *                  Must not be <code>null</code>.
 1276        * @param container the file or directory containing the class.
 1277        *
 1278        * @return the Class object read from the stream.
 1279        *
 1280        * @exception IOException if there is a problem reading the class from the
 1281        * stream.
 1282        * @exception SecurityException if there is a security problem while
 1283        * reading the class from the stream.
 1284        */
 1285       private Class getClassFromStream(InputStream stream, String classname, File container)
 1286               throws IOException, SecurityException {
 1287           ByteArrayOutputStream baos = new ByteArrayOutputStream();
 1288           int bytesRead = -1;
 1289           byte[] buffer = new byte[BUFFER_SIZE];
 1290   
 1291           while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
 1292               baos.write(buffer, 0, bytesRead);
 1293           }
 1294           byte[] classData = baos.toByteArray();
 1295           return defineClassFromData(container, classData, classname);
 1296       }
 1297   
 1298       /**
 1299        * Searches for and load a class on the classpath of this class loader.
 1300        *
 1301        * @param name The name of the class to be loaded. Must not be
 1302        *             <code>null</code>.
 1303        *
 1304        * @return the required Class object
 1305        *
 1306        * @exception ClassNotFoundException if the requested class does not exist
 1307        *                                   on this loader's classpath.
 1308        */
 1309       public Class findClass(String name) throws ClassNotFoundException {
 1310           log("Finding class " + name, Project.MSG_DEBUG);
 1311           return findClassInComponents(name);
 1312       }
 1313   
 1314       /**
 1315        * Indicate if the given file is in this loader's path
 1316        *
 1317        * @param component the file which is to be checked
 1318        *
 1319        * @return true if the file is in the class path
 1320        */
 1321       protected boolean isInPath(File component) {
 1322           return pathComponents.contains(component);
 1323       }
 1324   
 1325       /**
 1326        * Finds a class on the given classpath.
 1327        *
 1328        * @param name The name of the class to be loaded. Must not be
 1329        *             <code>null</code>.
 1330        *
 1331        * @return the required Class object
 1332        *
 1333        * @exception ClassNotFoundException if the requested class does not exist
 1334        * on this loader's classpath.
 1335        */
 1336       private Class findClassInComponents(String name)
 1337           throws ClassNotFoundException {
 1338           // we need to search the components of the path to see if
 1339           // we can find the class we want.
 1340           InputStream stream = null;
 1341           String classFilename = getClassFilename(name);
 1342           try {
 1343               Enumeration e = pathComponents.elements();
 1344               while (e.hasMoreElements()) {
 1345                   File pathComponent = (File) e.nextElement();
 1346                   try {
 1347                       stream = getResourceStream(pathComponent, classFilename);
 1348                       if (stream != null) {
 1349                           log("Loaded from " + pathComponent + " "
 1350                               + classFilename, Project.MSG_DEBUG);
 1351                           return getClassFromStream(stream, name, pathComponent);
 1352                       }
 1353                   } catch (SecurityException se) {
 1354                       throw se;
 1355                   } catch (IOException ioe) {
 1356                       // ioe.printStackTrace();
 1357                       log("Exception reading component " + pathComponent + " (reason: "
 1358                               + ioe.getMessage() + ")", Project.MSG_VERBOSE);
 1359                   }
 1360               }
 1361               throw new ClassNotFoundException(name);
 1362           } finally {
 1363               FileUtils.close(stream);
 1364           }
 1365       }
 1366   
 1367       /**
 1368        * Finds a system class (which should be loaded from the same classloader
 1369        * as the Ant core).
 1370        *
 1371        * For JDK 1.1 compatibility, this uses the findSystemClass method if
 1372        * no parent classloader has been specified.
 1373        *
 1374        * @param name The name of the class to be loaded.
 1375        *             Must not be <code>null</code>.
 1376        *
 1377        * @return the required Class object
 1378        *
 1379        * @exception ClassNotFoundException if the requested class does not exist
 1380        * on this loader's classpath.
 1381        */
 1382       private Class findBaseClass(String name) throws ClassNotFoundException {
 1383           return parent == null ? findSystemClass(name) : parent.loadClass(name);
 1384       }
 1385   
 1386       /**
 1387        * Cleans up any resources held by this classloader. Any open archive
 1388        * files are closed.
 1389        */
 1390       public synchronized void cleanup() {
 1391           for (Enumeration e = jarFiles.elements(); e.hasMoreElements();) {
 1392               JarFile jarFile = (JarFile) e.nextElement();
 1393               try {
 1394                   jarFile.close();
 1395               } catch (IOException ioe) {
 1396                   // ignore
 1397               }
 1398           }
 1399           jarFiles = new Hashtable();
 1400           if (project != null) {
 1401               project.removeBuildListener(this);
 1402           }
 1403           project = null;
 1404       }
 1405   
 1406       /**
 1407        * Gets the parent as has been specified in the constructor or via
 1408        * setParent.
 1409        *
 1410        * @since Ant 1.8.0
 1411        */
 1412       public ClassLoader getConfiguredParent() {
 1413           return parent;
 1414       }
 1415   
 1416       /**
 1417        * Empty implementation to satisfy the BuildListener interface.
 1418        *
 1419        * @param event the buildStarted event
 1420        */
 1421       public void buildStarted(BuildEvent event) {
 1422           // Not significant for the class loader.
 1423       }
 1424   
 1425       /**
 1426        * Cleans up any resources held by this classloader at the end
 1427        * of a build.
 1428        *
 1429        * @param event the buildFinished event
 1430        */
 1431       public void buildFinished(BuildEvent event) {
 1432           cleanup();
 1433       }
 1434   
 1435       /**
 1436        * Cleans up any resources held by this classloader at the end of
 1437        * a subbuild if it has been created for the subbuild's project
 1438        * instance.
 1439        *
 1440        * @param event the buildFinished event
 1441        *
 1442        * @since Ant 1.6.2
 1443        */
 1444       public void subBuildFinished(BuildEvent event) {
 1445           if (event.getProject() == project) {
 1446               cleanup();
 1447           }
 1448       }
 1449   
 1450       /**
 1451        * Empty implementation to satisfy the BuildListener interface.
 1452        *
 1453        * @param event the buildStarted event
 1454        *
 1455        * @since Ant 1.6.2
 1456        */
 1457       public void subBuildStarted(BuildEvent event) {
 1458           // Not significant for the class loader.
 1459       }
 1460   
 1461       /**
 1462        * Empty implementation to satisfy the BuildListener interface.
 1463        *
 1464        * @param event the targetStarted event
 1465        */
 1466       public void targetStarted(BuildEvent event) {
 1467           // Not significant for the class loader.
 1468       }
 1469   
 1470       /**
 1471        * Empty implementation to satisfy the BuildListener interface.
 1472        *
 1473        * @param event the targetFinished event
 1474        */
 1475       public void targetFinished(BuildEvent event) {
 1476           // Not significant for the class loader.
 1477       }
 1478   
 1479       /**
 1480        * Empty implementation to satisfy the BuildListener interface.
 1481        *
 1482        * @param event the taskStarted event
 1483        */
 1484       public void taskStarted(BuildEvent event) {
 1485           // Not significant for the class loader.
 1486       }
 1487   
 1488       /**
 1489        * Empty implementation to satisfy the BuildListener interface.
 1490        *
 1491        * @param event the taskFinished event
 1492        */
 1493       public void taskFinished(BuildEvent event) {
 1494           // Not significant for the class loader.
 1495       }
 1496   
 1497       /**
 1498        * Empty implementation to satisfy the BuildListener interface.
 1499        *
 1500        * @param event the messageLogged event
 1501        */
 1502       public void messageLogged(BuildEvent event) {
 1503           // Not significant for the class loader.
 1504       }
 1505   
 1506       /**
 1507        * add any libraries that come with different java versions
 1508        * here
 1509        */
 1510       public void addJavaLibraries() {
 1511           Vector packages = JavaEnvUtils.getJrePackages();
 1512           Enumeration e = packages.elements();
 1513           while (e.hasMoreElements()) {
 1514               String packageName = (String) e.nextElement();
 1515               addSystemPackageRoot(packageName);
 1516           }
 1517       }
 1518   
 1519       /**
 1520        * Returns a <code>String</code> representing this loader.
 1521        * @return the path that this classloader has.
 1522        */
 1523       public String toString() {
 1524           return "AntClassLoader[" + getClasspath() + "]";
 1525       }
 1526   
 1527       private static Class subClassToLoad = null;
 1528       private static final Class[] CONSTRUCTOR_ARGS = new Class[] {
 1529           ClassLoader.class, Project.class, Path.class, Boolean.TYPE
 1530       };
 1531   
 1532       static {
 1533           if (JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) {
 1534               try {
 1535                   subClassToLoad =
 1536                       Class.forName("org.apache.tools.ant.loader.AntClassLoader5");
 1537               } catch (ClassNotFoundException e) {
 1538                   // this is Java5 but the installation is lacking our subclass
 1539               }
 1540           }
 1541       }
 1542   
 1543       /**
 1544        * Factory method
 1545        */
 1546       public static AntClassLoader newAntClassLoader(ClassLoader parent,
 1547                                                      Project project,
 1548                                                      Path path,
 1549                                                      boolean parentFirst) {
 1550           if (subClassToLoad != null) {
 1551               return (AntClassLoader)
 1552                   ReflectUtil.newInstance(subClassToLoad,
 1553                                           CONSTRUCTOR_ARGS,
 1554                                           new Object[] {
 1555                                               parent, project, path,
 1556                                               Boolean.valueOf(parentFirst)
 1557                                           });
 1558           }
 1559           return new AntClassLoader(parent, project, path, parentFirst);
 1560       }
 1561   
 1562   }

Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [javadoc | source]