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