8198481: Coding style cleanups for src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
Reviewed-by: mchung, alanb

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