Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModulePathValidator.java
41159 views
/*1* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.File;28import java.io.IOException;29import java.io.PrintStream;30import java.lang.module.FindException;31import java.lang.module.ModuleDescriptor;32import java.lang.module.ModuleFinder;33import java.lang.module.ModuleReference;34import java.net.URI;35import java.nio.file.DirectoryStream;36import java.nio.file.Files;37import java.nio.file.NoSuchFileException;38import java.nio.file.Path;39import java.nio.file.attribute.BasicFileAttributes;40import java.util.Comparator;41import java.util.HashMap;42import java.util.Map;43import java.util.Optional;44import java.util.stream.Stream;4546/**47* A validator to check for errors and conflicts between modules.48*/4950class ModulePathValidator {51private static final String MODULE_INFO = "module-info.class";52private static final String INDENT = " ";5354private final Map<String, ModuleReference> nameToModule;55private final Map<String, ModuleReference> packageToModule;56private final PrintStream out;5758private int errorCount;5960private ModulePathValidator(PrintStream out) {61this.nameToModule = new HashMap<>();62this.packageToModule = new HashMap<>();63this.out = out;64}6566/**67* Scans and the validates all modules on the module path. The module path68* comprises the upgrade module path, system modules, and the application69* module path.70*71* @param out the print stream for output messages72* @return the number of errors found73*/74static int scanAllModules(PrintStream out) {75ModulePathValidator validator = new ModulePathValidator(out);7677// upgrade module path78String value = System.getProperty("jdk.module.upgrade.path");79if (value != null) {80Stream.of(value.split(File.pathSeparator))81.map(Path::of)82.forEach(validator::scan);83}8485// system modules86ModuleFinder.ofSystem().findAll().stream()87.sorted(Comparator.comparing(ModuleReference::descriptor))88.forEach(validator::process);8990// application module path91value = System.getProperty("jdk.module.path");92if (value != null) {93Stream.of(value.split(File.pathSeparator))94.map(Path::of)95.forEach(validator::scan);96}9798return validator.errorCount;99}100101/**102* Prints the module location and name.103*/104private void printModule(ModuleReference mref) {105mref.location()106.filter(uri -> !isJrt(uri))107.ifPresent(uri -> out.print(uri + " "));108ModuleDescriptor descriptor = mref.descriptor();109out.print(descriptor.name());110if (descriptor.isAutomatic())111out.print(" automatic");112out.println();113}114115/**116* Prints the module location and name, checks if the module is117* shadowed by a previously seen module, and finally checks for118* package conflicts with previously seen modules.119*/120private void process(ModuleReference mref) {121String name = mref.descriptor().name();122ModuleReference previous = nameToModule.putIfAbsent(name, mref);123if (previous != null) {124printModule(mref);125out.print(INDENT + "shadowed by ");126printModule(previous);127} else {128boolean first = true;129130// check for package conflicts when not shadowed131for (String pkg : mref.descriptor().packages()) {132previous = packageToModule.putIfAbsent(pkg, mref);133if (previous != null) {134if (first) {135printModule(mref);136first = false;137errorCount++;138}139String mn = previous.descriptor().name();140out.println(INDENT + "contains " + pkg141+ " conflicts with module " + mn);142}143}144}145}146147/**148* Scan an element on a module path. The element is a directory149* of modules, an exploded module, or a JAR file.150*/151private void scan(Path entry) {152BasicFileAttributes attrs;153try {154attrs = Files.readAttributes(entry, BasicFileAttributes.class);155} catch (NoSuchFileException ignore) {156return;157} catch (IOException ioe) {158out.println(entry + " " + ioe);159errorCount++;160return;161}162163String fn = entry.getFileName().toString();164if (attrs.isRegularFile() && fn.endsWith(".jar")) {165// JAR file, explicit or automatic module166scanModule(entry).ifPresent(this::process);167} else if (attrs.isDirectory()) {168Path mi = entry.resolve(MODULE_INFO);169if (Files.exists(mi)) {170// exploded module171scanModule(entry).ifPresent(this::process);172} else {173// directory of modules174scanDirectory(entry);175}176}177}178179/**180* Scan the JAR files and exploded modules in a directory.181*/182private void scanDirectory(Path dir) {183try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {184Map<String, Path> moduleToEntry = new HashMap<>();185186for (Path entry : stream) {187BasicFileAttributes attrs;188try {189attrs = Files.readAttributes(entry, BasicFileAttributes.class);190} catch (IOException ioe) {191out.println(entry + " " + ioe);192errorCount++;193continue;194}195196ModuleReference mref = null;197198String fn = entry.getFileName().toString();199if (attrs.isRegularFile() && fn.endsWith(".jar")) {200mref = scanModule(entry).orElse(null);201} else if (attrs.isDirectory()) {202Path mi = entry.resolve(MODULE_INFO);203if (Files.exists(mi)) {204mref = scanModule(entry).orElse(null);205}206}207208if (mref != null) {209String name = mref.descriptor().name();210Path previous = moduleToEntry.putIfAbsent(name, entry);211if (previous != null) {212// same name as other module in the directory213printModule(mref);214out.println(INDENT + "contains same module as "215+ previous.getFileName());216errorCount++;217} else {218process(mref);219}220}221}222} catch (IOException ioe) {223out.println(dir + " " + ioe);224errorCount++;225}226}227228/**229* Scan a JAR file or exploded module.230*/231private Optional<ModuleReference> scanModule(Path entry) {232ModuleFinder finder = ModuleFinder.of(entry);233try {234return finder.findAll().stream().findFirst();235} catch (FindException e) {236out.println(entry);237out.println(INDENT + e.getMessage());238Throwable cause = e.getCause();239if (cause != null) {240out.println(INDENT + cause);241}242errorCount++;243return Optional.empty();244}245}246247/**248* Returns true if the given URI is a jrt URI249*/250private static boolean isJrt(URI uri) {251return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));252}253}254255256