src/share/classes/sun/misc/URLClassPath.java

Print this page
rev 10187 : mq
   1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.misc;
  27 
  28 import java.util.*;
  29 import java.util.jar.JarFile;
  30 import sun.misc.JarIndex;
  31 import sun.misc.InvalidJarIndexException;
  32 import sun.net.www.ParseUtil;
  33 import java.util.zip.ZipEntry;
  34 import java.util.jar.JarEntry;
  35 import java.util.jar.Manifest;
  36 import java.util.jar.Attributes;
  37 import java.util.jar.Attributes.Name;
  38 import java.net.JarURLConnection;
  39 import java.net.MalformedURLException;
  40 import java.net.URL;

  41 import java.net.URLConnection;
  42 import java.net.HttpURLConnection;
  43 import java.net.URLStreamHandler;
  44 import java.net.URLStreamHandlerFactory;
  45 import java.io.*;
  46 import java.security.AccessController;
  47 import java.security.AccessControlException;
  48 import java.security.CodeSigner;
  49 import java.security.Permission;
  50 import java.security.PrivilegedAction;
  51 import java.security.PrivilegedExceptionAction;
  52 import java.security.cert.Certificate;
  53 import sun.misc.FileURLMapper;
  54 import sun.net.util.URLUtil;

  55 
  56 /**
  57  * This class is used to maintain a search path of URLs for loading classes
  58  * and resources from both JAR files and directories.
  59  *
  60  * @author  David Connelly
  61  */
  62 public class URLClassPath {
  63     final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
  64     final static String JAVA_VERSION;
  65     private static final boolean DEBUG;

  66     private static final boolean DISABLE_JAR_CHECKING;
  67 
  68     static {
  69         JAVA_VERSION = java.security.AccessController.doPrivileged(
  70             new sun.security.action.GetPropertyAction("java.version"));
  71         DEBUG        = (java.security.AccessController.doPrivileged(
  72             new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.debug")) != null);


  73         String p = java.security.AccessController.doPrivileged(
  74             new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.disableJarChecking"));
  75         DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
  76     }
  77 
  78     /* The original search path of URLs. */
  79     private ArrayList<URL> path = new ArrayList<URL>();
  80 
  81     /* The stack of unopened URLs */
  82     Stack<URL> urls = new Stack<URL>();
  83 
  84     /* The resulting search path of Loaders */
  85     ArrayList<Loader> loaders = new ArrayList<Loader>();
  86 
  87     /* Map of each URL opened to its corresponding Loader */
  88     HashMap<String, Loader> lmap = new HashMap<String, Loader>();
  89 
  90     /* The jar protocol handler to use when creating new URLs */
  91     private URLStreamHandler jarHandler;
  92 
  93     /* Whether this URLClassLoader has been closed yet */
  94     private boolean closed = false;


 132         closed = true;
 133         return result;
 134     }
 135 
 136     /**
 137      * Appends the specified URL to the search path of directory and JAR
 138      * file URLs from which to load classes and resources.
 139      * <p>
 140      * If the URL specified is null or is already in the list of
 141      * URLs, then invoking this method has no effect.
 142      */
 143     public synchronized void addURL(URL url) {
 144         if (closed)
 145             return;
 146         synchronized (urls) {
 147             if (url == null || path.contains(url))
 148                 return;
 149 
 150             urls.add(0, url);
 151             path.add(url);






 152         }
 153     }
 154 
 155     /**
 156      * Returns the original search path of URLs.
 157      */
 158     public URL[] getURLs() {
 159         synchronized (urls) {
 160             return path.toArray(new URL[path.size()]);
 161         }
 162     }
 163 
 164     /**
 165      * Finds the resource with the specified name on the URL search path
 166      * or null if not found or security check fails.
 167      *
 168      * @param name      the name of the resource
 169      * @param check     whether to perform a security check
 170      * @return a <code>URL</code> for the resource, or <code>null</code>
 171      * if the resource could not be found.
 172      */
 173     public URL findResource(String name, boolean check) {
 174         Loader loader;
 175         for (int i = 0; (loader = getLoader(i)) != null; i++) {

 176             URL url = loader.findResource(name, check);
 177             if (url != null) {
 178                 return url;
 179             }
 180         }
 181         return null;
 182     }
 183 
 184     /**
 185      * Finds the first Resource on the URL search path which has the specified
 186      * name. Returns null if no Resource could be found.
 187      *
 188      * @param name the name of the Resource
 189      * @param check     whether to perform a security check
 190      * @return the Resource, or null if not found
 191      */
 192     public Resource getResource(String name, boolean check) {
 193         if (DEBUG) {
 194             System.err.println("URLClassPath.getResource(\"" + name + "\")");
 195         }
 196 
 197         Loader loader;
 198         for (int i = 0; (loader = getLoader(i)) != null; i++) {

 199             Resource res = loader.getResource(name, check);
 200             if (res != null) {
 201                 return res;
 202             }
 203         }
 204         return null;
 205     }
 206 
 207     /**
 208      * Finds all resources on the URL search path with the given name.
 209      * Returns an enumeration of the URL objects.
 210      *
 211      * @param name the resource name
 212      * @return an Enumeration of all the urls having the specified name
 213      */
 214     public Enumeration<URL> findResources(final String name,
 215                                      final boolean check) {
 216         return new Enumeration<URL>() {
 217             private int index = 0;

 218             private URL url = null;
 219 
 220             private boolean next() {
 221                 if (url != null) {
 222                     return true;
 223                 } else {
 224                     Loader loader;
 225                     while ((loader = getLoader(index++)) != null) {
 226                         url = loader.findResource(name, check);
 227                         if (url != null) {
 228                             return true;
 229                         }
 230                     }
 231                     return false;
 232                 }
 233             }
 234 
 235             public boolean hasMoreElements() {
 236                 return next();
 237             }
 238 
 239             public URL nextElement() {
 240                 if (!next()) {
 241                     throw new NoSuchElementException();
 242                 }
 243                 URL u = url;
 244                 url = null;
 245                 return u;
 246             }
 247         };
 248     }
 249 
 250     public Resource getResource(String name) {
 251         return getResource(name, true);
 252     }
 253 
 254     /**
 255      * Finds all resources on the URL search path with the given name.
 256      * Returns an enumeration of the Resource objects.
 257      *
 258      * @param name the resource name
 259      * @return an Enumeration of all the resources having the specified name
 260      */
 261     public Enumeration<Resource> getResources(final String name,
 262                                     final boolean check) {
 263         return new Enumeration<Resource>() {
 264             private int index = 0;

 265             private Resource res = null;
 266 
 267             private boolean next() {
 268                 if (res != null) {
 269                     return true;
 270                 } else {
 271                     Loader loader;
 272                     while ((loader = getLoader(index++)) != null) {
 273                         res = loader.getResource(name, check);
 274                         if (res != null) {
 275                             return true;
 276                         }
 277                     }
 278                     return false;
 279                 }
 280             }
 281 
 282             public boolean hasMoreElements() {
 283                 return next();
 284             }
 285 
 286             public Resource nextElement() {
 287                 if (!next()) {
 288                     throw new NoSuchElementException();
 289                 }
 290                 Resource r = res;
 291                 res = null;
 292                 return r;
 293             }
 294         };
 295     }
 296 
 297     public Enumeration<Resource> getResources(final String name) {
 298         return getResources(name, true);
 299     }
 300 

















































































































































 301     /*
 302      * Returns the Loader at the specified position in the URL search
 303      * path. The URLs are opened and expanded as needed. Returns null
 304      * if the specified index is out of range.
 305      */
 306      private synchronized Loader getLoader(int index) {
 307         if (closed) {
 308             return null;
 309         }
 310          // Expand URL search path until the request can be satisfied
 311          // or the URL stack is empty.
 312         while (loaders.size() < index + 1) {
 313             // Pop the next URL from the URL stack
 314             URL url;
 315             synchronized (urls) {
 316                 if (urls.empty()) {
 317                     return null;
 318                 } else {
 319                     url = urls.pop();
 320                 }


 324             // but is referenced by a JAR index.)
 325             String urlNoFragString = URLUtil.urlNoFragString(url);
 326             if (lmap.containsKey(urlNoFragString)) {
 327                 continue;
 328             }
 329             // Otherwise, create a new Loader for the URL.
 330             Loader loader;
 331             try {
 332                 loader = getLoader(url);
 333                 // If the loader defines a local class path then add the
 334                 // URLs to the list of URLs to be opened.
 335                 URL[] urls = loader.getClassPath();
 336                 if (urls != null) {
 337                     push(urls);
 338                 }
 339             } catch (IOException e) {
 340                 // Silently ignore for now...
 341                 continue;
 342             }
 343             // Finally, add the Loader to the search path.

 344             loaders.add(loader);
 345             lmap.put(urlNoFragString, loader);



 346         }
 347         return loaders.get(index);
 348     }
 349 
 350     /*
 351      * Returns the Loader for the specified base URL.
 352      */
 353     private Loader getLoader(final URL url) throws IOException {
 354         try {
 355             return java.security.AccessController.doPrivileged(
 356                 new java.security.PrivilegedExceptionAction<Loader>() {
 357                 public Loader run() throws IOException {
 358                     String file = url.getFile();
 359                     if (file != null && file.endsWith("/")) {
 360                         if ("file".equals(url.getProtocol())) {
 361                             return new FileLoader(url);
 362                         } else {
 363                             return new Loader(url);
 364                         }
 365                     } else {


   1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.misc;
  27 
  28 import java.util.*;
  29 import java.util.jar.JarFile;
  30 import sun.misc.JarIndex;
  31 import sun.misc.InvalidJarIndexException;
  32 import sun.net.www.ParseUtil;
  33 import java.util.zip.ZipEntry;
  34 import java.util.jar.JarEntry;
  35 import java.util.jar.Manifest;
  36 import java.util.jar.Attributes;
  37 import java.util.jar.Attributes.Name;
  38 import java.net.JarURLConnection;
  39 import java.net.MalformedURLException;
  40 import java.net.URL;
  41 import java.net.URLClassLoader;
  42 import java.net.URLConnection;
  43 import java.net.HttpURLConnection;
  44 import java.net.URLStreamHandler;
  45 import java.net.URLStreamHandlerFactory;
  46 import java.io.*;
  47 import java.security.AccessController;
  48 import java.security.AccessControlException;
  49 import java.security.CodeSigner;
  50 import java.security.Permission;
  51 import java.security.PrivilegedAction;
  52 import java.security.PrivilegedExceptionAction;
  53 import java.security.cert.Certificate;
  54 import sun.misc.FileURLMapper;
  55 import sun.net.util.URLUtil;
  56 import sun.security.action.GetPropertyAction;
  57 
  58 /**
  59  * This class is used to maintain a search path of URLs for loading classes
  60  * and resources from both JAR files and directories.
  61  *
  62  * @author  David Connelly
  63  */
  64 public class URLClassPath {
  65     final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
  66     final static String JAVA_VERSION;
  67     private static final boolean DEBUG;
  68     private static final boolean DEBUG_LOOKUP_CACHE;
  69     private static final boolean DISABLE_JAR_CHECKING;
  70 
  71     static {
  72         JAVA_VERSION = java.security.AccessController.doPrivileged(
  73             new GetPropertyAction("java.version"));
  74         DEBUG        = (java.security.AccessController.doPrivileged(
  75             new GetPropertyAction("sun.misc.URLClassPath.debug")) != null);
  76         DEBUG_LOOKUP_CACHE = (java.security.AccessController.doPrivileged(
  77             new GetPropertyAction("sun.misc.URLClassPath.debugLookupCache")) != null);
  78         String p = java.security.AccessController.doPrivileged(
  79             new GetPropertyAction("sun.misc.URLClassPath.disableJarChecking"));
  80         DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
  81     }
  82 
  83     /* The original search path of URLs. */
  84     private ArrayList<URL> path = new ArrayList<URL>();
  85 
  86     /* The stack of unopened URLs */
  87     Stack<URL> urls = new Stack<URL>();
  88 
  89     /* The resulting search path of Loaders */
  90     ArrayList<Loader> loaders = new ArrayList<Loader>();
  91 
  92     /* Map of each URL opened to its corresponding Loader */
  93     HashMap<String, Loader> lmap = new HashMap<String, Loader>();
  94 
  95     /* The jar protocol handler to use when creating new URLs */
  96     private URLStreamHandler jarHandler;
  97 
  98     /* Whether this URLClassLoader has been closed yet */
  99     private boolean closed = false;


 137         closed = true;
 138         return result;
 139     }
 140 
 141     /**
 142      * Appends the specified URL to the search path of directory and JAR
 143      * file URLs from which to load classes and resources.
 144      * <p>
 145      * If the URL specified is null or is already in the list of
 146      * URLs, then invoking this method has no effect.
 147      */
 148     public synchronized void addURL(URL url) {
 149         if (closed)
 150             return;
 151         synchronized (urls) {
 152             if (url == null || path.contains(url))
 153                 return;
 154 
 155             urls.add(0, url);
 156             path.add(url);
 157 
 158             if (lookupCacheURLs != null) {
 159                 // The lookup cache is no longer valid, since getLookupCache()
 160                 // does not consider the newly added url.
 161                 disableAllLookupCaches();
 162             }
 163         }
 164     }
 165 
 166     /**
 167      * Returns the original search path of URLs.
 168      */
 169     public URL[] getURLs() {
 170         synchronized (urls) {
 171             return path.toArray(new URL[path.size()]);
 172         }
 173     }
 174 
 175     /**
 176      * Finds the resource with the specified name on the URL search path
 177      * or null if not found or security check fails.
 178      *
 179      * @param name      the name of the resource
 180      * @param check     whether to perform a security check
 181      * @return a <code>URL</code> for the resource, or <code>null</code>
 182      * if the resource could not be found.
 183      */
 184     public URL findResource(String name, boolean check) {
 185         Loader loader;
 186         int[] cache = getLookupCache(name);
 187         for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) {
 188             URL url = loader.findResource(name, check);
 189             if (url != null) {
 190                 return url;
 191             }
 192         }
 193         return null;
 194     }
 195 
 196     /**
 197      * Finds the first Resource on the URL search path which has the specified
 198      * name. Returns null if no Resource could be found.
 199      *
 200      * @param name the name of the Resource
 201      * @param check     whether to perform a security check
 202      * @return the Resource, or null if not found
 203      */
 204     public Resource getResource(String name, boolean check) {
 205         if (DEBUG) {
 206             System.err.println("URLClassPath.getResource(\"" + name + "\")");
 207         }
 208 
 209         Loader loader;
 210         int[] cache = getLookupCache(name);
 211         for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) {
 212             Resource res = loader.getResource(name, check);
 213             if (res != null) {
 214                 return res;
 215             }
 216         }
 217         return null;
 218     }
 219 
 220     /**
 221      * Finds all resources on the URL search path with the given name.
 222      * Returns an enumeration of the URL objects.
 223      *
 224      * @param name the resource name
 225      * @return an Enumeration of all the urls having the specified name
 226      */
 227     public Enumeration<URL> findResources(final String name,
 228                                      final boolean check) {
 229         return new Enumeration<URL>() {
 230             private int index = 0;
 231             private int[] cache = getLookupCache(name);
 232             private URL url = null;
 233 
 234             private boolean next() {
 235                 if (url != null) {
 236                     return true;
 237                 } else {
 238                     Loader loader;
 239                     while ((loader = getNextLoader(cache, index++)) != null) {
 240                         url = loader.findResource(name, check);
 241                         if (url != null) {
 242                             return true;
 243                         }
 244                     }
 245                     return false;
 246                 }
 247             }
 248 
 249             public boolean hasMoreElements() {
 250                 return next();
 251             }
 252 
 253             public URL nextElement() {
 254                 if (!next()) {
 255                     throw new NoSuchElementException();
 256                 }
 257                 URL u = url;
 258                 url = null;
 259                 return u;
 260             }
 261         };
 262     }
 263 
 264     public Resource getResource(String name) {
 265         return getResource(name, true);
 266     }
 267 
 268     /**
 269      * Finds all resources on the URL search path with the given name.
 270      * Returns an enumeration of the Resource objects.
 271      *
 272      * @param name the resource name
 273      * @return an Enumeration of all the resources having the specified name
 274      */
 275     public Enumeration<Resource> getResources(final String name,
 276                                     final boolean check) {
 277         return new Enumeration<Resource>() {
 278             private int index = 0;
 279             private int[] cache = getLookupCache(name);
 280             private Resource res = null;
 281 
 282             private boolean next() {
 283                 if (res != null) {
 284                     return true;
 285                 } else {
 286                     Loader loader;
 287                     while ((loader = getNextLoader(cache, index++)) != null) {
 288                         res = loader.getResource(name, check);
 289                         if (res != null) {
 290                             return true;
 291                         }
 292                     }
 293                     return false;
 294                 }
 295             }
 296 
 297             public boolean hasMoreElements() {
 298                 return next();
 299             }
 300 
 301             public Resource nextElement() {
 302                 if (!next()) {
 303                     throw new NoSuchElementException();
 304                 }
 305                 Resource r = res;
 306                 res = null;
 307                 return r;
 308             }
 309         };
 310     }
 311 
 312     public Enumeration<Resource> getResources(final String name) {
 313         return getResources(name, true);
 314     }
 315 
 316     private static volatile boolean lookupCacheEnabled
 317         = "true".equals(VM.getSavedProperty("sun.cds.enableSharedLookupCache"));
 318     private URL[] lookupCacheURLs;
 319     private ClassLoader lookupCacheLoader;
 320 
 321     synchronized void initLookupCache(ClassLoader loader) {
 322         if ((lookupCacheURLs = getLookupCacheURLs(loader)) != null) {
 323             lookupCacheLoader = loader;
 324         } else {
 325             // This JVM instance does not support lookup cache.
 326             disableAllLookupCaches();
 327         }
 328     }
 329 
 330     static void disableAllLookupCaches() {
 331         lookupCacheEnabled = false;
 332     }
 333 
 334     private static native URL[] getLookupCacheURLs(ClassLoader loader);
 335     private static native int[] getLookupCacheForClassLoader(ClassLoader loader,
 336                                                              String name);
 337     private static native boolean knownToNotExist0(ClassLoader loader,
 338                                                    String className);
 339 
 340     synchronized boolean knownToNotExist(String className) {
 341         if (lookupCacheURLs != null && lookupCacheEnabled) {
 342             return knownToNotExist0(lookupCacheLoader, className);
 343         }
 344 
 345         // Don't know if this class exists or not -- need to do a full search.
 346         return false;
 347     }
 348 
 349     /**
 350      * Returns an array of the index to lookupCacheURLs that may
 351      * contain the specified resource. The values in the returned
 352      * array are in strictly ascending order and must be a valid index
 353      * to lookupCacheURLs array.
 354      *
 355      * This method returns an empty array if the specified resource
 356      * cannot be found in this URLClassPath. If there is no lookup
 357      * cache or it's disabled, this method returns null and the lookup
 358      * should search the entire classpath.
 359      *
 360      * Example: if lookupCacheURLs contains {a.jar, b.jar, c.jar, d.jar}
 361      * and package "foo" only exists in a.jar and c.jar,
 362      * getLookupCache("foo/Bar.class") will return {0, 2}
 363      *
 364      * @param name the resource name
 365      * @return an array of the index to lookupCacheURLs that may contain the
 366      *         specified resource; or null if no lookup cache is used.
 367      */
 368     private synchronized int[] getLookupCache(String name) {
 369         if (lookupCacheURLs == null || !lookupCacheEnabled) {
 370             return null;
 371         }
 372 
 373         int[] cache = getLookupCacheForClassLoader(lookupCacheLoader, name);
 374         if (cache != null && cache.length > 0) {
 375             int maxindex = cache[cache.length - 1]; // cache[] is strictly ascending.
 376             if (!ensureLoaderOpened(maxindex)) {
 377                 if (DEBUG_LOOKUP_CACHE) {
 378                     System.out.println("Expanded loaders FAILED " +
 379                                        loaders.size() + " for maxindex=" + maxindex);
 380                 }
 381                 return null;
 382             }
 383         }
 384 
 385         return cache;
 386     }
 387 
 388     private boolean ensureLoaderOpened(int index) {
 389         if (loaders.size() <= index) {
 390             // Open all Loaders up to, and including, index
 391             if (getLoader(index) == null) {
 392                 return false;
 393             }
 394             if (!lookupCacheEnabled) {
 395                 // cache was invalidated as the result of the above call.
 396                 return false;
 397             }
 398             if (DEBUG_LOOKUP_CACHE) {
 399                 System.out.println("Expanded loaders " + loaders.size() +
 400                                    " to index=" + index);
 401             }
 402         }
 403         return true;
 404     }
 405 
 406     /*
 407      * The CLASS-PATH attribute was expanded by the VM when building
 408      * the resource lookup cache in the same order as the getLoader
 409      * method does. This method validates if the URL from the lookup
 410      * cache matches the URL of the Loader at the given index;
 411      * otherwise, this method disables the lookup cache.
 412      */
 413     private synchronized void validateLookupCache(int index,
 414                                                   String urlNoFragString) {
 415         if (lookupCacheURLs != null && lookupCacheEnabled) {
 416             if (index < lookupCacheURLs.length &&
 417                 urlNoFragString.equals(
 418                     URLUtil.urlNoFragString(lookupCacheURLs[index]))) {
 419                 return;
 420             }
 421             if (DEBUG || DEBUG_LOOKUP_CACHE) {
 422                 System.out.println("WARNING: resource lookup cache invalidated "
 423                                    + "for lookupCacheLoader at " + index);
 424             }
 425             disableAllLookupCaches();
 426         }
 427     }
 428 
 429     /**
 430      * Returns the next Loader that may contain the resource to
 431      * lookup. If the given cache is null, return loaders.get(index)
 432      * that may be lazily created; otherwise, cache[index] is the next
 433      * Loader that may contain the resource to lookup and so returns
 434      * loaders.get(cache[index]).
 435      *
 436      * If cache is non-null, loaders.get(cache[index]) must be present.
 437      *
 438      * @param cache lookup cache. If null, search the entire class path
 439      * @param index index to the given cache array; or to the loaders list.
 440      */
 441     private synchronized Loader getNextLoader(int[] cache, int index) {
 442         if (closed) {
 443             return null;
 444         }
 445         if (cache != null) {
 446             if (index < cache.length) {
 447                 Loader loader = loaders.get(cache[index]);
 448                 if (DEBUG_LOOKUP_CACHE) {
 449                     System.out.println("HASCACHE: Loading from : " + cache[index]
 450                                        + " = " + loader.getBaseURL());
 451                 }
 452                 return loader;
 453             } else {
 454                 return null; // finished iterating over cache[]
 455             }
 456         } else {
 457             return getLoader(index);
 458         }
 459     }
 460 
 461     /*
 462      * Returns the Loader at the specified position in the URL search
 463      * path. The URLs are opened and expanded as needed. Returns null
 464      * if the specified index is out of range.
 465      */
 466      private synchronized Loader getLoader(int index) {
 467         if (closed) {
 468             return null;
 469         }
 470          // Expand URL search path until the request can be satisfied
 471          // or the URL stack is empty.
 472         while (loaders.size() < index + 1) {
 473             // Pop the next URL from the URL stack
 474             URL url;
 475             synchronized (urls) {
 476                 if (urls.empty()) {
 477                     return null;
 478                 } else {
 479                     url = urls.pop();
 480                 }


 484             // but is referenced by a JAR index.)
 485             String urlNoFragString = URLUtil.urlNoFragString(url);
 486             if (lmap.containsKey(urlNoFragString)) {
 487                 continue;
 488             }
 489             // Otherwise, create a new Loader for the URL.
 490             Loader loader;
 491             try {
 492                 loader = getLoader(url);
 493                 // If the loader defines a local class path then add the
 494                 // URLs to the list of URLs to be opened.
 495                 URL[] urls = loader.getClassPath();
 496                 if (urls != null) {
 497                     push(urls);
 498                 }
 499             } catch (IOException e) {
 500                 // Silently ignore for now...
 501                 continue;
 502             }
 503             // Finally, add the Loader to the search path.
 504             validateLookupCache(loaders.size(), urlNoFragString);
 505             loaders.add(loader);
 506             lmap.put(urlNoFragString, loader);
 507         }
 508         if (DEBUG_LOOKUP_CACHE) {
 509             System.out.println("NOCACHE: Loading from : " + index );
 510         }
 511         return loaders.get(index);
 512     }
 513 
 514     /*
 515      * Returns the Loader for the specified base URL.
 516      */
 517     private Loader getLoader(final URL url) throws IOException {
 518         try {
 519             return java.security.AccessController.doPrivileged(
 520                 new java.security.PrivilegedExceptionAction<Loader>() {
 521                 public Loader run() throws IOException {
 522                     String file = url.getFile();
 523                     if (file != null && file.endsWith("/")) {
 524                         if ("file".equals(url.getProtocol())) {
 525                             return new FileLoader(url);
 526                         } else {
 527                             return new Loader(url);
 528                         }
 529                     } else {