Java 15 ModuleFinder encounters Error reading module



我正在尝试以编程方式加载一个使用 Java 平台模块系统的项目,因此我可以分析存在多少模块以及它们之间的关系。我写了以下代码:

public void retrieveModules() throws IOException {
moduleRefs = new ArrayList<ModuleReference>();
// search for all module-info.class
Stream<Path> classPaths = Files.walk(Paths.get(this.basePath));
List<String> moduleInfoClassFiles = classPaths
.filter(p -> p.getFileName()
.toString()
.toLowerCase()
.contains("module-info.class"))
.map(p -> p.toString())
.collect(Collectors.toList());
// load modules
for (String classFilePath : moduleInfoClassFiles) {
Path dir = Paths.get(classFilePath.replace("/module-info.class", ""));
ModuleFinder finder = ModuleFinder.of(dir);
moduleRefs.addAll(finder.findAll());
}
}

这段代码一直工作正常,因为finder.findAll()找到了模块,我可以检索正确的模块描述符。但是,当我尝试在 JUnit 5 上运行它时 (https://github.com/junit-team/junit5)

我收到以下错误消息:

Exception in thread "main" java.lang.module.FindException: Error reading module: /.../junit-team_junit5/junit-platform-engine/build/classes/java/module/org.junit.platform.commons
at java.base/jdk.internal.module.ModulePath.readModule(ModulePath.java:350)
at java.base/jdk.internal.module.ModulePath.scan(ModulePath.java:237)
at java.base/jdk.internal.module.ModulePath.scanNextEntry(ModulePath.java:190)
at java.base/jdk.internal.module.ModulePath.findAll(ModulePath.java:166)
at RepoAnalyzer.retrieveModules(RepoAnalyzer.java:41)
at RepoAnalyzer.main(RepoAnalyzer.java:23)
Caused by: java.lang.module.InvalidModuleDescriptorException: Package org.junit.platform.commons.util not found in module

我试图找出出了什么问题。我验证了包是否正确导出并且两个模块都需要。有谁知道我做错了什么?

无需对遇到的路径执行字符串操作。您的代码可以简化为

public void retrieveModules() throws IOException {
// search for all module-info.class
try(Stream<Path> classPaths = Files.walk(Paths.get(this.basePath))) {
moduleRefs = ModuleFinder.of(classPaths
.filter(p -> p.getFileName().toString().equals("module-info.class"))
.map(Path::getParent)
.toArray(Path[]::new)).findAll();
}
}

假设您可以将moduleRefs更改为SetCollection而不是List。否则,将表达式包装在new ArrayList<>(…)中。

似乎您正在扫描默认文件系统上的"松散模块",并且遇到的module-info.class没有明确声明所有软件包。ModuleFinder内部发生的情况相当于对ModuleDescriptor.read(InputStream in, Supplier<Set<String>> packageFinder)的调用,而提供的包查找器将扫描包含模块信息的目录的子树,以查找包含至少一个常规文件、类或资源的目录,并具有形成有效包名称的名称。所有这些都被视为模块的包。

该方法的文档指出:

抛出

InvalidModuleDescriptorException - 如果检测到无效的模块描述符,或者 packageFinder 返回的包集不包括从模块描述符获取的所有包

从模块描述符获取的包在packages()方法中命名:

集包括所有导出和打开的包,以及任何服务提供程序的包,以及主类的包。

因此,软件包org.junit.platform.commons.util已通过module-info标识为必需的,但未通过软件包查找器找到,即相应的目录可能不存在。

您可以尝试通过手动回溯这些步骤来缩小问题范围:

public void checkModules() throws IOException {
try(Stream<Path> classPaths = Files.walk(Paths.get(this.basePath))) {
classPaths
.filter(p -> p.getFileName().toString().equals("module-info.class"))
.forEach(this::check);
}
}
private void check(Path path) {
try {
ModuleDescriptor md = md(path);
System.out.println("module " + md.name());
for(String pkg: md.packages()) {
Path pLoc = path.getParent();
for(String sub: pkg.split("\.")) pLoc = pLoc.resolve(sub);
System.out.print("package " + pkg + ": " + pLoc);
if(!Files.exists(pLoc)) System.out.println(" does not exist");
else if(!Files.isDirectory(pLoc)) System.out.println(" is not a directory");
else if(!checkClassOrResource(pLoc))
System.out.println(" contains no class or resource");
else System.out.println(" seems ok");
}
}
catch(IOException ex) {
throw new UncheckedIOException(ex);
}
}
private boolean checkClassOrResource(Path pLoc) throws IOException {
try(Stream<Path> members = Files.list(pLoc)) {
return members.anyMatch(Files::isRegularFile);
}
}
private ModuleDescriptor md(Path p) throws IOException {
try(InputStream is = Files.newInputStream(p)) {
return ModuleDescriptor.read(is);
}
}

这将检查与包对应的目录并打印诊断消息。请注意,上面使用的没有包查找器的方法ModuleDescriptor.read(InputStream is)变体不会验证包位置,而是仅使用在module-info中找到的包位置,该已经包括与跨模块关系相关的所有包。如果您对分析中的其他包不感兴趣,只需使用该方法为每个module-info构造一个ModuleDescriptor并继续使用它们。

try(Stream<Path> classPaths = Files.walk(Paths.get(this.basePath))) {
List<ModuleDescriptor> modules = classPaths
.filter(p -> p.getFileName().toString().equals("module-info.class"))
.map(path -> {
try(InputStream is = Files.newInputStream(path)) {
return ModuleDescriptor.read(is);
} catch(IOException ex) {
throw new UncheckedIOException(ex);
}
})
.collect(Collectors.toList());
}

相关内容

最新更新