30 import java.lang.module.ModuleDescriptor;
31 import java.lang.module.ModuleDescriptor.Exports;
32 import java.lang.module.ModuleDescriptor.Provides;
33 import java.lang.module.ModuleDescriptor.Opens;
34 import java.lang.module.ModuleDescriptor.Requires;
35 import java.lang.module.ModuleDescriptor.Version;
36 import java.lang.module.ModuleFinder;
37 import java.lang.module.ModuleReader;
38 import java.lang.module.ModuleReference;
39 import java.lang.module.ResolutionException;
40 import java.lang.module.ResolvedModule;
41 import java.net.URI;
42 import java.nio.ByteBuffer;
43 import java.nio.file.Path;
44 import java.nio.file.Files;
45 import java.nio.file.Paths;
46 import java.nio.file.StandardCopyOption;
47 import java.util.*;
48 import java.util.function.Consumer;
49 import java.util.function.Function;
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.misc.JavaLangModuleAccess;
61 import jdk.internal.misc.SharedSecrets;
62 import jdk.internal.module.Checks;
63 import jdk.internal.module.ModuleHashes;
64 import jdk.internal.module.ModuleInfoExtender;
65 import jdk.internal.util.jar.JarIndex;
66
67 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
68 import static java.util.jar.JarFile.MANIFEST_NAME;
69 import static java.util.stream.Collectors.joining;
70 import static java.util.stream.Collectors.toSet;
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
79 class Main {
80 String program;
81 PrintWriter out, err;
82 String fname, mname, ename;
83 String zname = "";
84 String rootjar = null;
207 */
208 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag;
209
210 /* To support additional GNU Style informational options */
211 enum Info {
212 HELP(GNUStyleOptions::printHelp),
213 COMPAT_HELP(GNUStyleOptions::printCompatHelp),
214 USAGE_SUMMARY(GNUStyleOptions::printUsageSummary),
215 VERSION(GNUStyleOptions::printVersion);
216
217 private Consumer<PrintWriter> printFunction;
218 Info(Consumer<PrintWriter> f) { this.printFunction = f; }
219 void print(PrintWriter out) { printFunction.accept(out); }
220 };
221 Info info;
222
223 /* Modular jar related options */
224 boolean printModuleDescriptor;
225 Version moduleVersion;
226 Pattern modulesToHash;
227 ModuleFinder moduleFinder = ModuleFinder.of();
228
229 private static final String MODULE_INFO = "module-info.class";
230
231 static final String MANIFEST_DIR = "META-INF/";
232 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
233 static final String VERSION = "1.0";
234
235 private static ResourceBundle rsrc;
236
237 /**
238 * If true, maintain compatibility with JDK releases prior to 6.0 by
239 * timestamping extracted files with the time at which they are extracted.
240 * Default is to use the time given in the archive.
241 */
242 private static final boolean useExtractionTime =
243 Boolean.getBoolean("sun.tools.jar.useExtractionTime");
244
245 /**
246 * Initialize ResourceBundle
1982 try (BufferedInputStream bis = new BufferedInputStream(fis);
1983 ZipInputStream zis = new ZipInputStream(bis)) {
1984
1985 ZipEntry e;
1986 while ((e = zis.getNextEntry()) != null) {
1987 if (e.getName().equals(MODULE_INFO)) {
1988 printModuleDescriptor(zis);
1989 return true;
1990 }
1991 }
1992 }
1993 return false;
1994 }
1995
1996 static <T> String toString(Collection<T> set) {
1997 if (set.isEmpty()) { return ""; }
1998 return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
1999 .collect(joining(" "));
2000 }
2001
2002 private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
2003
2004 private void printModuleDescriptor(InputStream entryInputStream)
2005 throws IOException
2006 {
2007 ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
2008 StringBuilder sb = new StringBuilder();
2009 sb.append("\n");
2010 if (md.isOpen())
2011 sb.append("open ");
2012 sb.append(md.toNameAndVersion());
2013
2014 md.requires().stream()
2015 .sorted(Comparator.comparing(Requires::name))
2016 .forEach(r -> {
2017 sb.append("\n requires ");
2018 if (!r.modifiers().isEmpty())
2019 sb.append(toString(r.modifiers())).append(" ");
2020 sb.append(r.name());
2021 });
2022
2023 md.uses().stream().sorted()
2024 .forEach(p -> sb.append("\n uses ").append(p));
2025
2026 md.exports().stream()
2027 .sorted(Comparator.comparing(Exports::source))
2034 Set<String> concealed = new HashSet<>(md.packages());
2035 md.exports().stream().map(Exports::source).forEach(concealed::remove);
2036 md.opens().stream().map(Opens::source).forEach(concealed::remove);
2037 concealed.stream().sorted()
2038 .forEach(p -> sb.append("\n contains ").append(p));
2039
2040 md.provides().stream()
2041 .sorted(Comparator.comparing(Provides::service))
2042 .forEach(p -> sb.append("\n provides ").append(p.service())
2043 .append(" with ")
2044 .append(toString(p.providers())));
2045
2046 md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
2047
2048 md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
2049
2050 md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
2051
2052 md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
2053
2054 JLMA.hashes(md).ifPresent(hashes ->
2055 hashes.names().stream().sorted().forEach(
2056 mod -> sb.append("\n hashes ").append(mod).append(" ")
2057 .append(hashes.algorithm()).append(" ")
2058 .append(hashes.hashFor(mod))));
2059
2060 output(sb.toString());
2061 }
2062
2063 private static String toBinaryName(String classname) {
2064 return (classname.replace('.', '/')) + ".class";
2065 }
2066
2067 /* A module must have the implementation class of the services it 'provides'. */
2068 private boolean checkServices(byte[] moduleInfoBytes)
2069 throws IOException
2070 {
2071 ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
2072 Set<String> missing = md.provides()
2073 .stream()
2074 .map(Provides::providers)
2075 .flatMap(List::stream)
2076 .filter(p -> !jarEntries.contains(toBinaryName(p)))
2077 .collect(Collectors.toSet());
2078 if (missing.size() > 0) {
2079 missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
2080 return false;
2081 }
2082 return true;
2203
2204 // --module-version
2205 if (moduleVersion != null)
2206 extender.version(moduleVersion);
2207 else if (rootDescriptor.version().isPresent())
2208 extender.version(rootDescriptor.version().get());
2209
2210 // --hash-modules
2211 if (modulesToHash != null) {
2212 String mn = md.name();
2213 Hasher hasher = new Hasher(md, fname);
2214 ModuleHashes moduleHashes = hasher.computeHashes(mn);
2215 if (moduleHashes != null) {
2216 extender.hashes(moduleHashes);
2217 } else {
2218 // should it issue warning or silent?
2219 System.out.println("warning: no module is recorded in hash in " + mn);
2220 }
2221 }
2222
2223 extender.write(baos);
2224 return baos.toByteArray();
2225 }
2226
2227 /**
2228 * Compute and record hashes
2229 */
2230 private class Hasher {
2231 final ModuleFinder finder;
2232 final Map<String, Path> moduleNameToPath;
2233 final Set<String> modules;
2234 final Configuration configuration;
2235 Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
2236 // Create a module finder that finds the modular JAR
2237 // being created/updated
2238 URI uri = Paths.get(fname).toUri();
2239 ModuleReference mref = new ModuleReference(descriptor, uri,
2240 new Supplier<>() {
2241 @Override
2242 public ModuleReader get() {
2243 throw new UnsupportedOperationException("should not reach here");
2244 }
2245 });
2246
2247 // Compose a module finder with the module path and
2248 // the modular JAR being created or updated
2249 this.finder = ModuleFinder.compose(moduleFinder,
2250 new ModuleFinder() {
2251 @Override
2252 public Optional<ModuleReference> find(String name) {
2253 if (descriptor.name().equals(name))
2254 return Optional.of(mref);
2255 else
2256 return Optional.empty();
2257 }
2258
2259 @Override
2260 public Set<ModuleReference> findAll() {
2261 return Collections.singleton(mref);
2262 }
2263 });
2264
2265 // Determine the modules that matches the modulesToHash pattern
|
30 import java.lang.module.ModuleDescriptor;
31 import java.lang.module.ModuleDescriptor.Exports;
32 import java.lang.module.ModuleDescriptor.Provides;
33 import java.lang.module.ModuleDescriptor.Opens;
34 import java.lang.module.ModuleDescriptor.Requires;
35 import java.lang.module.ModuleDescriptor.Version;
36 import java.lang.module.ModuleFinder;
37 import java.lang.module.ModuleReader;
38 import java.lang.module.ModuleReference;
39 import java.lang.module.ResolutionException;
40 import java.lang.module.ResolvedModule;
41 import java.net.URI;
42 import java.nio.ByteBuffer;
43 import java.nio.file.Path;
44 import java.nio.file.Files;
45 import java.nio.file.Paths;
46 import java.nio.file.StandardCopyOption;
47 import java.util.*;
48 import java.util.function.Consumer;
49 import java.util.function.Function;
50 import java.util.regex.Pattern;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
53 import java.util.zip.*;
54 import java.util.jar.*;
55 import java.util.jar.Pack200.*;
56 import java.util.jar.Manifest;
57 import java.text.MessageFormat;
58
59 import jdk.internal.module.Checks;
60 import jdk.internal.module.ModuleHashes;
61 import jdk.internal.module.ModuleInfo;
62 import jdk.internal.module.ModuleInfoExtender;
63 import jdk.internal.module.ModuleResolution;
64 import jdk.internal.util.jar.JarIndex;
65
66 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
67 import static java.util.jar.JarFile.MANIFEST_NAME;
68 import static java.util.stream.Collectors.joining;
69 import static java.util.stream.Collectors.toSet;
70 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
71
72 /**
73 * This class implements a simple utility for creating files in the JAR
74 * (Java Archive) file format. The JAR format is based on the ZIP file
75 * format, with optional meta-information stored in a MANIFEST entry.
76 */
77 public
78 class Main {
79 String program;
80 PrintWriter out, err;
81 String fname, mname, ename;
82 String zname = "";
83 String rootjar = null;
206 */
207 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag;
208
209 /* To support additional GNU Style informational options */
210 enum Info {
211 HELP(GNUStyleOptions::printHelp),
212 COMPAT_HELP(GNUStyleOptions::printCompatHelp),
213 USAGE_SUMMARY(GNUStyleOptions::printUsageSummary),
214 VERSION(GNUStyleOptions::printVersion);
215
216 private Consumer<PrintWriter> printFunction;
217 Info(Consumer<PrintWriter> f) { this.printFunction = f; }
218 void print(PrintWriter out) { printFunction.accept(out); }
219 };
220 Info info;
221
222 /* Modular jar related options */
223 boolean printModuleDescriptor;
224 Version moduleVersion;
225 Pattern modulesToHash;
226 ModuleResolution moduleResolution = new ModuleResolution(0);
227 ModuleFinder moduleFinder = ModuleFinder.of();
228
229 private static final String MODULE_INFO = "module-info.class";
230
231 static final String MANIFEST_DIR = "META-INF/";
232 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
233 static final String VERSION = "1.0";
234
235 private static ResourceBundle rsrc;
236
237 /**
238 * If true, maintain compatibility with JDK releases prior to 6.0 by
239 * timestamping extracted files with the time at which they are extracted.
240 * Default is to use the time given in the archive.
241 */
242 private static final boolean useExtractionTime =
243 Boolean.getBoolean("sun.tools.jar.useExtractionTime");
244
245 /**
246 * Initialize ResourceBundle
1982 try (BufferedInputStream bis = new BufferedInputStream(fis);
1983 ZipInputStream zis = new ZipInputStream(bis)) {
1984
1985 ZipEntry e;
1986 while ((e = zis.getNextEntry()) != null) {
1987 if (e.getName().equals(MODULE_INFO)) {
1988 printModuleDescriptor(zis);
1989 return true;
1990 }
1991 }
1992 }
1993 return false;
1994 }
1995
1996 static <T> String toString(Collection<T> set) {
1997 if (set.isEmpty()) { return ""; }
1998 return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
1999 .collect(joining(" "));
2000 }
2001
2002 private void printModuleDescriptor(InputStream entryInputStream)
2003 throws IOException
2004 {
2005 ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null);
2006 ModuleDescriptor md = attrs.descriptor();
2007 ModuleHashes hashes = attrs.recordedHashes();
2008
2009 StringBuilder sb = new StringBuilder();
2010 sb.append("\n");
2011 if (md.isOpen())
2012 sb.append("open ");
2013 sb.append(md.toNameAndVersion());
2014
2015 md.requires().stream()
2016 .sorted(Comparator.comparing(Requires::name))
2017 .forEach(r -> {
2018 sb.append("\n requires ");
2019 if (!r.modifiers().isEmpty())
2020 sb.append(toString(r.modifiers())).append(" ");
2021 sb.append(r.name());
2022 });
2023
2024 md.uses().stream().sorted()
2025 .forEach(p -> sb.append("\n uses ").append(p));
2026
2027 md.exports().stream()
2028 .sorted(Comparator.comparing(Exports::source))
2035 Set<String> concealed = new HashSet<>(md.packages());
2036 md.exports().stream().map(Exports::source).forEach(concealed::remove);
2037 md.opens().stream().map(Opens::source).forEach(concealed::remove);
2038 concealed.stream().sorted()
2039 .forEach(p -> sb.append("\n contains ").append(p));
2040
2041 md.provides().stream()
2042 .sorted(Comparator.comparing(Provides::service))
2043 .forEach(p -> sb.append("\n provides ").append(p.service())
2044 .append(" with ")
2045 .append(toString(p.providers())));
2046
2047 md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
2048
2049 md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
2050
2051 md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
2052
2053 md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
2054
2055 if (hashes != null) {
2056 hashes.names().stream().sorted().forEach(
2057 mod -> sb.append("\n hashes ").append(mod).append(" ")
2058 .append(hashes.algorithm()).append(" ")
2059 .append(toHex(hashes.hashFor(mod))));
2060 }
2061
2062 output(sb.toString());
2063 }
2064
2065 private static String toHex(byte[] ba) {
2066 StringBuilder sb = new StringBuilder(ba.length);
2067 for (byte b: ba) {
2068 sb.append(String.format("%02x", b & 0xff));
2069 }
2070 return sb.toString();
2071 }
2072
2073 private static String toBinaryName(String classname) {
2074 return (classname.replace('.', '/')) + ".class";
2075 }
2076
2077 /* A module must have the implementation class of the services it 'provides'. */
2078 private boolean checkServices(byte[] moduleInfoBytes)
2079 throws IOException
2080 {
2081 ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
2082 Set<String> missing = md.provides()
2083 .stream()
2084 .map(Provides::providers)
2085 .flatMap(List::stream)
2086 .filter(p -> !jarEntries.contains(toBinaryName(p)))
2087 .collect(Collectors.toSet());
2088 if (missing.size() > 0) {
2089 missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
2090 return false;
2091 }
2092 return true;
2213
2214 // --module-version
2215 if (moduleVersion != null)
2216 extender.version(moduleVersion);
2217 else if (rootDescriptor.version().isPresent())
2218 extender.version(rootDescriptor.version().get());
2219
2220 // --hash-modules
2221 if (modulesToHash != null) {
2222 String mn = md.name();
2223 Hasher hasher = new Hasher(md, fname);
2224 ModuleHashes moduleHashes = hasher.computeHashes(mn);
2225 if (moduleHashes != null) {
2226 extender.hashes(moduleHashes);
2227 } else {
2228 // should it issue warning or silent?
2229 System.out.println("warning: no module is recorded in hash in " + mn);
2230 }
2231 }
2232
2233 if (moduleResolution.value() != 0) {
2234 extender.moduleResolution(moduleResolution);
2235 }
2236
2237 extender.write(baos);
2238 return baos.toByteArray();
2239 }
2240
2241 /**
2242 * Compute and record hashes
2243 */
2244 private class Hasher {
2245 final ModuleFinder finder;
2246 final Map<String, Path> moduleNameToPath;
2247 final Set<String> modules;
2248 final Configuration configuration;
2249 Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
2250 // Create a module finder that finds the modular JAR
2251 // being created/updated
2252 URI uri = Paths.get(fname).toUri();
2253 ModuleReference mref = new ModuleReference(descriptor, uri) {
2254 @Override
2255 public ModuleReader open() {
2256 throw new UnsupportedOperationException("should not reach here");
2257 }
2258 };
2259
2260 // Compose a module finder with the module path and
2261 // the modular JAR being created or updated
2262 this.finder = ModuleFinder.compose(moduleFinder,
2263 new ModuleFinder() {
2264 @Override
2265 public Optional<ModuleReference> find(String name) {
2266 if (descriptor.name().equals(name))
2267 return Optional.of(mref);
2268 else
2269 return Optional.empty();
2270 }
2271
2272 @Override
2273 public Set<ModuleReference> findAll() {
2274 return Collections.singleton(mref);
2275 }
2276 });
2277
2278 // Determine the modules that matches the modulesToHash pattern
|