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