Optional<ModuleDescriptor> describeModule(Path path, boolean reportFileNameBasedModuleAsEmpty) { try { var allModules = ModuleFinder.of(path).findAll(); if (allModules.size() != 1) { throw new IllegalStateException("expected to find single module, but got: " + allModules); } var reference = allModules.iterator().next(); var descriptor = reference.descriptor(); info("describeModule({0} -> {1})", path, descriptor); if (reportFileNameBasedModuleAsEmpty) { if (descriptor.isAutomatic()) { if (!isAutomaticModuleNameAttributeAvailable(reference)) { return Optional.empty(); } } } return Optional.of(descriptor); } catch (FindException e) { debug("finding module(s) failed: {0}", e); return Optional.empty(); } }
/** * Scan a JAR file or exploded module. */ private Optional<ModuleReference> scanModule(Path entry) { ModuleFinder finder = ModuleFinder.of(entry); try { return finder.findAll().stream().findFirst(); } catch (FindException e) { ostream.println(entry); ostream.println(INDENT + e.getMessage()); Throwable cause = e.getCause(); if (cause != null) { ostream.println(INDENT + cause); } errorFound = true; return Optional.empty(); } }
/** * Service provider dependency not found */ @Test(expectedExceptions = { FindException.class }) public void testServiceProviderDependencyNotFound() { // service provider dependency (on m3) not found ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .requires("m3") .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // should throw ResolutionException because m3 is not found Configuration cf = resolveAndBind(finder, "m1"); }
/** * Test JAR file with META-INF/services configuration file with bad * values or names. */ @Test(dataProvider = "badproviders", expectedExceptions = FindException.class) public void testBadProviderNames(String service, String provider) throws IOException { Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); // provider class Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); Files.createDirectories(providerClass.getParent()); Files.createFile(providerClass); // services configuration file Path services = tmpdir.resolve("META-INF").resolve("services"); Files.createDirectories(services); Files.write(services.resolve(service), Set.of(provider)); Path dir = Files.createTempDirectory(USER_DIR, "mods"); JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); // should throw FindException ModuleFinder.of(dir).findAll(); }
/** * Test JAR file with META-INF/services configuration file listing a * provider that is not in the module. */ @Test(expectedExceptions = FindException.class) public void testMissingProviderPackage() throws IOException { Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); // services configuration file Path services = tmpdir.resolve("META-INF").resolve("services"); Files.createDirectories(services); Files.write(services.resolve("p.S"), Set.of("q.P")); Path dir = Files.createTempDirectory(USER_DIR, "mods"); JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); // should throw FindException ModuleFinder.of(dir).findAll(); }
/** * Scans the given directory for packaged or exploded modules. * * @return a map of module name to ModuleReference for the modules found * in the directory * * @throws IOException if an I/O error occurs * @throws FindException if an error occurs scanning the entry or the * directory contains two or more modules with the same name */ private Map<String, ModuleReference> scanDirectory(Path dir) throws IOException { // The map of name -> mref of modules found in this directory. Map<String, ModuleReference> nameToReference = new HashMap<>(); try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path entry : stream) { BasicFileAttributes attrs; try { attrs = Files.readAttributes(entry, BasicFileAttributes.class); } catch (NoSuchFileException ignore) { // file has been removed or moved, ignore for now continue; } ModuleReference mref = readModule(entry, attrs); // module found if (mref != null) { // can have at most one version of a module in the directory String name = mref.descriptor().name(); ModuleReference previous = nameToReference.put(name, mref); if (previous != null) { String fn1 = fileName(mref); String fn2 = fileName(previous); throw new FindException("Two versions of module " + name + " found in " + dir + " (" + fn1 + " and " + fn2 + ")"); } } } } return nameToReference; }
/** * Constructs a Hasher to compute hashes. * * If a module name `M` is specified, it will compute the hashes of * modules that depend upon M directly or indirectly matching the * specified --hash-modules pattern and record in the ModuleHashes * attribute in M's module-info.class. * * @param name name of the module to record hashes * @param finder module finder for the specified --module-path */ Hasher(String name, ModuleFinder finder) { // Determine the modules that matches the pattern {@code modulesToHash} Set<String> roots = finder.findAll().stream() .map(mref -> mref.descriptor().name()) .filter(mn -> options.modulesToHash.matcher(mn).find()) .collect(Collectors.toSet()); // use system module path unless it creates a JMOD file for // a module that is present in the system image e.g. upgradeable // module ModuleFinder system; if (name != null && ModuleFinder.ofSystem().find(name).isPresent()) { system = ModuleFinder.of(); } else { system = ModuleFinder.ofSystem(); } // get a resolved module graph Configuration config = null; try { config = Configuration.empty().resolve(system, finder, roots); } catch (FindException | ResolutionException e) { throw new CommandException("err.module.resolution.fail", e.getMessage()); } this.moduleName = name; this.configuration = config; // filter modules resolved from the system module finder this.modules = config.modules().stream() .map(ResolvedModule::name) .filter(mn -> roots.contains(mn) && !system.find(mn).isPresent()) .collect(Collectors.toSet()); this.hashesBuilder = new ModuleHashesBuilder(config, modules); }
/** * Direct dependency not found */ @Test(expectedExceptions = { FindException.class }) public void testDirectDependencyNotFound() { ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); resolve(finder, "m1"); }
/** * Transitive dependency not found */ @Test(expectedExceptions = { FindException.class }) public void testTransitiveDependencyNotFound() { ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); resolve(finder, "m1"); }
/** * Test impossible mapping of JAR files to modules names */ @Test(dataProvider = "badjarnames", expectedExceptions = FindException.class) public void testBadNames(String fn, String ignore) throws IOException { Path dir = Files.createTempDirectory(USER_DIR, "mods"); Path jf = dir.resolve(fn); // create empty JAR file createDummyJarFile(jf); // should throw FindException ModuleFinder.of(dir).findAll(); }
/** * Test JAR files with the Automatic-Module-Name attribute with a value * that is not a legal module name. */ @Test(dataProvider = "badmodulenames", expectedExceptions = FindException.class) public void testBadAutomaticModuleNameAttribute(String name, String ignore) throws IOException { // should throw FindException testAutomaticModuleNameAttribute(name, null); }
/** * Test .class file in unnamed package (top-level directory) */ @Test(expectedExceptions = FindException.class) public void testClassInUnnamedPackage() throws IOException { Path dir = Files.createTempDirectory(USER_DIR, "mods"); createDummyJarFile(dir.resolve("m.jar"), "Mojo.class"); ModuleFinder finder = ModuleFinder.of(dir); finder.findAll(); }
private void scanJar(Path path, String group, String artifact, String version) { try { var all = ModuleFinder.of(path).findAll(); if (all.size() != 1) { System.out.println(" [!] expected single module, but got: " + all); return; } var descriptor = all.iterator().next().descriptor(); var module = descriptor.name(); var value = group + "\t" + artifact; var old = map.put(module, value); System.out.println(" [o] " + path); System.out.println( " [o] " + module + " = " + value + " (" + descriptor.rawVersion().orElse("?") + ")"); System.out.println( "| " + offset + " | [" + group + ":" + artifact + "](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22" + group + "%22%20AND%20a%3A%22" + artifact + "%22) | **" + module + "** | " + version + " |"); if (old == null) { return; } if (old.equals(value)) { System.err.println(" [!] already mapped " + module + " to " + value); } System.err.println(" [!] non-unique module name found: " + module); } catch (FindException e) { System.out.println(" [!] " + e); } }
/** * Reads a packaged or exploded module, returning a {@code ModuleReference} * to the module. Returns {@code null} if the entry is not recognized. * * @throws IOException if an I/O error occurs * @throws FindException if an error occurs parsing its module descriptor */ private ModuleReference readModule(Path entry, BasicFileAttributes attrs) throws IOException { try { // exploded module if (attrs.isDirectory()) { return readExplodedModule(entry); // may return null } // JAR or JMOD file if (attrs.isRegularFile()) { String fn = entry.getFileName().toString(); boolean isDefaultFileSystem = isDefaultFileSystem(entry); // JAR file if (fn.endsWith(".jar")) { if (isDefaultFileSystem) { return readJar(entry); } else { // the JAR file is in a custom file system so // need to copy it to the local file system Path tmpdir = Files.createTempDirectory("mlib"); Path target = Files.copy(entry, tmpdir.resolve(fn)); return readJar(target); } } // JMOD file if (isDefaultFileSystem && isLinkPhase && fn.endsWith(".jmod")) { return readJMod(entry); } } return null; } catch (InvalidModuleDescriptorException e) { throw new FindException("Error reading module: " + entry, e); } }
/** * Root module not found */ @Test(expectedExceptions = { FindException.class }) public void testRootNotFound() { resolve(ModuleFinder.of(), "m1"); }
/** * Test attempting to create a configuration with modules for different * platforms. */ @Test(dataProvider = "platformmismatch", expectedExceptions = FindException.class ) public void testPlatformMisMatch(String s1, String s2) throws IOException { testPlatformMatch(s1, s2); }