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 173 /** 174 * Define the {@code Package} with the given name. The specified 175 * location is a jrt URL to a named module in the run-time image, a 176 * file path to a module in an exploded run-time image, or the file 177 * path to an enty on the boot class path (java agent Boot-Class-Path 178 * or -Xbootclasspath/a. 179 * 180 * <p> If the given location is a JAR file containing a manifest, 181 * the defined Package contains the versioning information from 182 * the manifest, if present. 183 * 184 * @param name package name 185 * @param location location where the package is (jrt URL or file path) 186 */ 187 static Package definePackage(String name, String location) { 188 Module module = findModule(location); 189 if (module != null) { 190 // named module from runtime image or exploded module 191 if (name.isEmpty()) 192 throw new InternalError("empty package in " + location); 193 return JLA.definePackage(ClassLoaders.bootLoader(), name, module); 194 } 195 196 // package in unnamed module (-Xbootclasspath/a) 197 URL url = toFileURL(location); 198 Manifest man = url != null ? getManifest(location) : null; 199 200 return ClassLoaders.bootLoader().defineOrCheckPackage(name, man, url); 201 } 202 203 /** 204 * Finds the module at the given location defined to the boot loader. 205 * The module is either in runtime image or exploded image. 206 * Otherwise this method returns null. 207 */ 208 private static Module findModule(String location) { 209 String mn = null; 210 if (location.startsWith("jrt:/")) { 211 // named module in runtime image ("jrt:/".length() == 5) 212 mn = location.substring(5, location.length()); 213 } else { 214 // named module in exploded image 215 Path path = Paths.get(location); 216 Path modulesDir = Paths.get(JAVA_HOME, "modules"); 217 if (path.startsWith(modulesDir)) { 218 mn = path.getFileName().toString(); 219 } 220 } 221 222 if (mn != null) { 223 // named module from runtime image or exploded module 224 Optional<Module> om = Layer.boot().findModule(mn); 225 if (!om.isPresent()) 226 throw new InternalError(mn + " not in boot layer"); 227 return om.get(); 228 } 229 230 return null; 231 } 232 233 /** 234 * Returns URL if the given location is a regular file path. 235 */ 236 private static URL toFileURL(String location) { 237 return AccessController.doPrivileged(new PrivilegedAction<>() { 238 public URL run() { 239 Path path = Paths.get(location); 240 if (Files.isRegularFile(path)) { 241 try { 242 return path.toUri().toURL(); 243 } catch (MalformedURLException e) {} 244 } 245 return null; 246 } 247 }); 248 } 249 250 /** 251 * Returns the Manifest if the given location is a JAR file 252 * containing a manifest. 253 */ 254 private static Manifest getManifest(String location) { 255 return AccessController.doPrivileged(new PrivilegedAction<>() { 256 public Manifest run() { 257 Path jar = Paths.get(location); 258 try (InputStream in = Files.newInputStream(jar); 259 JarInputStream jis = new JarInputStream(in, false)) { 260 return jis.getManifest(); 261 } catch (IOException e) { 262 return null; 263 } 264 } 265 }); 266 } 267 } 268 269 /** 270 * Returns an array of the binary name of the packages defined by 271 * the boot loader, in VM internal form (forward slashes instead of dot). 272 */ 273 private static native String[] getSystemPackageNames(); 274 275 /** 276 * Returns the location of the package of the given name, if 277 * defined by the boot loader; otherwise {@code null} is returned. 278 * 279 * The location may be a module from the runtime image or exploded image, 280 * or from the boot class append path (i.e. -Xbootclasspath/a or 281 * BOOT-CLASS-PATH attribute specified in java agent). 282 */ 283 private static native String getSystemPackageLocation(String name); 284 private static native void setBootLoaderUnnamedModule0(Module module); 285 }