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 import sun.misc.URLClassPath;
  42 
  43 
  44 /**
  45  * Creates and provides access to the built-in extension and application class
  46  * loaders. It also creates the class loader that is used to locate resources
  47  * in modules defined to the boot class loader.
  48  */
  49 
  50 public class ClassLoaders {
  51 
  52     private ClassLoaders() { }
  53 
  54     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  55 
  56     // the built-in class loaders
  57     private static final BootClassLoader BOOT_LOADER;
  58     private static final ExtClassLoader EXT_LOADER;
  59     private static final AppClassLoader APP_LOADER;
  60 
  61     /**
  62      * Creates the built-in class loaders
  63      */
  64     static {
  65 
  66         // -Xbootclasspth/a or -javaagent Boot-Class-Path
  67         URLClassPath bcp = null;
  68         String s = VM.getSavedProperty("jdk.boot.class.path.append");
  69         if (s != null && s.length() > 0)
  70             bcp = toURLClassPath(s);
  71 
  72         // we have a class path if -cp is specified or -m is not specified
  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 || cp.length() == 0))
  77             cp = ".";
  78         if (cp != null && cp.length() > 0)
  79             ucp = toURLClassPath(cp);
  80 
  81 
  82         // create the class loaders
  83         BOOT_LOADER = new BootClassLoader(bcp);
  84         EXT_LOADER = new ExtClassLoader(BOOT_LOADER);
  85         APP_LOADER = new AppClassLoader(EXT_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 extension class loader.
 102      */
 103     public static ClassLoader extClassLoader() {
 104         return EXT_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 extension class loader, a unique type to make it easier to distinguish
 131      * from the application class loader.
 132      */
 133     private static class ExtClassLoader extends BuiltinClassLoader {
 134         static {
 135             if (!ClassLoader.registerAsParallelCapable())
 136                 throw new InternalError("Classloader is not registered" +
 137                     " as parallel capable.");
 138         }
 139 
 140         ExtClassLoader(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("Classloader is not registered" +
 163                     " as parallel capable.");
 164         }
 165 
 166         final URLClassPath ucp;
 167 
 168         AppClassLoader(ExtClassLoader parent, URLClassPath ucp) {
 169             super(parent, ucp);
 170             this.ucp = ucp;
 171         }
 172 
 173         @Override
 174         protected Class<?> loadClass(String cn, boolean resolve)
 175             throws ClassNotFoundException
 176         {
 177             // for compatibility reasons, say where restricted package list has
 178             // been updated to list API packages in the unnamed module.
 179             SecurityManager sm = System.getSecurityManager();
 180             if (sm != null) {
 181                 int i = cn.lastIndexOf('.');
 182                 if (i != -1) {
 183                     sm.checkPackageAccess(cn.substring(0, i));
 184                 }
 185             }
 186 
 187             return super.loadClass(cn, resolve);
 188         }
 189 
 190         @Override
 191         protected PermissionCollection getPermissions(CodeSource cs) {
 192             PermissionCollection perms = super.getPermissions(cs);
 193             perms.add(new RuntimePermission("exitVM"));
 194             return perms;
 195         }
 196 
 197         /**
 198          * Called by the VM to support dynamic additions to the class path
 199          *
 200          * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
 201          */
 202         void appendToClassPathForInstrumentation(String path) {
 203             appendToUCP(path, ucp);
 204         }
 205 
 206         /**
 207          * Called by the VM to support define package for AppCDS
 208          *
 209          * Shared classes are returned in ClassLoader::findLoadedClass
 210          * that bypass the defineClass call.
 211          */
 212         private Package definePackage(String pn, Module module) {
 213             return JLA.definePackage(this, pn, module);
 214         }
 215 
 216         /**
 217          * Called by the VM to support define package for AppCDS
 218          */
 219         protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
 220             return super.defineOrCheckPackage(pn, man, url);
 221         }
 222     }
 223 
 224     /**
 225      * Returns a {@code URLClassPath} of file URLs to each of the elements in
 226      * the given class path.
 227      */
 228     private static URLClassPath toURLClassPath(String cp) {
 229         URLClassPath ucp = new URLClassPath(new URL[0]);
 230         appendToUCP(cp, ucp);
 231         return ucp;
 232     }
 233 
 234     /**
 235      * Converts the elements in the given class path to file URLs and adds
 236      * them to the given URLClassPath.
 237      */
 238     private static void appendToUCP(String cp, URLClassPath ucp) {
 239         String[] elements = cp.split(File.pathSeparator);
 240         if (elements.length == 0) {
 241             // contains path separator(s) only, default to current directory
 242             // to be compatible with long standing behavior
 243             elements = new String[] { "" };
 244         }
 245         for (String s: elements) {
 246             try {
 247                 URL url = Paths.get(s).toRealPath().toUri().toURL();
 248                 ucp.addURL(url);
 249             } catch (InvalidPathException | IOException ignore) {
 250                 // malformed path string or class path element does not exist
 251             }
 252         }
 253     }
 254 
 255 }