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