jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
changeset 45004 ea3137042a61
parent 44545 83b611b88ac8
child 45281 122c607f82da
child 45339 f2ab1225d11f
equal deleted inserted replaced
44789:73fd39e0702e 45004:ea3137042a61
    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;
    81 import java.util.jar.Manifest;
    87 import java.util.jar.Manifest;
    82 import java.util.stream.Collectors;
    88 import java.util.stream.Collectors;
    83 import java.util.stream.Stream;
    89 import java.util.stream.Stream;
    84 
    90 
    85 import jdk.internal.misc.VM;
    91 import jdk.internal.misc.VM;
       
    92 import jdk.internal.module.ModuleBootstrap;
    86 import jdk.internal.module.Modules;
    93 import jdk.internal.module.Modules;
    87 
    94 
    88 
    95 
    89 public final class LauncherHelper {
    96 public final class LauncherHelper {
    90 
    97 
    96             "JavaFX-Application-Class";
   103             "JavaFX-Application-Class";
    97     private static final String JAVAFX_APPLICATION_CLASS_NAME =
   104     private static final String JAVAFX_APPLICATION_CLASS_NAME =
    98             "javafx.application.Application";
   105             "javafx.application.Application";
    99     private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
   106     private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
   100             "sun.launcher.LauncherHelper$FXHelper";
   107             "sun.launcher.LauncherHelper$FXHelper";
       
   108     private static final String LAUNCHER_AGENT_CLASS = "Launcher-Agent-Class";
   101     private static final String MAIN_CLASS = "Main-Class";
   109     private static final String MAIN_CLASS = "Main-Class";
   102     private static final String ADD_EXPORTS = "Add-Exports";
   110     private static final String ADD_EXPORTS = "Add-Exports";
   103     private static final String ADD_OPENS = "Add-Opens";
   111     private static final String ADD_OPENS = "Add-Opens";
   104 
   112 
   105     private static StringBuilder outBuf = new StringBuilder();
   113     private static StringBuilder outBuf = new StringBuilder();
   406 
   414 
   407     static void initOutput(boolean printToStderr) {
   415     static void initOutput(boolean printToStderr) {
   408         ostream =  (printToStderr) ? System.err : System.out;
   416         ostream =  (printToStderr) ? System.err : System.out;
   409     }
   417     }
   410 
   418 
       
   419     static void initOutput(PrintStream ps) {
       
   420         ostream = ps;
       
   421     }
       
   422 
   411     static String getMainClassFromJar(String jarname) {
   423     static String getMainClassFromJar(String jarname) {
   412         String mainValue = null;
   424         String mainValue;
   413         try (JarFile jarFile = new JarFile(jarname)) {
   425         try (JarFile jarFile = new JarFile(jarname)) {
   414             Manifest manifest = jarFile.getManifest();
   426             Manifest manifest = jarFile.getManifest();
   415             if (manifest == null) {
   427             if (manifest == null) {
   416                 abort(null, "java.launcher.jar.error2", jarname);
   428                 abort(null, "java.launcher.jar.error2", jarname);
   417             }
   429             }
   422 
   434 
   423             // Main-Class
   435             // Main-Class
   424             mainValue = mainAttrs.getValue(MAIN_CLASS);
   436             mainValue = mainAttrs.getValue(MAIN_CLASS);
   425             if (mainValue == null) {
   437             if (mainValue == null) {
   426                 abort(null, "java.launcher.jar.error3", jarname);
   438                 abort(null, "java.launcher.jar.error3", jarname);
       
   439             }
       
   440 
       
   441             // Launcher-Agent-Class (only check for this when Main-Class present)
       
   442             String agentClass = mainAttrs.getValue(LAUNCHER_AGENT_CLASS);
       
   443             if (agentClass != null) {
       
   444                 ModuleLayer.boot().findModule("java.instrument").ifPresent(m -> {
       
   445                     try {
       
   446                         String cn = "sun.instrument.InstrumentationImpl";
       
   447                         Class<?> clazz = Class.forName(cn, false, null);
       
   448                         Method loadAgent = clazz.getMethod("loadAgent", String.class);
       
   449                         loadAgent.invoke(null, jarname);
       
   450                     } catch (Throwable e) {
       
   451                         if (e instanceof InvocationTargetException) e = e.getCause();
       
   452                         abort(e, "java.launcher.jar.error4", jarname);
       
   453                     }
       
   454                 });
   427             }
   455             }
   428 
   456 
   429             // Add-Exports and Add-Opens
   457             // Add-Exports and Add-Opens
   430             String exports = mainAttrs.getValue(ADD_EXPORTS);
   458             String exports = mainAttrs.getValue(ADD_EXPORTS);
   431             if (exports != null) {
   459             if (exports != null) {
   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 }