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