BuiltinClassLoader.java revision 17234:f8b19df2115a
1/*
2 * Copyright (c) 2015, 2016, 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
26package jdk.internal.loader;
27
28import java.io.File;
29import java.io.FilePermission;
30import java.io.IOException;
31import java.io.InputStream;
32import java.lang.module.ModuleDescriptor;
33import java.lang.module.ModuleReference;
34import java.lang.module.ModuleReader;
35import java.lang.ref.SoftReference;
36import java.net.MalformedURLException;
37import java.net.URI;
38import java.net.URL;
39import java.nio.ByteBuffer;
40import java.security.AccessController;
41import java.security.CodeSigner;
42import java.security.CodeSource;
43import java.security.Permission;
44import java.security.PermissionCollection;
45import java.security.PrivilegedAction;
46import java.security.PrivilegedActionException;
47import java.security.PrivilegedExceptionAction;
48import java.security.SecureClassLoader;
49import java.util.ArrayList;
50import java.util.Collections;
51import java.util.Enumeration;
52import java.util.Iterator;
53import java.util.List;
54import java.util.Map;
55import java.util.NoSuchElementException;
56import java.util.Optional;
57import java.util.concurrent.ConcurrentHashMap;
58import java.util.jar.Attributes;
59import java.util.jar.Manifest;
60import java.util.stream.Stream;
61
62import jdk.internal.misc.VM;
63import jdk.internal.module.ModulePatcher.PatchedModuleReader;
64import jdk.internal.module.SystemModules;
65import jdk.internal.module.Resources;
66
67
68/**
69 * The platform or application class loader. Resources loaded from modules
70 * defined to the boot class loader are also loaded via an instance of this
71 * ClassLoader type.
72 *
73 * <p> This ClassLoader supports loading of classes and resources from modules.
74 * Modules are defined to the ClassLoader by invoking the {@link #loadModule}
75 * method. Defining a module to this ClassLoader has the effect of making the
76 * types in the module visible. </p>
77 *
78 * <p> This ClassLoader also supports loading of classes and resources from a
79 * class path of URLs that are specified to the ClassLoader at construction
80 * time. The class path may expand at runtime (the Class-Path attribute in JAR
81 * files or via instrumentation agents). </p>
82 *
83 * <p> The delegation model used by this ClassLoader differs to the regular
84 * delegation model. When requested to load a class then this ClassLoader first
85 * maps the class name to its package name. If there is a module defined to a
86 * BuiltinClassLoader containing this package then the class loader delegates
87 * directly to that class loader. If there isn't a module containing the
88 * package then it delegates the search to the parent class loader and if not
89 * found in the parent then it searches the class path. The main difference
90 * between this and the usual delegation model is that it allows the platform
91 * class loader to delegate to the application class loader, important with
92 * upgraded modules defined to the platform class loader.
93 */
94
95public class BuiltinClassLoader
96    extends SecureClassLoader
97{
98    static {
99        if (!ClassLoader.registerAsParallelCapable())
100            throw new InternalError("Unable to register as parallel capable");
101    }
102
103    // parent ClassLoader
104    private final BuiltinClassLoader parent;
105
106    // the URL class path or null if there is no class path
107    private final URLClassPath ucp;
108
109
110    /**
111     * A module defined/loaded by a built-in class loader.
112     *
113     * A LoadedModule encapsulates a ModuleReference along with its CodeSource
114     * URL to avoid needing to create this URL when defining classes.
115     */
116    private static class LoadedModule {
117        private final BuiltinClassLoader loader;
118        private final ModuleReference mref;
119        private final URL codeSourceURL;          // may be null
120
121        LoadedModule(BuiltinClassLoader loader, ModuleReference mref) {
122            URL url = null;
123            if (mref.location().isPresent()) {
124                try {
125                    url = mref.location().get().toURL();
126                } catch (MalformedURLException | IllegalArgumentException e) { }
127            }
128            this.loader = loader;
129            this.mref = mref;
130            this.codeSourceURL = url;
131        }
132
133        BuiltinClassLoader loader() { return loader; }
134        ModuleReference mref() { return mref; }
135        String name() { return mref.descriptor().name(); }
136        URL codeSourceURL() { return codeSourceURL; }
137    }
138
139
140    // maps package name to loaded module for modules in the boot layer
141    private static final Map<String, LoadedModule> packageToModule
142        = new ConcurrentHashMap<>(SystemModules.PACKAGES_IN_BOOT_LAYER);
143
144    // maps a module name to a module reference
145    private final Map<String, ModuleReference> nameToModule;
146
147    // maps a module reference to a module reader
148    private final Map<ModuleReference, ModuleReader> moduleToReader;
149
150    // cache of resource name -> list of URLs.
151    // used only for resources that are not in module packages
152    private volatile SoftReference<Map<String, List<URL>>> resourceCache;
153
154    /**
155     * Create a new instance.
156     */
157    BuiltinClassLoader(String name, BuiltinClassLoader parent, URLClassPath ucp) {
158        // ensure getParent() returns null when the parent is the boot loader
159        super(name, parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
160
161        this.parent = parent;
162        this.ucp = ucp;
163
164        this.nameToModule = new ConcurrentHashMap<>();
165        this.moduleToReader = new ConcurrentHashMap<>();
166    }
167
168    /**
169     * Returns {@code true} if there is a class path associated with this
170     * class loader.
171     */
172    boolean hasClassPath() {
173        return ucp != null;
174    }
175
176    /**
177     * Register a module this class loader. This has the effect of making the
178     * types in the module visible.
179     */
180    public void loadModule(ModuleReference mref) {
181        String mn = mref.descriptor().name();
182        if (nameToModule.putIfAbsent(mn, mref) != null) {
183            throw new InternalError(mn + " already defined to this loader");
184        }
185
186        LoadedModule loadedModule = new LoadedModule(this, mref);
187        for (String pn : mref.descriptor().packages()) {
188            LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule);
189            if (other != null) {
190                throw new InternalError(pn + " in modules " + mn + " and "
191                                        + other.mref().descriptor().name());
192            }
193        }
194
195        // clear resources cache if VM is already initialized
196        if (VM.isModuleSystemInited() && resourceCache != null) {
197            resourceCache = null;
198        }
199    }
200
201    /**
202     * Returns the {@code ModuleReference} for the named module defined to
203     * this class loader; or {@code null} if not defined.
204     *
205     * @param name The name of the module to find
206     */
207    protected ModuleReference findModule(String name) {
208        return nameToModule.get(name);
209    }
210
211
212    // -- finding resources
213
214    /**
215     * Returns a URL to a resource of the given name in a module defined to
216     * this class loader.
217     */
218    @Override
219    public URL findResource(String mn, String name) throws IOException {
220        URL url = null;
221
222        if (mn != null) {
223            // find in module
224            ModuleReference mref = nameToModule.get(mn);
225            if (mref != null) {
226                url = findResource(mref, name);
227            }
228        } else {
229            // find on class path
230            url = findResourceOnClassPath(name);
231        }
232
233        return checkURL(url);  // check access before returning
234    }
235
236    /**
237     * Returns an input stream to a resource of the given name in a module
238     * defined to this class loader.
239     */
240    public InputStream findResourceAsStream(String mn, String name)
241        throws IOException
242    {
243        // Need URL to resource when running with a security manager so that
244        // the right permission check is done.
245        if (System.getSecurityManager() != null || mn == null) {
246            URL url = findResource(mn, name);
247            return (url != null) ? url.openStream() : null;
248        }
249
250        // find in module defined to this loader, no security manager
251        ModuleReference mref = nameToModule.get(mn);
252        if (mref != null) {
253            return moduleReaderFor(mref).open(name).orElse(null);
254        } else {
255            return null;
256        }
257    }
258
259    /**
260     * Finds a resource with the given name in the modules defined to this
261     * class loader or its class path.
262     */
263    @Override
264    public URL findResource(String name) {
265        String pn = Resources.toPackageName(name);
266        LoadedModule module = packageToModule.get(pn);
267        if (module != null) {
268
269            // resource is in a package of a module defined to this loader
270            if (module.loader() == this) {
271                URL url;
272                try {
273                    url = findResource(module.name(), name); // checks URL
274                } catch (IOException ioe) {
275                    return null;
276                }
277                if (url != null
278                    && (name.endsWith(".class")
279                        || url.toString().endsWith("/")
280                        || isOpen(module.mref(), pn))) {
281                    return url;
282                }
283            }
284
285        } else {
286
287            // not in a module package but may be in module defined to this loader
288            try {
289                List<URL> urls = findMiscResource(name);
290                if (!urls.isEmpty()) {
291                    URL url = urls.get(0);
292                    if (url != null) {
293                        return checkURL(url); // check access before returning
294                    }
295                }
296            } catch (IOException ioe) {
297                return null;
298            }
299
300        }
301
302        // search class path
303        URL url = findResourceOnClassPath(name);
304        return checkURL(url);
305    }
306
307    /**
308     * Returns an enumeration of URL objects to all the resources with the
309     * given name in modules defined to this class loader or on the class
310     * path of this loader.
311     */
312    @Override
313    public Enumeration<URL> findResources(String name) throws IOException {
314        List<URL> checked = new ArrayList<>();  // list of checked URLs
315
316        String pn = Resources.toPackageName(name);
317        LoadedModule module = packageToModule.get(pn);
318        if (module != null) {
319
320            // resource is in a package of a module defined to this loader
321            if (module.loader() == this) {
322                URL url = findResource(module.name(), name); // checks URL
323                if (url != null
324                    && (name.endsWith(".class")
325                        || url.toString().endsWith("/")
326                        || isOpen(module.mref(), pn))) {
327                    checked.add(url);
328                }
329            }
330
331        } else {
332            // not in a package of a module defined to this loader
333            for (URL url : findMiscResource(name)) {
334                url = checkURL(url);
335                if (url != null) {
336                    checked.add(url);
337                }
338            }
339        }
340
341        // class path (not checked)
342        Enumeration<URL> e = findResourcesOnClassPath(name);
343
344        // concat the checked URLs and the (not checked) class path
345        return new Enumeration<>() {
346            final Iterator<URL> iterator = checked.iterator();
347            URL next;
348            private boolean hasNext() {
349                if (next != null) {
350                    return true;
351                } else if (iterator.hasNext()) {
352                    next = iterator.next();
353                    return true;
354                } else {
355                    // need to check each URL
356                    while (e.hasMoreElements() && next == null) {
357                        next = checkURL(e.nextElement());
358                    }
359                    return next != null;
360                }
361            }
362            @Override
363            public boolean hasMoreElements() {
364                return hasNext();
365            }
366            @Override
367            public URL nextElement() {
368                if (hasNext()) {
369                    URL result = next;
370                    next = null;
371                    return result;
372                } else {
373                    throw new NoSuchElementException();
374                }
375            }
376        };
377
378    }
379
380    /**
381     * Returns the list of URLs to a "miscellaneous" resource in modules
382     * defined to this loader. A miscellaneous resource is not in a module
383     * package, e.g. META-INF/services/p.S.
384     *
385     * The cache used by this method avoids repeated searching of all modules.
386     */
387    private List<URL> findMiscResource(String name) throws IOException {
388        SoftReference<Map<String, List<URL>>> ref = this.resourceCache;
389        Map<String, List<URL>> map = (ref != null) ? ref.get() : null;
390        if (map == null) {
391            map = new ConcurrentHashMap<>();
392            this.resourceCache = new SoftReference<>(map);
393        } else {
394            List<URL> urls = map.get(name);
395            if (urls != null)
396                return urls;
397        }
398
399        // search all modules for the resource
400        List<URL> urls;
401        try {
402            urls = AccessController.doPrivileged(
403                new PrivilegedExceptionAction<>() {
404                    @Override
405                    public List<URL> run() throws IOException {
406                        List<URL> result = null;
407                        for (ModuleReference mref : nameToModule.values()) {
408                            URI u = moduleReaderFor(mref).find(name).orElse(null);
409                            if (u != null) {
410                                try {
411                                    if (result == null)
412                                        result = new ArrayList<>();
413                                    result.add(u.toURL());
414                                } catch (MalformedURLException |
415                                         IllegalArgumentException e) {
416                                }
417                            }
418                        }
419                        return (result != null) ? result : Collections.emptyList();
420                    }
421                });
422        } catch (PrivilegedActionException pae) {
423            throw (IOException) pae.getCause();
424        }
425
426        // only cache resources after VM is fully initialized
427        if (VM.isModuleSystemInited()) {
428            map.putIfAbsent(name, urls);
429        }
430
431        return urls;
432    }
433
434    /**
435     * Returns the URL to a resource in a module or {@code null} if not found.
436     */
437    private URL findResource(ModuleReference mref, String name) throws IOException {
438        URI u;
439        if (System.getSecurityManager() == null) {
440            u = moduleReaderFor(mref).find(name).orElse(null);
441        } else {
442            try {
443                u = AccessController.doPrivileged(new PrivilegedExceptionAction<> () {
444                    @Override
445                    public URI run() throws IOException {
446                        return moduleReaderFor(mref).find(name).orElse(null);
447                    }
448                });
449            } catch (PrivilegedActionException pae) {
450                throw (IOException) pae.getCause();
451            }
452        }
453        if (u != null) {
454            try {
455                return u.toURL();
456            } catch (MalformedURLException | IllegalArgumentException e) { }
457        }
458        return null;
459    }
460
461    /**
462     * Returns the URL to a resource in a module. Returns {@code null} if not found
463     * or an I/O error occurs.
464     */
465    private URL findResourceOrNull(ModuleReference mref, String name) {
466        try {
467            return findResource(mref, name);
468        } catch (IOException ignore) {
469            return null;
470        }
471    }
472
473    /**
474     * Returns a URL to a resource on the class path.
475     */
476    private URL findResourceOnClassPath(String name) {
477        if (hasClassPath()) {
478            if (System.getSecurityManager() == null) {
479                return ucp.findResource(name, false);
480            } else {
481                PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
482                return AccessController.doPrivileged(pa);
483            }
484        } else {
485            // no class path
486            return null;
487        }
488    }
489
490    /**
491     * Returns the URLs of all resources of the given name on the class path.
492     */
493    private Enumeration<URL> findResourcesOnClassPath(String name) {
494        if (hasClassPath()) {
495            if (System.getSecurityManager() == null) {
496                return ucp.findResources(name, false);
497            } else {
498                PrivilegedAction<Enumeration<URL>> pa;
499                pa = () -> ucp.findResources(name, false);
500                return AccessController.doPrivileged(pa);
501            }
502        } else {
503            // no class path
504            return Collections.emptyEnumeration();
505        }
506    }
507
508    // -- finding/loading classes
509
510    /**
511     * Finds the class with the specified binary name.
512     */
513    @Override
514    protected Class<?> findClass(String cn) throws ClassNotFoundException {
515        // no class loading until VM is fully initialized
516        if (!VM.isModuleSystemInited())
517            throw new ClassNotFoundException(cn);
518
519        // find the candidate module for this class
520        LoadedModule loadedModule = findLoadedModule(cn);
521
522        Class<?> c = null;
523        if (loadedModule != null) {
524
525            // attempt to load class in module defined to this loader
526            if (loadedModule.loader() == this) {
527                c = findClassInModuleOrNull(loadedModule, cn);
528            }
529
530        } else {
531
532            // search class path
533            if (hasClassPath()) {
534                c = findClassOnClassPathOrNull(cn);
535            }
536
537        }
538
539        // not found
540        if (c == null)
541            throw new ClassNotFoundException(cn);
542
543        return c;
544    }
545
546    /**
547     * Finds the class with the specified binary name in a module.
548     * This method returns {@code null} if the class cannot be found
549     * or not defined in the specified module.
550     */
551    @Override
552    protected Class<?> findClass(String mn, String cn) {
553        if (mn != null) {
554            // find the candidate module for this class
555            LoadedModule loadedModule = findLoadedModule(mn, cn);
556            if (loadedModule == null) {
557                return null;
558            }
559
560            // attempt to load class in module defined to this loader
561            assert loadedModule.loader() == this;
562            return findClassInModuleOrNull(loadedModule, cn);
563        }
564
565        // search class path
566        if (hasClassPath()) {
567            return findClassOnClassPathOrNull(cn);
568        }
569
570        return null;
571    }
572
573    /**
574     * Loads the class with the specified binary name.
575     */
576    @Override
577    protected Class<?> loadClass(String cn, boolean resolve)
578        throws ClassNotFoundException
579    {
580        Class<?> c = loadClassOrNull(cn, resolve);
581        if (c == null)
582            throw new ClassNotFoundException(cn);
583        return c;
584    }
585
586    /**
587     * A variation of {@code loadCass} to load a class with the specified
588     * binary name. This method returns {@code null} when the class is not
589     * found.
590     */
591    protected Class<?> loadClassOrNull(String cn, boolean resolve) {
592        synchronized (getClassLoadingLock(cn)) {
593            // check if already loaded
594            Class<?> c = findLoadedClass(cn);
595
596            if (c == null) {
597
598                // find the candidate module for this class
599                LoadedModule loadedModule = findLoadedModule(cn);
600                if (loadedModule != null) {
601
602                    // package is in a module
603                    BuiltinClassLoader loader = loadedModule.loader();
604                    if (loader == this) {
605                        if (VM.isModuleSystemInited()) {
606                            c = findClassInModuleOrNull(loadedModule, cn);
607                        }
608                    } else {
609                        // delegate to the other loader
610                        c = loader.loadClassOrNull(cn);
611                    }
612
613                } else {
614
615                    // check parent
616                    if (parent != null) {
617                        c = parent.loadClassOrNull(cn);
618                    }
619
620                    // check class path
621                    if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
622                        c = findClassOnClassPathOrNull(cn);
623                    }
624                }
625
626            }
627
628            if (resolve && c != null)
629                resolveClass(c);
630
631            return c;
632        }
633    }
634
635    /**
636     * A variation of {@code loadCass} to load a class with the specified
637     * binary name. This method returns {@code null} when the class is not
638     * found.
639     */
640    protected  Class<?> loadClassOrNull(String cn) {
641        return loadClassOrNull(cn, false);
642    }
643
644    /**
645     * Find the candidate loaded module for the given class name.
646     * Returns {@code null} if none of the modules defined to this
647     * class loader contain the API package for the class.
648     */
649    private LoadedModule findLoadedModule(String cn) {
650        int pos = cn.lastIndexOf('.');
651        if (pos < 0)
652            return null; // unnamed package
653
654        String pn = cn.substring(0, pos);
655        return packageToModule.get(pn);
656    }
657
658    /**
659     * Find the candidate loaded module for the given class name
660     * in the named module.  Returns {@code null} if the named module
661     * is not defined to this class loader or does not contain
662     * the API package for the class.
663     */
664    private LoadedModule findLoadedModule(String mn, String cn) {
665        LoadedModule loadedModule = findLoadedModule(cn);
666        if (loadedModule != null && mn.equals(loadedModule.name())) {
667            return loadedModule;
668        } else {
669            return null;
670        }
671    }
672
673    /**
674     * Finds the class with the specified binary name if in a module
675     * defined to this ClassLoader.
676     *
677     * @return the resulting Class or {@code null} if not found
678     */
679    private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
680        if (System.getSecurityManager() == null) {
681            return defineClass(cn, loadedModule);
682        } else {
683            PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
684            return AccessController.doPrivileged(pa);
685        }
686    }
687
688    /**
689     * Finds the class with the specified binary name on the class path.
690     *
691     * @return the resulting Class or {@code null} if not found
692     */
693    private Class<?> findClassOnClassPathOrNull(String cn) {
694        String path = cn.replace('.', '/').concat(".class");
695        if (System.getSecurityManager() == null) {
696            Resource res = ucp.getResource(path, false);
697            if (res != null) {
698                try {
699                    return defineClass(cn, res);
700                } catch (IOException ioe) {
701                    // TBD on how I/O errors should be propagated
702                }
703            }
704            return null;
705        } else {
706            // avoid use of lambda here
707            PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
708                public Class<?> run() {
709                    Resource res = ucp.getResource(path, false);
710                    if (res != null) {
711                        try {
712                            return defineClass(cn, res);
713                        } catch (IOException ioe) {
714                            // TBD on how I/O errors should be propagated
715                        }
716                    }
717                    return null;
718                }
719            };
720            return AccessController.doPrivileged(pa);
721        }
722    }
723
724    /**
725     * Defines the given binary class name to the VM, loading the class
726     * bytes from the given module.
727     *
728     * @return the resulting Class or {@code null} if an I/O error occurs
729     */
730    private Class<?> defineClass(String cn, LoadedModule loadedModule) {
731        ModuleReference mref = loadedModule.mref();
732        ModuleReader reader = moduleReaderFor(mref);
733
734        try {
735            ByteBuffer bb = null;
736            URL csURL = null;
737
738            // locate class file, special handling for patched modules to
739            // avoid locating the resource twice
740            String rn = cn.replace('.', '/').concat(".class");
741            if (reader instanceof PatchedModuleReader) {
742                Resource r = ((PatchedModuleReader)reader).findResource(rn);
743                if (r != null) {
744                    bb = r.getByteBuffer();
745                    csURL = r.getCodeSourceURL();
746                }
747            } else {
748                bb = reader.read(rn).orElse(null);
749                csURL = loadedModule.codeSourceURL();
750            }
751
752            if (bb == null) {
753                // class not found
754                return null;
755            }
756
757            CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);
758            try {
759                // define class to VM
760                return defineClass(cn, bb, cs);
761
762            } finally {
763                reader.release(bb);
764            }
765
766        } catch (IOException ioe) {
767            // TBD on how I/O errors should be propagated
768            return null;
769        }
770    }
771
772    /**
773     * Defines the given binary class name to the VM, loading the class
774     * bytes via the given Resource object.
775     *
776     * @return the resulting Class
777     * @throws IOException if reading the resource fails
778     * @throws SecurityException if there is a sealing violation (JAR spec)
779     */
780    private Class<?> defineClass(String cn, Resource res) throws IOException {
781        URL url = res.getCodeSourceURL();
782
783        // if class is in a named package then ensure that the package is defined
784        int pos = cn.lastIndexOf('.');
785        if (pos != -1) {
786            String pn = cn.substring(0, pos);
787            Manifest man = res.getManifest();
788            defineOrCheckPackage(pn, man, url);
789        }
790
791        // defines the class to the runtime
792        ByteBuffer bb = res.getByteBuffer();
793        if (bb != null) {
794            CodeSigner[] signers = res.getCodeSigners();
795            CodeSource cs = new CodeSource(url, signers);
796            return defineClass(cn, bb, cs);
797        } else {
798            byte[] b = res.getBytes();
799            CodeSigner[] signers = res.getCodeSigners();
800            CodeSource cs = new CodeSource(url, signers);
801            return defineClass(cn, b, 0, b.length, cs);
802        }
803    }
804
805
806    // -- packages
807
808    /**
809     * Defines a package in this ClassLoader. If the package is already defined
810     * then its sealing needs to be checked if sealed by the legacy sealing
811     * mechanism.
812     *
813     * @throws SecurityException if there is a sealing violation (JAR spec)
814     */
815    protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
816        Package pkg = getAndVerifyPackage(pn, man, url);
817        if (pkg == null) {
818            try {
819                if (man != null) {
820                    pkg = definePackage(pn, man, url);
821                } else {
822                    pkg = definePackage(pn, null, null, null, null, null, null, null);
823                }
824            } catch (IllegalArgumentException iae) {
825                // defined by another thread so need to re-verify
826                pkg = getAndVerifyPackage(pn, man, url);
827                if (pkg == null)
828                    throw new InternalError("Cannot find package: " + pn);
829            }
830        }
831        return pkg;
832    }
833
834    /**
835     * Get the Package with the specified package name. If defined
836     * then verify that it against the manifest and code source.
837     *
838     * @throws SecurityException if there is a sealing violation (JAR spec)
839     */
840    private Package getAndVerifyPackage(String pn, Manifest man, URL url) {
841        Package pkg = getDefinedPackage(pn);
842        if (pkg != null) {
843            if (pkg.isSealed()) {
844                if (!pkg.isSealed(url)) {
845                    throw new SecurityException(
846                        "sealing violation: package " + pn + " is sealed");
847                }
848            } else {
849                // can't seal package if already defined without sealing
850                if ((man != null) && isSealed(pn, man)) {
851                    throw new SecurityException(
852                        "sealing violation: can't seal package " + pn +
853                        ": already defined");
854                }
855            }
856        }
857        return pkg;
858    }
859
860    /**
861     * Defines a new package in this ClassLoader. The attributes in the specified
862     * Manifest are use to get the package version and sealing information.
863     *
864     * @throws IllegalArgumentException if the package name duplicates an
865     * existing package either in this class loader or one of its ancestors
866     */
867    private Package definePackage(String pn, Manifest man, URL url) {
868        String specTitle = null;
869        String specVersion = null;
870        String specVendor = null;
871        String implTitle = null;
872        String implVersion = null;
873        String implVendor = null;
874        String sealed = null;
875        URL sealBase = null;
876
877        if (man != null) {
878            Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/"));
879            if (attr != null) {
880                specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
881                specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
882                specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
883                implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
884                implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
885                implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
886                sealed = attr.getValue(Attributes.Name.SEALED);
887            }
888
889            attr = man.getMainAttributes();
890            if (attr != null) {
891                if (specTitle == null)
892                    specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
893                if (specVersion == null)
894                    specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
895                if (specVendor == null)
896                    specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
897                if (implTitle == null)
898                    implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
899                if (implVersion == null)
900                    implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
901                if (implVendor == null)
902                    implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
903                if (sealed == null)
904                    sealed = attr.getValue(Attributes.Name.SEALED);
905            }
906
907            // package is sealed
908            if ("true".equalsIgnoreCase(sealed))
909                sealBase = url;
910        }
911        return definePackage(pn,
912                specTitle,
913                specVersion,
914                specVendor,
915                implTitle,
916                implVersion,
917                implVendor,
918                sealBase);
919    }
920
921    /**
922     * Returns {@code true} if the specified package name is sealed according to
923     * the given manifest.
924     */
925    private boolean isSealed(String pn, Manifest man) {
926        String path = pn.replace('.', '/').concat("/");
927        Attributes attr = man.getAttributes(path);
928        String sealed = null;
929        if (attr != null)
930            sealed = attr.getValue(Attributes.Name.SEALED);
931        if (sealed == null && (attr = man.getMainAttributes()) != null)
932            sealed = attr.getValue(Attributes.Name.SEALED);
933        return "true".equalsIgnoreCase(sealed);
934    }
935
936    // -- permissions
937
938    /**
939     * Returns the permissions for the given CodeSource.
940     */
941    @Override
942    protected PermissionCollection getPermissions(CodeSource cs) {
943        PermissionCollection perms = super.getPermissions(cs);
944
945        // add the permission to access the resource
946        URL url = cs.getLocation();
947        if (url == null)
948            return perms;
949        Permission p = null;
950        try {
951            p = url.openConnection().getPermission();
952            if (p != null) {
953                // for directories then need recursive access
954                if (p instanceof FilePermission) {
955                    String path = p.getName();
956                    if (path.endsWith(File.separator)) {
957                        path += "-";
958                        p = new FilePermission(path, "read");
959                    }
960                }
961                perms.add(p);
962            }
963        } catch (IOException ioe) { }
964
965        return perms;
966    }
967
968
969    // -- miscellaneous supporting methods
970
971    /**
972     * Returns the ModuleReader for the given module.
973     */
974    private ModuleReader moduleReaderFor(ModuleReference mref) {
975        return moduleToReader.computeIfAbsent(mref, BuiltinClassLoader::createModuleReader);
976    }
977
978    /**
979     * Creates a ModuleReader for the given module.
980     */
981    private static ModuleReader createModuleReader(ModuleReference mref) {
982        try {
983            return mref.open();
984        } catch (IOException e) {
985            // Return a null module reader to avoid a future class load
986            // attempting to open the module again.
987            return new NullModuleReader();
988        }
989    }
990
991    /**
992     * A ModuleReader that doesn't read any resources.
993     */
994    private static class NullModuleReader implements ModuleReader {
995        @Override
996        public Optional<URI> find(String name) {
997            return Optional.empty();
998        }
999        @Override
1000        public Stream<String> list() {
1001            return Stream.empty();
1002        }
1003        @Override
1004        public void close() {
1005            throw new InternalError("Should not get here");
1006        }
1007    };
1008
1009    /**
1010     * Returns true if the given module opens the given package
1011     * unconditionally.
1012     *
1013     * @implNote This method currently iterates over each of the open
1014     * packages. This will be replaced once the ModuleDescriptor.Opens
1015     * API is updated.
1016     */
1017    private boolean isOpen(ModuleReference mref, String pn) {
1018        ModuleDescriptor descriptor = mref.descriptor();
1019        if (descriptor.isOpen() || descriptor.isAutomatic())
1020            return true;
1021        for (ModuleDescriptor.Opens opens : descriptor.opens()) {
1022            String source = opens.source();
1023            if (!opens.isQualified() && source.equals(pn)) {
1024                return true;
1025            }
1026        }
1027        return false;
1028    }
1029
1030    /**
1031     * Checks access to the given URL. We use URLClassPath for consistent
1032     * checking with java.net.URLClassLoader.
1033     */
1034    private static URL checkURL(URL url) {
1035        return URLClassPath.checkURL(url);
1036    }
1037}
1038