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
26 package jdk.internal.loader;
27
28 import java.io.File;
29 import java.io.FilePermission;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.lang.module.ModuleReference;
33 import java.lang.module.ModuleReader;
34 import java.net.MalformedURLException;
35 import java.net.URI;
36 import java.net.URL;
37 import java.nio.ByteBuffer;
38 import java.security.AccessController;
39 import java.security.CodeSigner;
40 import java.security.CodeSource;
41 import java.security.Permission;
42 import java.security.PermissionCollection;
43 import java.security.PrivilegedAction;
44 import java.security.PrivilegedActionException;
45 import java.security.PrivilegedExceptionAction;
46 import java.security.SecureClassLoader;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.Enumeration;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Optional;
53 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.jar.Attributes;
55 import java.util.jar.Manifest;
56
57 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
58 import jdk.internal.misc.VM;
59
60
61 /**
62 * The platform or application class loader. Resources loaded from modules
63 * defined to the boot class loader are also loaded via an instance of this
64 * ClassLoader type.
65 *
66 * <p> This ClassLoader supports loading of classes and resources from modules.
67 * Modules are defined to the ClassLoader by invoking the {@link #loadModule}
68 * method. Defining a module to this ClassLoader has the effect of making the
69 * types in the module visible. </p>
70 *
71 * <p> This ClassLoader also supports loading of classes and resources from a
72 * class path of URLs that are specified to the ClassLoader at construction
73 * time. The class path may expand at runtime (the Class-Path attribute in JAR
74 * files or via instrumentation agents). </p>
75 *
76 * <p> The delegation model used by this ClassLoader differs to the regular
77 * delegation model. When requested to load a class then this ClassLoader first
78 * maps the class name to its package name. If there is a module defined to a
79 * BuiltinClassLoader containing this package then the class loader delegates
80 * directly to that class loader. If there isn't a module containing the
81 * package then it delegates the search to the parent class loader and if not
82 * found in the parent then it searches the class path. The main difference
83 * between this and the usual delegation model is that it allows the platform
84 * class loader to delegate to the application class loader, important with
85 * upgraded modules defined to the platform class loader.
86 */
87
88 public class BuiltinClassLoader
89 extends SecureClassLoader
90 {
91 static {
92 if (!ClassLoader.registerAsParallelCapable())
93 throw new InternalError();
94 }
95
96 // parent ClassLoader
97 private final BuiltinClassLoader parent;
98
99 // the URL class path or null if there is no class path
100 private final URLClassPath ucp;
101
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 define classes.
108 */
109 private static class LoadedModule {
110 private final BuiltinClassLoader loader;
111 private final ModuleReference mref;
112 private final URL codeSourceURL; // may be null
113
114 LoadedModule(BuiltinClassLoader loader, ModuleReference mref) {
115 URL url = null;
116 if (mref.location().isPresent()) {
117 try {
118 url = mref.location().getWhenPresent().toURL();
119 } catch (MalformedURLException e) { }
120 }
121 this.loader = loader;
122 this.mref = mref;
123 this.codeSourceURL = url;
124 }
125
126 BuiltinClassLoader loader() { return loader; }
127 ModuleReference mref() { return mref; }
128 String name() { return mref.descriptor().name(); }
129 URL codeSourceURL() { return codeSourceURL; }
130 }
131
132
133 // maps package name to loaded module for modules in the boot layer
134 private static final Map<String, LoadedModule> packageToModule
135 = new ConcurrentHashMap<>(1024);
136
137 // maps a module name to a module reference
138 private final Map<String, ModuleReference> nameToModule;
139
140 // maps a module reference to a module reader
141 private final Map<ModuleReference, ModuleReader> moduleToReader;
142
143
144 /**
145 * Create a new instance.
146 */
147 BuiltinClassLoader(BuiltinClassLoader parent, URLClassPath ucp) {
148 // ensure getParent() returns null when the parent is the boot loader
149 super(parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
150
151 this.parent = parent;
152 this.ucp = ucp;
153
154 this.nameToModule = new ConcurrentHashMap<>();
155 this.moduleToReader = new ConcurrentHashMap<>();
156 }
157
158 /**
159 * Register a module this this class loader. This has the effect of making
160 * the types in the module visible.
161 */
162 public void loadModule(ModuleReference mref) {
163 String mn = mref.descriptor().name();
164 if (nameToModule.putIfAbsent(mn, mref) != null) {
165 throw new InternalError(mn + " already defined to this loader");
166 }
167
168 LoadedModule loadedModule = new LoadedModule(this, mref);
169 for (String pn : mref.descriptor().packages()) {
170 LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule);
171 if (other != null) {
172 throw new InternalError(pn + " in modules " + mn + " and "
173 + other.mref().descriptor().name());
174 }
175 }
176 }
177
178 /**
179 * Returns the {@code ModuleReference} for the named module defined to
180 * this class loader; or {@code null} if not defined.
181 *
182 * @param name The name of the module to find
183 */
184 protected ModuleReference findModule(String name) {
185 return nameToModule.get(name);
186 }
187
188
189 // -- finding resources
190
191 /**
192 * Returns a URL to a resource of the given name in a module defined to
193 * this class loader.
194 */
195 @Override
196 public URL findResource(String mn, String name) throws IOException {
197 ModuleReference mref = nameToModule.get(mn);
198 if (mref == null)
199 return null; // not defined to this class loader
200
201 URL url;
202
203 try {
204 url = AccessController.doPrivileged(
205 new PrivilegedExceptionAction<URL>() {
206 @Override
207 public URL run() throws IOException {
208 URI u = moduleReaderFor(mref).find(name).orElse(null);
209 if (u != null) {
210 try {
211 return u.toURL();
212 } catch (MalformedURLException e) { }
213 }
214 return null;
215 }
216 });
217 } catch (PrivilegedActionException pae) {
218 throw (IOException) pae.getCause();
219 }
220
221 // check access to the URL
222 return checkURL(url);
223 }
224
225 /**
226 * Returns an input stream to a resource of the given name in a module
227 * defined to this class loader.
228 */
229 public InputStream findResourceAsStream(String mn, String name)
230 throws IOException
231 {
232 // Need URL to resource when running with a security manager so that
233 // the right permission check is done.
234 SecurityManager sm = System.getSecurityManager();
235 if (sm != null) {
236
237 URL url = findResource(mn, name);
238 return (url != null) ? url.openStream() : null;
239
240 } else {
241
242 ModuleReference mref = nameToModule.get(mn);
243 if (mref == null)
244 return null; // not defined to this class loader
245
246 try {
247 return AccessController.doPrivileged(
248 new PrivilegedExceptionAction<InputStream>() {
249 @Override
250 public InputStream run() throws IOException {
251 return moduleReaderFor(mref).open(name).orElse(null);
252 }
253 });
254 } catch (PrivilegedActionException pae) {
255 throw (IOException) pae.getCause();
256 }
257 }
258 }
259
260 /**
261 * Finds the resource with the given name on the class path of this class
262 * loader.
263 */
264 @Override
265 public URL findResource(String name) {
266 if (ucp != null) {
267 PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
268 URL url = AccessController.doPrivileged(pa);
269 return checkURL(url);
270 } else {
271 return null;
272 }
273 }
274
275 /**
276 * Returns an enumeration of URL objects to all the resources with the
277 * given name on the class path of this class loader.
278 */
279 @Override
280 public Enumeration<URL> findResources(String name) throws IOException {
281 if (ucp != null) {
282 List<URL> result = new ArrayList<>();
283 PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false);
284 Enumeration<URL> e = AccessController.doPrivileged(pa);
285 while (e.hasMoreElements()) {
286 URL url = checkURL(e.nextElement());
287 if (url != null) {
288 result.add(url);
289 }
290 }
291 return Collections.enumeration(result); // checked URLs
292 } else {
293 return Collections.emptyEnumeration();
294 }
295 }
296
297
298 // -- finding/loading classes
299
300 /**
301 * Finds the class with the specified binary name.
302 */
303 @Override
304 protected Class<?> findClass(String cn) throws ClassNotFoundException {
305 // no class loading until VM is fully initialized
306 if (!VM.isModuleSystemInited())
307 throw new ClassNotFoundException(cn);
308
309 // find the candidate module for this class
310 LoadedModule loadedModule = findLoadedModule(cn);
311
312 Class<?> c = null;
313 if (loadedModule != null) {
314
315 // attempt to load class in module defined to this loader
316 if (loadedModule.loader() == this) {
317 c = findClassInModuleOrNull(loadedModule, cn);
318 }
319
320 } else {
321
322 // search class path
323 if (ucp != null) {
324 c = findClassOnClassPathOrNull(cn);
325 }
326
327 }
328
329 // not found
330 if (c == null)
331 throw new ClassNotFoundException(cn);
332
333 return c;
334 }
335
336 /**
337 * Finds the class with the specified binary name in a given module.
338 * This method returns {@code null} if the class cannot be found.
339 */
340 @Override
341 protected Class<?> findClass(String mn, String cn) {
342 ModuleReference mref = nameToModule.get(mn);
343 if (mref == null)
344 return null; // not defined to this class loader
345
346 // find the candidate module for this class
347 LoadedModule loadedModule = findLoadedModule(cn);
348 if (loadedModule == null || !loadedModule.name().equals(mn)) {
349 return null; // module name does not match
350 }
351
352 // attempt to load class in module defined to this loader
353 assert loadedModule.loader() == this;
354 return findClassInModuleOrNull(loadedModule, cn);
355 }
356
357 /**
358 * Loads the class with the specified binary name.
359 */
360 @Override
361 protected Class<?> loadClass(String cn, boolean resolve)
362 throws ClassNotFoundException
363 {
364 Class<?> c = loadClassOrNull(cn, resolve);
365 if (c == null)
366 throw new ClassNotFoundException(cn);
367 return c;
368 }
369
370 /**
371 * A variation of {@code loadCass} to load a class with the specified
372 * binary name. This method returns {@code null} when the class is not
373 * found.
374 */
375 protected Class<?> loadClassOrNull(String cn, boolean resolve) {
376 synchronized (getClassLoadingLock(cn)) {
377 // check if already loaded
378 Class<?> c = findLoadedClass(cn);
379
380 if (c == null) {
381
382 // find the candidate module for this class
383 LoadedModule loadedModule = findLoadedModule(cn);
384 if (loadedModule != null) {
385
386 // package is in a module
387 BuiltinClassLoader loader = loadedModule.loader();
388 if (loader == this) {
389 if (VM.isModuleSystemInited()) {
390 c = findClassInModuleOrNull(loadedModule, cn);
391 }
392 } else {
393 // delegate to the other loader
394 c = loader.loadClassOrNull(cn);
395 }
396
397 } else {
398
399 // check parent
400 if (parent != null) {
401 c = parent.loadClassOrNull(cn);
402 }
403
404 // check class path
405 if (c == null && ucp != null && VM.isModuleSystemInited()) {
406 c = findClassOnClassPathOrNull(cn);
407 }
408 }
409
410 }
411
412 if (resolve && c != null)
413 resolveClass(c);
414
415 return c;
416 }
417 }
418
419 /**
420 * A variation of {@code loadCass} to load a class with the specified
421 * binary name. This method returns {@code null} when the class is not
422 * found.
423 */
424 protected Class<?> loadClassOrNull(String cn) {
425 return loadClassOrNull(cn, false);
426 }
427
428 /**
429 * Find the candidate loaded module for the given class name.
430 * Returns {@code null} if none of the modules defined to this
431 * class loader contain the API package for the class.
432 */
433 private LoadedModule findLoadedModule(String cn) {
434 int pos = cn.lastIndexOf('.');
435 if (pos < 0)
436 return null; // unnamed package
437
438 String pn = cn.substring(0, pos);
439 return packageToModule.get(pn);
440 }
441
442 /**
443 * Finds the class with the specified binary name if in a module
444 * defined to this ClassLoader.
445 *
446 * @return the resulting Class or {@code null} if not found
447 */
448 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
449 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
450 return AccessController.doPrivileged(pa);
451 }
452
453 /**
454 * Finds the class with the specified binary name on the class path.
455 *
456 * @return the resulting Class or {@code null} if not found
457 */
458 private Class<?> findClassOnClassPathOrNull(String cn) {
459 return AccessController.doPrivileged(
460 new PrivilegedAction<Class<?>>() {
461 public Class<?> run() {
462 String path = cn.replace('.', '/').concat(".class");
463 Resource res = ucp.getResource(path, false);
464 if (res != null) {
465 try {
466 return defineClass(cn, res);
467 } catch (IOException ioe) {
468 // TBD on how I/O errors should be propagated
469 }
470 }
471 return null;
472 }
473 });
474 }
475
476 /**
477 * Defines the given binary class name to the VM, loading the class
478 * bytes from the given module.
479 *
480 * @return the resulting Class or {@code null} if an I/O error occurs
481 */
482 private Class<?> defineClass(String cn, LoadedModule loadedModule) {
483 ModuleReference mref = loadedModule.mref();
484 ModuleReader reader = moduleReaderFor(mref);
485
486 try {
487 ByteBuffer bb = null;
488 URL csURL = null;
489
490 // locate class file, special handling for patched modules to
491 // avoid locating the resource twice
492 String rn = cn.replace('.', '/').concat(".class");
493 if (reader instanceof PatchedModuleReader) {
494 Resource r = ((PatchedModuleReader)reader).findResource(rn);
495 if (r != null) {
496 bb = r.getByteBuffer();
497 csURL = r.getCodeSourceURL();
498 }
499 } else {
500 bb = reader.read(rn).orElse(null);
501 csURL = loadedModule.codeSourceURL();
502 }
503
504 if (bb == null) {
505 // class not found
506 return null;
507 }
508
509 CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);
510 try {
511 // define class to VM
512 return defineClass(cn, bb, cs);
513
514 } finally {
515 reader.release(bb);
516 }
517
518 } catch (IOException ioe) {
519 // TBD on how I/O errors should be propagated
520 return null;
521 }
522 }
523
524 /**
525 * Defines the given binary class name to the VM, loading the class
526 * bytes via the given Resource object.
527 *
528 * @return the resulting Class
529 * @throws IOException if reading the resource fails
530 * @throws SecurityException if there is a sealing violation (JAR spec)
531 */
532 private Class<?> defineClass(String cn, Resource res) throws IOException {
533 URL url = res.getCodeSourceURL();
534
535 // if class is in a named package then ensure that the package is defined
536 int pos = cn.lastIndexOf('.');
537 if (pos != -1) {
538 String pn = cn.substring(0, pos);
539 Manifest man = res.getManifest();
540 defineOrCheckPackage(pn, man, url);
541 }
542
543 // defines the class to the runtime
544 ByteBuffer bb = res.getByteBuffer();
545 if (bb != null) {
546 CodeSigner[] signers = res.getCodeSigners();
547 CodeSource cs = new CodeSource(url, signers);
548 return defineClass(cn, bb, cs);
549 } else {
550 byte[] b = res.getBytes();
551 CodeSigner[] signers = res.getCodeSigners();
552 CodeSource cs = new CodeSource(url, signers);
553 return defineClass(cn, b, 0, b.length, cs);
554 }
555 }
556
557
558 // -- packages
559
560 /**
561 * Defines a package in this ClassLoader. If the package is already defined
562 * then its sealing needs to be checked if sealed by the legacy sealing
563 * mechanism.
564 *
565 * @throws SecurityException if there is a sealing violation (JAR spec)
566 */
567 protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
568 Package pkg = getAndVerifyPackage(pn, man, url);
569 if (pkg == null) {
570 try {
571 if (man != null) {
572 pkg = definePackage(pn, man, url);
573 } else {
574 pkg = definePackage(pn, null, null, null, null, null, null, null);
575 }
576 } catch (IllegalArgumentException iae) {
577 // defined by another thread so need to re-verify
578 pkg = getAndVerifyPackage(pn, man, url);
579 if (pkg == null)
580 throw new InternalError("Cannot find package: " + pn);
581 }
582 }
583 return pkg;
584 }
585
586 /**
587 * Get the Package with the specified package name. If defined
588 * then verify that it against the manifest and code source.
589 *
590 * @throws SecurityException if there is a sealing violation (JAR spec)
591 */
592 private Package getAndVerifyPackage(String pn, Manifest man, URL url) {
593 Package pkg = getDefinedPackage(pn);
594 if (pkg != null) {
595 if (pkg.isSealed()) {
596 if (!pkg.isSealed(url)) {
597 throw new SecurityException(
598 "sealing violation: package " + pn + " is sealed");
599 }
600 } else {
601 // can't seal package if already defined without sealing
602 if ((man != null) && isSealed(pn, man)) {
603 throw new SecurityException(
604 "sealing violation: can't seal package " + pn +
605 ": already defined");
606 }
607 }
608 }
609 return pkg;
610 }
611
612 /**
613 * Defines a new package in this ClassLoader. The attributes in the specified
614 * Manifest are use to get the package version and sealing information.
615 *
616 * @throws IllegalArgumentException if the package name duplicates an
617 * existing package either in this class loader or one of its ancestors
618 */
619 private Package definePackage(String pn, Manifest man, URL url) {
620 String specTitle = null;
621 String specVersion = null;
622 String specVendor = null;
623 String implTitle = null;
624 String implVersion = null;
625 String implVendor = null;
626 String sealed = null;
627 URL sealBase = null;
628
629 if (man != null) {
630 Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/"));
631 if (attr != null) {
632 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
633 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
634 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
635 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
636 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
637 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
638 sealed = attr.getValue(Attributes.Name.SEALED);
639 }
640
641 attr = man.getMainAttributes();
642 if (attr != null) {
643 if (specTitle == null)
644 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
645 if (specVersion == null)
646 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
647 if (specVendor == null)
648 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
649 if (implTitle == null)
650 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
651 if (implVersion == null)
652 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
653 if (implVendor == null)
654 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
655 if (sealed == null)
656 sealed = attr.getValue(Attributes.Name.SEALED);
657 }
658
659 // package is sealed
660 if ("true".equalsIgnoreCase(sealed))
661 sealBase = url;
662 }
663 return definePackage(pn,
664 specTitle,
665 specVersion,
666 specVendor,
667 implTitle,
668 implVersion,
669 implVendor,
670 sealBase);
671 }
672
673 /**
674 * Returns {@code true} if the specified package name is sealed according to
675 * the given manifest.
676 */
677 private boolean isSealed(String pn, Manifest man) {
678 String path = pn.replace('.', '/').concat("/");
679 Attributes attr = man.getAttributes(path);
680 String sealed = null;
681 if (attr != null)
682 sealed = attr.getValue(Attributes.Name.SEALED);
683 if (sealed == null && (attr = man.getMainAttributes()) != null)
684 sealed = attr.getValue(Attributes.Name.SEALED);
685 return "true".equalsIgnoreCase(sealed);
686 }
687
688 // -- permissions
689
690 /**
691 * Returns the permissions for the given CodeSource.
692 */
693 @Override
694 protected PermissionCollection getPermissions(CodeSource cs) {
695 PermissionCollection perms = super.getPermissions(cs);
696
697 // add the permission to access the resource
698 URL url = cs.getLocation();
699 if (url == null)
700 return perms;
701 Permission p = null;
702 try {
703 p = url.openConnection().getPermission();
704 if (p != null) {
705 // for directories then need recursive access
706 if (p instanceof FilePermission) {
707 String path = p.getName();
708 if (path.endsWith(File.separator)) {
709 path += "-";
710 p = new FilePermission(path, "read");
711 }
712 }
713 perms.add(p);
714 }
715 } catch (IOException ioe) { }
716
717 return perms;
718 }
719
720
721 // -- miscellaneous supporting methods
722
723 /**
724 * Returns the ModuleReader for the given module.
725 */
726 private ModuleReader moduleReaderFor(ModuleReference mref) {
727 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref));
728 }
729
730 /**
731 * Creates a ModuleReader for the given module.
732 */
733 private ModuleReader createModuleReader(ModuleReference mref) {
734 try {
735 return mref.open();
736 } catch (IOException e) {
737 // Return a null module reader to avoid a future class load
738 // attempting to open the module again.
739 return new NullModuleReader();
740 }
741 }
742
743 /**
744 * A ModuleReader that doesn't read any resources.
745 */
746 private static class NullModuleReader implements ModuleReader {
747 @Override
748 public Optional<URI> find(String name) {
749 return Optional.empty();
750 }
751 @Override
752 public void close() {
753 throw new InternalError("Should not get here");
754 }
755 };
756
757 /**
758 * Checks access to the given URL. We use URLClassPath for consistent
759 * checking with java.net.URLClassLoader.
760 */
761 private static URL checkURL(URL url) {
762 return URLClassPath.checkURL(url);
763 }
764 }