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.IOException;
  30 import java.lang.reflect.Module;
  31 import java.net.URL;
  32 import java.nio.file.InvalidPathException;
  33 import java.nio.file.Paths;
  34 import java.security.CodeSource;
  35 import java.security.PermissionCollection;
  36 import java.util.jar.Manifest;
  37 
  38 import jdk.internal.misc.JavaLangAccess;
  39 import jdk.internal.misc.SharedSecrets;
  40 import jdk.internal.misc.VM;
  41 
  42 
  43 /**
  44  * Creates and provides access to the built-in platform and application class
  45  * loaders. It also creates the class loader that is used to locate resources
  46  * in modules defined to the boot class loader.
  47  */
  48 
  49 public class ClassLoaders {
  50 
  51     private ClassLoaders() { }
  52 
  53     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  54 
  55     // the built-in class loaders
  56     private static final BootClassLoader BOOT_LOADER;
  57     private static final PlatformClassLoader PLATFORM_LOADER;
  58     private static final AppClassLoader APP_LOADER;
  59 
  60     /**
  61      * Creates the built-in class loaders
  62      */
  63     static {
  64 
  65         // -Xbootclasspth/a or -javaagent Boot-Class-Path
  66         URLClassPath bcp = null;
  67         String s = VM.getSavedProperty("jdk.boot.class.path.append");
  68         if (s != null && s.length() > 0)
  69             bcp = toURLClassPath(s);
  70 
  71         // we have a class path if -cp is specified or -m is not specified.
  72         // If neither is specified then default to -cp <working directory>.
  73         URLClassPath ucp = null;
  74         String mainMid = System.getProperty("jdk.module.main");
  75         String cp = System.getProperty("java.class.path");
  76         if (mainMid == null && cp == null)
  77             cp = "";
  78         if (cp != null)
  79             ucp = toURLClassPath(cp);
  80 
  81 
  82         // create the class loaders
  83         BOOT_LOADER = new BootClassLoader(bcp);
  84         PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
  85         APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
  86     }
  87 
  88     /**
  89      * Returns the class loader that is used to find resources in modules
  90      * defined to the boot class loader.
  91      *
  92      * @apiNote This method is not public, it should instead be used via
  93      * the BootLoader class that provides a restricted API to this class
  94      * loader.
  95      */
  96     static BuiltinClassLoader bootLoader() {
  97         return BOOT_LOADER;
  98     }
  99 
 100     /**
 101      * Returns the platform class loader.
 102      */
 103     public static ClassLoader platformClassLoader() {
 104         return PLATFORM_LOADER;
 105     }
 106 
 107     /**
 108      * Returns the application class loader.
 109      */
 110     public static ClassLoader appClassLoader() {
 111         return APP_LOADER;
 112     }
 113 
 114     /**
 115      * The class loader that is used to find resources in modules defined to
 116      * the boot class loader. It is not used for class loading.
 117      */
 118     private static class BootClassLoader extends BuiltinClassLoader {
 119         BootClassLoader(URLClassPath bcp) {
 120             super(null, bcp);
 121         }
 122 
 123         @Override
 124         protected Class<?> loadClassOrNull(String cn) {
 125             return JLA.findBootstrapClassOrNull(this, cn);
 126         }
 127     };
 128 
 129     /**
 130      * The platform class loader, a unique type to make it easier to distinguish
 131      * from the application class loader.
 132      */
 133     private static class PlatformClassLoader extends BuiltinClassLoader {
 134         static {
 135             if (!ClassLoader.registerAsParallelCapable())
 136                 throw new InternalError();
 137         }
 138 
 139         PlatformClassLoader(BootClassLoader parent) {
 140             super(parent, null);
 141         }
 142 
 143         /**
 144          * Called by the VM to support define package for AppCDS.
 145          *
 146          * Shared classes are returned in ClassLoader::findLoadedClass
 147          * that bypass the defineClass call.
 148          */
 149         private Package definePackage(String pn, Module module) {
 150             return JLA.definePackage(this, pn, module);
 151         }
 152     }
 153 
 154     /**
 155      * The application class loader that is a {@code BuiltinClassLoader} with
 156      * customizations to be compatible with long standing behavior.
 157      */
 158     private static class AppClassLoader extends BuiltinClassLoader {
 159         static {
 160             if (!ClassLoader.registerAsParallelCapable())
 161                 throw new InternalError();
 162         }
 163 
 164         final URLClassPath ucp;
 165 
 166         AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
 167             super(parent, ucp);
 168             this.ucp = ucp;
 169         }
 170 
 171         @Override
 172         protected Class<?> loadClass(String cn, boolean resolve)
 173             throws ClassNotFoundException
 174         {
 175             // for compatibility reasons, say where restricted package list has
 176             // been updated to list API packages in the unnamed module.
 177             SecurityManager sm = System.getSecurityManager();
 178             if (sm != null) {
 179                 int i = cn.lastIndexOf('.');
 180                 if (i != -1) {
 181                     sm.checkPackageAccess(cn.substring(0, i));
 182                 }
 183             }
 184 
 185             return super.loadClass(cn, resolve);
 186         }
 187 
 188         @Override
 189         protected PermissionCollection getPermissions(CodeSource cs) {
 190             PermissionCollection perms = super.getPermissions(cs);
 191             perms.add(new RuntimePermission("exitVM"));
 192             return perms;
 193         }
 194 
 195         /**
 196          * Called by the VM to support dynamic additions to the class path
 197          *
 198          * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
 199          */
 200         void appendToClassPathForInstrumentation(String path) {
 201             addClassPathToUCP(path, ucp);
 202         }
 203 
 204         /**
 205          * Called by the VM to support define package for AppCDS
 206          *
 207          * Shared classes are returned in ClassLoader::findLoadedClass
 208          * that bypass the defineClass call.
 209          */
 210         private Package definePackage(String pn, Module module) {
 211             return JLA.definePackage(this, pn, module);
 212         }
 213 
 214         /**
 215          * Called by the VM to support define package for AppCDS
 216          */
 217         protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
 218             return super.defineOrCheckPackage(pn, man, url);
 219         }
 220     }
 221 
 222     /**
 223      * Returns a {@code URLClassPath} of file URLs to each of the elements in
 224      * the given class path.
 225      */
 226     private static URLClassPath toURLClassPath(String cp) {
 227         URLClassPath ucp = new URLClassPath(new URL[0]);
 228         addClassPathToUCP(cp, ucp);
 229         return ucp;
 230     }
 231 
 232     /**
 233      * Converts the elements in the given class path to file URLs and adds
 234      * them to the given URLClassPath.
 235      */
 236     private static void addClassPathToUCP(String cp, URLClassPath ucp) {
 237         int off = 0;
 238         int next;
 239         while ((next = cp.indexOf(File.pathSeparator, off)) != -1) {
 240             URL url = toFileURL(cp.substring(off, next));
 241             if (url != null)
 242                 ucp.addURL(url);
 243             off = next + 1;
 244         }
 245 
 246         // remaining
 247         URL url = toFileURL(cp.substring(off));
 248         if (url != null)
 249             ucp.addURL(url);
 250     }
 251 
 252     /**
 253      * Attempts to convert the given string to a file URL.
 254      *
 255      * @apiNote This is called by the VM
 256      */
 257     private static URL toFileURL(String s) {
 258         try {
 259             return Paths.get(s).toRealPath().toUri().toURL();
 260         } catch (InvalidPathException | IOException ignore) {
 261             // malformed path string or class path element does not exist
 262             return null;
 263         }
 264     }
 265 
 266 }