1 /* 2 * Copyright (c) 2014, 2016, 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 26 package jdk.internal.module; 27 28 import java.io.File; 29 import java.lang.module.Configuration; 30 import java.lang.module.ModuleReference; 31 import java.lang.module.ModuleFinder; 32 import java.lang.module.ResolvedModule; 33 import java.lang.reflect.Layer; 34 import java.lang.reflect.Module; 35 import java.nio.file.Path; 36 import java.nio.file.Paths; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.HashSet; 40 import java.util.Map; 41 import java.util.Optional; 42 import java.util.Set; 43 import java.util.function.Function; 44 import java.util.stream.Collectors; 45 46 import jdk.internal.loader.BootLoader; 47 import jdk.internal.loader.BuiltinClassLoader; 48 import jdk.internal.perf.PerfCounter; 49 50 /** 51 * Initializes/boots the module system. 52 * 53 * The {@link #boot() boot} method is called early in the startup to initialize 54 * the module system. In summary, the boot method creates a Configuration by 55 * resolving a set of module names specified via the launcher (or equivalent) 56 * -m and -addmods options. The modules are located on a module path that is 57 * constructed from the upgrade, system and application module paths. The 58 * Configuration is reified by creating the boot Layer with each module in the 59 * the configuration defined to one of the built-in class loaders. The mapping 60 * of modules to class loaders is statically mapped in a helper class. 61 */ 62 63 public final class ModuleBootstrap { 64 private ModuleBootstrap() { } 65 66 private static final String JAVA_BASE = "java.base"; 67 68 // the token for "all unnamed modules" 69 private static final String ALL_UNNAMED = "ALL-UNNAMED"; 70 71 // the token for "all system modules" 72 private static final String ALL_SYSTEM = "ALL-SYSTEM"; 73 74 // the token for "all modules on the module path" 75 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; 76 77 // ModuleFinder for the initial configuration 78 private static ModuleFinder initialFinder; 79 80 /** 81 * Returns the ModuleFinder for the initial configuration 82 */ 83 public static ModuleFinder finder() { 84 assert initialFinder != null; 85 return initialFinder; 86 } 87 88 /** 89 * Initialize the module system, returning the boot Layer. 90 * 91 * @see java.lang.System#initPhase2() 92 */ 93 public static Layer boot() { 94 95 long t0 = System.nanoTime(); 96 97 // system module path 98 ModuleFinder systemModulePath = ModuleFinder.ofSystem(); 99 100 // Once we have the system module path then we define the base module. 101 // We do this here so that java.base is defined to the VM as early as 102 // possible and also that resources in the base module can be located 103 // for error messages that may happen from here on. 104 Optional<ModuleReference> obase = systemModulePath.find(JAVA_BASE); 105 if (!obase.isPresent()) 106 throw new InternalError(JAVA_BASE + " not found"); 107 ModuleReference base = obase.get(); 108 BootLoader.loadModule(base); 109 Modules.defineModule(null, base.descriptor(), base.location().orElse(null)); 110 111 112 // -upgrademodulepath option specified to launcher 113 ModuleFinder upgradeModulePath 114 = createModulePathFinder("jdk.upgrade.module.path"); 115 116 // -modulepath option specified to the launcher 117 ModuleFinder appModulePath = createModulePathFinder("jdk.module.path"); 118 119 // The module finder: [-upgrademodulepath] system-module-path [-modulepath] 120 ModuleFinder finder = systemModulePath; 121 if (upgradeModulePath != null) 122 finder = ModuleFinder.compose(upgradeModulePath, finder); 123 if (appModulePath != null) 124 finder = ModuleFinder.compose(finder, appModulePath); 125 126 // launcher -m option to specify the initial module 127 String mainModule = System.getProperty("jdk.module.main"); 128 129 // additional module(s) specified by -addmods 130 boolean addAllSystemModules = false; 131 boolean addAllApplicationModules = false; 132 Set<String> addModules = null; 133 String propValue = System.getProperty("jdk.launcher.addmods"); 134 if (propValue != null) { 135 addModules = new HashSet<>(); 136 for (String mod: propValue.split(",")) { 137 switch (mod) { 138 case ALL_SYSTEM: 139 addAllSystemModules = true; 140 break; 141 case ALL_MODULE_PATH: 142 addAllApplicationModules = true; 143 break; 144 default : 145 addModules.add(mod); 146 } 147 } 148 } 149 150 // The root modules to resolve 151 Set<String> roots = new HashSet<>(); 152 153 // main/initial module 154 if (mainModule != null) { 155 roots.add(mainModule); 156 if (addAllApplicationModules) 157 fail(ALL_MODULE_PATH + " not allowed with initial module"); 158 } 159 160 // If -addmods is specified then those modules need to be resolved 161 if (addModules != null) 162 roots.addAll(addModules); 163 164 165 // -limitmods 166 boolean limitmods = false; 167 propValue = System.getProperty("jdk.launcher.limitmods"); 168 if (propValue != null) { 169 Set<String> mods = new HashSet<>(); 170 for (String mod: propValue.split(",")) { 171 mods.add(mod); 172 } 173 finder = limitFinder(finder, mods, roots); 174 limitmods = true; 175 } 176 177 178 // If there is no initial module specified then assume that the 179 // initial module is the unnamed module of the application class 180 // loader. By convention, and for compatibility, this is 181 // implemented by putting the names of all modules on the system 182 // module path into the set of modules to resolve. 183 // 184 // If `-addmods ALL-SYSTEM` is used then all modules on the system 185 // module path will be resolved, irrespective of whether an initial 186 // module is specified. 187 // 188 // If `-addmods ALL-MODULE-PATH` is used, and no initial module is 189 // specified, then all modules on the application module path will 190 // be resolved. 191 // 192 if (mainModule == null || addAllSystemModules) { 193 Set<ModuleReference> mrefs; 194 if (addAllApplicationModules) { 195 assert mainModule == null; 196 mrefs = finder.findAll(); 197 } else { 198 mrefs = systemModulePath.findAll(); 199 if (limitmods) { 200 ModuleFinder f = finder; 201 mrefs = mrefs.stream() 202 .filter(m -> f.find(m.descriptor().name()).isPresent()) 203 .collect(Collectors.toSet()); 204 } 205 } 206 // map to module names 207 for (ModuleReference mref : mrefs) { 208 roots.add(mref.descriptor().name()); 209 } 210 } 211 212 long t1 = System.nanoTime(); 213 214 // run the resolver to create the configuration 215 216 Configuration cf = Configuration.empty() 217 .resolveRequiresAndUses(finder, 218 ModuleFinder.empty(), 219 roots); 220 221 // time to create configuration 222 PerfCounters.resolveTime.addElapsedTimeFrom(t1); 223 224 // mapping of modules to class loaders 225 Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf); 226 227 // check that all modules to be mapped to the boot loader will be 228 // loaded from the system module path 229 if (finder != systemModulePath) { 230 for (ResolvedModule resolvedModule : cf.modules()) { 231 ModuleReference mref = resolvedModule.reference(); 232 String name = mref.descriptor().name(); 233 ClassLoader cl = clf.apply(name); 234 if (cl == null) { 235 236 if (upgradeModulePath != null 237 && upgradeModulePath.find(name).isPresent()) 238 fail(name + ": cannot be loaded from upgrade module path"); 239 240 if (!systemModulePath.find(name).isPresent()) 241 fail(name + ": cannot be loaded from application module path"); 242 } 243 } 244 } 245 246 long t2 = System.nanoTime(); 247 248 // define modules to VM/runtime 249 Layer bootLayer = Layer.empty().defineModules(cf, clf); 250 251 PerfCounters.layerCreateTime.addElapsedTimeFrom(t2); 252 253 long t3 = System.nanoTime(); 254 255 // define the module to its class loader, except java.base 256 for (ResolvedModule resolvedModule : cf.modules()) { 257 ModuleReference mref = resolvedModule.reference(); 258 String name = mref.descriptor().name(); 259 ClassLoader cl = clf.apply(name); 260 if (cl == null) { 261 if (!name.equals(JAVA_BASE)) BootLoader.loadModule(mref); 262 } else { 263 ((BuiltinClassLoader)cl).loadModule(mref); 264 } 265 } 266 267 PerfCounters.loadModulesTime.addElapsedTimeFrom(t3); 268 269 // -XaddReads and -XaddExports 270 addExtraReads(bootLayer); 271 addExtraExports(bootLayer); 272 273 // total time to initialize 274 PerfCounters.bootstrapTime.addElapsedTimeFrom(t0); 275 276 // remember the ModuleFinder 277 initialFinder = finder; 278 279 return bootLayer; 280 } 281 282 /** 283 * Returns a ModuleFinder that limits observability to the given root 284 * modules, their transitive dependences, plus a set of other modules. 285 */ 286 private static ModuleFinder limitFinder(ModuleFinder finder, 287 Set<String> roots, 288 Set<String> otherMods) 289 { 290 // resolve all root modules 291 Configuration cf = Configuration.empty() 292 .resolveRequires(finder, 293 ModuleFinder.empty(), 294 roots); 295 296 // module name -> reference 297 Map<String, ModuleReference> map = new HashMap<>(); 298 cf.modules().stream() 299 .map(ResolvedModule::reference) 300 .forEach(mref -> map.put(mref.descriptor().name(), mref)); 301 302 // set of modules that are observable 303 Set<ModuleReference> mrefs = new HashSet<>(map.values()); 304 305 // add the other modules 306 for (String mod : otherMods) { 307 Optional<ModuleReference> omref = finder.find(mod); 308 if (omref.isPresent()) { 309 ModuleReference mref = omref.get(); 310 map.putIfAbsent(mod, mref); 311 mrefs.add(mref); 312 } else { 313 // no need to fail 314 } 315 } 316 317 return new ModuleFinder() { 318 @Override 319 public Optional<ModuleReference> find(String name) { 320 return Optional.ofNullable(map.get(name)); 321 } 322 @Override 323 public Set<ModuleReference> findAll() { 324 return mrefs; 325 } 326 }; 327 } 328 329 /** 330 * Creates a finder from the module path that is the value of the given 331 * system property. 332 */ 333 private static ModuleFinder createModulePathFinder(String prop) { 334 String s = System.getProperty(prop); 335 if (s == null) { 336 return null; 337 } else { 338 String[] dirs = s.split(File.pathSeparator); 339 Path[] paths = new Path[dirs.length]; 340 int i = 0; 341 for (String dir: dirs) { 342 paths[i++] = Paths.get(dir); 343 } 344 return ModuleFinder.of(paths); 345 } 346 } 347 348 349 /** 350 * Process the -XaddReads options to add any additional read edges that 351 * are specified on the command-line. 352 */ 353 private static void addExtraReads(Layer bootLayer) { 354 355 // decode the command line options 356 Map<String, Set<String>> map = decode("jdk.launcher.addreads."); 357 358 for (Map.Entry<String, Set<String>> e : map.entrySet()) { 359 360 // the key is $MODULE 361 String mn = e.getKey(); 362 Optional<Module> om = bootLayer.findModule(mn); 363 if (!om.isPresent()) 364 fail("Unknown module: " + mn); 365 Module m = om.get(); 366 367 // the value is the set of other modules (by name) 368 for (String name : e.getValue()) { 369 370 Module other; 371 if (ALL_UNNAMED.equals(name)) { 372 other = null; // loose 373 } else { 374 om = bootLayer.findModule(name); 375 if (!om.isPresent()) 376 fail("Unknown module: " + name); 377 other = om.get(); 378 } 379 380 Modules.addReads(m, other); 381 } 382 } 383 } 384 385 386 /** 387 * Process the -XaddExports options to add any additional read edges that 388 * are specified on the command-line. 389 */ 390 private static void addExtraExports(Layer bootLayer) { 391 392 // decode the command line options 393 Map<String, Set<String>> map = decode("jdk.launcher.addexports."); 394 395 for (Map.Entry<String, Set<String>> e : map.entrySet()) { 396 397 // the key is $MODULE/$PACKAGE 398 String key = e.getKey(); 399 String[] s = key.split("/"); 400 if (s.length != 2) 401 fail("Unable to parse: " + key); 402 403 String mn = s[0]; 404 String pn = s[1]; 405 406 // The exporting module is in the boot layer 407 Module m; 408 Optional<Module> om = bootLayer.findModule(mn); 409 if (!om.isPresent()) 410 fail("Unknown module: " + mn); 411 m = om.get(); 412 413 // the value is the set of modules to export to (by name) 414 for (String name : e.getValue()) { 415 boolean allUnnamed = false; 416 Module other = null; 417 if (ALL_UNNAMED.equals(name)) { 418 allUnnamed = true; 419 } else { 420 om = bootLayer.findModule(name); 421 if (om.isPresent()) { 422 other = om.get(); 423 } else { 424 fail("Unknown module: " + name); 425 } 426 } 427 428 if (allUnnamed) { 429 Modules.addExportsToAllUnnamed(m, pn); 430 } else { 431 Modules.addExports(m, pn, other); 432 } 433 } 434 } 435 } 436 437 438 /** 439 * Decodes the values of -XaddReads or -XaddExports options 440 * 441 * The format of the options is: $KEY=$MODULE(,$MODULE)* 442 * 443 * For transition purposes, this method allows the first usage to be 444 * $KEY=$MODULE(,$KEY=$MODULE) 445 * This format will eventually be removed. 446 */ 447 private static Map<String, Set<String>> decode(String prefix) { 448 int index = 0; 449 String value = System.getProperty(prefix + index); 450 if (value == null) 451 return Collections.emptyMap(); 452 453 Map<String, Set<String>> map = new HashMap<>(); 454 455 while (value != null) { 456 457 int pos = value.indexOf('='); 458 if (pos == -1) 459 fail("Unable to parse: " + value); 460 if (pos == 0) 461 fail("Missing module name in: " + value); 462 463 // key is <module> or <module>/<package> 464 String key = value.substring(0, pos); 465 466 String rhs = value.substring(pos+1); 467 if (rhs.isEmpty()) 468 fail("Unable to parse: " + value); 469 470 // new format $MODULE(,$MODULE)* or old format $(MODULE)=... 471 pos = rhs.indexOf('='); 472 473 // old format only allowed in first -X option 474 if (pos >= 0 && index > 0) 475 fail("Unable to parse: " + value); 476 477 if (pos == -1) { 478 479 // new format: $KEY=$MODULE(,$MODULE)* 480 481 Set<String> values = map.get(key); 482 if (values != null) 483 fail(key + " specified more than once"); 484 485 values = new HashSet<>(); 486 map.put(key, values); 487 for (String s : rhs.split(",")) { 488 if (s.length() > 0) values.add(s); 489 } 490 491 } else { 492 493 // old format: $KEY=$MODULE(,$KEY=$MODULE)* 494 495 assert index == 0; // old format only allowed in first usage 496 497 for (String expr : value.split(",")) { 498 if (expr.length() > 0) { 499 String[] s = expr.split("="); 500 if (s.length != 2) 501 fail("Unable to parse: " + expr); 502 503 map.computeIfAbsent(s[0], k -> new HashSet<>()).add(s[1]); 504 } 505 } 506 } 507 508 index++; 509 value = System.getProperty(prefix + index); 510 } 511 512 return map; 513 } 514 515 516 /** 517 * Throws a RuntimeException with the given message 518 */ 519 static void fail(String m) { 520 throw new RuntimeException(m); 521 } 522 523 static class PerfCounters { 524 static PerfCounter resolveTime 525 = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime"); 526 static PerfCounter layerCreateTime 527 = PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime"); 528 static PerfCounter loadModulesTime 529 = PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime"); 530 static PerfCounter bootstrapTime 531 = PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime"); 532 } 533 }