1 /*
2  * Copyright (c) 1997, 2021, 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 java.net;
27 
28 import java.io.Closeable;
29 import java.io.File;
30 import java.io.FilePermission;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.security.AccessControlContext;
34 import java.security.AccessController;
35 import java.security.CodeSigner;
36 import java.security.CodeSource;
37 import java.security.Permission;
38 import java.security.PermissionCollection;
39 import java.security.PrivilegedAction;
40 import java.security.PrivilegedExceptionAction;
41 import java.security.SecureClassLoader;
42 import java.util.Enumeration;
43 import java.util.List;
44 import java.util.NoSuchElementException;
45 import java.util.Objects;
46 import java.util.Set;
47 import java.util.WeakHashMap;
48 import java.util.jar.Attributes;
49 import java.util.jar.Attributes.Name;
50 import java.util.jar.JarFile;
51 import java.util.jar.Manifest;
52 
53 import jdk.internal.loader.Resource;
54 import jdk.internal.loader.URLClassPath;
55 import jdk.internal.access.SharedSecrets;
56 import jdk.internal.perf.PerfCounter;
57 import sun.net.www.ParseUtil;
58 import sun.security.util.SecurityConstants;
59 
60 /**
61  * This class loader is used to load classes and resources from a search
62  * path of URLs referring to both JAR files and directories. Any {@code jar:}
63  * scheme URL (see {@link java.net.JarURLConnection}) is assumed to refer to a
64  * JAR file.  Any {@code file:} scheme URL that ends with a '/' is assumed to
65  * refer to a directory. Otherwise, the URL is assumed to refer to a JAR file
66  * which will be opened as needed.
67  * <p>
68  * This class loader supports the loading of classes and resources from the
69  * contents of a <a href="../util/jar/JarFile.html#multirelease">multi-release</a>
70  * JAR file that is referred to by a given URL.
71  * <p>
72  * The AccessControlContext of the thread that created the instance of
73  * URLClassLoader will be used when subsequently loading classes and
74  * resources.
75  * <p>
76  * The classes that are loaded are by default granted permission only to
77  * access the URLs specified when the URLClassLoader was created.
78  *
79  * @author  David Connelly
80  * @since   1.2
81  */
82 public class URLClassLoader extends SecureClassLoader implements Closeable {
83     /* The search path for classes and resources */
84     private final URLClassPath ucp;
85 
86     /* The context to be used when loading classes and resources */
87     @SuppressWarnings("removal")
88     private final AccessControlContext acc;
89 
90     /**
91      * Constructs a new URLClassLoader for the given URLs. The URLs will be
92      * searched in the order specified for classes and resources after first
93      * searching in the specified parent class loader.  Any {@code jar:}
94      * scheme URL is assumed to refer to a JAR file.  Any {@code file:} scheme
95      * URL that ends with a '/' is assumed to refer to a directory.  Otherwise,
96      * the URL is assumed to refer to a JAR file which will be downloaded and
97      * opened as needed.
98      *
99      * <p>If there is a security manager, this method first
100      * calls the security manager's {@code checkCreateClassLoader} method
101      * to ensure creation of a class loader is allowed.
102      *
103      * @param      urls the URLs from which to load classes and resources
104      * @param      parent the parent class loader for delegation
105      * @throws     SecurityException  if a security manager exists and its
106      *             {@code checkCreateClassLoader} method doesn't allow
107      *             creation of a class loader.
108      * @throws     NullPointerException if {@code urls} or any of its
109      *             elements is {@code null}.
110      * @see SecurityManager#checkCreateClassLoader
111      */
112     @SuppressWarnings("removal")
URLClassLoader(URL[] urls, ClassLoader parent)113     public URLClassLoader(URL[] urls, ClassLoader parent) {
114         super(parent);
115         this.acc = AccessController.getContext();
116         this.ucp = new URLClassPath(urls, acc);
117     }
118 
URLClassLoader(String name, URL[] urls, ClassLoader parent, @SuppressWarnings(R) AccessControlContext acc)119     URLClassLoader(String name, URL[] urls, ClassLoader parent,
120                    @SuppressWarnings("removal") AccessControlContext acc) {
121         super(name, parent);
122         this.acc = acc;
123         this.ucp = new URLClassPath(urls, acc);
124     }
125 
126     /**
127      * Constructs a new URLClassLoader for the specified URLs using the
128      * default delegation parent {@code ClassLoader}. The URLs will
129      * be searched in the order specified for classes and resources after
130      * first searching in the parent class loader. Any URL that ends with
131      * a '/' is assumed to refer to a directory. Otherwise, the URL is
132      * assumed to refer to a JAR file which will be downloaded and opened
133      * as needed.
134      *
135      * <p>If there is a security manager, this method first
136      * calls the security manager's {@code checkCreateClassLoader} method
137      * to ensure creation of a class loader is allowed.
138      *
139      * @param      urls the URLs from which to load classes and resources
140      *
141      * @throws     SecurityException  if a security manager exists and its
142      *             {@code checkCreateClassLoader} method doesn't allow
143      *             creation of a class loader.
144      * @throws     NullPointerException if {@code urls} or any of its
145      *             elements is {@code null}.
146      * @see SecurityManager#checkCreateClassLoader
147      */
148     @SuppressWarnings("removal")
URLClassLoader(URL[] urls)149     public URLClassLoader(URL[] urls) {
150         super();
151         this.acc = AccessController.getContext();
152         this.ucp = new URLClassPath(urls, acc);
153     }
154 
URLClassLoader(URL[] urls, @SuppressWarnings(R) AccessControlContext acc)155     URLClassLoader(URL[] urls, @SuppressWarnings("removal") AccessControlContext acc) {
156         super();
157         this.acc = acc;
158         this.ucp = new URLClassPath(urls, acc);
159     }
160 
161     /**
162      * Constructs a new URLClassLoader for the specified URLs, parent
163      * class loader, and URLStreamHandlerFactory. The parent argument
164      * will be used as the parent class loader for delegation. The
165      * factory argument will be used as the stream handler factory to
166      * obtain protocol handlers when creating new jar URLs.
167      *
168      * <p>If there is a security manager, this method first
169      * calls the security manager's {@code checkCreateClassLoader} method
170      * to ensure creation of a class loader is allowed.
171      *
172      * @param  urls the URLs from which to load classes and resources
173      * @param  parent the parent class loader for delegation
174      * @param  factory the URLStreamHandlerFactory to use when creating URLs
175      *
176      * @throws SecurityException  if a security manager exists and its
177      *         {@code checkCreateClassLoader} method doesn't allow
178      *         creation of a class loader.
179      * @throws NullPointerException if {@code urls} or any of its
180      *         elements is {@code null}.
181      * @see SecurityManager#checkCreateClassLoader
182      */
183     @SuppressWarnings("removal")
URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)184     public URLClassLoader(URL[] urls, ClassLoader parent,
185                           URLStreamHandlerFactory factory) {
186         super(parent);
187         this.acc = AccessController.getContext();
188         this.ucp = new URLClassPath(urls, factory, acc);
189     }
190 
191 
192     /**
193      * Constructs a new named {@code URLClassLoader} for the specified URLs.
194      * The URLs will be searched in the order specified for classes
195      * and resources after first searching in the specified parent class loader.
196      * Any URL that ends with a '/' is assumed to refer to a directory.
197      * Otherwise, the URL is assumed to refer to a JAR file which will be
198      * downloaded and opened as needed.
199      *
200      * @param  name class loader name; or {@code null} if not named
201      * @param  urls the URLs from which to load classes and resources
202      * @param  parent the parent class loader for delegation
203      *
204      * @throws IllegalArgumentException if the given name is empty.
205      * @throws NullPointerException if {@code urls} or any of its
206      *         elements is {@code null}.
207      *
208      * @throws SecurityException if a security manager exists and its
209      *         {@link SecurityManager#checkCreateClassLoader()} method doesn't
210      *         allow creation of a class loader.
211      *
212      * @since 9
213      */
214     @SuppressWarnings("removal")
URLClassLoader(String name, URL[] urls, ClassLoader parent)215     public URLClassLoader(String name,
216                           URL[] urls,
217                           ClassLoader parent) {
218         super(name, parent);
219         this.acc = AccessController.getContext();
220         this.ucp = new URLClassPath(urls, acc);
221     }
222 
223     /**
224      * Constructs a new named {@code URLClassLoader} for the specified URLs,
225      * parent class loader, and URLStreamHandlerFactory.
226      * The parent argument will be used as the parent class loader for delegation.
227      * The factory argument will be used as the stream handler factory to
228      * obtain protocol handlers when creating new jar URLs.
229      *
230      * @param  name class loader name; or {@code null} if not named
231      * @param  urls the URLs from which to load classes and resources
232      * @param  parent the parent class loader for delegation
233      * @param  factory the URLStreamHandlerFactory to use when creating URLs
234      *
235      * @throws IllegalArgumentException if the given name is empty.
236      * @throws NullPointerException if {@code urls} or any of its
237      *         elements is {@code null}.
238      *
239      * @throws SecurityException if a security manager exists and its
240      *         {@code checkCreateClassLoader} method doesn't allow
241      *         creation of a class loader.
242      *
243      * @since 9
244      */
245     @SuppressWarnings("removal")
URLClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)246     public URLClassLoader(String name, URL[] urls, ClassLoader parent,
247                           URLStreamHandlerFactory factory) {
248         super(name, parent);
249         this.acc = AccessController.getContext();
250         this.ucp = new URLClassPath(urls, factory, acc);
251     }
252 
253     /* A map (used as a set) to keep track of closeable local resources
254      * (either JarFiles or FileInputStreams). We don't care about
255      * Http resources since they don't need to be closed.
256      *
257      * If the resource is coming from a jar file
258      * we keep a (weak) reference to the JarFile object which can
259      * be closed if URLClassLoader.close() called. Due to jar file
260      * caching there will typically be only one JarFile object
261      * per underlying jar file.
262      *
263      * For file resources, which is probably a less common situation
264      * we have to keep a weak reference to each stream.
265      */
266 
267     private WeakHashMap<Closeable,Void>
268         closeables = new WeakHashMap<>();
269 
270     /**
271      * Returns an input stream for reading the specified resource.
272      * If this loader is closed, then any resources opened by this method
273      * will be closed.
274      *
275      * <p> The search order is described in the documentation for {@link
276      * #getResource(String)}.  </p>
277      *
278      * @param  name
279      *         The resource name
280      *
281      * @return  An input stream for reading the resource, or {@code null}
282      *          if the resource could not be found
283      *
284      * @throws  NullPointerException If {@code name} is {@code null}
285      *
286      * @since  1.7
287      */
getResourceAsStream(String name)288     public InputStream getResourceAsStream(String name) {
289         Objects.requireNonNull(name);
290         URL url = getResource(name);
291         try {
292             if (url == null) {
293                 return null;
294             }
295             URLConnection urlc = url.openConnection();
296             InputStream is = urlc.getInputStream();
297             if (urlc instanceof JarURLConnection juc) {
298                 JarFile jar = juc.getJarFile();
299                 synchronized (closeables) {
300                     if (!closeables.containsKey(jar)) {
301                         closeables.put(jar, null);
302                     }
303                 }
304             } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) {
305                 synchronized (closeables) {
306                     closeables.put(is, null);
307                 }
308             }
309             return is;
310         } catch (IOException e) {
311             return null;
312         }
313     }
314 
315    /**
316     * Closes this URLClassLoader, so that it can no longer be used to load
317     * new classes or resources that are defined by this loader.
318     * Classes and resources defined by any of this loader's parents in the
319     * delegation hierarchy are still accessible. Also, any classes or resources
320     * that are already loaded, are still accessible.
321     * <p>
322     * In the case of jar: and file: URLs, it also closes any files
323     * that were opened by it. If another thread is loading a
324     * class when the {@code close} method is invoked, then the result of
325     * that load is undefined.
326     * <p>
327     * The method makes a best effort attempt to close all opened files,
328     * by catching {@link IOException}s internally. Unchecked exceptions
329     * and errors are not caught. Calling close on an already closed
330     * loader has no effect.
331     *
332     * @throws    IOException if closing any file opened by this class loader
333     * resulted in an IOException. Any such exceptions are caught internally.
334     * If only one is caught, then it is re-thrown. If more than one exception
335     * is caught, then the second and following exceptions are added
336     * as suppressed exceptions of the first one caught, which is then re-thrown.
337     *
338     * @throws    SecurityException if a security manager is set, and it denies
339     *   {@link RuntimePermission}{@code ("closeClassLoader")}
340     *
341     * @since 1.7
342     */
close()343     public void close() throws IOException {
344         @SuppressWarnings("removal")
345         SecurityManager security = System.getSecurityManager();
346         if (security != null) {
347             security.checkPermission(new RuntimePermission("closeClassLoader"));
348         }
349         List<IOException> errors = ucp.closeLoaders();
350 
351         // now close any remaining streams.
352 
353         synchronized (closeables) {
354             Set<Closeable> keys = closeables.keySet();
355             for (Closeable c : keys) {
356                 try {
357                     c.close();
358                 } catch (IOException ioex) {
359                     errors.add(ioex);
360                 }
361             }
362             closeables.clear();
363         }
364 
365         if (errors.isEmpty()) {
366             return;
367         }
368 
369         IOException firstex = errors.remove(0);
370 
371         // Suppress any remaining exceptions
372 
373         for (IOException error: errors) {
374             firstex.addSuppressed(error);
375         }
376         throw firstex;
377     }
378 
379     /**
380      * Appends the specified URL to the list of URLs to search for
381      * classes and resources.
382      * <p>
383      * If the URL specified is {@code null} or is already in the
384      * list of URLs, or if this loader is closed, then invoking this
385      * method has no effect.
386      *
387      * @param url the URL to be added to the search path of URLs
388      */
addURL(URL url)389     protected void addURL(URL url) {
390         ucp.addURL(url);
391     }
392 
393     /**
394      * Returns the search path of URLs for loading classes and resources.
395      * This includes the original list of URLs specified to the constructor,
396      * along with any URLs subsequently appended by the addURL() method.
397      * @return the search path of URLs for loading classes and resources.
398      */
getURLs()399     public URL[] getURLs() {
400         return ucp.getURLs();
401     }
402 
403     /**
404      * Finds and loads the class with the specified name from the URL search
405      * path. Any URLs referring to JAR files are loaded and opened as needed
406      * until the class is found.
407      *
408      * @param     name the name of the class
409      * @return    the resulting class
410      * @throws    ClassNotFoundException if the class could not be found,
411      *            or if the loader is closed.
412      * @throws    NullPointerException if {@code name} is {@code null}.
413      */
414     @SuppressWarnings("removal")
findClass(final String name)415     protected Class<?> findClass(final String name)
416         throws ClassNotFoundException
417     {
418         final Class<?> result;
419         try {
420             result = AccessController.doPrivileged(
421                 new PrivilegedExceptionAction<>() {
422                     public Class<?> run() throws ClassNotFoundException {
423                         String path = name.replace('.', '/').concat(".class");
424                         Resource res = ucp.getResource(path, false);
425                         if (res != null) {
426                             try {
427                                 return defineClass(name, res);
428                             } catch (IOException e) {
429                                 throw new ClassNotFoundException(name, e);
430                             } catch (ClassFormatError e2) {
431                                 if (res.getDataError() != null) {
432                                     e2.addSuppressed(res.getDataError());
433                                 }
434                                 throw e2;
435                             }
436                         } else {
437                             return null;
438                         }
439                     }
440                 }, acc);
441         } catch (java.security.PrivilegedActionException pae) {
442             throw (ClassNotFoundException) pae.getException();
443         }
444         if (result == null) {
445             throw new ClassNotFoundException(name);
446         }
447         return result;
448     }
449 
450     /*
451      * Retrieve the package using the specified package name.
452      * If non-null, verify the package using the specified code
453      * source and manifest.
454      */
getAndVerifyPackage(String pkgname, Manifest man, URL url)455     private Package getAndVerifyPackage(String pkgname,
456                                         Manifest man, URL url) {
457         Package pkg = getDefinedPackage(pkgname);
458         if (pkg != null) {
459             // Package found, so check package sealing.
460             if (pkg.isSealed()) {
461                 // Verify that code source URL is the same.
462                 if (!pkg.isSealed(url)) {
463                     throw new SecurityException(
464                         "sealing violation: package " + pkgname + " is sealed");
465                 }
466             } else {
467                 // Make sure we are not attempting to seal the package
468                 // at this code source URL.
469                 if ((man != null) && isSealed(pkgname, man)) {
470                     throw new SecurityException(
471                         "sealing violation: can't seal package " + pkgname +
472                         ": already loaded");
473                 }
474             }
475         }
476         return pkg;
477     }
478 
479     /*
480      * Defines a Class using the class bytes obtained from the specified
481      * Resource. The resulting Class must be resolved before it can be
482      * used.
483      */
defineClass(String name, Resource res)484     private Class<?> defineClass(String name, Resource res) throws IOException {
485         long t0 = System.nanoTime();
486         int i = name.lastIndexOf('.');
487         URL url = res.getCodeSourceURL();
488         if (i != -1) {
489             String pkgname = name.substring(0, i);
490             // Check if package already loaded.
491             Manifest man = res.getManifest();
492             if (getAndVerifyPackage(pkgname, man, url) == null) {
493                 try {
494                     if (man != null) {
495                         definePackage(pkgname, man, url);
496                     } else {
497                         definePackage(pkgname, null, null, null, null, null, null, null);
498                     }
499                 } catch (IllegalArgumentException iae) {
500                     // parallel-capable class loaders: re-verify in case of a
501                     // race condition
502                     if (getAndVerifyPackage(pkgname, man, url) == null) {
503                         // Should never happen
504                         throw new AssertionError("Cannot find package " +
505                                                  pkgname);
506                     }
507                 }
508             }
509         }
510         // Now read the class bytes and define the class
511         java.nio.ByteBuffer bb = res.getByteBuffer();
512         if (bb != null) {
513             // Use (direct) ByteBuffer:
514             CodeSigner[] signers = res.getCodeSigners();
515             CodeSource cs = new CodeSource(url, signers);
516             PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
517             return defineClass(name, bb, cs);
518         } else {
519             byte[] b = res.getBytes();
520             // must read certificates AFTER reading bytes.
521             CodeSigner[] signers = res.getCodeSigners();
522             CodeSource cs = new CodeSource(url, signers);
523             PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
524             return defineClass(name, b, 0, b.length, cs);
525         }
526     }
527 
528     /**
529      * Defines a new package by name in this {@code URLClassLoader}.
530      * The attributes contained in the specified {@code Manifest}
531      * will be used to obtain package version and sealing information.
532      * For sealed packages, the additional URL specifies the code source URL
533      * from which the package was loaded.
534      *
535      * @param name  the package name
536      * @param man   the {@code Manifest} containing package version and sealing
537      *              information
538      * @param url   the code source url for the package, or null if none
539      * @throws      IllegalArgumentException if the package name is
540      *              already defined by this class loader
541      * @return      the newly defined {@code Package} object
542      *
543      * @revised 9
544      */
definePackage(String name, Manifest man, URL url)545     protected Package definePackage(String name, Manifest man, URL url) {
546         String specTitle = null, specVersion = null, specVendor = null;
547         String implTitle = null, implVersion = null, implVendor = null;
548         String sealed = null;
549         URL sealBase = null;
550 
551         Attributes attr = SharedSecrets.javaUtilJarAccess()
552                 .getTrustedAttributes(man, name.replace('.', '/').concat("/"));
553         if (attr != null) {
554             specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
555             specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
556             specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
557             implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
558             implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
559             implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
560             sealed      = attr.getValue(Name.SEALED);
561         }
562         attr = man.getMainAttributes();
563         if (attr != null) {
564             if (specTitle == null) {
565                 specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
566             }
567             if (specVersion == null) {
568                 specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
569             }
570             if (specVendor == null) {
571                 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
572             }
573             if (implTitle == null) {
574                 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
575             }
576             if (implVersion == null) {
577                 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
578             }
579             if (implVendor == null) {
580                 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
581             }
582             if (sealed == null) {
583                 sealed = attr.getValue(Name.SEALED);
584             }
585         }
586         if ("true".equalsIgnoreCase(sealed)) {
587             sealBase = url;
588         }
589         return definePackage(name, specTitle, specVersion, specVendor,
590                              implTitle, implVersion, implVendor, sealBase);
591     }
592 
593     /*
594      * Returns true if the specified package name is sealed according to the
595      * given manifest.
596      *
597      * @throws SecurityException if the package name is untrusted in the manifest
598      */
isSealed(String name, Manifest man)599     private boolean isSealed(String name, Manifest man) {
600         Attributes attr = SharedSecrets.javaUtilJarAccess()
601                 .getTrustedAttributes(man, name.replace('.', '/').concat("/"));
602         String sealed = null;
603         if (attr != null) {
604             sealed = attr.getValue(Name.SEALED);
605         }
606         if (sealed == null) {
607             if ((attr = man.getMainAttributes()) != null) {
608                 sealed = attr.getValue(Name.SEALED);
609             }
610         }
611         return "true".equalsIgnoreCase(sealed);
612     }
613 
614     /**
615      * Finds the resource with the specified name on the URL search path.
616      *
617      * @param name the name of the resource
618      * @return a {@code URL} for the resource, or {@code null}
619      * if the resource could not be found, or if the loader is closed.
620      */
findResource(final String name)621     public URL findResource(final String name) {
622         /*
623          * The same restriction to finding classes applies to resources
624          */
625         @SuppressWarnings("removal")
626         URL url = AccessController.doPrivileged(
627             new PrivilegedAction<>() {
628                 public URL run() {
629                     return ucp.findResource(name, true);
630                 }
631             }, acc);
632 
633         return url != null ? URLClassPath.checkURL(url) : null;
634     }
635 
636     /**
637      * Returns an Enumeration of URLs representing all of the resources
638      * on the URL search path having the specified name.
639      *
640      * @param name the resource name
641      * @throws    IOException if an I/O exception occurs
642      * @return An {@code Enumeration} of {@code URL}s.
643      *         If the loader is closed, the Enumeration contains no elements.
644      */
findResources(final String name)645     public Enumeration<URL> findResources(final String name)
646         throws IOException
647     {
648         final Enumeration<URL> e = ucp.findResources(name, true);
649 
650         return new Enumeration<>() {
651             private URL url = null;
652 
653             private boolean next() {
654                 if (url != null) {
655                     return true;
656                 }
657                 do {
658                     @SuppressWarnings("removal")
659                     URL u = AccessController.doPrivileged(
660                         new PrivilegedAction<>() {
661                             public URL run() {
662                                 if (!e.hasMoreElements())
663                                     return null;
664                                 return e.nextElement();
665                             }
666                         }, acc);
667                     if (u == null)
668                         break;
669                     url = URLClassPath.checkURL(u);
670                 } while (url == null);
671                 return url != null;
672             }
673 
674             public URL nextElement() {
675                 if (!next()) {
676                     throw new NoSuchElementException();
677                 }
678                 URL u = url;
679                 url = null;
680                 return u;
681             }
682 
683             public boolean hasMoreElements() {
684                 return next();
685             }
686         };
687     }
688 
689     /**
690      * Returns the permissions for the given codesource object.
691      * The implementation of this method first calls super.getPermissions
692      * and then adds permissions based on the URL of the codesource.
693      * <p>
694      * If the protocol of this URL is "jar", then the permission granted
695      * is based on the permission that is required by the URL of the Jar
696      * file.
697      * <p>
698      * If the protocol is "file" and there is an authority component, then
699      * permission to connect to and accept connections from that authority
700      * may be granted. If the protocol is "file"
701      * and the path specifies a file, then permission to read that
702      * file is granted. If protocol is "file" and the path is
703      * a directory, permission is granted to read all files
704      * and (recursively) all files and subdirectories contained in
705      * that directory.
706      * <p>
707      * If the protocol is not "file", then permission
708      * to connect to and accept connections from the URL's host is granted.
709      * @param codesource the codesource
710      * @throws    NullPointerException if {@code codesource} is {@code null}.
711      * @return the permissions granted to the codesource
712      */
713     @SuppressWarnings("removal")
getPermissions(CodeSource codesource)714     protected PermissionCollection getPermissions(CodeSource codesource)
715     {
716         PermissionCollection perms = super.getPermissions(codesource);
717 
718         URL url = codesource.getLocation();
719 
720         Permission p;
721         URLConnection urlConnection;
722 
723         try {
724             urlConnection = url.openConnection();
725             p = urlConnection.getPermission();
726         } catch (java.io.IOException ioe) {
727             p = null;
728             urlConnection = null;
729         }
730 
731         if (p instanceof FilePermission) {
732             // if the permission has a separator char on the end,
733             // it means the codebase is a directory, and we need
734             // to add an additional permission to read recursively
735             String path = p.getName();
736             if (path.endsWith(File.separator)) {
737                 path += "-";
738                 p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
739             }
740         } else if ((p == null) && (url.getProtocol().equals("file"))) {
741             String path = url.getFile().replace('/', File.separatorChar);
742             path = ParseUtil.decode(path);
743             if (path.endsWith(File.separator))
744                 path += "-";
745             p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
746         } else {
747             /**
748              * Not loading from a 'file:' URL so we want to give the class
749              * permission to connect to and accept from the remote host
750              * after we've made sure the host is the correct one and is valid.
751              */
752             URL locUrl = url;
753             if (urlConnection instanceof JarURLConnection) {
754                 locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
755             }
756             String host = locUrl.getHost();
757             if (host != null && !host.isEmpty())
758                 p = new SocketPermission(host,
759                                          SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
760         }
761 
762         // make sure the person that created this class loader
763         // would have this permission
764 
765         if (p != null) {
766             final SecurityManager sm = System.getSecurityManager();
767             if (sm != null) {
768                 final Permission fp = p;
769                 AccessController.doPrivileged(new PrivilegedAction<>() {
770                     public Void run() throws SecurityException {
771                         sm.checkPermission(fp);
772                         return null;
773                     }
774                 }, acc);
775             }
776             perms.add(p);
777         }
778         return perms;
779     }
780 
781     /**
782      * Creates a new instance of URLClassLoader for the specified
783      * URLs and parent class loader. If a security manager is
784      * installed, the {@code loadClass} method of the URLClassLoader
785      * returned by this method will invoke the
786      * {@code SecurityManager.checkPackageAccess} method before
787      * loading the class.
788      *
789      * @param urls the URLs to search for classes and resources
790      * @param parent the parent class loader for delegation
791      * @throws     NullPointerException if {@code urls} or any of its
792      *             elements is {@code null}.
793      * @return the resulting class loader
794      */
newInstance(final URL[] urls, final ClassLoader parent)795     public static URLClassLoader newInstance(final URL[] urls,
796                                              final ClassLoader parent) {
797         // Save the caller's context
798         @SuppressWarnings("removal")
799         final AccessControlContext acc = AccessController.getContext();
800         // Need a privileged block to create the class loader
801         @SuppressWarnings("removal")
802         URLClassLoader ucl = AccessController.doPrivileged(
803             new PrivilegedAction<>() {
804                 public URLClassLoader run() {
805                     return new FactoryURLClassLoader(null, urls, parent, acc);
806                 }
807             });
808         return ucl;
809     }
810 
811     /**
812      * Creates a new instance of URLClassLoader for the specified
813      * URLs and default parent class loader. If a security manager is
814      * installed, the {@code loadClass} method of the URLClassLoader
815      * returned by this method will invoke the
816      * {@code SecurityManager.checkPackageAccess} before
817      * loading the class.
818      *
819      * @param urls the URLs to search for classes and resources
820      * @throws     NullPointerException if {@code urls} or any of its
821      *             elements is {@code null}.
822      * @return the resulting class loader
823      */
newInstance(final URL[] urls)824     public static URLClassLoader newInstance(final URL[] urls) {
825         // Save the caller's context
826         @SuppressWarnings("removal")
827         final AccessControlContext acc = AccessController.getContext();
828         // Need a privileged block to create the class loader
829         @SuppressWarnings("removal")
830         URLClassLoader ucl = AccessController.doPrivileged(
831             new PrivilegedAction<>() {
832                 public URLClassLoader run() {
833                     return new FactoryURLClassLoader(urls, acc);
834                 }
835             });
836         return ucl;
837     }
838 
839     static {
ClassLoader.registerAsParallelCapable()840         ClassLoader.registerAsParallelCapable();
841     }
842 }
843 
844 final class FactoryURLClassLoader extends URLClassLoader {
845 
846     static {
847         ClassLoader.registerAsParallelCapable();
848     }
849 
850     FactoryURLClassLoader(String name, URL[] urls, ClassLoader parent,
851                           @SuppressWarnings("removal") AccessControlContext acc) {
852         super(name, urls, parent, acc);
853     }
854 
855     FactoryURLClassLoader(URL[] urls, @SuppressWarnings("removal") AccessControlContext acc) {
856         super(urls, acc);
857     }
858 
859     public final Class<?> loadClass(String name, boolean resolve)
860         throws ClassNotFoundException
861     {
862         // First check if we have permission to access the package. This
863         // should go away once we've added support for exported packages.
864         @SuppressWarnings("removal")
865         SecurityManager sm = System.getSecurityManager();
866         if (sm != null) {
867             int i = name.lastIndexOf('.');
868             if (i != -1) {
869                 sm.checkPackageAccess(name.substring(0, i));
870             }
871         }
872         return super.loadClass(name, resolve);
873     }
874 }
875