41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Optional; 50 import java.util.Set; 51 import java.util.jar.JarEntry; 52 import java.util.jar.JarFile; 53 54 import jdk.internal.loader.Resource; 55 import jdk.internal.misc.JavaLangModuleAccess; 56 import jdk.internal.misc.SharedSecrets; 57 import sun.net.www.ParseUtil; 58 59 60 /** 61 * Provides support for patching modules in the boot layer with -Xpatch. 62 */ 63 64 public final class ModulePatcher { 65 66 private static final JavaLangModuleAccess JLMA 67 = SharedSecrets.getJavaLangModuleAccess(); 68 69 // the prefix of the system properties that encode the value of -Xpatch 70 private static final String PATCH_PROPERTY_PREFIX = "jdk.launcher.patch."; 71 72 // module name -> sequence of patches (directories or JAR files) 73 private static final Map<String, List<Path>> PATCH_MAP = decodeProperties(); 74 75 private ModulePatcher() { } 76 77 78 /** 79 * Decodes the values of -Xpatch options, returning a Map of module name to 80 * list of file paths. 81 * 82 * @throws IllegalArgumentException if the the module name is missing or 83 * -Xpatch is used more than once to patch the same module 84 */ 85 private static Map<String, List<Path>> decodeProperties() { 86 87 int index = 0; 88 String value = System.getProperty(PATCH_PROPERTY_PREFIX + index); 89 if (value == null) 90 return Collections.emptyMap(); // -Xpatch not specified 91 92 Map<String, List<Path>> map = new HashMap<>(); 93 while (value != null) { 94 95 // <module>=<file>(:<file>)* 96 97 int pos = value.indexOf('='); 98 if (pos == -1) 99 throwIAE("Unable to parse: " + value); 100 if (pos == 0) 101 throwIAE("Missing module name: " + value); 102 103 String mn = value.substring(0, pos); 104 List<Path> list = map.get(mn); 105 if (list != null) 106 throwIAE("Module " + mn + " specified more than once"); 107 list = new ArrayList<>(); 108 map.put(mn, list); 109 110 String paths = value.substring(pos+1); 111 for (String path : paths.split(File.pathSeparator)) { 112 if (!path.isEmpty()) { 113 list.add(Paths.get(path)); 114 } 115 } 116 117 index++; 118 value = System.getProperty(PATCH_PROPERTY_PREFIX + index); 119 } 120 121 return map; 122 } 123 124 125 /** 126 * Returns a module reference that interposes on the given module if 127 * needed. If there are no patches for the given module then the module 128 * reference is simply returned. Otherwise the patches for the module 129 * are scanned (to find any new concealed packages) and a new module 130 * reference is returned. 131 * 132 * @throws UncheckedIOException if an I/O error is detected 133 */ 134 public static ModuleReference interposeIfNeeded(ModuleReference mref) { 135 136 ModuleDescriptor descriptor = mref.descriptor(); 137 String mn = descriptor.name(); 138 139 // if there are no patches for the module then nothing to do 140 List<Path> paths = PATCH_MAP.get(mn); 141 if (paths == null) 142 return mref; 143 144 145 // scan the JAR file or directory tree to get the set of packages 520 } 521 }; 522 } 523 } 524 525 526 /** 527 * Derives a package name from a file path to a .class file. 528 */ 529 private static String toPackageName(Path top, Path file) { 530 Path entry = top.relativize(file); 531 Path parent = entry.getParent(); 532 if (parent == null) { 533 return warnUnnamedPackage(top, entry.toString()); 534 } else { 535 return parent.toString().replace(File.separatorChar, '.'); 536 } 537 } 538 539 /** 540 * Derives a package name from the name of an entry in a JAR file. 541 */ 542 private static String toPackageName(Path file, JarEntry entry) { 543 String name = entry.getName(); 544 int index = name.lastIndexOf("/"); 545 if (index == -1) { 546 return warnUnnamedPackage(file, name); 547 } else { 548 return name.substring(0, index).replace('/', '.'); 549 } 550 } 551 552 private static String warnUnnamedPackage(Path file, String e) { 553 System.err.println("WARNING: " + e + " not allowed in patch: " + file); 554 return ""; 555 } 556 557 private static void throwIAE(String msg) { 558 throw new IllegalArgumentException(msg); 559 } | 41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Optional; 50 import java.util.Set; 51 import java.util.jar.JarEntry; 52 import java.util.jar.JarFile; 53 54 import jdk.internal.loader.Resource; 55 import jdk.internal.misc.JavaLangModuleAccess; 56 import jdk.internal.misc.SharedSecrets; 57 import sun.net.www.ParseUtil; 58 59 60 /** 61 * Provides support for patching modules in the boot layer with --patch-module. 62 */ 63 64 public final class ModulePatcher { 65 66 private static final JavaLangModuleAccess JLMA 67 = SharedSecrets.getJavaLangModuleAccess(); 68 69 // the prefix of the system properties that encode the value of --patch-module 70 private static final String PATCH_PROPERTY_PREFIX = "jdk.module.patch."; 71 72 // module name -> sequence of patches (directories or JAR files) 73 private static final Map<String, List<Path>> PATCH_MAP = decodeProperties(); 74 75 private ModulePatcher() { } 76 77 /** 78 * Decodes the values of --patch-module options, returning a Map of module 79 * name to list of file paths. 80 * 81 * @throws IllegalArgumentException if the the module name is missing or 82 * --patch-module is used more than once to patch the same module 83 */ 84 private static Map<String, List<Path>> decodeProperties() { 85 86 int index = 0; 87 String value = getAndRemoveProperty(PATCH_PROPERTY_PREFIX + index); 88 if (value == null) 89 return Collections.emptyMap(); // --patch-module not specified 90 91 Map<String, List<Path>> map = new HashMap<>(); 92 while (value != null) { 93 94 // <module>=<file>(:<file>)* 95 96 int pos = value.indexOf('='); 97 if (pos == -1) 98 throwIAE("Unable to parse: " + value); 99 if (pos == 0) 100 throwIAE("Missing module name: " + value); 101 102 String mn = value.substring(0, pos); 103 List<Path> list = map.get(mn); 104 if (list != null) 105 throwIAE("Module " + mn + " specified more than once"); 106 list = new ArrayList<>(); 107 map.put(mn, list); 108 109 String paths = value.substring(pos+1); 110 for (String path : paths.split(File.pathSeparator)) { 111 if (!path.isEmpty()) { 112 list.add(Paths.get(path)); 113 } 114 } 115 116 index++; 117 value = getAndRemoveProperty(PATCH_PROPERTY_PREFIX + index); 118 } 119 120 return map; 121 } 122 123 124 /** 125 * Returns {@code true} is --patch-module is specified to patch modules 126 * in the boot layer. 127 */ 128 static boolean isBootLayerPatched() { 129 return !PATCH_MAP.isEmpty(); 130 } 131 132 /** 133 * Returns a module reference that interposes on the given module if 134 * needed. If there are no patches for the given module then the module 135 * reference is simply returned. Otherwise the patches for the module 136 * are scanned (to find any new concealed packages) and a new module 137 * reference is returned. 138 * 139 * @throws UncheckedIOException if an I/O error is detected 140 */ 141 public static ModuleReference interposeIfNeeded(ModuleReference mref) { 142 143 ModuleDescriptor descriptor = mref.descriptor(); 144 String mn = descriptor.name(); 145 146 // if there are no patches for the module then nothing to do 147 List<Path> paths = PATCH_MAP.get(mn); 148 if (paths == null) 149 return mref; 150 151 152 // scan the JAR file or directory tree to get the set of packages 527 } 528 }; 529 } 530 } 531 532 533 /** 534 * Derives a package name from a file path to a .class file. 535 */ 536 private static String toPackageName(Path top, Path file) { 537 Path entry = top.relativize(file); 538 Path parent = entry.getParent(); 539 if (parent == null) { 540 return warnUnnamedPackage(top, entry.toString()); 541 } else { 542 return parent.toString().replace(File.separatorChar, '.'); 543 } 544 } 545 546 /** 547 * Gets and remove the named system property 548 */ 549 private static String getAndRemoveProperty(String key) { 550 return (String)System.getProperties().remove(key); 551 } 552 553 /** 554 * Derives a package name from the name of an entry in a JAR file. 555 */ 556 private static String toPackageName(Path file, JarEntry entry) { 557 String name = entry.getName(); 558 int index = name.lastIndexOf("/"); 559 if (index == -1) { 560 return warnUnnamedPackage(file, name); 561 } else { 562 return name.substring(0, index).replace('/', '.'); 563 } 564 } 565 566 private static String warnUnnamedPackage(Path file, String e) { 567 System.err.println("WARNING: " + e + " not allowed in patch: " + file); 568 return ""; 569 } 570 571 private static void throwIAE(String msg) { 572 throw new IllegalArgumentException(msg); 573 } |