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