< prev index next >
src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 56,72 ****
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.MessageFormat;
- import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
- import java.util.Deque;
- import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
--- 56,69 ----
*** 99,108 ****
--- 96,106 ----
import jdk.internal.joptsimple.OptionSet;
import jdk.internal.joptsimple.OptionSpec;
import jdk.internal.joptsimple.ValueConverter;
import jdk.internal.loader.ResourceHelper;
import jdk.internal.module.ModuleHashes;
+ import jdk.internal.module.ModuleHashesBuilder;
import jdk.internal.module.ModuleInfo;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModulePath;
import jdk.internal.module.ModuleResolution;
import jdk.tools.jlink.internal.Utils;
*** 284,294 ****
return true;
}
}
private boolean hashModules() {
! return new Hasher(options.moduleFinder).run();
}
private boolean describe() throws IOException {
try (JmodFile jf = new JmodFile(options.jmodFile)) {
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
--- 282,312 ----
return true;
}
}
private boolean hashModules() {
! if (options.dryrun) {
! out.println("Dry run:");
! }
!
! Hasher hasher = new Hasher(options.moduleFinder);
! hasher.computeHashes().forEach((mn, hashes) -> {
! if (options.dryrun) {
! out.format("%s%n", mn);
! hashes.names().stream()
! .sorted()
! .forEach(name -> out.format(" hashes %s %s %s%n",
! name, hashes.algorithm(), toHex(hashes.hashFor(name))));
! } else {
! try {
! hasher.updateModuleInfo(mn, hashes);
! } catch (IOException ex) {
! throw new UncheckedIOException(ex);
! }
! }
! });
! return true;
}
private boolean describe() throws IOException {
try (JmodFile jf = new JmodFile(options.jmodFile)) {
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
*** 375,385 ****
JmodFileWriter jmod = new JmodFileWriter();
// create jmod with temporary name to avoid it being examined
// when scanning the module path
Path target = options.jmodFile;
! Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
try {
try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget)) {
jmod.write(jos);
}
Files.move(tempTarget, target);
--- 393,404 ----
JmodFileWriter jmod = new JmodFileWriter();
// create jmod with temporary name to avoid it being examined
// when scanning the module path
Path target = options.jmodFile;
! Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir"));
! Path tempTarget = tmpdir.resolve(target.getFileName().toString() + ".tmp");
try {
try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget)) {
jmod.write(jos);
}
Files.move(tempTarget, target);
*** 409,419 ****
final String mainClass = options.mainClass;
final String osName = options.osName;
final String osArch = options.osArch;
final String osVersion = options.osVersion;
final List<PathMatcher> excludes = options.excludes;
- final Hasher hasher = hasher();
final ModuleResolution moduleResolution = options.moduleResolution;
JmodFileWriter() { }
/**
--- 428,437 ----
*** 512,523 ****
// --module-version
if (moduleVersion != null)
extender.version(moduleVersion);
! if (hasher != null) {
! ModuleHashes moduleHashes = hasher.computeHashes(descriptor.name());
if (moduleHashes != null) {
extender.hashes(moduleHashes);
} else {
warning("warn.no.module.hashes", descriptor.name());
}
--- 530,550 ----
// --module-version
if (moduleVersion != null)
extender.version(moduleVersion);
! // --hash-modules
! if (options.modulesToHash != null) {
! // To compute hashes, it creates a Configuration to resolve
! // a module graph. The post-resolution check requires
! // the packages in ModuleDescriptor be available for validation.
! ModuleDescriptor md;
! try (InputStream is = miSupplier.get()) {
! md = ModuleDescriptor.read(is, () -> packages);
! }
!
! ModuleHashes moduleHashes = computeHashes(md);
if (moduleHashes != null) {
extender.hashes(moduleHashes);
} else {
warning("warn.no.module.hashes", descriptor.name());
}
*** 555,584 ****
* as the roots.
*
* The jmod file is being created and does not exist in the
* given modulepath.
*/
! private Hasher hasher() {
! if (options.modulesToHash == null)
! return null;
!
! try {
! Supplier<InputStream> miSupplier = newModuleInfoSupplier();
! if (miSupplier == null) {
! throw new IOException(MODULE_INFO + " not found");
! }
!
! ModuleDescriptor descriptor;
! try (InputStream in = miSupplier.get()) {
! descriptor = ModuleDescriptor.read(in);
! }
!
URI uri = options.jmodFile.toUri();
ModuleReference mref = new ModuleReference(descriptor, uri) {
@Override
public ModuleReader open() {
! throw new UnsupportedOperationException();
}
};
// compose a module finder with the module path and also
// a module finder that can find the jmod file being created
--- 582,598 ----
* as the roots.
*
* The jmod file is being created and does not exist in the
* given modulepath.
*/
! private ModuleHashes computeHashes(ModuleDescriptor descriptor) {
! String mn = descriptor.name();
URI uri = options.jmodFile.toUri();
ModuleReference mref = new ModuleReference(descriptor, uri) {
@Override
public ModuleReader open() {
! throw new UnsupportedOperationException("opening " + mn);
}
};
// compose a module finder with the module path and also
// a module finder that can find the jmod file being created
*** 595,608 ****
public Set<ModuleReference> findAll() {
return Collections.singleton(mref);
}
});
! return new Hasher(finder);
! } catch (IOException e) {
! throw new UncheckedIOException(e);
! }
}
/**
* Returns the set of all packages on the given class path.
*/
--- 609,619 ----
public Set<ModuleReference> findAll() {
return Collections.singleton(mref);
}
});
! return new Hasher(mn, finder).computeHashes().get(mn);
}
/**
* Returns the set of all packages on the given class path.
*/
*** 787,982 ****
/**
* Compute and record hashes
*/
private class Hasher {
- final ModuleFinder moduleFinder;
- final Map<String, Path> moduleNameToPath;
- final Set<String> modules;
final Configuration configuration;
! final boolean dryrun = options.dryrun;
Hasher(ModuleFinder finder) {
! this.moduleFinder = finder;
// Determine the modules that matches the pattern {@code modulesToHash}
! this.modules = moduleFinder.findAll().stream()
.map(mref -> mref.descriptor().name())
.filter(mn -> options.modulesToHash.matcher(mn).find())
.collect(Collectors.toSet());
! // a map from a module name to Path of the packaged module
! this.moduleNameToPath = moduleFinder.findAll().stream()
! .map(mref -> mref.descriptor().name())
! .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
!
// get a resolved module graph
Configuration config = null;
try {
! config = Configuration.empty()
! .resolveRequires(ModuleFinder.ofSystem(), moduleFinder, modules);
} catch (ResolutionException e) {
! warning("warn.module.resolution.fail", e.getMessage());
}
this.configuration = config;
- }
-
- /**
- * This method is for jmod hash command.
- *
- * Identify the base modules in the module graph, i.e. no outgoing edge
- * to any of the modules to be hashed.
- *
- * For each base module M, compute the hashes of all modules that depend
- * upon M directly or indirectly. Then update M's module-info.class
- * to record the hashes.
- */
- boolean run() {
- if (configuration == null)
- return false;
-
- // transposed graph containing the the packaged modules and
- // its transitive dependences matching --hash-modules
- Map<String, Set<String>> graph = new HashMap<>();
- for (String root : modules) {
- Deque<String> deque = new ArrayDeque<>();
- deque.add(root);
- Set<String> visited = new HashSet<>();
- while (!deque.isEmpty()) {
- String mn = deque.pop();
- if (!visited.contains(mn)) {
- visited.add(mn);
-
- if (modules.contains(mn))
- graph.computeIfAbsent(mn, _k -> new HashSet<>());
! ResolvedModule resolvedModule = configuration.findModule(mn).get();
! for (ResolvedModule dm : resolvedModule.reads()) {
! String name = dm.name();
! if (!visited.contains(name)) {
! deque.push(name);
! }
!
! // reverse edge
! if (modules.contains(name) && modules.contains(mn)) {
! graph.computeIfAbsent(name, _k -> new HashSet<>()).add(mn);
! }
! }
! }
! }
! }
!
! if (dryrun)
! out.println("Dry run:");
! // each node in a transposed graph is a matching packaged module
! // in which the hash of the modules that depend upon it is recorded
! graph.entrySet().stream()
! .filter(e -> !e.getValue().isEmpty())
! .forEach(e -> {
! String mn = e.getKey();
! Map<String, Path> modulesForHash = e.getValue().stream()
! .collect(Collectors.toMap(Function.identity(),
! moduleNameToPath::get));
! ModuleHashes hashes = ModuleHashes.generate(modulesForHash, "SHA-256");
! if (dryrun) {
! out.format("%s%n", mn);
! hashes.names().stream()
! .sorted()
! .forEach(name -> out.format(" hashes %s %s %s%n",
! name, hashes.algorithm(), hashes.hashFor(name)));
! } else {
! try {
! updateModuleInfo(mn, hashes);
! } catch (IOException ex) {
! throw new UncheckedIOException(ex);
! }
! }
! });
! return true;
}
/**
! * Compute hashes of the specified module.
*
! * It records the hashing modules that depend upon the specified
! * module directly or indirectly.
*/
! ModuleHashes computeHashes(String name) {
! if (configuration == null)
return null;
! // the transposed graph includes all modules in the resolved graph
! Map<String, Set<String>> graph = transpose();
!
! // find the modules that transitively depend upon the specified name
! Deque<String> deque = new ArrayDeque<>();
! deque.add(name);
! Set<String> mods = visitNodes(graph, deque);
!
! // filter modules matching the pattern specified --hash-modules
! // as well as itself as the jmod file is being generated
! Map<String, Path> modulesForHash = mods.stream()
! .filter(mn -> !mn.equals(name) && modules.contains(mn))
! .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
!
! if (modulesForHash.isEmpty())
! return null;
!
! return ModuleHashes.generate(modulesForHash, "SHA-256");
! }
!
! /**
! * Returns all nodes traversed from the given roots.
! */
! private Set<String> visitNodes(Map<String, Set<String>> graph,
! Deque<String> roots) {
! Set<String> visited = new HashSet<>();
! while (!roots.isEmpty()) {
! String mn = roots.pop();
! if (!visited.contains(mn)) {
! visited.add(mn);
! // the given roots may not be part of the graph
! if (graph.containsKey(mn)) {
! for (String dm : graph.get(mn)) {
! if (!visited.contains(dm)) {
! roots.push(dm);
! }
! }
! }
! }
! }
! return visited;
! }
!
! /**
! * Returns a transposed graph from the resolved module graph.
! */
! private Map<String, Set<String>> transpose() {
! Map<String, Set<String>> transposedGraph = new HashMap<>();
! Deque<String> deque = new ArrayDeque<>(modules);
!
! Set<String> visited = new HashSet<>();
! while (!deque.isEmpty()) {
! String mn = deque.pop();
! if (!visited.contains(mn)) {
! visited.add(mn);
!
! transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
!
! ResolvedModule resolvedModule = configuration.findModule(mn).get();
! for (ResolvedModule dm : resolvedModule.reads()) {
! String name = dm.name();
! if (!visited.contains(name)) {
! deque.push(name);
! }
!
! // reverse edge
! transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
! .add(mn);
! }
! }
}
- return transposedGraph;
}
/**
* Reads the given input stream of module-info.class and write
* the extended module-info.class with the given ModuleHashes
--- 798,889 ----
/**
* Compute and record hashes
*/
private class Hasher {
final Configuration configuration;
! final ModuleHashesBuilder hashesBuilder;
! final Set<String> modules;
! final Optional<String> moduleName; // a specific module to record hashes
!
! /**
! * This constructor is for jmod hash command.
! *
! * This Hasher will determine which modules to record hashes, i.e.
! * the module in a subgraph of modules to be hashed and that
! * has no outgoing edges. It will record in each of these modules,
! * say `M`, with the the hashes of modules that depend upon M
! * directly or indirectly matching the specified --hash-modules pattern.
! */
Hasher(ModuleFinder finder) {
! this(null, finder);
! }
!
! /**
! * 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 he module to record hashes
! * @param finder module finder for the specified --module-path
! */
! Hasher(String name, ModuleFinder finder) {
! this.moduleName = Optional.ofNullable(name);
!
// 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().resolveRequires(system, finder, roots);
} catch (ResolutionException e) {
! throw new CommandException("err.module.resolution.fail", e.getMessage());
}
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);
}
/**
! * Returns a map of a module M to record hashes of the modules
! * that depend upon M directly or indirectly.
*
! * For jmod hash command, each entry in the returned map is a module
! * in a subgraph containing the modules specified in the --hash-modules
! * pattern but it has no outgoing edges.
*/
! Map<String, ModuleHashes> computeHashes() {
! if (hashesBuilder == null)
return null;
! if (moduleName.isPresent()) {
! return hashesBuilder.computeHashes(Set.of(moduleName.get()));
! } else {
! return hashesBuilder.computeHashes(modules);
}
}
/**
* Reads the given input stream of module-info.class and write
* the extended module-info.class with the given ModuleHashes
*** 991,1005 ****
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
extender.hashes(hashes);
extender.write(out);
}
! private void updateModuleInfo(String name, ModuleHashes moduleHashes)
throws IOException
{
! Path target = moduleNameToPath.get(name);
! Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
try {
if (target.getFileName().toString().endsWith(".jmod")) {
updateJmodFile(target, tempTarget, moduleHashes);
} else {
updateModularJar(target, tempTarget, moduleHashes);
--- 898,913 ----
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
extender.hashes(hashes);
extender.write(out);
}
! void updateModuleInfo(String name, ModuleHashes moduleHashes)
throws IOException
{
! Path target = moduleToPath(name);
! Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir"));
! Path tempTarget = tmpdir.resolve(target.getFileName().toString() + ".tmp");
try {
if (target.getFileName().toString().endsWith(".jmod")) {
updateJmodFile(target, tempTarget, moduleHashes);
} else {
updateModularJar(target, tempTarget, moduleHashes);
*** 1073,1086 ****
});
}
}
private Path moduleToPath(String name) {
! ModuleReference mref = moduleFinder.find(name).orElseThrow(
() -> new InternalError("Selected module " + name + " not on module path"));
! URI uri = mref.location().get();
Path path = Paths.get(uri);
String fn = path.getFileName().toString();
if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) {
throw new InternalError(path + " is not a modular JAR or jmod file");
}
--- 981,994 ----
});
}
}
private Path moduleToPath(String name) {
! ResolvedModule rm = configuration.findModule(name).orElseThrow(
() -> new InternalError("Selected module " + name + " not on module path"));
! URI uri = rm.reference().location().get();
Path path = Paths.get(uri);
String fn = path.getFileName().toString();
if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) {
throw new InternalError(path + " is not a modular JAR or jmod file");
}
< prev index next >