< prev index next >

src/java.base/share/classes/jdk/internal/loader/URLClassPath.java

Print this page
8198484: URLClassPath should use an ArrayDeque instead of a Stack
Reviewed-by: alanb, mchung, plevart, psandoz


  29 import java.io.File;
  30 import java.io.FileInputStream;
  31 import java.io.FileNotFoundException;
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.net.HttpURLConnection;
  35 import java.net.JarURLConnection;
  36 import java.net.MalformedURLException;
  37 import java.net.URL;
  38 import java.net.URLConnection;
  39 import java.net.URLStreamHandler;
  40 import java.net.URLStreamHandlerFactory;
  41 import java.security.AccessControlContext;
  42 import java.security.AccessControlException;
  43 import java.security.AccessController;
  44 import java.security.CodeSigner;
  45 import java.security.Permission;
  46 import java.security.PrivilegedActionException;
  47 import java.security.PrivilegedExceptionAction;
  48 import java.security.cert.Certificate;

  49 import java.util.ArrayList;
  50 import java.util.Arrays;
  51 import java.util.Collections;
  52 import java.util.Enumeration;
  53 import java.util.HashMap;
  54 import java.util.HashSet;
  55 import java.util.LinkedList;
  56 import java.util.List;
  57 import java.util.NoSuchElementException;
  58 import java.util.Properties;
  59 import java.util.Set;
  60 import java.util.Stack;
  61 import java.util.StringTokenizer;
  62 import java.util.jar.JarFile;
  63 import java.util.zip.ZipEntry;
  64 import java.util.jar.JarEntry;
  65 import java.util.jar.Manifest;
  66 import java.util.jar.Attributes;
  67 import java.util.jar.Attributes.Name;
  68 import java.util.zip.ZipFile;
  69 
  70 import jdk.internal.misc.JavaNetURLAccess;
  71 import jdk.internal.misc.JavaUtilZipFileAccess;
  72 import jdk.internal.misc.SharedSecrets;
  73 import jdk.internal.util.jar.InvalidJarIndexError;
  74 import jdk.internal.util.jar.JarIndex;
  75 import sun.net.util.URLUtil;
  76 import sun.net.www.ParseUtil;
  77 import sun.security.action.GetPropertyAction;
  78 
  79 /**
  80  * This class is used to maintain a search path of URLs for loading classes


  84  */
  85 public class URLClassPath {
  86     private static final String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
  87     private static final String JAVA_VERSION;
  88     private static final boolean DEBUG;
  89     private static final boolean DISABLE_JAR_CHECKING;
  90     private static final boolean DISABLE_ACC_CHECKING;
  91 
  92     static {
  93         Properties props = GetPropertyAction.privilegedGetProperties();
  94         JAVA_VERSION = props.getProperty("java.version");
  95         DEBUG = (props.getProperty("sun.misc.URLClassPath.debug") != null);
  96         String p = props.getProperty("sun.misc.URLClassPath.disableJarChecking");
  97         DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
  98 
  99         p = props.getProperty("jdk.net.URLClassPath.disableRestrictedPermissions");
 100         DISABLE_ACC_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
 101     }
 102 
 103     /* The original search path of URLs. */
 104     private final List<URL> path;
 105 
 106     /* The stack of unopened URLs */
 107     private final Stack<URL> unopenedUrls = new Stack<>();
 108 
 109     /* The resulting search path of Loaders */
 110     private final ArrayList<Loader> loaders = new ArrayList<>();
 111 
 112     /* Map of each URL opened to its corresponding Loader */
 113     private final HashMap<String, Loader> lmap = new HashMap<>();
 114 
 115     /* The jar protocol handler to use when creating new URLs */
 116     private final URLStreamHandler jarHandler;
 117 
 118     /* Whether this URLClassLoader has been closed yet */
 119     private boolean closed = false;
 120 
 121     /* The context to be used when loading classes and resources.  If non-null
 122      * this is the context that was captured during the creation of the
 123      * URLClassLoader. null implies no additional security restrictions. */
 124     private final AccessControlContext acc;
 125 
 126     /**
 127      * Creates a new URLClassPath for the given URLs. The URLs will be
 128      * searched in the order specified for classes and resources. A URL
 129      * ending with a '/' is assumed to refer to a directory. Otherwise,
 130      * the URL is assumed to refer to a JAR file.
 131      *
 132      * @param urls the directory and JAR file URLs to search for classes
 133      *        and resources
 134      * @param factory the URLStreamHandlerFactory to use when creating new URLs
 135      * @param acc the context to be used when loading classes and resources, may
 136      *            be null
 137      */
 138     public URLClassPath(URL[] urls,
 139                         URLStreamHandlerFactory factory,
 140                         AccessControlContext acc) {
 141         List<URL> path = new ArrayList<>(urls.length);

 142         for (URL url : urls) {
 143             path.add(url);

 144         }
 145         this.path = path;
 146         push(urls);

 147         if (factory != null) {
 148             jarHandler = factory.createURLStreamHandler("jar");
 149         } else {
 150             jarHandler = null;
 151         }
 152         if (DISABLE_ACC_CHECKING)
 153             this.acc = null;
 154         else
 155             this.acc = acc;
 156     }
 157 
 158     public URLClassPath(URL[] urls, AccessControlContext acc) {
 159         this(urls, null, acc);
 160     }
 161 
 162     /**
 163      * Constructs a URLClassPath from a class path string.
 164      *
 165      * @param cp the class path string
 166      * @param skipEmptyElements indicates if empty elements are ignored or
 167      *        treated as the current working directory
 168      *
 169      * @apiNote Used to create the application class path.
 170      */
 171     URLClassPath(String cp, boolean skipEmptyElements) {
 172         List<URL> path = new ArrayList<>();
 173         if (cp != null) {
 174             // map each element of class path to a file URL
 175             int off = 0;
 176             int next;
 177             while ((next = cp.indexOf(File.pathSeparator, off)) != -1) {
 178                 String element = cp.substring(off, next);
 179                 if (element.length() > 0 || !skipEmptyElements) {
 180                     URL url = toFileURL(element);
 181                     if (url != null) path.add(url);
 182                 }
 183                 off = next + 1;
 184             }
 185 
 186             // remaining element
 187             String element = cp.substring(off);
 188             if (element.length() > 0 || !skipEmptyElements) {
 189                 URL url = toFileURL(element);
 190                 if (url != null) path.add(url);
 191             }
 192 
 193             // push the URLs
 194             for (int i = path.size() - 1; i >= 0; --i) {
 195                 unopenedUrls.push(path.get(i));
 196             }
 197         }
 198 








 199         this.path = path;
 200         this.jarHandler = null;
 201         this.acc = null;
 202     }
 203 
 204     public synchronized List<IOException> closeLoaders() {
 205         if (closed) {
 206             return Collections.emptyList();
 207         }
 208         List<IOException> result = new LinkedList<>();
 209         for (Loader loader : loaders) {
 210             try {
 211                 loader.close();
 212             } catch (IOException e) {
 213                 result.add(e);
 214             }
 215         }
 216         closed = true;
 217         return result;
 218     }
 219 
 220     /**
 221      * Appends the specified URL to the search path of directory and JAR
 222      * file URLs from which to load classes and resources.
 223      * <p>
 224      * If the URL specified is null or is already in the list of
 225      * URLs, then invoking this method has no effect.
 226      */
 227     public synchronized void addURL(URL url) {
 228         if (closed)
 229             return;
 230         synchronized (unopenedUrls) {
 231             if (url == null || path.contains(url))
 232                 return;
 233 
 234             unopenedUrls.add(0, url);
 235             path.add(url);
 236         }
 237     }

 238 
 239     /**
 240      * Appends the specified file path as a file URL to the search path.
 241      */
 242     public void addFile(String s) {
 243         URL url = toFileURL(s);
 244         if (url != null) {
 245             addURL(url);
 246         }
 247     }
 248 
 249     /**
 250      * Returns a file URL for the given file path.
 251      */
 252     private static URL toFileURL(String s) {
 253         try {
 254             File f = new File(s).getCanonicalFile();
 255             return ParseUtil.fileToEncodedURL(f);
 256         } catch (IOException e) {
 257             return null;


 397                 res = null;
 398                 return r;
 399             }
 400         };
 401     }
 402 
 403     public Enumeration<Resource> getResources(final String name) {
 404         return getResources(name, true);
 405     }
 406 
 407     /*
 408      * Returns the Loader at the specified position in the URL search
 409      * path. The URLs are opened and expanded as needed. Returns null
 410      * if the specified index is out of range.
 411      */
 412     private synchronized Loader getLoader(int index) {
 413         if (closed) {
 414             return null;
 415         }
 416          // Expand URL search path until the request can be satisfied
 417          // or the URL stack is empty.
 418         while (loaders.size() < index + 1) {
 419             // Pop the next URL from the URL stack
 420             URL url;
 421             synchronized (unopenedUrls) {
 422                 if (unopenedUrls.empty()) {

 423                     return null;
 424                 } else {
 425                     url = unopenedUrls.pop();
 426                 }
 427             }
 428             // Skip this URL if it already has a Loader. (Loader
 429             // may be null in the case where URL has not been opened
 430             // but is referenced by a JAR index.)
 431             String urlNoFragString = URLUtil.urlNoFragString(url);
 432             if (lmap.containsKey(urlNoFragString)) {
 433                 continue;
 434             }
 435             // Otherwise, create a new Loader for the URL.
 436             Loader loader;
 437             try {
 438                 loader = getLoader(url);
 439                 // If the loader defines a local class path then add the
 440                 // URLs to the list of URLs to be opened.
 441                 URL[] urls = loader.getClassPath();
 442                 if (urls != null) {
 443                     push(urls);
 444                 }
 445             } catch (IOException e) {
 446                 // Silently ignore for now...
 447                 continue;
 448             } catch (SecurityException se) {
 449                 // Always silently ignore. The context, if there is one, that
 450                 // this URLClassPath was given during construction will never
 451                 // have permission to access the URL.
 452                 if (DEBUG) {
 453                     System.err.println("Failed to access " + url + ", " + se );
 454                 }
 455                 continue;
 456             }
 457             // Finally, add the Loader to the search path.
 458             loaders.add(loader);
 459             lmap.put(urlNoFragString, loader);
 460         }


 485                                 }
 486                             } else {
 487                                 return new JarLoader(url, jarHandler, lmap, acc);
 488                             }
 489                         }
 490                     }, acc);
 491         } catch (PrivilegedActionException pae) {
 492             throw (IOException)pae.getException();
 493         }
 494     }
 495 
 496     private static final JavaNetURLAccess JNUA
 497             = SharedSecrets.getJavaNetURLAccess();
 498 
 499     private static boolean isDefaultJarHandler(URL u) {
 500         URLStreamHandler h = JNUA.getHandler(u);
 501         return h instanceof sun.net.www.protocol.jar.Handler;
 502     }
 503 
 504     /*
 505      * Pushes the specified URLs onto the list of unopened URLs.
 506      */
 507     private void push(URL[] urls) {
 508         synchronized (unopenedUrls) {
 509             for (int i = urls.length - 1; i >= 0; --i) {
 510                 unopenedUrls.push(urls[i]);
 511             }
 512         }
 513     }
 514 
 515     /*
 516      * Checks whether the resource URL should be returned.
 517      * Returns null on security check failure.
 518      * Called by java.net.URLClassLoader.
 519      */
 520     public static URL checkURL(URL url) {
 521         if (url != null) {
 522             try {
 523                 check(url);
 524             } catch (Exception e) {
 525                 return null;
 526             }
 527         }
 528         return url;
 529     }
 530 




  29 import java.io.File;
  30 import java.io.FileInputStream;
  31 import java.io.FileNotFoundException;
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.net.HttpURLConnection;
  35 import java.net.JarURLConnection;
  36 import java.net.MalformedURLException;
  37 import java.net.URL;
  38 import java.net.URLConnection;
  39 import java.net.URLStreamHandler;
  40 import java.net.URLStreamHandlerFactory;
  41 import java.security.AccessControlContext;
  42 import java.security.AccessControlException;
  43 import java.security.AccessController;
  44 import java.security.CodeSigner;
  45 import java.security.Permission;
  46 import java.security.PrivilegedActionException;
  47 import java.security.PrivilegedExceptionAction;
  48 import java.security.cert.Certificate;
  49 import java.util.ArrayDeque;
  50 import java.util.ArrayList;
  51 import java.util.Arrays;
  52 import java.util.Collections;
  53 import java.util.Enumeration;
  54 import java.util.HashMap;
  55 import java.util.HashSet;
  56 import java.util.LinkedList;
  57 import java.util.List;
  58 import java.util.NoSuchElementException;
  59 import java.util.Properties;
  60 import java.util.Set;

  61 import java.util.StringTokenizer;
  62 import java.util.jar.JarFile;
  63 import java.util.zip.ZipEntry;
  64 import java.util.jar.JarEntry;
  65 import java.util.jar.Manifest;
  66 import java.util.jar.Attributes;
  67 import java.util.jar.Attributes.Name;
  68 import java.util.zip.ZipFile;
  69 
  70 import jdk.internal.misc.JavaNetURLAccess;
  71 import jdk.internal.misc.JavaUtilZipFileAccess;
  72 import jdk.internal.misc.SharedSecrets;
  73 import jdk.internal.util.jar.InvalidJarIndexError;
  74 import jdk.internal.util.jar.JarIndex;
  75 import sun.net.util.URLUtil;
  76 import sun.net.www.ParseUtil;
  77 import sun.security.action.GetPropertyAction;
  78 
  79 /**
  80  * This class is used to maintain a search path of URLs for loading classes


  84  */
  85 public class URLClassPath {
  86     private static final String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
  87     private static final String JAVA_VERSION;
  88     private static final boolean DEBUG;
  89     private static final boolean DISABLE_JAR_CHECKING;
  90     private static final boolean DISABLE_ACC_CHECKING;
  91 
  92     static {
  93         Properties props = GetPropertyAction.privilegedGetProperties();
  94         JAVA_VERSION = props.getProperty("java.version");
  95         DEBUG = (props.getProperty("sun.misc.URLClassPath.debug") != null);
  96         String p = props.getProperty("sun.misc.URLClassPath.disableJarChecking");
  97         DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
  98 
  99         p = props.getProperty("jdk.net.URLClassPath.disableRestrictedPermissions");
 100         DISABLE_ACC_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
 101     }
 102 
 103     /* The original search path of URLs. */
 104     private final ArrayList<URL> path;
 105 
 106     /* The deque of unopened URLs */
 107     private final ArrayDeque<URL> unopenedUrls;
 108 
 109     /* The resulting search path of Loaders */
 110     private final ArrayList<Loader> loaders = new ArrayList<>();
 111 
 112     /* Map of each URL opened to its corresponding Loader */
 113     private final HashMap<String, Loader> lmap = new HashMap<>();
 114 
 115     /* The jar protocol handler to use when creating new URLs */
 116     private final URLStreamHandler jarHandler;
 117 
 118     /* Whether this URLClassLoader has been closed yet */
 119     private boolean closed = false;
 120 
 121     /* The context to be used when loading classes and resources.  If non-null
 122      * this is the context that was captured during the creation of the
 123      * URLClassLoader. null implies no additional security restrictions. */
 124     private final AccessControlContext acc;
 125 
 126     /**
 127      * Creates a new URLClassPath for the given URLs. The URLs will be
 128      * searched in the order specified for classes and resources. A URL
 129      * ending with a '/' is assumed to refer to a directory. Otherwise,
 130      * the URL is assumed to refer to a JAR file.
 131      *
 132      * @param urls the directory and JAR file URLs to search for classes
 133      *        and resources
 134      * @param factory the URLStreamHandlerFactory to use when creating new URLs
 135      * @param acc the context to be used when loading classes and resources, may
 136      *            be null
 137      */
 138     public URLClassPath(URL[] urls,
 139                         URLStreamHandlerFactory factory,
 140                         AccessControlContext acc) {
 141         ArrayList<URL> path = new ArrayList<>(urls.length);
 142         ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(urls.length);
 143         for (URL url : urls) {
 144             path.add(url);
 145             unopenedUrls.add(url);
 146         }
 147         this.path = path;
 148         this.unopenedUrls = unopenedUrls;
 149 
 150         if (factory != null) {
 151             jarHandler = factory.createURLStreamHandler("jar");
 152         } else {
 153             jarHandler = null;
 154         }
 155         if (DISABLE_ACC_CHECKING)
 156             this.acc = null;
 157         else
 158             this.acc = acc;
 159     }
 160 
 161     public URLClassPath(URL[] urls, AccessControlContext acc) {
 162         this(urls, null, acc);
 163     }
 164 
 165     /**
 166      * Constructs a URLClassPath from a class path string.
 167      *
 168      * @param cp the class path string
 169      * @param skipEmptyElements indicates if empty elements are ignored or
 170      *        treated as the current working directory
 171      *
 172      * @apiNote Used to create the application class path.
 173      */
 174     URLClassPath(String cp, boolean skipEmptyElements) {
 175         ArrayList<URL> path = new ArrayList<>();
 176         if (cp != null) {
 177             // map each element of class path to a file URL
 178             int off = 0;
 179             int next;
 180             while ((next = cp.indexOf(File.pathSeparator, off)) != -1) {
 181                 String element = cp.substring(off, next);
 182                 if (element.length() > 0 || !skipEmptyElements) {
 183                     URL url = toFileURL(element);
 184                     if (url != null) path.add(url);
 185                 }
 186                 off = next + 1;
 187             }
 188 
 189             // remaining element
 190             String element = cp.substring(off);
 191             if (element.length() > 0 || !skipEmptyElements) {
 192                 URL url = toFileURL(element);
 193                 if (url != null) path.add(url);
 194             }





 195         }
 196 
 197         // can't use ArrayDeque#addAll or new ArrayDeque(Collection);
 198         // it's too early in the bootstrap to trigger use of lambdas
 199         int size = path.size();
 200         ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(size);
 201         for (int i = 0; i < size; i++)
 202             unopenedUrls.add(path.get(i));
 203 
 204         this.unopenedUrls = unopenedUrls;
 205         this.path = path;
 206         this.jarHandler = null;
 207         this.acc = null;
 208     }
 209 
 210     public synchronized List<IOException> closeLoaders() {
 211         if (closed) {
 212             return Collections.emptyList();
 213         }
 214         List<IOException> result = new LinkedList<>();
 215         for (Loader loader : loaders) {
 216             try {
 217                 loader.close();
 218             } catch (IOException e) {
 219                 result.add(e);
 220             }
 221         }
 222         closed = true;
 223         return result;
 224     }
 225 
 226     /**
 227      * Appends the specified URL to the search path of directory and JAR
 228      * file URLs from which to load classes and resources.
 229      * <p>
 230      * If the URL specified is null or is already in the list of
 231      * URLs, then invoking this method has no effect.
 232      */
 233     public synchronized void addURL(URL url) {
 234         if (closed || url == null)
 235             return;
 236         synchronized (unopenedUrls) {
 237             if (! path.contains(url)) {
 238                 unopenedUrls.addLast(url);


 239                 path.add(url);
 240             }
 241         }
 242     }
 243 
 244     /**
 245      * Appends the specified file path as a file URL to the search path.
 246      */
 247     public void addFile(String s) {
 248         URL url = toFileURL(s);
 249         if (url != null) {
 250             addURL(url);
 251         }
 252     }
 253 
 254     /**
 255      * Returns a file URL for the given file path.
 256      */
 257     private static URL toFileURL(String s) {
 258         try {
 259             File f = new File(s).getCanonicalFile();
 260             return ParseUtil.fileToEncodedURL(f);
 261         } catch (IOException e) {
 262             return null;


 402                 res = null;
 403                 return r;
 404             }
 405         };
 406     }
 407 
 408     public Enumeration<Resource> getResources(final String name) {
 409         return getResources(name, true);
 410     }
 411 
 412     /*
 413      * Returns the Loader at the specified position in the URL search
 414      * path. The URLs are opened and expanded as needed. Returns null
 415      * if the specified index is out of range.
 416      */
 417     private synchronized Loader getLoader(int index) {
 418         if (closed) {
 419             return null;
 420         }
 421         // Expand URL search path until the request can be satisfied
 422         // or unopenedUrls is exhausted.
 423         while (loaders.size() < index + 1) {
 424             final URL url;

 425             synchronized (unopenedUrls) {
 426                 url = unopenedUrls.pollFirst();
 427                 if (url == null)
 428                     return null;



 429             }
 430             // Skip this URL if it already has a Loader. (Loader
 431             // may be null in the case where URL has not been opened
 432             // but is referenced by a JAR index.)
 433             String urlNoFragString = URLUtil.urlNoFragString(url);
 434             if (lmap.containsKey(urlNoFragString)) {
 435                 continue;
 436             }
 437             // Otherwise, create a new Loader for the URL.
 438             Loader loader;
 439             try {
 440                 loader = getLoader(url);
 441                 // If the loader defines a local class path then add the
 442                 // URLs as the next URLs to be opened.
 443                 URL[] urls = loader.getClassPath();
 444                 if (urls != null) {
 445                     push(urls);
 446                 }
 447             } catch (IOException e) {
 448                 // Silently ignore for now...
 449                 continue;
 450             } catch (SecurityException se) {
 451                 // Always silently ignore. The context, if there is one, that
 452                 // this URLClassPath was given during construction will never
 453                 // have permission to access the URL.
 454                 if (DEBUG) {
 455                     System.err.println("Failed to access " + url + ", " + se );
 456                 }
 457                 continue;
 458             }
 459             // Finally, add the Loader to the search path.
 460             loaders.add(loader);
 461             lmap.put(urlNoFragString, loader);
 462         }


 487                                 }
 488                             } else {
 489                                 return new JarLoader(url, jarHandler, lmap, acc);
 490                             }
 491                         }
 492                     }, acc);
 493         } catch (PrivilegedActionException pae) {
 494             throw (IOException)pae.getException();
 495         }
 496     }
 497 
 498     private static final JavaNetURLAccess JNUA
 499             = SharedSecrets.getJavaNetURLAccess();
 500 
 501     private static boolean isDefaultJarHandler(URL u) {
 502         URLStreamHandler h = JNUA.getHandler(u);
 503         return h instanceof sun.net.www.protocol.jar.Handler;
 504     }
 505 
 506     /*
 507      * Pushes the specified URLs onto the head of unopened URLs.
 508      */
 509     private void push(URL[] urls) {
 510         synchronized (unopenedUrls) {
 511             for (int i = urls.length - 1; i >= 0; --i) {
 512                 unopenedUrls.addFirst(urls[i]);
 513             }
 514         }
 515     }
 516 
 517     /*
 518      * Checks whether the resource URL should be returned.
 519      * Returns null on security check failure.
 520      * Called by java.net.URLClassLoader.
 521      */
 522     public static URL checkURL(URL url) {
 523         if (url != null) {
 524             try {
 525                 check(url);
 526             } catch (Exception e) {
 527                 return null;
 528             }
 529         }
 530         return url;
 531     }
 532 


< prev index next >