30 import java.lang.module.InvalidModuleDescriptorException;
31 import java.lang.module.ModuleDescriptor;
32 import java.lang.module.ModuleDescriptor.Exports;
33 import java.lang.module.ModuleDescriptor.Provides;
34 import java.lang.module.ModuleDescriptor.Opens;
35 import java.lang.module.ModuleDescriptor.Requires;
36 import java.lang.module.ModuleDescriptor.Version;
37 import java.lang.module.ModuleFinder;
38 import java.lang.module.ModuleReader;
39 import java.lang.module.ModuleReference;
40 import java.lang.module.ResolutionException;
41 import java.lang.module.ResolvedModule;
42 import java.net.URI;
43 import java.nio.ByteBuffer;
44 import java.nio.file.Path;
45 import java.nio.file.Files;
46 import java.nio.file.Paths;
47 import java.nio.file.StandardCopyOption;
48 import java.util.*;
49 import java.util.function.Consumer;
50 import java.util.function.Function;
51 import java.util.function.Supplier;
52 import java.util.regex.Pattern;
53 import java.util.stream.Collectors;
54 import java.util.stream.Stream;
55 import java.util.zip.*;
56 import java.util.jar.*;
57 import java.util.jar.Pack200.*;
58 import java.util.jar.Manifest;
59 import java.text.MessageFormat;
60
61 import jdk.internal.module.Checks;
62 import jdk.internal.module.ModuleHashes;
63 import jdk.internal.module.ModuleInfo;
64 import jdk.internal.module.ModuleInfoExtender;
65 import jdk.internal.module.ModuleResolution;
66 import jdk.internal.util.jar.JarIndex;
67
68 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
69 import static java.util.jar.JarFile.MANIFEST_NAME;
70 import static java.util.stream.Collectors.joining;
71 import static java.util.stream.Collectors.toSet;
72 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
73
74 /**
75 * This class implements a simple utility for creating files in the JAR
76 * (Java Archive) file format. The JAR format is based on the ZIP file
77 * format, with optional meta-information stored in a MANIFEST entry.
78 */
79 public class Main {
80 String program;
81 PrintWriter out, err;
82 String fname, mname, ename;
83 String zname = "";
84 String rootjar = null;
85
86 private static final int BASE_VERSION = 0;
87
88 private static class Entry {
89 final String name;
90 final File file;
91 final boolean isDir;
1913
1914 // Add (or replace) the Packages attribute
1915 extender.packages(packages);
1916
1917 // --main-class
1918 if (ename != null)
1919 extender.mainClass(ename);
1920
1921 // --module-version
1922 if (moduleVersion != null)
1923 extender.version(moduleVersion);
1924
1925 // --hash-modules
1926 if (modulesToHash != null) {
1927 String mn = md.name();
1928 Hasher hasher = new Hasher(md, fname);
1929 ModuleHashes moduleHashes = hasher.computeHashes(mn);
1930 if (moduleHashes != null) {
1931 extender.hashes(moduleHashes);
1932 } else {
1933 // should it issue warning or silent?
1934 System.out.println("warning: no module is recorded in hash in " + mn);
1935 }
1936 }
1937
1938 if (moduleResolution.value() != 0) {
1939 extender.moduleResolution(moduleResolution);
1940 }
1941
1942 extender.write(baos);
1943 return baos.toByteArray();
1944 }
1945
1946 /**
1947 * Compute and record hashes
1948 */
1949 private class Hasher {
1950 final ModuleFinder finder;
1951 final Map<String, Path> moduleNameToPath;
1952 final Set<String> modules;
1953 final Configuration configuration;
1954 Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
1955 // Create a module finder that finds the modular JAR
1956 // being created/updated
1957 URI uri = Paths.get(fname).toUri();
1958 ModuleReference mref = new ModuleReference(descriptor, uri) {
1959 @Override
1960 public ModuleReader open() {
1961 throw new UnsupportedOperationException("should not reach here");
1962 }
1963 };
1964
1965 // Compose a module finder with the module path and
1966 // the modular JAR being created or updated
1967 this.finder = ModuleFinder.compose(moduleFinder,
1968 new ModuleFinder() {
1969 @Override
1970 public Optional<ModuleReference> find(String name) {
1971 if (descriptor.name().equals(name))
1972 return Optional.of(mref);
1973 else
1974 return Optional.empty();
1975 }
1976
1977 @Override
1978 public Set<ModuleReference> findAll() {
1979 return Collections.singleton(mref);
1980 }
1981 });
1982
1983 // Determine the modules that matches the modulesToHash pattern
1984 this.modules = moduleFinder.findAll().stream()
1985 .map(moduleReference -> moduleReference.descriptor().name())
1986 .filter(mn -> modulesToHash.matcher(mn).find())
1987 .collect(Collectors.toSet());
1988
1989 // a map from a module name to Path of the modular JAR
1990 this.moduleNameToPath = moduleFinder.findAll().stream()
1991 .map(ModuleReference::descriptor)
1992 .map(ModuleDescriptor::name)
1993 .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
1994
1995 Configuration config = null;
1996 try {
1997 config = Configuration.empty()
1998 .resolveRequires(ModuleFinder.ofSystem(), finder, modules);
1999 } catch (ResolutionException e) {
2000 // should it throw an error? or emit a warning
2001 System.out.println("warning: " + e.getMessage());
2002 }
2003 this.configuration = config;
2004 }
2005
2006 /**
2007 * Compute hashes of the modules that depend upon the specified
2008 * module directly or indirectly.
2009 */
2010 ModuleHashes computeHashes(String name) {
2011 // the transposed graph includes all modules in the resolved graph
2012 Map<String, Set<String>> graph = transpose();
2013
2014 // find the modules that transitively depend upon the specified name
2015 Deque<String> deque = new ArrayDeque<>();
2016 deque.add(name);
2017 Set<String> mods = visitNodes(graph, deque);
2018
2019 // filter modules matching the pattern specified in --hash-modules,
2020 // as well as the modular jar file that is being created / updated
2021 Map<String, Path> modulesForHash = mods.stream()
2022 .filter(mn -> !mn.equals(name) && modules.contains(mn))
2023 .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
2024
2025 if (modulesForHash.isEmpty())
2026 return null;
2027
2028 return ModuleHashes.generate(modulesForHash, "SHA-256");
2029 }
2030
2031 /**
2032 * Returns all nodes traversed from the given roots.
2033 */
2034 private Set<String> visitNodes(Map<String, Set<String>> graph,
2035 Deque<String> roots) {
2036 Set<String> visited = new HashSet<>();
2037 while (!roots.isEmpty()) {
2038 String mn = roots.pop();
2039 if (!visited.contains(mn)) {
2040 visited.add(mn);
2041
2042 // the given roots may not be part of the graph
2043 if (graph.containsKey(mn)) {
2044 for (String dm : graph.get(mn)) {
2045 if (!visited.contains(dm))
2046 roots.push(dm);
2047 }
2048 }
2049 }
2050 }
2051 return visited;
2052 }
2053
2054 /**
2055 * Returns a transposed graph from the resolved module graph.
2056 */
2057 private Map<String, Set<String>> transpose() {
2058 Map<String, Set<String>> transposedGraph = new HashMap<>();
2059 Deque<String> deque = new ArrayDeque<>(modules);
2060
2061 Set<String> visited = new HashSet<>();
2062 while (!deque.isEmpty()) {
2063 String mn = deque.pop();
2064 if (!visited.contains(mn)) {
2065 visited.add(mn);
2066
2067 // add an empty set
2068 transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
2069
2070 ResolvedModule resolvedModule = configuration.findModule(mn).get();
2071 for (ResolvedModule dm : resolvedModule.reads()) {
2072 String name = dm.name();
2073 if (!visited.contains(name)) {
2074 deque.push(name);
2075 }
2076 // reverse edge
2077 transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
2078 .add(mn);
2079 }
2080 }
2081 }
2082 return transposedGraph;
2083 }
2084
2085 private Path moduleToPath(String name) {
2086 ModuleReference mref = moduleFinder.find(name).orElseThrow(
2087 () -> new InternalError(formatMsg2("error.hash.dep",name , name)));
2088
2089 URI uri = mref.location().get();
2090 Path path = Paths.get(uri);
2091 String fn = path.getFileName().toString();
2092 if (!fn.endsWith(".jar")) {
2093 throw new UnsupportedOperationException(path + " is not a modular JAR");
2094 }
2095 return path;
2096 }
2097 }
2098 }
|
30 import java.lang.module.InvalidModuleDescriptorException;
31 import java.lang.module.ModuleDescriptor;
32 import java.lang.module.ModuleDescriptor.Exports;
33 import java.lang.module.ModuleDescriptor.Provides;
34 import java.lang.module.ModuleDescriptor.Opens;
35 import java.lang.module.ModuleDescriptor.Requires;
36 import java.lang.module.ModuleDescriptor.Version;
37 import java.lang.module.ModuleFinder;
38 import java.lang.module.ModuleReader;
39 import java.lang.module.ModuleReference;
40 import java.lang.module.ResolutionException;
41 import java.lang.module.ResolvedModule;
42 import java.net.URI;
43 import java.nio.ByteBuffer;
44 import java.nio.file.Path;
45 import java.nio.file.Files;
46 import java.nio.file.Paths;
47 import java.nio.file.StandardCopyOption;
48 import java.util.*;
49 import java.util.function.Consumer;
50 import java.util.function.Supplier;
51 import java.util.regex.Pattern;
52 import java.util.stream.Collectors;
53 import java.util.stream.Stream;
54 import java.util.zip.*;
55 import java.util.jar.*;
56 import java.util.jar.Pack200.*;
57 import java.util.jar.Manifest;
58 import java.text.MessageFormat;
59
60 import jdk.internal.module.Checks;
61 import jdk.internal.module.ModuleHashes;
62 import jdk.internal.module.ModuleHashesBuilder;
63 import jdk.internal.module.ModuleInfo;
64 import jdk.internal.module.ModuleInfoExtender;
65 import jdk.internal.module.ModuleResolution;
66 import jdk.internal.util.jar.JarIndex;
67
68 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
69 import static java.util.jar.JarFile.MANIFEST_NAME;
70 import static java.util.stream.Collectors.joining;
71 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
72
73 /**
74 * This class implements a simple utility for creating files in the JAR
75 * (Java Archive) file format. The JAR format is based on the ZIP file
76 * format, with optional meta-information stored in a MANIFEST entry.
77 */
78 public class Main {
79 String program;
80 PrintWriter out, err;
81 String fname, mname, ename;
82 String zname = "";
83 String rootjar = null;
84
85 private static final int BASE_VERSION = 0;
86
87 private static class Entry {
88 final String name;
89 final File file;
90 final boolean isDir;
1912
1913 // Add (or replace) the Packages attribute
1914 extender.packages(packages);
1915
1916 // --main-class
1917 if (ename != null)
1918 extender.mainClass(ename);
1919
1920 // --module-version
1921 if (moduleVersion != null)
1922 extender.version(moduleVersion);
1923
1924 // --hash-modules
1925 if (modulesToHash != null) {
1926 String mn = md.name();
1927 Hasher hasher = new Hasher(md, fname);
1928 ModuleHashes moduleHashes = hasher.computeHashes(mn);
1929 if (moduleHashes != null) {
1930 extender.hashes(moduleHashes);
1931 } else {
1932 warn("warning: no module is recorded in hash in " + mn);
1933 }
1934 }
1935
1936 if (moduleResolution.value() != 0) {
1937 extender.moduleResolution(moduleResolution);
1938 }
1939
1940 extender.write(baos);
1941 return baos.toByteArray();
1942 }
1943
1944 /**
1945 * Compute and record hashes
1946 */
1947 private class Hasher {
1948 final ModuleHashesBuilder hashesBuilder;
1949 final ModuleFinder finder;
1950 final Set<String> modules;
1951 Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
1952 // Create a module finder that finds the modular JAR
1953 // being created/updated
1954 URI uri = Paths.get(fname).toUri();
1955 ModuleReference mref = new ModuleReference(descriptor, uri) {
1956 @Override
1957 public ModuleReader open() {
1958 throw new UnsupportedOperationException("should not reach here");
1959 }
1960 };
1961
1962 // Compose a module finder with the module path and
1963 // the modular JAR being created or updated
1964 this.finder = ModuleFinder.compose(moduleFinder,
1965 new ModuleFinder() {
1966 @Override
1967 public Optional<ModuleReference> find(String name) {
1968 if (descriptor.name().equals(name))
1969 return Optional.of(mref);
1970 else
1971 return Optional.empty();
1972 }
1973
1974 @Override
1975 public Set<ModuleReference> findAll() {
1976 return Collections.singleton(mref);
1977 }
1978 });
1979
1980 // Determine the modules that matches the pattern {@code modulesToHash}
1981 Set<String> roots = finder.findAll().stream()
1982 .map(ref -> ref.descriptor().name())
1983 .filter(mn -> modulesToHash.matcher(mn).find())
1984 .collect(Collectors.toSet());
1985
1986 // use system module path unless it creates a modular JAR for
1987 // a module that is present in the system image e.g. upgradeable
1988 // module
1989 ModuleFinder system;
1990 String name = descriptor.name();
1991 if (name != null && ModuleFinder.ofSystem().find(name).isPresent()) {
1992 system = ModuleFinder.of();
1993 } else {
1994 system = ModuleFinder.ofSystem();
1995 }
1996 // get a resolved module graph
1997 Configuration config =
1998 Configuration.empty().resolveRequires(system, finder, roots);
1999
2000 // filter modules resolved from the system module finder
2001 this.modules = config.modules().stream()
2002 .map(ResolvedModule::name)
2003 .filter(mn -> roots.contains(mn) && !system.find(mn).isPresent())
2004 .collect(Collectors.toSet());
2005
2006 this.hashesBuilder = new ModuleHashesBuilder(config, modules);
2007 }
2008
2009 /**
2010 * Compute hashes of the specified module.
2011 *
2012 * It records the hashing modules that depend upon the specified
2013 * module directly or indirectly.
2014 */
2015 ModuleHashes computeHashes(String name) {
2016 if (hashesBuilder == null)
2017 return null;
2018
2019 return hashesBuilder.computeHashes(Set.of(name)).get(name);
2020 }
2021 }
2022 }
|