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.lang.module.Configuration;
  32 import java.lang.module.ModuleDescriptor;
  33 import java.lang.module.ModuleReader;
  34 import java.lang.module.ModuleReference;
  35 import java.lang.module.ResolvedModule;
  36 import java.lang.reflect.Layer;
  37 import java.net.MalformedURLException;
  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.nio.ByteBuffer;
  41 import java.security.AccessControlContext;
  42 import java.security.AccessController;
  43 import java.security.CodeSigner;
  44 import java.security.CodeSource;
  45 import java.security.Permission;
  46 import java.security.PermissionCollection;
  47 import java.security.PrivilegedAction;
  48 import java.security.PrivilegedActionException;
  49 import java.security.PrivilegedExceptionAction;
  50 import java.security.SecureClassLoader;
  51 import java.util.Collection;
  52 import java.util.HashMap;
  53 import java.util.Map;
  54 import java.util.Optional;
  55 import java.util.concurrent.ConcurrentHashMap;
  56 
  57 
  58 /**
  59  * A class loader that loads classes and resources from a collection of
  60  * modules, or from a single module where the class loader is a member
  61  * of a pool of class loaders.
  62  *
  63  * <p> The delegation model used by this ClassLoader differs to the regular
  64  * delegation model. When requested to load a class then this ClassLoader first
  65  * maps the class name to its package name. If there a module defined to the
  66  * Loader containing the package then the class loader attempts to load from
  67  * that module. If the package is instead defined to a module in a "remote"
  68  * ClassLoader then this class loader delegates directly to that class loader.
  69  * The map of package name to remote class loader is created based on the
  70  * modules read by modules defined to this class loader. If the package is not
  71  * local or remote then this class loader will delegate to the parent class
  72  * loader. This allows automatic modules (for example) to link to types in the
  73  * unnamed module of the parent class loader.
  74  *
  75  * @see Layer#createWithOneLoader
  76  * @see Layer#createWithManyLoaders
  77  */
  78 
  79 public final class Loader extends SecureClassLoader {
  80 
  81     static {
  82         ClassLoader.registerAsParallelCapable();
  83     }
  84 
  85     // the loader pool is in a pool, can be null
  86     private final LoaderPool pool;
  87 
  88     // parent ClassLoader, can be null
  89     private final ClassLoader parent;
  90 
  91     // maps a module name to a module reference
  92     private final Map<String, ModuleReference> nameToModule;
  93 
  94     // maps package name to a module loaded by this class loader
  95     private final Map<String, LoadedModule> localPackageToModule;
  96 
  97     // maps package name to a remote class loader, populated post initialization
  98     private final Map<String, ClassLoader> remotePackageToLoader
  99         = new ConcurrentHashMap<>();
 100 
 101     // maps a module reference to a module reader, populated lazily
 102     private final Map<ModuleReference, ModuleReader> moduleToReader
 103         = new ConcurrentHashMap<>();
 104 
 105     // ACC used when loading classes and resources */
 106     private final AccessControlContext acc;
 107 
 108     /**
 109      * A module defined/loaded to a {@code Loader}.
 110      */
 111     private static class LoadedModule {
 112         private final ModuleReference mref;
 113         private final URL url;          // may be null
 114         private final CodeSource cs;
 115 
 116         LoadedModule(ModuleReference mref) {
 117             URL url = null;
 118             if (mref.location().isPresent()) {
 119                 try {
 120                     url = mref.location().get().toURL();
 121                 } catch (MalformedURLException e) { }
 122             }
 123             this.mref = mref;
 124             this.url = url;
 125             this.cs = new CodeSource(url, (CodeSigner[]) null);
 126         }
 127 
 128         ModuleReference mref() { return mref; }
 129         String name() { return mref.descriptor().name(); }
 130         URL location() { return url; }
 131         CodeSource codeSource() { return cs; }
 132     }
 133 
 134 
 135     /**
 136      * Creates a {@code Loader} in a loader pool that loads classes/resources
 137      * from one module.
 138      */
 139     public Loader(ResolvedModule resolvedModule,
 140                   LoaderPool pool,
 141                   ClassLoader parent)
 142     {
 143         super(parent);
 144 
 145         this.pool = pool;
 146         this.parent = parent;
 147 
 148         ModuleReference mref = resolvedModule.reference();
 149         ModuleDescriptor descriptor = mref.descriptor();
 150         String mn = descriptor.name();
 151         this.nameToModule = Map.of(mn, mref);
 152 
 153         Map<String, LoadedModule> localPackageToModule = new HashMap<>();
 154         LoadedModule lm = new LoadedModule(mref);
 155         descriptor.packages().forEach(pn -> localPackageToModule.put(pn, lm));
 156         this.localPackageToModule = localPackageToModule;
 157 
 158         this.acc = AccessController.getContext();
 159     }
 160 
 161     /**
 162      * Creates a {@code Loader} that loads classes/resources from a collection
 163      * of modules.
 164      *
 165      * @throws IllegalArgumentException
 166      *         If two or more modules have the same package
 167      */
 168     public Loader(Collection<ResolvedModule> modules, ClassLoader parent) {
 169         super(parent);
 170 
 171         this.pool = null;
 172         this.parent = parent;
 173 
 174         Map<String, ModuleReference> nameToModule = new HashMap<>();
 175         Map<String, LoadedModule> localPackageToModule = new HashMap<>();
 176         for (ResolvedModule resolvedModule : modules) {
 177             ModuleReference mref = resolvedModule.reference();
 178             ModuleDescriptor descriptor = mref.descriptor();
 179             nameToModule.put(descriptor.name(), mref);
 180             descriptor.packages().forEach(pn -> {
 181                 LoadedModule lm = new LoadedModule(mref);
 182                 if (localPackageToModule.put(pn, lm) != null)
 183                     throw new IllegalArgumentException("Package "
 184                         + pn + " in more than one module");
 185             });
 186         }
 187         this.nameToModule = nameToModule;
 188         this.localPackageToModule = localPackageToModule;
 189 
 190         this.acc = AccessController.getContext();
 191     }
 192 
 193 
 194     /**
 195      * Completes initialization of this Loader. This method populates
 196      * remotePackageToLoader with the packages of the remote modules, where
 197      * "remote modules" are the modules read by modules defined to this loader.
 198      *
 199      * @param cf the Configuration containing at least modules to be defined to
 200      *           this class loader
 201      *
 202      * @param parentLayer the parent Layer
 203      */
 204     public Loader initRemotePackageMap(Configuration cf, Layer parentLayer) {
 205 
 206         for (String name : nameToModule.keySet()) {
 207 
 208             ResolvedModule resolvedModule = cf.findModule(name).get();
 209             assert resolvedModule.configuration() == cf;
 210 
 211             for (ResolvedModule other : resolvedModule.reads()) {
 212                 String mn = other.name();
 213                 ClassLoader loader;
 214 
 215                 if (other.configuration() == cf) {
 216 
 217                     // The module reads another module in the newly created
 218                     // layer. If all modules are defined to the same class
 219                     // loader then the packages are local.
 220                     if (pool == null) {
 221                         assert nameToModule.containsKey(mn);
 222                         continue;
 223                     }
 224 
 225                     loader = pool.loaderFor(mn);
 226                     assert loader != null;
 227 
 228                 } else {
 229 
 230                     // find the layer contains the module that is read
 231                     Layer layer = parentLayer;
 232                     while (layer != null) {
 233                         if (layer.configuration() == other.configuration()) {
 234                             break;
 235                         }
 236                         layer = layer.parent().orElse(null);
 237                     }
 238                     assert layer != null;
 239 
 240                     // find the class loader for the module in the layer
 241                     // For now we use the platform loader for modules defined to the
 242                     // boot loader
 243                     assert layer.findModule(mn).isPresent();
 244                     loader = layer.findLoader(mn);
 245                     if (loader == null)
 246                         loader = ClassLoaders.platformClassLoader();
 247                 }
 248 
 249                 // find the packages that are exported to the target module
 250                 String target = resolvedModule.name();
 251                 ModuleDescriptor descriptor = other.reference().descriptor();
 252                 for (ModuleDescriptor.Exports e : descriptor.exports()) {
 253                     boolean delegate;
 254                     if (e.isQualified()) {
 255                         // qualified export in same configuration
 256                         delegate = (other.configuration() == cf)
 257                                 && e.targets().contains(target);
 258                     } else {
 259                         // unqualified
 260                         delegate = true;
 261                     }
 262 
 263                     if (delegate) {
 264                         String pn = e.source();
 265                         ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader);
 266                         if (l != null && l != loader) {
 267                             throw new IllegalArgumentException("Package "
 268                                 + pn + " cannot be imported from multiple loaders");
 269                         }
 270 
 271                     }
 272                 }
 273             }
 274 
 275         }
 276 
 277         return this;
 278     }
 279 
 280     /**
 281      * Returns the loader pool that this loader is in or {@code null} if this
 282      * loader is not in a loader pool.
 283      */
 284     public LoaderPool pool() {
 285         return pool;
 286     }
 287 
 288 
 289     // -- resources --
 290 
 291 
 292     /**
 293      * Returns a URL to a resource of the given name in a module defined to
 294      * this class loader.
 295      */
 296     @Override
 297     protected URL findResource(String mn, String name) throws IOException {
 298         ModuleReference mref = nameToModule.get(mn);
 299         if (mref == null)
 300             return null;   // not defined to this class loader
 301 
 302         try {
 303             return AccessController.doPrivileged(
 304                 new PrivilegedExceptionAction<URL>() {
 305                     @Override
 306                     public URL run() throws IOException {
 307                         Optional<URI> ouri = moduleReaderFor(mref).find(name);
 308                         if (ouri.isPresent()) {
 309                             try {
 310                                 return ouri.get().toURL();
 311                             } catch (MalformedURLException e) { }
 312                         }
 313                         return null;
 314                     }
 315                 }, acc);
 316         } catch (PrivilegedActionException pae) {
 317             throw (IOException) pae.getCause();
 318         }
 319     }
 320 
 321 
 322     // -- finding/loading classes
 323 
 324     /**
 325      * Finds the class with the specified binary name.
 326      */
 327     @Override
 328     protected Class<?> findClass(String cn) throws ClassNotFoundException {
 329         Class<?> c = null;
 330         LoadedModule loadedModule = findLoadedModule(cn);
 331         if (loadedModule != null)
 332             c = findClassInModuleOrNull(loadedModule, cn);
 333         if (c == null)
 334             throw new ClassNotFoundException(cn);
 335         return c;
 336     }
 337 
 338     /**
 339      * Finds the class with the specified binary name in a given module.
 340      * This method returns {@code null} if the class cannot be found.
 341      */
 342     @Override
 343     protected Class<?> findClass(String mn, String cn) {
 344         Class<?> c = null;
 345         LoadedModule loadedModule = findLoadedModule(cn);
 346         if (loadedModule != null && loadedModule.name().equals(mn))
 347             c = findClassInModuleOrNull(loadedModule, cn);
 348         return c;
 349     }
 350 
 351     /**
 352      * Loads the class with the specified binary name.
 353      */
 354     @Override
 355     protected Class<?> loadClass(String cn, boolean resolve)
 356         throws ClassNotFoundException
 357     {
 358         SecurityManager sm = System.getSecurityManager();
 359         if (sm != null) {
 360             String pn = packageName(cn);
 361             if (!pn.isEmpty()) {
 362                 sm.checkPackageAccess(pn);
 363             }
 364         }
 365 
 366         synchronized (getClassLoadingLock(cn)) {
 367             // check if already loaded
 368             Class<?> c = findLoadedClass(cn);
 369 
 370             if (c == null) {
 371 
 372                 LoadedModule loadedModule = findLoadedModule(cn);
 373 
 374                 if (loadedModule != null) {
 375 
 376                     // class is in module defined to this class loader
 377                     c = findClassInModuleOrNull(loadedModule, cn);
 378 
 379                 } else {
 380 
 381                     // type in another module or visible via the parent loader
 382                     String pn = packageName(cn);
 383                     ClassLoader loader = remotePackageToLoader.get(pn);
 384                     if (loader == null) {
 385                         // type not in a module read by any of the modules
 386                         // defined to this loader, so delegate to parent
 387                         // class loader
 388                         loader = parent;
 389                     }
 390                     if (loader == null) {
 391                         c = BootLoader.loadClassOrNull(cn);
 392                     } else {
 393                         c = loader.loadClass(cn);
 394                     }
 395 
 396                 }
 397             }
 398 
 399             if (c == null)
 400                 throw new ClassNotFoundException(cn);
 401 
 402             if (resolve)
 403                 resolveClass(c);
 404 
 405             return c;
 406         }
 407     }
 408 
 409 
 410     /**
 411      * Finds the class with the specified binary name if in a module
 412      * defined to this ClassLoader.
 413      *
 414      * @return the resulting Class or {@code null} if not found
 415      */
 416     private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
 417         PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
 418         return AccessController.doPrivileged(pa, acc);
 419     }
 420 
 421     /**
 422      * Defines the given binary class name to the VM, loading the class
 423      * bytes from the given module.
 424      *
 425      * @return the resulting Class or {@code null} if an I/O error occurs
 426      */
 427     private Class<?> defineClass(String cn, LoadedModule loadedModule) {
 428         ModuleReader reader = moduleReaderFor(loadedModule.mref());
 429 
 430         try {
 431             // read class file
 432             String rn = cn.replace('.', '/').concat(".class");
 433             ByteBuffer bb = reader.read(rn).orElse(null);
 434             if (bb == null) {
 435                 // class not found
 436                 return null;
 437             }
 438 
 439             try {
 440                 return defineClass(cn, bb, loadedModule.codeSource());
 441             } finally {
 442                 reader.release(bb);
 443             }
 444 
 445         } catch (IOException ioe) {
 446             // TBD on how I/O errors should be propagated
 447             return null;
 448         }
 449     }
 450 
 451 
 452     // -- permissions
 453 
 454     /**
 455      * Returns the permissions for the given CodeSource.
 456      */
 457     @Override
 458     protected PermissionCollection getPermissions(CodeSource cs) {
 459         PermissionCollection perms = super.getPermissions(cs);
 460 
 461         URL url = cs.getLocation();
 462         if (url == null)
 463             return perms;
 464 
 465         // add the permission to access the resource
 466         try {
 467             Permission p = url.openConnection().getPermission();
 468             if (p != null) {
 469                 // for directories then need recursive access
 470                 if (p instanceof FilePermission) {
 471                     String path = p.getName();
 472                     if (path.endsWith(File.separator)) {
 473                         path += "-";
 474                         p = new FilePermission(path, "read");
 475                     }
 476                 }
 477                 perms.add(p);
 478             }
 479         } catch (IOException ioe) { }
 480 
 481         return perms;
 482     }
 483 
 484 
 485     // -- miscellaneous supporting methods
 486 
 487     /**
 488      * Find the candidate module for the given class name.
 489      * Returns {@code null} if none of the modules defined to this
 490      * class loader contain the API package for the class.
 491      */
 492     private LoadedModule findLoadedModule(String cn) {
 493         String pn = packageName(cn);
 494         return pn.isEmpty() ? null : localPackageToModule.get(pn);
 495     }
 496 
 497     /**
 498      * Returns the package name for the given class name
 499      */
 500     private String packageName(String cn) {
 501         int pos = cn.lastIndexOf('.');
 502         return (pos < 0) ? "" : cn.substring(0, pos);
 503     }
 504 
 505 
 506     /**
 507      * Returns the ModuleReader for the given module.
 508      */
 509     private ModuleReader moduleReaderFor(ModuleReference mref) {
 510         return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref));
 511     }
 512 
 513     /**
 514      * Creates a ModuleReader for the given module.
 515      */
 516     private ModuleReader createModuleReader(ModuleReference mref) {
 517         try {
 518             return mref.open();
 519         } catch (IOException e) {
 520             // Return a null module reader to avoid a future class load
 521             // attempting to open the module again.
 522             return new NullModuleReader();
 523         }
 524     }
 525 
 526     /**
 527      * A ModuleReader that doesn't read any resources.
 528      */
 529     private static class NullModuleReader implements ModuleReader {
 530         @Override
 531         public Optional<URI> find(String name) {
 532             return Optional.empty();
 533         }
 534         @Override
 535         public void close() {
 536             throw new InternalError("Should not get here");
 537         }
 538     }
 539 
 540 }