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