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 package jdk.internal.loader;
  26 
  27 import java.io.IOException;
  28 import java.io.InputStream;
  29 import java.lang.module.ModuleReference;
  30 import java.net.MalformedURLException;
  31 import java.net.URI;
  32 import java.net.URL;
  33 import java.nio.file.Files;
  34 import java.nio.file.Path;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.Arrays;
  38 import java.util.Enumeration;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import java.util.jar.JarInputStream;
  41 import java.util.jar.Manifest;
  42 import java.util.stream.Stream;
  43 
  44 import jdk.internal.access.JavaLangAccess;
  45 import jdk.internal.access.SharedSecrets;
  46 import jdk.internal.module.Modules;
  47 import jdk.internal.module.ServicesCatalog;
  48 import jdk.internal.util.StaticProperty;
  49 
  50 /**
  51  * Find resources and packages in modules defined to the boot class loader or
  52  * resources and packages on the "boot class path" specified via -Xbootclasspath/a.
  53  */
  54 
  55 public class BootLoader {
  56     private BootLoader() { }
  57 
  58     // The unnamed module for the boot loader
  59     private static final Module UNNAMED_MODULE;
  60     private static final String JAVA_HOME = StaticProperty.javaHome();
  61 
  62     static {
  63         UNNAMED_MODULE = SharedSecrets.getJavaLangAccess().defineUnnamedModule(null);
  64         setBootLoaderUnnamedModule0(UNNAMED_MODULE);
  65     }
  66 
  67     // ServiceCatalog for the boot class loader
  68     private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create();
  69 
  70     // ClassLoaderValue map for the boot class loader
  71     private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
  72         = new ConcurrentHashMap<>();
  73 
  74     // native libraries loaded by the boot class loader
  75     private static final NativeLibraries NATIVE_LIBS
  76         = NativeLibraries.jniNativeLibraries(null);
  77 
  78     /**
  79      * Returns the unnamed module for the boot loader.
  80      */
  81     public static Module getUnnamedModule() {
  82         return UNNAMED_MODULE;
  83     }
  84 
  85     /**
  86      * Returns the ServiceCatalog for modules defined to the boot class loader.
  87      */
  88     public static ServicesCatalog getServicesCatalog() {
  89         return SERVICES_CATALOG;
  90     }
  91 
  92     /**
  93      * Returns the ClassLoaderValue map for the boot class loader.
  94      */
  95     public static ConcurrentHashMap<?, ?> getClassLoaderValueMap() {
  96         return CLASS_LOADER_VALUE_MAP;
  97     }
  98 
  99     /**
 100      * Returns NativeLibraries for the boot class loader.
 101      */
 102     public static NativeLibraries getNativeLibraries() {
 103         return NATIVE_LIBS;
 104     }
 105 
 106     /**
 107      * Returns {@code true} if there is a class path associated with the
 108      * BootLoader.
 109      */
 110     public static boolean hasClassPath() {
 111         return ClassLoaders.bootLoader().hasClassPath();
 112     }
 113 
 114     /**
 115      * Registers a module with this class loader so that its classes
 116      * (and resources) become visible via this class loader.
 117      */
 118     public static void loadModule(ModuleReference mref) {
 119         ClassLoaders.bootLoader().loadModule(mref);
 120     }
 121 
 122     /**
 123      * Loads the Class object with the given name defined to the boot loader.
 124      */
 125     public static Class<?> loadClassOrNull(String name) {
 126         return ClassLoaders.bootLoader().loadClassOrNull(name);
 127     }
 128 
 129     /**
 130      * Loads the Class object with the given name in the given module
 131      * defined to the boot loader. Returns {@code null} if not found.
 132      */
 133     public static Class<?> loadClass(Module module, String name) {
 134         Class<?> c = loadClassOrNull(name);
 135         if (c != null && c.getModule() == module) {
 136             return c;
 137         } else {
 138             return null;
 139         }
 140     }
 141 
 142     /**
 143      * Loads a native library from the system library path.
 144      */
 145     public static void loadLibrary(String name) {
 146         if (System.getSecurityManager() == null) {
 147             BootLoader.getNativeLibraries().loadLibrary(name);
 148         } else {
 149             AccessController.doPrivileged(new java.security.PrivilegedAction<>() {
 150                 public Void run() {
 151                     BootLoader.getNativeLibraries().loadLibrary(name);
 152                     return null;
 153                 }
 154             });
 155         }
 156     }
 157 
 158     /**
 159      * Returns a URL to a resource in a module defined to the boot loader.
 160      */
 161     public static URL findResource(String mn, String name) throws IOException {
 162         return ClassLoaders.bootLoader().findResource(mn, name);
 163     }
 164 
 165     /**
 166      * Returns an input stream to a resource in a module defined to the
 167      * boot loader.
 168      */
 169     public static InputStream findResourceAsStream(String mn, String name)
 170         throws IOException
 171     {
 172         return ClassLoaders.bootLoader().findResourceAsStream(mn, name);
 173     }
 174 
 175     /**
 176      * Returns the URL to the given resource in any of the modules
 177      * defined to the boot loader and the boot class path.
 178      */
 179     public static URL findResource(String name) {
 180         return ClassLoaders.bootLoader().findResource(name);
 181     }
 182 
 183     /**
 184      * Returns an Iterator to iterate over the resources of the given name
 185      * in any of the modules defined to the boot loader.
 186      */
 187     public static Enumeration<URL> findResources(String name) throws IOException {
 188         return ClassLoaders.bootLoader().findResources(name);
 189     }
 190 
 191     /**
 192      * Define a package for the given class to the boot loader, if not already
 193      * defined.
 194      */
 195     public static Package definePackage(Class<?> c) {
 196         return getDefinedPackage(c.getPackageName());
 197     }
 198 
 199     /**
 200      * Returns the Package of the given name defined to the boot loader or null
 201      * if the package has not been defined.
 202      */
 203     public static Package getDefinedPackage(String pn) {
 204         Package pkg = ClassLoaders.bootLoader().getDefinedPackage(pn);
 205         if (pkg == null) {
 206             String location = getSystemPackageLocation(pn.replace('.', '/'));
 207             if (location != null) {
 208                 pkg = PackageHelper.definePackage(pn.intern(), location);
 209             }
 210         }
 211         return pkg;
 212     }
 213 
 214     /**
 215      * Returns a stream of the packages defined to the boot loader.
 216      */
 217     public static Stream<Package> packages() {
 218         return Arrays.stream(getSystemPackageNames())
 219                      .map(name -> getDefinedPackage(name.replace('/', '.')));
 220     }
 221 
 222     /**
 223      * Helper class to define {@code Package} objects for packages in modules
 224      * defined to the boot loader.
 225      */
 226     static class PackageHelper {
 227         private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 228 
 229         /**
 230          * Define the {@code Package} with the given name. The specified
 231          * location is a jrt URL to a named module in the run-time image,
 232          * a file URL to a module in an exploded run-time image, or a file
 233          * path to an entry on the boot class path (java agent Boot-Class-Path
 234          * or -Xbootclasspath/a.
 235          *
 236          * <p> If the given location is a JAR file containing a manifest,
 237          * the defined Package contains the versioning information from
 238          * the manifest, if present.
 239          *
 240          * @param name     package name
 241          * @param location location where the package is (jrt URL or file URL
 242          *                 for a named module in the run-time or exploded image;
 243          *                 a file path for a package from -Xbootclasspath/a)
 244          */
 245         static Package definePackage(String name, String location) {
 246             Module module = findModule(location);
 247             if (module != null) {
 248                 // named module from runtime image or exploded module
 249                 if (name.isEmpty())
 250                     throw new InternalError("empty package in " + location);
 251                 return JLA.definePackage(ClassLoaders.bootLoader(), name, module);
 252             }
 253 
 254             // package in unnamed module (-Xbootclasspath/a)
 255             URL url = toFileURL(location);
 256             Manifest man = url != null ? getManifest(location) : null;
 257 
 258             return ClassLoaders.bootLoader().defineOrCheckPackage(name, man, url);
 259         }
 260 
 261         /**
 262          * Finds the module at the given location defined to the boot loader.
 263          * The module is either in runtime image or exploded image.
 264          * Otherwise this method returns null.
 265          */
 266         private static Module findModule(String location) {
 267             String mn = null;
 268             if (location.startsWith("jrt:/")) {
 269                 // named module in runtime image ("jrt:/".length() == 5)
 270                 mn = location.substring(5, location.length());
 271             } else if (location.startsWith("file:/")) {
 272                 // named module in exploded image
 273                 Path path = Path.of(URI.create(location));
 274                 Path modulesDir = Path.of(JAVA_HOME, "modules");
 275                 if (path.startsWith(modulesDir)) {
 276                     mn = path.getFileName().toString();
 277                 }
 278             }
 279 
 280             // return the Module object for the module name. The Module may
 281             // in the boot layer or a child layer for the case that the module
 282             // is loaded into a running VM
 283             if (mn != null) {
 284                 String name = mn;
 285                 return Modules.findLoadedModule(mn)
 286                     .orElseThrow(() -> new InternalError(name + " not loaded"));
 287             } else {
 288                 return null;
 289             }
 290         }
 291 
 292         /**
 293          * Returns URL if the given location is a regular file path.
 294          */
 295         private static URL toFileURL(String location) {
 296             return AccessController.doPrivileged(new PrivilegedAction<>() {
 297                 public URL run() {
 298                     Path path = Path.of(location);
 299                     if (Files.isRegularFile(path)) {
 300                         try {
 301                             return path.toUri().toURL();
 302                         } catch (MalformedURLException e) {}
 303                     }
 304                     return null;
 305                 }
 306             });
 307         }
 308 
 309         /**
 310          * Returns the Manifest if the given location is a JAR file
 311          * containing a manifest.
 312          */
 313         private static Manifest getManifest(String location) {
 314             return AccessController.doPrivileged(new PrivilegedAction<>() {
 315                 public Manifest run() {
 316                     Path jar = Path.of(location);
 317                     try (InputStream in = Files.newInputStream(jar);
 318                          JarInputStream jis = new JarInputStream(in, false)) {
 319                         return jis.getManifest();
 320                     } catch (IOException e) {
 321                         return null;
 322                     }
 323                 }
 324             });
 325         }
 326     }
 327 
 328     /**
 329      * Returns an array of the binary name of the packages defined by
 330      * the boot loader, in VM internal form (forward slashes instead of dot).
 331      */
 332     private static native String[] getSystemPackageNames();
 333 
 334     /**
 335      * Returns the location of the package of the given name, if
 336      * defined by the boot loader; otherwise {@code null} is returned.
 337      *
 338      * The location may be a module from the runtime image or exploded image,
 339      * or from the boot class append path (i.e. -Xbootclasspath/a or
 340      * BOOT-CLASS-PATH attribute specified in java agent).
 341      */
 342     private static native String getSystemPackageLocation(String name);
 343     private static native void setBootLoaderUnnamedModule0(Module module);
 344 }