41 */ |
41 */ |
42 import java.io.File; |
42 import java.io.File; |
43 import java.io.IOException; |
43 import java.io.IOException; |
44 import java.io.PrintStream; |
44 import java.io.PrintStream; |
45 import java.io.UnsupportedEncodingException; |
45 import java.io.UnsupportedEncodingException; |
46 import java.lang.module.ModuleFinder; |
46 import java.lang.module.Configuration; |
47 import java.lang.module.ModuleReference; |
47 import java.lang.module.FindException; |
48 import java.lang.module.ModuleDescriptor; |
48 import java.lang.module.ModuleDescriptor; |
49 import java.lang.module.ModuleDescriptor.Requires; |
49 import java.lang.module.ModuleDescriptor.Requires; |
50 import java.lang.module.ModuleDescriptor.Exports; |
50 import java.lang.module.ModuleDescriptor.Exports; |
51 import java.lang.module.ModuleDescriptor.Opens; |
51 import java.lang.module.ModuleDescriptor.Opens; |
52 import java.lang.module.ModuleDescriptor.Provides; |
52 import java.lang.module.ModuleDescriptor.Provides; |
|
53 import java.lang.module.ModuleFinder; |
|
54 import java.lang.module.ModuleReference; |
|
55 import java.lang.module.ResolvedModule; |
|
56 import java.lang.reflect.InvocationTargetException; |
53 import java.lang.reflect.Method; |
57 import java.lang.reflect.Method; |
54 import java.lang.reflect.Modifier; |
58 import java.lang.reflect.Modifier; |
55 import java.math.BigDecimal; |
59 import java.math.BigDecimal; |
56 import java.math.RoundingMode; |
60 import java.math.RoundingMode; |
57 import java.net.URI; |
61 import java.net.URI; |
58 import java.nio.charset.Charset; |
62 import java.nio.charset.Charset; |
59 import java.nio.file.DirectoryStream; |
63 import java.nio.file.DirectoryStream; |
60 import java.nio.file.Files; |
64 import java.nio.file.Files; |
|
65 import java.nio.file.NoSuchFileException; |
61 import java.nio.file.Path; |
66 import java.nio.file.Path; |
|
67 import java.nio.file.Paths; |
|
68 import java.nio.file.attribute.BasicFileAttributes; |
62 import java.text.Normalizer; |
69 import java.text.Normalizer; |
63 import java.text.MessageFormat; |
70 import java.text.MessageFormat; |
64 import java.util.ArrayList; |
71 import java.util.ArrayList; |
65 import java.util.Collection; |
|
66 import java.util.Collections; |
72 import java.util.Collections; |
67 import java.util.Comparator; |
73 import java.util.Comparator; |
68 import java.util.HashSet; |
74 import java.util.HashMap; |
69 import java.util.Iterator; |
75 import java.util.Iterator; |
70 import java.util.List; |
76 import java.util.List; |
71 import java.util.Locale; |
77 import java.util.Locale; |
72 import java.util.Locale.Category; |
78 import java.util.Locale.Category; |
73 import java.util.Map; |
79 import java.util.Map; |
911 fxLauncherMethod.invoke(null, |
939 fxLauncherMethod.invoke(null, |
912 new Object[] {fxLaunchName, fxLaunchMode, args}); |
940 new Object[] {fxLaunchName, fxLaunchMode, args}); |
913 } |
941 } |
914 } |
942 } |
915 |
943 |
916 private static void formatCommaList(PrintStream out, |
|
917 String prefix, |
|
918 Collection<?> list) |
|
919 { |
|
920 if (list.isEmpty()) |
|
921 return; |
|
922 out.format("%s", prefix); |
|
923 boolean first = true; |
|
924 for (Object ob : list) { |
|
925 if (first) { |
|
926 out.format(" %s", ob); |
|
927 first = false; |
|
928 } else { |
|
929 out.format(", %s", ob); |
|
930 } |
|
931 } |
|
932 out.format("%n"); |
|
933 } |
|
934 |
|
935 /** |
944 /** |
936 * Called by the launcher to list the observable modules. |
945 * Called by the launcher to list the observable modules. |
937 * If called without any sub-options then the output is a simple list of |
946 */ |
938 * the modules. If called with sub-options then the sub-options are the |
947 static void listModules() { |
939 * names of the modules to list (e.g. --list-modules java.base,java.desktop) |
948 initOutput(System.out); |
940 */ |
949 |
941 static void listModules(boolean printToStderr, String optionFlag) |
950 ModuleBootstrap.limitedFinder().findAll().stream() |
942 throws IOException, ClassNotFoundException |
951 .sorted(new JrtFirstComparator()) |
943 { |
952 .forEach(LauncherHelper::showModule); |
944 initOutput(printToStderr); |
953 } |
945 |
954 |
946 ModuleFinder finder = jdk.internal.module.ModuleBootstrap.finder(); |
955 /** |
947 int colon = optionFlag.indexOf('='); |
956 * Called by the launcher to show the resolved modules |
948 if (colon == -1) { |
957 */ |
949 finder.findAll().stream() |
958 static void showResolvedModules() { |
950 .sorted(Comparator.comparing(ModuleReference::descriptor)) |
959 initOutput(System.out); |
951 .forEach(mref -> describeModule(finder, mref, false)); |
960 |
952 } else { |
961 ModuleLayer bootLayer = ModuleLayer.boot(); |
953 String[] names = optionFlag.substring(colon+1).split(","); |
962 Configuration cf = bootLayer.configuration(); |
954 for (String name: names) { |
963 |
955 ModuleReference mref = finder.find(name).orElse(null); |
964 cf.modules().stream() |
956 if (mref == null) { |
965 .map(ResolvedModule::reference) |
957 System.err.format("%s not found%n", name); |
966 .sorted(new JrtFirstComparator()) |
958 continue; |
967 .forEach(LauncherHelper::showModule); |
959 } |
968 } |
960 describeModule(finder, mref, true); |
969 |
961 } |
970 /** |
962 } |
971 * Called by the launcher to describe a module |
963 } |
972 */ |
964 |
973 static void describeModule(String moduleName) { |
965 /** |
974 initOutput(System.out); |
966 * Describes the given module. |
975 |
967 */ |
976 ModuleFinder finder = ModuleBootstrap.limitedFinder(); |
968 static void describeModule(ModuleFinder finder, |
977 ModuleReference mref = finder.find(moduleName).orElse(null); |
969 ModuleReference mref, |
978 if (mref == null) { |
970 boolean verbose) |
979 abort(null, "java.launcher.module.error4", moduleName); |
971 { |
980 } |
972 ModuleDescriptor md = mref.descriptor(); |
981 ModuleDescriptor md = mref.descriptor(); |
973 ostream.print("module " + midAndLocation(md, mref.location())); |
982 |
974 if (md.isAutomatic()) |
983 // one-line summary |
975 ostream.print(" automatic"); |
984 showModule(mref); |
976 ostream.println(); |
|
977 |
|
978 if (!verbose) |
|
979 return; |
|
980 |
985 |
981 // unqualified exports (sorted by package) |
986 // unqualified exports (sorted by package) |
982 Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source)); |
987 md.exports().stream() |
983 md.exports().stream().filter(e -> !e.isQualified()).forEach(exports::add); |
988 .filter(e -> !e.isQualified()) |
984 for (Exports e : exports) { |
989 .sorted(Comparator.comparing(Exports::source)) |
985 String modsAndSource = Stream.concat(toStringStream(e.modifiers()), |
990 .map(e -> Stream.concat(Stream.of(e.source()), |
986 Stream.of(e.source())) |
991 toStringStream(e.modifiers())) |
|
992 .collect(Collectors.joining(" "))) |
|
993 .forEach(sourceAndMods -> ostream.format("exports %s%n", sourceAndMods)); |
|
994 |
|
995 // dependences |
|
996 for (Requires r : md.requires()) { |
|
997 String nameAndMods = Stream.concat(Stream.of(r.name()), |
|
998 toStringStream(r.modifiers())) |
987 .collect(Collectors.joining(" ")); |
999 .collect(Collectors.joining(" ")); |
988 ostream.format(" exports %s%n", modsAndSource); |
1000 ostream.format("requires %s", nameAndMods); |
989 } |
1001 finder.find(r.name()) |
990 |
1002 .map(ModuleReference::descriptor) |
991 for (Requires d : md.requires()) { |
1003 .filter(ModuleDescriptor::isAutomatic) |
992 ostream.format(" requires %s", d); |
1004 .ifPresent(any -> ostream.print(" automatic")); |
993 String suffix = finder.find(d.name()) |
1005 ostream.println(); |
994 .map(ModuleReference::descriptor) |
1006 } |
995 .map(any -> any.isAutomatic() ? " automatic" : "") |
1007 |
996 .orElse(" not found"); |
1008 // service use and provides |
997 ostream.println(suffix); |
|
998 } |
|
999 for (String s : md.uses()) { |
1009 for (String s : md.uses()) { |
1000 ostream.format(" uses %s%n", s); |
1010 ostream.format("uses %s%n", s); |
1001 } |
1011 } |
1002 |
|
1003 for (Provides ps : md.provides()) { |
1012 for (Provides ps : md.provides()) { |
1004 ostream.format(" provides %s with %s%n", ps.service(), |
1013 String names = ps.providers().stream().collect(Collectors.joining(" ")); |
1005 ps.providers().stream().collect(Collectors.joining(", "))); |
1014 ostream.format("provides %s with %s%n", ps.service(), names); |
|
1015 |
1006 } |
1016 } |
1007 |
1017 |
1008 // qualified exports |
1018 // qualified exports |
1009 for (Exports e : md.exports()) { |
1019 for (Exports e : md.exports()) { |
1010 if (e.isQualified()) { |
1020 if (e.isQualified()) { |
1011 String modsAndSource = Stream.concat(toStringStream(e.modifiers()), |
1021 String who = e.targets().stream().collect(Collectors.joining(" ")); |
1012 Stream.of(e.source())) |
1022 ostream.format("qualified exports %s to %s%n", e.source(), who); |
1013 .collect(Collectors.joining(" ")); |
|
1014 ostream.format(" exports %s", modsAndSource); |
|
1015 formatCommaList(ostream, " to", e.targets()); |
|
1016 } |
1023 } |
1017 } |
1024 } |
1018 |
1025 |
1019 // open packages |
1026 // open packages |
1020 for (Opens obj: md.opens()) { |
1027 for (Opens opens: md.opens()) { |
1021 String modsAndSource = Stream.concat(toStringStream(obj.modifiers()), |
1028 if (opens.isQualified()) |
1022 Stream.of(obj.source())) |
1029 ostream.print("qualified "); |
|
1030 String sourceAndMods = Stream.concat(Stream.of(opens.source()), |
|
1031 toStringStream(opens.modifiers())) |
1023 .collect(Collectors.joining(" ")); |
1032 .collect(Collectors.joining(" ")); |
1024 ostream.format(" opens %s", modsAndSource); |
1033 ostream.format("opens %s", sourceAndMods); |
1025 if (obj.isQualified()) |
1034 if (opens.isQualified()) { |
1026 formatCommaList(ostream, " to", obj.targets()); |
1035 String who = opens.targets().stream().collect(Collectors.joining(" ")); |
1027 else |
1036 ostream.format(" to %s", who); |
1028 ostream.println(); |
1037 } |
|
1038 ostream.println(); |
1029 } |
1039 } |
1030 |
1040 |
1031 // non-exported/non-open packages |
1041 // non-exported/non-open packages |
1032 Set<String> concealed = new TreeSet<>(md.packages()); |
1042 Set<String> concealed = new TreeSet<>(md.packages()); |
1033 md.exports().stream().map(Exports::source).forEach(concealed::remove); |
1043 md.exports().stream().map(Exports::source).forEach(concealed::remove); |
1034 md.opens().stream().map(Opens::source).forEach(concealed::remove); |
1044 md.opens().stream().map(Opens::source).forEach(concealed::remove); |
1035 concealed.forEach(p -> ostream.format(" contains %s%n", p)); |
1045 concealed.forEach(p -> ostream.format("contains %s%n", p)); |
1036 } |
1046 } |
1037 |
1047 |
1038 static <T> String toString(Set<T> s) { |
1048 /** |
1039 return toStringStream(s).collect(Collectors.joining(" ")); |
1049 * Prints a single line with the module name, version and modifiers |
1040 } |
1050 */ |
1041 |
1051 private static void showModule(ModuleReference mref) { |
1042 static <T> Stream<String> toStringStream(Set<T> s) { |
1052 ModuleDescriptor md = mref.descriptor(); |
|
1053 ostream.print(md.toNameAndVersion()); |
|
1054 mref.location() |
|
1055 .filter(uri -> !isJrt(uri)) |
|
1056 .ifPresent(uri -> ostream.format(" %s", uri)); |
|
1057 if (md.isOpen()) |
|
1058 ostream.print(" open"); |
|
1059 if (md.isAutomatic()) |
|
1060 ostream.print(" automatic"); |
|
1061 ostream.println(); |
|
1062 } |
|
1063 |
|
1064 /** |
|
1065 * A ModuleReference comparator that considers modules in the run-time |
|
1066 * image to be less than modules than not in the run-time image. |
|
1067 */ |
|
1068 private static class JrtFirstComparator implements Comparator<ModuleReference> { |
|
1069 private final Comparator<ModuleReference> real; |
|
1070 |
|
1071 JrtFirstComparator() { |
|
1072 this.real = Comparator.comparing(ModuleReference::descriptor); |
|
1073 } |
|
1074 |
|
1075 @Override |
|
1076 public int compare(ModuleReference a, ModuleReference b) { |
|
1077 if (isJrt(a)) { |
|
1078 return isJrt(b) ? real.compare(a, b) : -1; |
|
1079 } else { |
|
1080 return isJrt(b) ? 1 : real.compare(a, b); |
|
1081 } |
|
1082 } |
|
1083 } |
|
1084 |
|
1085 private static <T> Stream<String> toStringStream(Set<T> s) { |
1043 return s.stream().map(e -> e.toString().toLowerCase()); |
1086 return s.stream().map(e -> e.toString().toLowerCase()); |
1044 } |
1087 } |
1045 |
1088 |
1046 static String midAndLocation(ModuleDescriptor md, Optional<URI> location ) { |
1089 private static boolean isJrt(ModuleReference mref) { |
1047 URI loc = location.orElse(null); |
1090 return isJrt(mref.location().orElse(null)); |
1048 if (loc == null || loc.getScheme().equalsIgnoreCase("jrt")) |
1091 } |
1049 return md.toNameAndVersion(); |
1092 |
1050 else |
1093 private static boolean isJrt(URI uri) { |
1051 return md.toNameAndVersion() + " (" + loc + ")"; |
1094 return (uri != null && uri.getScheme().equalsIgnoreCase("jrt")); |
|
1095 } |
|
1096 |
|
1097 /** |
|
1098 * Called by the launcher to validate the modules on the upgrade and |
|
1099 * application module paths. |
|
1100 * |
|
1101 * @return {@code true} if no errors are found |
|
1102 */ |
|
1103 private static boolean validateModules() { |
|
1104 initOutput(System.out); |
|
1105 |
|
1106 ModuleValidator validator = new ModuleValidator(); |
|
1107 |
|
1108 // upgrade module path |
|
1109 String value = System.getProperty("jdk.module.upgrade.path"); |
|
1110 if (value != null) { |
|
1111 Stream.of(value.split(File.pathSeparator)) |
|
1112 .map(Paths::get) |
|
1113 .forEach(validator::scan); |
|
1114 } |
|
1115 |
|
1116 // system modules |
|
1117 ModuleFinder.ofSystem().findAll().stream() |
|
1118 .sorted(Comparator.comparing(ModuleReference::descriptor)) |
|
1119 .forEach(validator::process); |
|
1120 |
|
1121 // application module path |
|
1122 value = System.getProperty("jdk.module.path"); |
|
1123 if (value != null) { |
|
1124 Stream.of(value.split(File.pathSeparator)) |
|
1125 .map(Paths::get) |
|
1126 .forEach(validator::scan); |
|
1127 } |
|
1128 |
|
1129 return !validator.foundErrors(); |
|
1130 } |
|
1131 |
|
1132 /** |
|
1133 * A simple validator to check for errors and conflicts between modules. |
|
1134 */ |
|
1135 static class ModuleValidator { |
|
1136 private static final String MODULE_INFO = "module-info.class"; |
|
1137 |
|
1138 private Map<String, ModuleReference> nameToModule = new HashMap<>(); |
|
1139 private Map<String, ModuleReference> packageToModule = new HashMap<>(); |
|
1140 private boolean errorFound; |
|
1141 |
|
1142 /** |
|
1143 * Returns true if at least one error was found |
|
1144 */ |
|
1145 boolean foundErrors() { |
|
1146 return errorFound; |
|
1147 } |
|
1148 |
|
1149 /** |
|
1150 * Prints the module location and name. |
|
1151 */ |
|
1152 private void printModule(ModuleReference mref) { |
|
1153 mref.location() |
|
1154 .filter(uri -> !isJrt(uri)) |
|
1155 .ifPresent(uri -> ostream.print(uri + " ")); |
|
1156 ModuleDescriptor descriptor = mref.descriptor(); |
|
1157 ostream.print(descriptor.name()); |
|
1158 if (descriptor.isAutomatic()) |
|
1159 ostream.print(" automatic"); |
|
1160 ostream.println(); |
|
1161 } |
|
1162 |
|
1163 /** |
|
1164 * Prints the module location and name, checks if the module is |
|
1165 * shadowed by a previously seen module, and finally checks for |
|
1166 * package conflicts with previously seen modules. |
|
1167 */ |
|
1168 void process(ModuleReference mref) { |
|
1169 printModule(mref); |
|
1170 |
|
1171 String name = mref.descriptor().name(); |
|
1172 ModuleReference previous = nameToModule.putIfAbsent(name, mref); |
|
1173 if (previous != null) { |
|
1174 ostream.print(INDENT + "shadowed by "); |
|
1175 printModule(previous); |
|
1176 } else { |
|
1177 // check for package conflicts when not shadowed |
|
1178 for (String pkg : mref.descriptor().packages()) { |
|
1179 previous = packageToModule.putIfAbsent(pkg, mref); |
|
1180 if (previous != null) { |
|
1181 String mn = previous.descriptor().name(); |
|
1182 ostream.println(INDENT + "contains " + pkg |
|
1183 + " conflicts with module " + mn); |
|
1184 errorFound = true; |
|
1185 } |
|
1186 } |
|
1187 } |
|
1188 } |
|
1189 |
|
1190 /** |
|
1191 * Scan an element on a module path. The element is a directory |
|
1192 * of modules, an exploded module, or a JAR file. |
|
1193 */ |
|
1194 void scan(Path entry) { |
|
1195 BasicFileAttributes attrs; |
|
1196 try { |
|
1197 attrs = Files.readAttributes(entry, BasicFileAttributes.class); |
|
1198 } catch (NoSuchFileException ignore) { |
|
1199 return; |
|
1200 } catch (IOException ioe) { |
|
1201 ostream.println(entry + " " + ioe); |
|
1202 errorFound = true; |
|
1203 return; |
|
1204 } |
|
1205 |
|
1206 String fn = entry.getFileName().toString(); |
|
1207 if (attrs.isRegularFile() && fn.endsWith(".jar")) { |
|
1208 // JAR file, explicit or automatic module |
|
1209 scanModule(entry).ifPresent(this::process); |
|
1210 } else if (attrs.isDirectory()) { |
|
1211 Path mi = entry.resolve(MODULE_INFO); |
|
1212 if (Files.exists(mi)) { |
|
1213 // exploded module |
|
1214 scanModule(entry).ifPresent(this::process); |
|
1215 } else { |
|
1216 // directory of modules |
|
1217 scanDirectory(entry); |
|
1218 } |
|
1219 } |
|
1220 } |
|
1221 |
|
1222 /** |
|
1223 * Scan the JAR files and exploded modules in a directory. |
|
1224 */ |
|
1225 private void scanDirectory(Path dir) { |
|
1226 try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { |
|
1227 Map<String, Path> moduleToEntry = new HashMap<>(); |
|
1228 |
|
1229 for (Path entry : stream) { |
|
1230 BasicFileAttributes attrs; |
|
1231 try { |
|
1232 attrs = Files.readAttributes(entry, BasicFileAttributes.class); |
|
1233 } catch (IOException ioe) { |
|
1234 ostream.println(entry + " " + ioe); |
|
1235 errorFound = true; |
|
1236 continue; |
|
1237 } |
|
1238 |
|
1239 ModuleReference mref = null; |
|
1240 |
|
1241 String fn = entry.getFileName().toString(); |
|
1242 if (attrs.isRegularFile() && fn.endsWith(".jar")) { |
|
1243 mref = scanModule(entry).orElse(null); |
|
1244 } else if (attrs.isDirectory()) { |
|
1245 Path mi = entry.resolve(MODULE_INFO); |
|
1246 if (Files.exists(mi)) { |
|
1247 mref = scanModule(entry).orElse(null); |
|
1248 } |
|
1249 } |
|
1250 |
|
1251 if (mref != null) { |
|
1252 String name = mref.descriptor().name(); |
|
1253 Path previous = moduleToEntry.putIfAbsent(name, entry); |
|
1254 if (previous != null) { |
|
1255 // same name as other module in the directory |
|
1256 printModule(mref); |
|
1257 ostream.println(INDENT + "contains same module as " |
|
1258 + previous.getFileName()); |
|
1259 errorFound = true; |
|
1260 } else { |
|
1261 process(mref); |
|
1262 } |
|
1263 } |
|
1264 } |
|
1265 } catch (IOException ioe) { |
|
1266 ostream.println(dir + " " + ioe); |
|
1267 errorFound = true; |
|
1268 } |
|
1269 } |
|
1270 |
|
1271 /** |
|
1272 * Scan a JAR file or exploded module. |
|
1273 */ |
|
1274 private Optional<ModuleReference> scanModule(Path entry) { |
|
1275 ModuleFinder finder = ModuleFinder.of(entry); |
|
1276 try { |
|
1277 return finder.findAll().stream().findFirst(); |
|
1278 } catch (FindException e) { |
|
1279 ostream.println(entry); |
|
1280 ostream.println(INDENT + e.getMessage()); |
|
1281 Throwable cause = e.getCause(); |
|
1282 if (cause != null) { |
|
1283 ostream.println(INDENT + cause); |
|
1284 } |
|
1285 errorFound = true; |
|
1286 return Optional.empty(); |
|
1287 } |
|
1288 } |
1052 } |
1289 } |
1053 } |
1290 } |