1 /*
   2  * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.loader;
  27 
  28 import java.io.IOException;
  29 import java.net.URL;
  30 import java.nio.file.InvalidPathException;
  31 import java.nio.file.Path;
  32 import java.security.CodeSource;
  33 import java.security.PermissionCollection;
  34 import java.util.jar.Manifest;
  35 
  36 import jdk.internal.access.JavaLangAccess;
  37 import jdk.internal.access.SharedSecrets;
  38 import jdk.internal.misc.VM;
  39 import jdk.internal.vm.annotation.Stable;
  40 
  41 /**
  42  * Creates and provides access to the built-in platform and application class
  43  * loaders. It also creates the class loader that is used to locate resources
  44  * in modules defined to the boot class loader.
  45  */
  46 
  47 public class ClassLoaders {
  48 
  49     private ClassLoaders() { }
  50 
  51     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  52 
  53     // the built-in class loaders
  54     private static final BootClassLoader BOOT_LOADER;
  55     private static final PlatformClassLoader PLATFORM_LOADER;
  56     private static final AppClassLoader APP_LOADER;
  57 
  58     static class ArchivedData {
  59         private final BootClassLoader boot_loader;
  60         private final PlatformClassLoader platform_loader;
  61         private final AppClassLoader app_loader;
  62 
  63         private ArchivedData() {
  64             boot_loader = BOOT_LOADER;
  65             platform_loader = PLATFORM_LOADER;
  66             app_loader = APP_LOADER;
  67         }
  68 
  69         static void archive() {
  70             singleton = new ArchivedData();
  71         }
  72 
  73         static ArchivedData get() {
  74             return singleton;
  75         }
  76 
  77         private static ArchivedData singleton;
  78     }
  79 
  80     // Creates the built-in class loaders.
  81     static {
  82         VM.initializeFromArchive(ArchivedData.class);
  83         ArchivedData archivedData = ArchivedData.get();
  84         if (archivedData != null) {
  85             // assert VM.getSavedProperty("jdk.boot.class.path.append") == null
  86             BOOT_LOADER = archivedData.boot_loader;
  87             PLATFORM_LOADER = archivedData.platform_loader;
  88         } else {
  89             // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
  90             String append = VM.getSavedProperty("jdk.boot.class.path.append");
  91             BOOT_LOADER =
  92                 new BootClassLoader((append != null && !append.isEmpty())
  93                     ? new URLClassPath(append, true)
  94                     : null);
  95             PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
  96         }
  97         // A class path is required when no initial module is specified.
  98         // In this case the class path defaults to "", meaning the current
  99         // working directory.  When an initial module is specified, on the
 100         // contrary, we drop this historic interpretation of the empty
 101         // string and instead treat it as unspecified.
 102         String cp = System.getProperty("java.class.path");
 103         if (cp == null || cp.isEmpty()) {
 104             String initialModuleName = System.getProperty("jdk.module.main");
 105             cp = (initialModuleName == null) ? "" : null;
 106         }
 107         URLClassPath ucp = new URLClassPath(cp, false);
 108         if (archivedData != null) {
 109             APP_LOADER = archivedData.app_loader;
 110             APP_LOADER.setClassPath(ucp);
 111         } else {
 112             APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
 113             ArchivedData.archive();
 114         }
 115     }
 116 
 117     /**
 118      * Returns the class loader that is used to find resources in modules
 119      * defined to the boot class loader.
 120      *
 121      * @apiNote This method is not public, it should instead be used via
 122      * the BootLoader class that provides a restricted API to this class
 123      * loader.
 124      */
 125     static BuiltinClassLoader bootLoader() {
 126         return BOOT_LOADER;
 127     }
 128 
 129     /**
 130      * Returns the platform class loader.
 131      */
 132     public static ClassLoader platformClassLoader() {
 133         return PLATFORM_LOADER;
 134     }
 135 
 136     /**
 137      * Returns the application class loader.
 138      */
 139     public static ClassLoader appClassLoader() {
 140         return APP_LOADER;
 141     }
 142 
 143     /**
 144      * The class loader that is used to find resources in modules defined to
 145      * the boot class loader. It is not used for class loading.
 146      */
 147     private static class BootClassLoader extends BuiltinClassLoader {
 148         BootClassLoader(URLClassPath bcp) {
 149             super(null, null, bcp);
 150         }
 151 
 152         @Override
 153         protected Class<?> loadClassOrNull(String cn, boolean resolve) {
 154             return JLA.findBootstrapClassOrNull(this, cn);
 155         }
 156     };
 157 
 158     /**
 159      * The platform class loader, a unique type to make it easier to distinguish
 160      * from the application class loader.
 161      */
 162     private static class PlatformClassLoader extends BuiltinClassLoader {
 163         static {
 164             if (!ClassLoader.registerAsParallelCapable())
 165                 throw new InternalError();
 166         }
 167 
 168         PlatformClassLoader(BootClassLoader parent) {
 169             super("platform", parent, null);
 170         }
 171     }
 172 
 173     /**
 174      * The application class loader that is a {@code BuiltinClassLoader} with
 175      * customizations to be compatible with long standing behavior.
 176      */
 177     private static class AppClassLoader extends BuiltinClassLoader {
 178         static {
 179             if (!ClassLoader.registerAsParallelCapable())
 180                 throw new InternalError();
 181         }
 182 
 183         @Stable URLClassPath ucp;
 184         void setClassPath(URLClassPath ucp) {
 185             super.setClassPath(ucp);
 186             this.ucp = ucp;
 187         }
 188 
 189         AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
 190             super("app", parent, ucp);
 191             this.ucp = ucp;
 192         }
 193 
 194         @Override
 195         protected Class<?> loadClass(String cn, boolean resolve)
 196             throws ClassNotFoundException
 197         {
 198             // for compatibility reasons, say where restricted package list has
 199             // been updated to list API packages in the unnamed module.
 200             SecurityManager sm = System.getSecurityManager();
 201             if (sm != null) {
 202                 int i = cn.lastIndexOf('.');
 203                 if (i != -1) {
 204                     sm.checkPackageAccess(cn.substring(0, i));
 205                 }
 206             }
 207 
 208             return super.loadClass(cn, resolve);
 209         }
 210 
 211         @Override
 212         protected PermissionCollection getPermissions(CodeSource cs) {
 213             PermissionCollection perms = super.getPermissions(cs);
 214             perms.add(new RuntimePermission("exitVM"));
 215             return perms;
 216         }
 217 
 218         /**
 219          * Called by the VM to support dynamic additions to the class path
 220          *
 221          * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
 222          */
 223         void appendToClassPathForInstrumentation(String path) {
 224             ucp.addFile(path);
 225         }
 226 
 227         /**
 228          * Called by the VM to support define package for AppCDS
 229          */
 230         protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
 231             return super.defineOrCheckPackage(pn, man, url);
 232         }
 233 
 234         // Called from VM only, during -Xshare:dump
 235         private void resetArchivedStates() {
 236             ucp = null;
 237         }
 238     }
 239 
 240     /**
 241      * Attempts to convert the given string to a file URL.
 242      *
 243      * @apiNote This is called by the VM
 244      */
 245     @Deprecated
 246     private static URL toFileURL(String s) {
 247         try {
 248             // Use an intermediate File object to construct a URI/URL without
 249             // authority component as URLClassPath can't handle URLs with a UNC
 250             // server name in the authority component.
 251             return Path.of(s).toRealPath().toFile().toURI().toURL();
 252         } catch (InvalidPathException | IOException ignore) {
 253             // malformed path string or class path element does not exist
 254             return null;
 255         }
 256     }
 257 }