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