8172432: jar cleanup/update for module and mrm jar
8171830: jar tool should validate if any exported or open package is missing
Reviewed-by: mchung, psandoz, chegar
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Thu Jan 12 16:41:08 2017 -0800
@@ -169,9 +169,9 @@
new Option(true, OptionType.CREATE_UPDATE, "--warn-if-resolved") {
void process(Main jartool, String opt, String arg) throws BadArgs {
ModuleResolution mres = ModuleResolution.empty();
- if (jartool.moduleResolution.doNotResolveByDefault())
+ if (jartool.moduleResolution.doNotResolveByDefault()) {
mres.withDoNotResolveByDefault();
-
+ }
if (arg.equals("deprecated")) {
jartool.moduleResolution = mres.withDeprecated();
} else if (arg.equals("deprecated-for-removal")) {
@@ -201,26 +201,27 @@
// Other options
new Option(true, true, OptionType.OTHER, "--help", "-h") {
void process(Main jartool, String opt, String arg) throws BadArgs {
- if (arg == null) {
- jartool.info = Main.Info.HELP;
- return;
+ if (jartool.info == null) {
+ if (arg == null) {
+ jartool.info = GNUStyleOptions::printHelp; // Main.Info.HELP;
+ return;
+ }
+ if (!arg.equals("compat"))
+ throw new BadArgs("error.illegal.option", arg).showUsage(true);
+ // jartool.info = Main.Info.COMPAT_HELP;
+ jartool.info = GNUStyleOptions::printCompatHelp;
}
-
- if (!arg.equals("compat"))
- throw new BadArgs("error.illegal.option", arg).showUsage(true);
-
- jartool.info = Main.Info.COMPAT_HELP;
}
},
new Option(false, OptionType.OTHER, "--help-extra") {
void process(Main jartool, String opt, String arg) throws BadArgs {
- jartool.info = Main.Info.HELP_EXTRA;
+ jartool.info = GNUStyleOptions::printHelpExtra;
}
},
new Option(false, OptionType.OTHER, "--version") {
void process(Main jartool, String opt, String arg) {
if (jartool.info == null)
- jartool.info = Main.Info.VERSION;
+ jartool.info = GNUStyleOptions::printVersion;
}
}
};
@@ -279,14 +280,14 @@
static int parseOptions(Main jartool, String[] args) throws BadArgs {
int count = 0;
if (args.length == 0) {
- jartool.info = Main.Info.USAGE_TRYHELP;
+ jartool.info = GNUStyleOptions::printUsageTryHelp; // never be here
return 0;
}
// process options
for (; count < args.length; count++) {
- if (args[count].charAt(0) != '-' || args[count].equals("-C")
- || args[count].equals("--release"))
+ if (args[count].charAt(0) != '-' || args[count].equals("-C") ||
+ args[count].equals("--release"))
break;
String name = args[count];
@@ -322,15 +323,15 @@
throw new BadArgs("error.unrecognized.option", name).showUsage(true);
}
- static void printHelp(PrintWriter out) {
- printHelp(out, false);
+ static void printHelpExtra(PrintWriter out) {
+ printHelp0(out, true);
}
- static void printHelpExtra(PrintWriter out) {
- printHelp(out, true);
+ static void printHelp(PrintWriter out) {
+ printHelp0(out, false);
}
- private static void printHelp(PrintWriter out, boolean printExtra) {
+ private static void printHelp0(PrintWriter out, boolean printExtra) {
out.format("%s%n", Main.getMsg("main.help.preopt"));
for (OptionType type : OptionType.values()) {
boolean typeHeadingWritten = false;
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Thu Jan 12 16:41:08 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -27,6 +27,7 @@
import java.io.*;
import java.lang.module.Configuration;
+import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Provides;
@@ -47,6 +48,7 @@
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -74,58 +76,24 @@
* (Java Archive) file format. The JAR format is based on the ZIP file
* format, with optional meta-information stored in a MANIFEST entry.
*/
-public
-class Main {
+public class Main {
String program;
PrintWriter out, err;
String fname, mname, ename;
String zname = "";
String rootjar = null;
- Set<String> concealedPackages = new HashSet<>(); // used by Validator
private static final int BASE_VERSION = 0;
- class Entry {
- final String basename;
- final String entryname;
+ private static class Entry {
+ final String name;
final File file;
final boolean isDir;
- Entry(File file, String basename, String entryname) {
- this.file = file;
- this.isDir = file.isDirectory();
- this.basename = basename;
- this.entryname = entryname;
- }
-
- Entry(int version, File file) {
+ Entry(File file, String name, boolean isDir) {
this.file = file;
- String path = file.getPath();
- if (file.isDirectory()) {
- isDir = true;
- path = path.endsWith(File.separator) ? path :
- path + File.separator;
- } else {
- isDir = false;
- }
- EntryName en = new EntryName(path, version);
- basename = en.baseName;
- entryname = en.entryName;
- }
-
- /**
- * Returns a new Entry that trims the versions directory.
- *
- * This entry should be a valid entry matching the given version.
- */
- Entry toVersionedEntry(int version) {
- assert isValidVersionedEntry(this, version);
-
- if (version == BASE_VERSION)
- return this;
-
- EntryName en = new EntryName(trimVersionsDir(basename, version), version);
- return new Entry(this.file, en.baseName, en.entryName);
+ this.isDir = isDir;
+ this.name = name;
}
@Override
@@ -141,32 +109,6 @@
}
}
- class EntryName {
- final String baseName;
- final String entryName;
-
- EntryName(String name, int version) {
- name = name.replace(File.separatorChar, '/');
- String matchPath = "";
- for (String path : pathsMap.get(version)) {
- if (name.startsWith(path)
- && (path.length() > matchPath.length())) {
- matchPath = path;
- }
- }
- name = safeName(name.substring(matchPath.length()));
- // the old implementaton doesn't remove
- // "./" if it was led by "/" (?)
- if (name.startsWith("./")) {
- name = name.substring(2);
- }
- baseName = name;
- entryName = (version > BASE_VERSION)
- ? VERSIONS_DIR + version + "/" + baseName
- : baseName;
- }
- }
-
// An entryName(path)->Entry map generated during "expand", it helps to
// decide whether or not an existing entry in a jar file needs to be
// replaced, during the "update" operation.
@@ -175,11 +117,8 @@
// All entries need to be added/updated.
Set<Entry> entries = new LinkedHashSet<>();
- // All packages.
- Set<String> packages = new HashSet<>();
- // All actual entries added, or existing, in the jar file ( excl manifest
- // and module-info.class ). Populated during create or update.
- Set<String> jarEntries = new HashSet<>();
+ // module-info.class entries need to be added/updated.
+ Map<String,byte[]> moduleInfos = new HashMap<>();
// A paths Set for each version, where each Set contains directories
// specified by the "-C" operation.
@@ -208,19 +147,7 @@
boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag, dflag;
/* To support additional GNU Style informational options */
- enum Info {
- HELP(GNUStyleOptions::printHelp),
- HELP_EXTRA(GNUStyleOptions::printHelpExtra),
- COMPAT_HELP(GNUStyleOptions::printCompatHelp),
- USAGE_TRYHELP(GNUStyleOptions::printUsageTryHelp),
- VERSION(GNUStyleOptions::printVersion);
-
- private Consumer<PrintWriter> printFunction;
- Info(Consumer<PrintWriter> f) { this.printFunction = f; }
- void print(PrintWriter out) { printFunction.accept(out); }
- };
- Info info;
-
+ Consumer<PrintWriter> info;
/* Modular jar related options */
Version moduleVersion;
@@ -228,8 +155,7 @@
ModuleResolution moduleResolution = ModuleResolution.empty();
ModuleFinder moduleFinder = ModuleFinder.of();
- private static final String MODULE_INFO = "module-info.class";
-
+ static final String MODULE_INFO = "module-info.class";
static final String MANIFEST_DIR = "META-INF/";
static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
static final String VERSION = "1.0";
@@ -324,7 +250,6 @@
}
}
}
-
if (cflag) {
Manifest manifest = null;
if (!Mflag) {
@@ -347,72 +272,60 @@
addMultiRelease(manifest);
}
}
-
- Map<String,Path> moduleInfoPaths = new HashMap<>();
- for (int version : filesMap.keySet()) {
- String[] files = filesMap.get(version);
- expand(null, files, false, moduleInfoPaths, version);
- }
-
- Map<String,byte[]> moduleInfos = new LinkedHashMap<>();
- if (!moduleInfoPaths.isEmpty()) {
- if (!checkModuleInfos(moduleInfoPaths))
- return false;
-
- // root module-info first
- byte[] b = readModuleInfo(moduleInfoPaths.get(MODULE_INFO));
- moduleInfos.put(MODULE_INFO, b);
- for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
- moduleInfos.putIfAbsent(e.getKey(), readModuleInfo(e.getValue()));
-
- if (!addExtendedModuleAttributes(moduleInfos))
- return false;
+ expand();
+ if (!moduleInfos.isEmpty()) {
+ // All actual file entries (excl manifest and module-info.class)
+ Set<String> jentries = new HashSet<>();
+ // all packages if it's a class or resource
+ Set<String> packages = new HashSet<>();
+ entries.stream()
+ .filter(e -> !e.isDir)
+ .forEach( e -> {
+ addPackageIfNamed(packages, e.name);
+ jentries.add(e.name);
+ });
+ addExtendedModuleAttributes(moduleInfos, packages);
// Basic consistency checks for modular jars.
- if (!checkServices(moduleInfos.get(MODULE_INFO)))
+ if (!checkModuleInfo(moduleInfos.get(MODULE_INFO), jentries))
return false;
} else if (moduleVersion != null || modulesToHash != null) {
error(getMsg("error.module.options.without.info"));
return false;
}
-
if (vflag && fname == null) {
// Disable verbose output so that it does not appear
// on stdout along with file data
// error("Warning: -v option ignored");
vflag = false;
}
-
final String tmpbase = (fname == null)
? "tmpjar"
: fname.substring(fname.indexOf(File.separatorChar) + 1);
+
File tmpfile = createTemporaryFile(tmpbase, ".jar");
-
try (OutputStream out = new FileOutputStream(tmpfile)) {
- create(new BufferedOutputStream(out, 4096), manifest, moduleInfos);
+ create(new BufferedOutputStream(out, 4096), manifest);
}
-
if (nflag) {
File packFile = createTemporaryFile(tmpbase, ".pack");
try {
Packer packer = Pack200.newPacker();
Map<String, String> p = packer.properties();
p.put(Packer.EFFORT, "1"); // Minimal effort to conserve CPU
- try (
- JarFile jarFile = new JarFile(tmpfile.getCanonicalPath());
- OutputStream pack = new FileOutputStream(packFile)
- ) {
+ try (JarFile jarFile = new JarFile(tmpfile.getCanonicalPath());
+ OutputStream pack = new FileOutputStream(packFile))
+ {
packer.pack(jarFile, pack);
}
if (tmpfile.exists()) {
tmpfile.delete();
}
tmpfile = createTemporaryFile(tmpbase, ".jar");
- try (
- OutputStream out = new FileOutputStream(tmpfile);
- JarOutputStream jos = new JarOutputStream(out)
- ) {
+ try (OutputStream out = new FileOutputStream(tmpfile);
+ JarOutputStream jos = new JarOutputStream(out))
+ {
Unpacker unpacker = Pack200.newUnpacker();
unpacker.unpack(packFile, jos);
}
@@ -420,9 +333,7 @@
Files.deleteIfExists(packFile.toPath());
}
}
-
validateAndClose(tmpfile);
-
} else if (uflag) {
File inputFile = null, tmpFile = null;
if (fname != null) {
@@ -432,39 +343,20 @@
vflag = false;
tmpFile = createTemporaryFile("tmpjar", ".jar");
}
-
- Map<String,Path> moduleInfoPaths = new HashMap<>();
- for (int version : filesMap.keySet()) {
- String[] files = filesMap.get(version);
- expand(null, files, true, moduleInfoPaths, version);
+ expand();
+ try (FileInputStream in = (fname != null) ? new FileInputStream(inputFile)
+ : new FileInputStream(FileDescriptor.in);
+ FileOutputStream out = new FileOutputStream(tmpFile);
+ InputStream manifest = (!Mflag && (mname != null)) ?
+ (new FileInputStream(mname)) : null;
+ ) {
+ boolean updateOk = update(in, new BufferedOutputStream(out),
+ manifest, moduleInfos, null);
+ if (ok) {
+ ok = updateOk;
+ }
}
-
- Map<String,byte[]> moduleInfos = new HashMap<>();
- for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
- moduleInfos.put(e.getKey(), readModuleInfo(e.getValue()));
-
- try (
- FileInputStream in = (fname != null) ? new FileInputStream(inputFile)
- : new FileInputStream(FileDescriptor.in);
- FileOutputStream out = new FileOutputStream(tmpFile);
- InputStream manifest = (!Mflag && (mname != null)) ?
- (new FileInputStream(mname)) : null;
- ) {
- boolean updateOk = update(in, new BufferedOutputStream(out),
- manifest, moduleInfos, null);
- if (ok) {
- ok = updateOk;
- }
- }
-
- // Consistency checks for modular jars.
- if (!moduleInfos.isEmpty()) {
- if(!checkServices(moduleInfos.get(MODULE_INFO)))
- return false;
- }
-
validateAndClose(tmpFile);
-
} else if (tflag) {
replaceFSC(filesMap);
// For the "list table contents" action, access using the
@@ -542,12 +434,15 @@
private void validateAndClose(File tmpfile) throws IOException {
if (ok && isMultiRelease) {
- ok = validate(tmpfile.getCanonicalPath());
- if (!ok) {
- error(formatMsg("error.validator.jarfile.invalid", fname));
+ try {
+ ok = Validator.validate(this, tmpfile);
+ if (!ok) {
+ error(formatMsg("error.validator.jarfile.invalid", fname));
+ }
+ } catch (IOException e) {
+ error(formatMsg2("error.validator.jarfile.exception", fname, e.getMessage()));
}
}
-
Path path = tmpfile.toPath();
try {
if (ok) {
@@ -572,78 +467,9 @@
Stream<String> filesToEntryNames(Map.Entry<Integer,String[]> fileEntries) {
int version = fileEntries.getKey();
+ Set<String> cpaths = pathsMap.get(version);
return Stream.of(fileEntries.getValue())
- .map(f -> (new EntryName(f, version)).entryName);
- }
-
- // sort base entries before versioned entries, and sort entry classes with
- // nested classes so that the top level class appears before the associated
- // nested class
- private Comparator<JarEntry> entryComparator = (je1, je2) -> {
- String s1 = je1.getName();
- String s2 = je2.getName();
- if (s1.equals(s2)) return 0;
- boolean b1 = s1.startsWith(VERSIONS_DIR);
- boolean b2 = s2.startsWith(VERSIONS_DIR);
- if (b1 && !b2) return 1;
- if (!b1 && b2) return -1;
- int n = 0; // starting char for String compare
- if (b1 && b2) {
- // normally strings would be sorted so "10" goes before "9", but
- // version number strings need to be sorted numerically
- n = VERSIONS_DIR.length(); // skip the common prefix
- int i1 = s1.indexOf('/', n);
- int i2 = s1.indexOf('/', n);
- if (i1 == -1) throw new InvalidJarException(s1);
- if (i2 == -1) throw new InvalidJarException(s2);
- // shorter version numbers go first
- if (i1 != i2) return i1 - i2;
- // otherwise, handle equal length numbers below
- }
- int l1 = s1.length();
- int l2 = s2.length();
- int lim = Math.min(l1, l2);
- for (int k = n; k < lim; k++) {
- char c1 = s1.charAt(k);
- char c2 = s2.charAt(k);
- if (c1 != c2) {
- // change natural ordering so '.' comes before '$'
- // i.e. top level classes come before nested classes
- if (c1 == '$' && c2 == '.') return 1;
- if (c1 == '.' && c2 == '$') return -1;
- return c1 - c2;
- }
- }
- return l1 - l2;
- };
-
- private boolean validate(String fname) {
- boolean valid;
-
- try (JarFile jf = new JarFile(fname)) {
- Validator validator = new Validator(this, jf);
- jf.stream()
- .filter(e -> !e.isDirectory())
- .filter(e -> !e.getName().equals(MANIFEST_NAME))
- .filter(e -> !e.getName().endsWith(MODULE_INFO))
- .sorted(entryComparator)
- .forEachOrdered(validator);
- valid = validator.isValid();
- } catch (IOException e) {
- error(formatMsg2("error.validator.jarfile.exception", fname, e.getMessage()));
- valid = false;
- } catch (InvalidJarException e) {
- error(formatMsg("error.validator.bad.entry.name", e.getMessage()));
- valid = false;
- }
- return valid;
- }
-
- private static class InvalidJarException extends RuntimeException {
- private static final long serialVersionUID = -3642329147299217726L;
- InvalidJarException(String msg) {
- super(msg);
- }
+ .map(f -> toVersionedName(toEntryName(f, cpaths, false), version));
}
/**
@@ -668,20 +494,22 @@
// Note: flags.length == 2 can be treated as the short version of
// the GNU option since the there cannot be any other options,
// excluding -C, as per the old way.
- if (flags.startsWith("--")
- || (flags.startsWith("-") && flags.length() == 2)) {
+ if (flags.startsWith("--") ||
+ (flags.startsWith("-") && flags.length() == 2)) {
try {
count = GNUStyleOptions.parseOptions(this, args);
} catch (GNUStyleOptions.BadArgs x) {
if (info == null) {
- error(x.getMessage());
- if (x.showUsage)
- Info.USAGE_TRYHELP.print(err);
+ if (x.showUsage) {
+ usageError(x.getMessage());
+ } else {
+ error(x.getMessage());
+ }
return false;
}
}
if (info != null) {
- info.print(out);
+ info.accept(out);
return true;
}
} else {
@@ -851,19 +679,55 @@
* Add the package of the given resource name if it's a .class
* or a resource in a named package.
*/
- boolean addPackageIfNamed(String name) {
+ void addPackageIfNamed(Set<String> packages, String name) {
if (name.startsWith(VERSIONS_DIR)) {
- throw new InternalError(name);
+ // trim the version dir prefix
+ int i0 = VERSIONS_DIR.length();
+ int i = name.indexOf('/', i0);
+ if (i <= 0) {
+ warn(formatMsg("warn.release.unexpected.versioned.entry", name));
+ return;
+ }
+ while (i0 < i) {
+ char c = name.charAt(i0);
+ if (c < '0' || c > '9') {
+ warn(formatMsg("warn.release.unexpected.versioned.entry", name));
+ return;
+ }
+ i0++;
+ }
+ name = name.substring(i + 1, name.length());
}
-
String pn = toPackageName(name);
// add if this is a class or resource in a package
if (Checks.isJavaIdentifier(pn)) {
packages.add(pn);
- return true;
+ }
+ }
+
+ private String toEntryName(String name, Set<String> cpaths, boolean isDir) {
+ name = name.replace(File.separatorChar, '/');
+ if (isDir) {
+ name = name.endsWith("/") ? name : name + "/";
}
+ String matchPath = "";
+ for (String path : cpaths) {
+ if (name.startsWith(path) && path.length() > matchPath.length()) {
+ matchPath = path;
+ }
+ }
+ name = safeName(name.substring(matchPath.length()));
+ // the old implementaton doesn't remove
+ // "./" if it was led by "/" (?)
+ if (name.startsWith("./")) {
+ name = name.substring(2);
+ }
+ return name;
+ }
- return false;
+ private static String toVersionedName(String name, int version) {
+ return version > BASE_VERSION
+ ? VERSIONS_DIR + version + "/" + name : name;
}
private static String toPackageName(String path) {
@@ -875,57 +739,23 @@
}
}
- /*
- * Returns true if the given entry is a valid entry of the given version.
- */
- private boolean isValidVersionedEntry(Entry entry, int version) {
- String name = entry.basename;
- if (name.startsWith(VERSIONS_DIR) && version != BASE_VERSION) {
- int i = name.indexOf('/', VERSIONS_DIR.length());
- // name == -1 -> not a versioned directory, something else
- if (i == -1)
- return false;
- try {
- String v = name.substring(VERSIONS_DIR.length(), i);
- return Integer.valueOf(v) == version;
- } catch (NumberFormatException x) {
- return false;
- }
+ private void expand() throws IOException {
+ for (int version : filesMap.keySet()) {
+ String[] files = filesMap.get(version);
+ expand(null, files, pathsMap.get(version), version);
}
- return true;
- }
-
- /*
- * Trim META-INF/versions/$version/ from the given name if the
- * given name is a versioned entry of the given version; or
- * of any version if the given version is BASE_VERSION
- */
- private String trimVersionsDir(String name, int version) {
- if (name.startsWith(VERSIONS_DIR)) {
- int i = name.indexOf('/', VERSIONS_DIR.length());
- if (i >= 0) {
- try {
- String v = name.substring(VERSIONS_DIR.length(), i);
- if (version == BASE_VERSION || Integer.valueOf(v) == version) {
- return name.substring(i + 1, name.length());
- }
- } catch (NumberFormatException x) {}
- }
- throw new InternalError("unexpected versioned entry: " +
- name + " version " + version);
- }
- return name;
}
/**
* Expands list of files to process into full list of all files that
* can be found by recursively descending directories.
+ *
+ * @param dir parent directory
+ * @param file s list of files to expand
+ * @param cpaths set of directories specified by -C option for the files
+ * @throws IOException if an I/O error occurs
*/
- void expand(File dir,
- String[] files,
- boolean isUpdate,
- Map<String,Path> moduleInfoPaths,
- int version)
+ private void expand(File dir, String[] files, Set<String> cpaths, int version)
throws IOException
{
if (files == null)
@@ -938,47 +768,48 @@
else
f = new File(dir, files[i]);
- Entry e = new Entry(version, f);
- String entryName = e.entryname;
- Entry entry = e;
- if (e.basename.startsWith(VERSIONS_DIR) && isValidVersionedEntry(e, version)) {
- entry = e.toVersionedEntry(version);
- }
- if (f.isFile()) {
- if (entryName.endsWith(MODULE_INFO)) {
- moduleInfoPaths.put(entryName, f.toPath());
- if (isUpdate)
- entryMap.put(entryName, entry);
- } else if (isValidVersionedEntry(entry, version)) {
- if (entries.add(entry)) {
- jarEntries.add(entryName);
- // add the package if it's a class or resource
- addPackageIfNamed(trimVersionsDir(entry.basename, version));
- if (isUpdate)
- entryMap.put(entryName, entry);
- }
- } else {
+ boolean isDir = f.isDirectory();
+ String name = toEntryName(f.getPath(), cpaths, isDir);
+
+ if (version != BASE_VERSION) {
+ if (name.startsWith(VERSIONS_DIR)) {
+ // the entry starts with VERSIONS_DIR and version != BASE_VERSION,
+ // which means the "[dirs|files]" in --release v [dirs|files]
+ // includes VERSIONS_DIR-ed entries --> warning and skip (?)
error(formatMsg2("error.release.unexpected.versioned.entry",
- entry.basename, String.valueOf(version)));
+ name, String.valueOf(version)));
ok = false;
+ return;
}
- } else if (f.isDirectory()) {
- if (isValidVersionedEntry(entry, version)) {
- if (entries.add(entry)) {
- if (isUpdate) {
- entryMap.put(entryName, entry);
- }
+ name = toVersionedName(name, version);
+ }
+
+ if (f.isFile()) {
+ Entry e = new Entry(f, name, false);
+ if (isModuleInfoEntry(name)) {
+ moduleInfos.putIfAbsent(name, Files.readAllBytes(f.toPath()));
+ if (uflag)
+ entryMap.put(name, e);
+ } else if (entries.add(e)) {
+ if (uflag)
+ entryMap.put(name, e);
+ }
+ } else if (isDir) {
+ Entry e = new Entry(f, name, true);
+ if (entries.add(e)) {
+ // utilize entryMap for the duplicate dir check even in
+ // case of cflag == true.
+ // dir name confilict/duplicate could happen with -C option.
+ // just remove the last "e" from the "entries" (zos will fail
+ // with "duplicated" entries), but continue expanding the
+ // sub tree
+ if (entryMap.containsKey(name)) {
+ entries.remove(e);
+ } else {
+ entryMap.put(name, e);
}
- } else if (entry.basename.equals(VERSIONS_DIR)) {
- if (vflag) {
- output(formatMsg("out.ignore.entry", entry.basename));
- }
- } else {
- error(formatMsg2("error.release.unexpected.versioned.entry",
- entry.basename, String.valueOf(version)));
- ok = false;
+ expand(f, f.list(), cpaths, version);
}
- expand(f, f.list(), isUpdate, moduleInfoPaths, version);
} else {
error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
ok = false;
@@ -989,52 +820,36 @@
/**
* Creates a new JAR file.
*/
- void create(OutputStream out, Manifest manifest, Map<String,byte[]> moduleInfos)
- throws IOException
+ void create(OutputStream out, Manifest manifest) throws IOException
{
- ZipOutputStream zos = new JarOutputStream(out);
- if (flag0) {
- zos.setMethod(ZipOutputStream.STORED);
- }
- // TODO: check module-info attributes against manifest ??
- if (manifest != null) {
- if (vflag) {
- output(getMsg("out.added.manifest"));
- }
- ZipEntry e = new ZipEntry(MANIFEST_DIR);
- e.setTime(System.currentTimeMillis());
- e.setSize(0);
- e.setCrc(0);
- zos.putNextEntry(e);
- e = new ZipEntry(MANIFEST_NAME);
- e.setTime(System.currentTimeMillis());
+ try (ZipOutputStream zos = new JarOutputStream(out)) {
if (flag0) {
- crc32Manifest(e, manifest);
+ zos.setMethod(ZipOutputStream.STORED);
}
- zos.putNextEntry(e);
- manifest.write(zos);
- zos.closeEntry();
- }
- for (Map.Entry<String,byte[]> mi : moduleInfos.entrySet()) {
- String entryName = mi.getKey();
- byte[] miBytes = mi.getValue();
- if (vflag) {
- output(formatMsg("out.added.module-info", entryName));
+ // TODO: check module-info attributes against manifest ??
+ if (manifest != null) {
+ if (vflag) {
+ output(getMsg("out.added.manifest"));
+ }
+ ZipEntry e = new ZipEntry(MANIFEST_DIR);
+ e.setTime(System.currentTimeMillis());
+ e.setSize(0);
+ e.setCrc(0);
+ zos.putNextEntry(e);
+ e = new ZipEntry(MANIFEST_NAME);
+ e.setTime(System.currentTimeMillis());
+ if (flag0) {
+ crc32Manifest(e, manifest);
+ }
+ zos.putNextEntry(e);
+ manifest.write(zos);
+ zos.closeEntry();
}
- ZipEntry e = new ZipEntry(mi.getKey());
- e.setTime(System.currentTimeMillis());
- if (flag0) {
- crc32ModuleInfo(e, miBytes);
+ updateModuleInfo(moduleInfos, zos);
+ for (Entry entry : entries) {
+ addFile(zos, entry);
}
- zos.putNextEntry(e);
- ByteArrayInputStream in = new ByteArrayInputStream(miBytes);
- in.transferTo(zos);
- zos.closeEntry();
}
- for (Entry entry : entries) {
- addFile(zos, entry);
- }
- zos.close();
}
private char toUpperCaseASCII(char c) {
@@ -1062,30 +877,6 @@
}
/**
- * Returns true of the given module-info's are located in acceptable
- * locations. Otherwise, outputs an appropriate message and returns false.
- */
- private boolean checkModuleInfos(Map<String,?> moduleInfos) {
- // there must always be, at least, a root module-info
- if (!moduleInfos.containsKey(MODULE_INFO)) {
- error(getMsg("error.versioned.info.without.root"));
- return false;
- }
-
- // module-info can only appear in the root, or a versioned section
- Optional<String> other = moduleInfos.keySet().stream()
- .filter(x -> !x.equals(MODULE_INFO))
- .filter(x -> !x.startsWith(VERSIONS_DIR))
- .findFirst();
-
- if (other.isPresent()) {
- error(formatMsg("error.unexpected.module-info", other.get()));
- return false;
- }
- return true;
- }
-
- /**
* Updates an existing jar file.
*/
boolean update(InputStream in, OutputStream out,
@@ -1099,6 +890,10 @@
boolean foundManifest = false;
boolean updateOk = true;
+ // All actual entries added/updated/existing, in the jar file (excl manifest
+ // and module-info.class ).
+ Set<String> jentries = new HashSet<>();
+
if (jarIndex != null) {
addIndex(jarIndex, zos);
}
@@ -1108,7 +903,7 @@
String name = e.getName();
boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME);
- boolean isModuleInfoEntry = name.endsWith(MODULE_INFO);
+ boolean isModuleInfoEntry = isModuleInfoEntry(name);
if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME))
|| (Mflag && isManifestEntry)) {
@@ -1127,7 +922,6 @@
return false;
}
}
-
// Update the manifest.
Manifest old = new Manifest(zis);
if (newManifest != null) {
@@ -1137,7 +931,7 @@
return false;
}
} else if (moduleInfos != null && isModuleInfoEntry) {
- moduleInfos.putIfAbsent(name, readModuleInfo(zis));
+ moduleInfos.putIfAbsent(name, zis.readAllBytes());
} else {
boolean isDir = e.isDirectory();
if (!entryMap.containsKey(name)) { // copy the old stuff
@@ -1160,11 +954,8 @@
entries.remove(ent);
isDir = ent.isDir;
}
-
- jarEntries.add(name);
if (!isDir) {
- // add the package if it's a class or resource
- addPackageIfNamed(trimVersionsDir(name, BASE_VERSION));
+ jentries.add(name);
}
}
}
@@ -1172,6 +963,9 @@
// add the remaining new files
for (Entry entry : entries) {
addFile(zos, entry);
+ if (!entry.isDir) {
+ jentries.add(entry.name);
+ }
}
if (!foundManifest) {
if (newManifest != null) {
@@ -1188,35 +982,24 @@
}
}
}
-
- if (moduleInfos != null && !moduleInfos.isEmpty()) {
- if (!checkModuleInfos(moduleInfos))
+ if (updateOk) {
+ if (moduleInfos != null && !moduleInfos.isEmpty()) {
+ Set<String> pkgs = new HashSet<>();
+ jentries.forEach( je -> addPackageIfNamed(pkgs, je));
+ addExtendedModuleAttributes(moduleInfos, pkgs);
+ updateOk = checkModuleInfo(moduleInfos.get(MODULE_INFO), jentries);
+ updateModuleInfo(moduleInfos, zos);
+ // TODO: check manifest main classes, etc
+ } else if (moduleVersion != null || modulesToHash != null) {
+ error(getMsg("error.module.options.without.info"));
updateOk = false;
-
- if (updateOk) {
- if (!addExtendedModuleAttributes(moduleInfos))
- updateOk = false;
}
-
- // TODO: check manifest main classes, etc
-
- if (updateOk) {
- for (Map.Entry<String,byte[]> mi : moduleInfos.entrySet()) {
- if (!updateModuleInfo(mi.getValue(), zos, mi.getKey()))
- updateOk = false;
- }
- }
- } else if (moduleVersion != null || modulesToHash != null) {
- error(getMsg("error.module.options.without.info"));
- updateOk = false;
}
-
zis.close();
zos.close();
return updateOk;
}
-
private void addIndex(JarIndex index, ZipOutputStream zos)
throws IOException
{
@@ -1232,20 +1015,25 @@
zos.closeEntry();
}
- private boolean updateModuleInfo(byte[] moduleInfoBytes, ZipOutputStream zos, String entryName)
+ private void updateModuleInfo(Map<String,byte[]> moduleInfos, ZipOutputStream zos)
throws IOException
{
- ZipEntry e = new ZipEntry(entryName);
- e.setTime(System.currentTimeMillis());
- if (flag0) {
- crc32ModuleInfo(e, moduleInfoBytes);
+ String fmt = uflag ? "out.update.module-info": "out.added.module-info";
+ for (Map.Entry<String,byte[]> mi : moduleInfos.entrySet()) {
+ String name = mi.getKey();
+ byte[] bytes = mi.getValue();
+ ZipEntry e = new ZipEntry(name);
+ e.setTime(System.currentTimeMillis());
+ if (flag0) {
+ crc32ModuleInfo(e, bytes);
+ }
+ zos.putNextEntry(e);
+ zos.write(bytes);
+ zos.closeEntry();
+ if (vflag) {
+ output(formatMsg(fmt, name));
+ }
}
- zos.putNextEntry(e);
- zos.write(moduleInfoBytes);
- if (vflag) {
- output(formatMsg("out.update.module-info", entryName));
- }
- return true;
}
private boolean updateManifest(Manifest m, ZipOutputStream zos)
@@ -1358,11 +1146,9 @@
* Adds a new file entry to the ZIP output stream.
*/
void addFile(ZipOutputStream zos, Entry entry) throws IOException {
- // skip the generation of directory entries for META-INF/versions/*/
- if (entry.basename.isEmpty()) return;
File file = entry.file;
- String name = entry.entryname;
+ String name = entry.name;
boolean isDir = entry.isDir;
if (name.equals("") || name.equals(".") || name.equals(zname)) {
@@ -1444,11 +1230,8 @@
* @throws IOException if an I/O error occurs
*/
private void copy(File from, OutputStream to) throws IOException {
- InputStream in = new FileInputStream(from);
- try {
+ try (InputStream in = new FileInputStream(from)) {
copy(in, to);
- } finally {
- in.close();
}
}
@@ -1461,11 +1244,8 @@
* @throws IOException if an I/O error occurs
*/
private void copy(InputStream from, File to) throws IOException {
- OutputStream out = new FileOutputStream(to);
- try {
+ try (OutputStream out = new FileOutputStream(to)) {
copy(from, out);
- } finally {
- out.close();
}
}
@@ -1825,7 +1605,7 @@
*/
void usageError(String s) {
err.println(s);
- Info.USAGE_TRYHELP.print(err);
+ err.println(getMsg("main.usage.summary.try"));
}
/**
@@ -1934,16 +1714,6 @@
return tmpfile;
}
- private static byte[] readModuleInfo(InputStream zis) throws IOException {
- return zis.readAllBytes();
- }
-
- private static byte[] readModuleInfo(Path path) throws IOException {
- try (InputStream is = Files.newInputStream(path)) {
- return is.readAllBytes();
- }
- }
-
// Modular jar support
static <T> String toString(Collection<T> c,
@@ -1951,7 +1721,6 @@
CharSequence suffix ) {
if (c.isEmpty())
return "";
-
return c.stream().map(e -> e.toString())
.collect(joining(", ", prefix, suffix));
}
@@ -2045,136 +1814,84 @@
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
- if (hashes != null) {
- hashes.names().stream().sorted().forEach(
- mod -> sb.append("\n hashes ").append(mod).append(" ")
- .append(hashes.algorithm()).append(" ")
- .append(toHex(hashes.hashFor(mod))));
+ if (hashes != null) {
+ hashes.names().stream().sorted().forEach(
+ mod -> sb.append("\n hashes ").append(mod).append(" ")
+ .append(hashes.algorithm()).append(" ")
+ .append(toHex(hashes.hashFor(mod))));
}
output(sb.toString());
}
private static String toHex(byte[] ba) {
- StringBuilder sb = new StringBuilder(ba.length);
+ StringBuilder sb = new StringBuilder(ba.length << 1);
for (byte b: ba) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
- private static String toBinaryName(String classname) {
+ static String toBinaryName(String classname) {
return (classname.replace('.', '/')) + ".class";
}
- /* A module must have the implementation class of the services it 'provides'. */
- private boolean checkServices(byte[] moduleInfoBytes)
+ private boolean checkModuleInfo(byte[] moduleInfoBytes, Set<String> entries)
throws IOException
{
- ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
- Set<String> missing = md.provides()
- .stream()
- .map(Provides::providers)
- .flatMap(List::stream)
- .filter(p -> !jarEntries.contains(toBinaryName(p)))
- .collect(Collectors.toSet());
- if (missing.size() > 0) {
- missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
- return false;
+ boolean ok = true;
+ if (moduleInfoBytes != null) { // no root module-info.class if null
+ try {
+ // ModuleDescriptor.read() checks open/exported pkgs vs packages
+ ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
+ // A module must have the implementation class of the services it 'provides'.
+ if (md.provides().stream().map(Provides::providers).flatMap(List::stream)
+ .filter(p -> !entries.contains(toBinaryName(p)))
+ .peek(p -> fatalError(formatMsg("error.missing.provider", p)))
+ .count() != 0) {
+ ok = false;
+ }
+ } catch (InvalidModuleDescriptorException x) {
+ fatalError(x.getMessage());
+ ok = false;
+ }
}
- return true;
+ return ok;
}
/**
* Adds extended modules attributes to the given module-info's. The given
* Map values are updated in-place. Returns false if an error occurs.
*/
- private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
+ private void addExtendedModuleAttributes(Map<String,byte[]> moduleInfos,
+ Set<String> packages)
throws IOException
{
- assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
-
- ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
- ModuleDescriptor rd = ModuleDescriptor.read(bb);
-
- concealedPackages = findConcealedPackages(rd);
-
for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
- ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
- if (!(isValidVersionedDescriptor(vd, rd)))
- return false;
- e.setValue(extendedInfoBytes(rd, vd, e.getValue(), packages));
+ ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
+ e.setValue(extendedInfoBytes(md, e.getValue(), packages));
}
- return true;
- }
-
- private Set<String> findConcealedPackages(ModuleDescriptor md) {
- Objects.requireNonNull(md);
- Set<String> concealed = new HashSet<>(packages);
- md.exports().stream().map(Exports::source).forEach(concealed::remove);
- md.opens().stream().map(Opens::source).forEach(concealed::remove);
- return concealed;
- }
-
- private static boolean isPlatformModule(String name) {
- return name.startsWith("java.") || name.startsWith("jdk.");
}
- /**
- * Tells whether or not the given versioned module descriptor's attributes
- * are valid when compared against the given root module descriptor.
- *
- * A versioned module descriptor must be identical to the root module
- * descriptor, with two exceptions:
- * - A versioned descriptor can have different non-public `requires`
- * clauses of platform ( `java.*` and `jdk.*` ) modules, and
- * - A versioned descriptor can have different `uses` clauses, even of
- * service types defined outside of the platform modules.
- */
- private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
- ModuleDescriptor rd)
- throws IOException
- {
- if (!rd.name().equals(vd.name())) {
- fatalError(getMsg("error.versioned.info.name.notequal"));
- return false;
- }
- if (!rd.requires().equals(vd.requires())) {
- Set<Requires> rootRequires = rd.requires();
- for (Requires r : vd.requires()) {
- if (rootRequires.contains(r)) {
- continue;
- } else if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) {
- fatalError(getMsg("error.versioned.info.requires.transitive"));
+ static boolean isModuleInfoEntry(String name) {
+ // root or versioned module-info.class
+ if (name.endsWith(MODULE_INFO)) {
+ int end = name.length() - MODULE_INFO.length();
+ if (end == 0)
+ return true;
+ if (name.startsWith(VERSIONS_DIR)) {
+ int off = VERSIONS_DIR.length();
+ if (off == end) // meta-inf/versions/module-info.class
return false;
- } else if (!isPlatformModule(r.name())) {
- fatalError(getMsg("error.versioned.info.requires.added"));
- return false;
+ while (off < end - 1) {
+ char c = name.charAt(off++);
+ if (c < '0' || c > '9')
+ return false;
}
- }
- for (Requires r : rootRequires) {
- Set<Requires> mdRequires = vd.requires();
- if (mdRequires.contains(r)) {
- continue;
- } else if (!isPlatformModule(r.name())) {
- fatalError(getMsg("error.versioned.info.requires.dropped"));
- return false;
- }
+ return name.charAt(off) == '/';
}
}
- if (!rd.exports().equals(vd.exports())) {
- fatalError(getMsg("error.versioned.info.exports.notequal"));
- return false;
- }
- if (!rd.opens().equals(vd.opens())) {
- fatalError(getMsg("error.versioned.info.opens.notequal"));
- return false;
- }
- if (!rd.provides().equals(vd.provides())) {
- fatalError(getMsg("error.versioned.info.provides.notequal"));
- return false;
- }
- return true;
+ return false;
}
/**
@@ -2185,8 +1902,7 @@
* then the corresponding class file attributes are added to the
* module-info here.
*/
- private byte[] extendedInfoBytes(ModuleDescriptor rootDescriptor,
- ModuleDescriptor md,
+ private byte[] extendedInfoBytes(ModuleDescriptor md,
byte[] miBytes,
Set<String> packages)
throws IOException
@@ -2201,14 +1917,10 @@
// --main-class
if (ename != null)
extender.mainClass(ename);
- else if (rootDescriptor.mainClass().isPresent())
- extender.mainClass(rootDescriptor.mainClass().get());
// --module-version
if (moduleVersion != null)
extender.version(moduleVersion);
- else if (rootDescriptor.version().isPresent())
- extender.version(rootDescriptor.version().get());
// --hash-modules
if (modulesToHash != null) {
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java Thu Jan 12 16:41:08 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 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
@@ -25,32 +25,120 @@
package sun.tools.jar;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.module.InvalidModuleDescriptorException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.InvalidModuleDescriptorException;
+import java.lang.module.ModuleDescriptor.Opens;
+import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
-final class Validator implements Consumer<JarEntry> {
+import static java.util.jar.JarFile.MANIFEST_NAME;
+import static sun.tools.jar.Main.VERSIONS_DIR;
+import static sun.tools.jar.Main.MODULE_INFO;
+import static sun.tools.jar.Main.getMsg;
+import static sun.tools.jar.Main.formatMsg;
+import static sun.tools.jar.Main.formatMsg2;
+import static sun.tools.jar.Main.toBinaryName;
+import static sun.tools.jar.Main.isModuleInfoEntry;
+
+final class Validator {
private final static boolean DEBUG = Boolean.getBoolean("jar.debug");
private final Map<String,FingerPrint> fps = new HashMap<>();
- private final int vdlen = Main.VERSIONS_DIR.length();
+ private static final int vdlen = VERSIONS_DIR.length();
private final Main main;
private final JarFile jf;
private int oldVersion = -1;
private String currentTopLevelName;
private boolean isValid = true;
+ private Set<String> concealedPkgs;
+ private ModuleDescriptor md;
- Validator(Main main, JarFile jf) {
+ private Validator(Main main, JarFile jf) {
this.main = main;
this.jf = jf;
+ loadModuleDescriptor();
+ }
+
+ static boolean validate(Main main, File f) throws IOException {
+ return new Validator(main, new JarFile(f)).validate();
+ }
+
+ private boolean validate() {
+ try {
+ jf.stream()
+ .filter(e -> !e.isDirectory() &&
+ !e.getName().equals(MANIFEST_NAME))
+ .sorted(entryComparator)
+ .forEachOrdered(e -> validate(e));
+ return isValid;
+ } catch (InvalidJarException e) {
+ error(formatMsg("error.validator.bad.entry.name", e.getMessage()));
+ }
+ return false;
+ }
+
+ private static class InvalidJarException extends RuntimeException {
+ private static final long serialVersionUID = -3642329147299217726L;
+ InvalidJarException(String msg) {
+ super(msg);
+ }
}
- boolean isValid() {
- return isValid;
- }
+ // sort base entries before versioned entries, and sort entry classes with
+ // nested classes so that the top level class appears before the associated
+ // nested class
+ private static Comparator<JarEntry> entryComparator = (je1, je2) -> {
+ String s1 = je1.getName();
+ String s2 = je2.getName();
+ if (s1.equals(s2)) return 0;
+ boolean b1 = s1.startsWith(VERSIONS_DIR);
+ boolean b2 = s2.startsWith(VERSIONS_DIR);
+ if (b1 && !b2) return 1;
+ if (!b1 && b2) return -1;
+ int n = 0; // starting char for String compare
+ if (b1 && b2) {
+ // normally strings would be sorted so "10" goes before "9", but
+ // version number strings need to be sorted numerically
+ n = VERSIONS_DIR.length(); // skip the common prefix
+ int i1 = s1.indexOf('/', n);
+ int i2 = s1.indexOf('/', n);
+ if (i1 == -1) throw new InvalidJarException(s1);
+ if (i2 == -1) throw new InvalidJarException(s2);
+ // shorter version numbers go first
+ if (i1 != i2) return i1 - i2;
+ // otherwise, handle equal length numbers below
+ }
+ int l1 = s1.length();
+ int l2 = s2.length();
+ int lim = Math.min(l1, l2);
+ for (int k = n; k < lim; k++) {
+ char c1 = s1.charAt(k);
+ char c2 = s2.charAt(k);
+ if (c1 != c2) {
+ // change natural ordering so '.' comes before '$'
+ // i.e. top level classes come before nested classes
+ if (c1 == '$' && c2 == '.') return 1;
+ if (c1 == '.' && c2 == '$') return -1;
+ return c1 - c2;
+ }
+ }
+ return l1 - l2;
+ };
/*
* Validator has state and assumes entries provided to accept are ordered
@@ -59,7 +147,7 @@
* classes must be ordered so that the top level class is before the associated
* nested class(es).
*/
- public void accept(JarEntry je) {
+ public void validate(JarEntry je) {
String entryName = je.getName();
// directories are always accepted
@@ -68,13 +156,20 @@
return;
}
+ // validate the versioned module-info
+ if (isModuleInfoEntry(entryName)) {
+ if (entryName.length() != MODULE_INFO.length())
+ checkModuleDescriptor(je);
+ return;
+ }
+
// figure out the version and basename from the JarEntry
int version;
String basename;
- if (entryName.startsWith(Main.VERSIONS_DIR)) {
+ if (entryName.startsWith(VERSIONS_DIR)) {
int n = entryName.indexOf("/", vdlen);
if (n == -1) {
- main.error(Main.formatMsg("error.validator.version.notnumber", entryName));
+ error(formatMsg("error.validator.version.notnumber", entryName));
isValid = false;
return;
}
@@ -82,12 +177,12 @@
try {
version = Integer.parseInt(v);
} catch (NumberFormatException x) {
- main.error(Main.formatMsg("error.validator.version.notnumber", entryName));
+ error(formatMsg("error.validator.version.notnumber", entryName));
isValid = false;
return;
}
if (n == entryName.length()) {
- main.error(Main.formatMsg("error.validator.entryname.tooshort", entryName));
+ error(formatMsg("error.validator.entryname.tooshort", entryName));
isValid = false;
return;
}
@@ -108,7 +203,7 @@
try (InputStream is = jf.getInputStream(je)) {
fp = new FingerPrint(basename, is.readAllBytes());
} catch (IOException x) {
- main.error(x.getMessage());
+ error(x.getMessage());
isValid = false;
return;
}
@@ -123,7 +218,7 @@
fps.put(internalName, fp);
return;
}
- main.error(Main.formatMsg("error.validator.isolated.nested.class", entryName));
+ error(formatMsg("error.validator.isolated.nested.class", entryName));
isValid = false;
return;
}
@@ -153,11 +248,11 @@
}
if (fp.isPublicClass()) {
if (!isConcealed(internalName)) {
- main.error(Main.formatMsg("error.validator.new.public.class", entryName));
+ error(Main.formatMsg("error.validator.new.public.class", entryName));
isValid = false;
return;
}
- main.warn(Main.formatMsg("warn.validator.concealed.public.class", entryName));
+ warn(formatMsg("warn.validator.concealed.public.class", entryName));
debug("%s is a public class entry in a concealed package", entryName);
}
debug("%s is a non-public class entry", entryName);
@@ -173,7 +268,7 @@
// are the two classes/resources identical?
if (fp.isIdentical(matchFp)) {
- main.warn(Main.formatMsg("warn.validator.identical.entry", entryName));
+ warn(formatMsg("warn.validator.identical.entry", entryName));
return; // it's okay, just takes up room
}
debug("sha1 not equal -- different bytes");
@@ -188,12 +283,12 @@
}
debug("%s is a class entry", entryName);
if (!fp.isCompatibleVersion(matchFp)) {
- main.error(Main.formatMsg("error.validator.incompatible.class.version", entryName));
+ error(formatMsg("error.validator.incompatible.class.version", entryName));
isValid = false;
return;
}
if (!fp.isSameAPI(matchFp)) {
- main.error(Main.formatMsg("error.validator.different.api", entryName));
+ error(formatMsg("error.validator.different.api", entryName));
isValid = false;
return;
}
@@ -208,17 +303,118 @@
}
debug("%s is a resource", entryName);
- main.warn(Main.formatMsg("warn.validator.resources.with.same.name", entryName));
+ warn(formatMsg("warn.validator.resources.with.same.name", entryName));
fps.put(internalName, fp);
return;
}
+ private void loadModuleDescriptor() {
+ ZipEntry je = jf.getEntry(MODULE_INFO);
+ if (je != null) {
+ try (InputStream jis = jf.getInputStream(je)) {
+ md = ModuleDescriptor.read(jis);
+ concealedPkgs = new HashSet<>(md.packages());
+ md.exports().stream().map(Exports::source).forEach(concealedPkgs::remove);
+ md.opens().stream().map(Opens::source).forEach(concealedPkgs::remove);
+ return;
+ } catch (Exception x) {
+ error(x.getMessage() + " : " + je.getName());
+ this.isValid = false;
+ }
+ }
+ md = null;
+ concealedPkgs = Collections.emptySet();
+ }
+
+ private static boolean isPlatformModule(String name) {
+ return name.startsWith("java.") || name.startsWith("jdk.");
+ }
+
+ /**
+ * Checks whether or not the given versioned module descriptor's attributes
+ * are valid when compared against the root module descriptor.
+ *
+ * A versioned module descriptor must be identical to the root module
+ * descriptor, with two exceptions:
+ * - A versioned descriptor can have different non-public `requires`
+ * clauses of platform ( `java.*` and `jdk.*` ) modules, and
+ * - A versioned descriptor can have different `uses` clauses, even of
+ * service types defined outside of the platform modules.
+ */
+ private void checkModuleDescriptor(JarEntry je) {
+ try (InputStream is = jf.getInputStream(je)) {
+ ModuleDescriptor root = this.md;
+ ModuleDescriptor md = null;
+ try {
+ md = ModuleDescriptor.read(is);
+ } catch (InvalidModuleDescriptorException x) {
+ error(x.getMessage());
+ isValid = false;
+ return;
+ }
+ if (root == null) {
+ this.md = md;
+ } else {
+ if (!root.name().equals(md.name())) {
+ error(getMsg("error.versioned.info.name.notequal"));
+ isValid = false;
+ }
+ if (!root.requires().equals(md.requires())) {
+ Set<Requires> rootRequires = root.requires();
+ for (Requires r : md.requires()) {
+ if (rootRequires.contains(r))
+ continue;
+ if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) {
+ error(getMsg("error.versioned.info.requires.transitive"));
+ isValid = false;
+ } else if (!isPlatformModule(r.name())) {
+ error(getMsg("error.versioned.info.requires.added"));
+ isValid = false;
+ }
+ }
+ for (Requires r : rootRequires) {
+ Set<Requires> mdRequires = md.requires();
+ if (mdRequires.contains(r))
+ continue;
+ if (!isPlatformModule(r.name())) {
+ error(getMsg("error.versioned.info.requires.dropped"));
+ isValid = false;
+ }
+ }
+ }
+ if (!root.exports().equals(md.exports())) {
+ error(getMsg("error.versioned.info.exports.notequal"));
+ isValid = false;
+ }
+ if (!root.opens().equals(md.opens())) {
+ error(getMsg("error.versioned.info.opens.notequal"));
+ isValid = false;
+ }
+ if (!root.provides().equals(md.provides())) {
+ error(getMsg("error.versioned.info.provides.notequal"));
+ isValid = false;
+ }
+ if (!root.mainClass().equals(md.mainClass())) {
+ error(formatMsg("error.validator.info.manclass.notequal", je.getName()));
+ isValid = false;
+ }
+ if (!root.version().equals(md.version())) {
+ error(formatMsg("error.validator.info.version.notequal", je.getName()));
+ isValid = false;
+ }
+ }
+ } catch (IOException x) {
+ error(x.getMessage());
+ isValid = false;
+ }
+ }
+
private boolean checkInternalName(String entryName, String basename, String internalName) {
String className = className(basename);
if (internalName.equals(className)) {
return true;
}
- main.error(Main.formatMsg2("error.validator.names.mismatch",
+ error(formatMsg2("error.validator.names.mismatch",
entryName, internalName.replace("/", ".")));
return false;
}
@@ -231,7 +427,7 @@
return true;
}
debug("top level class was not accepted");
- main.error(Main.formatMsg("error.validator.isolated.nested.class", entryName));
+ error(formatMsg("error.validator.isolated.nested.class", entryName));
return false;
}
@@ -240,16 +436,24 @@
}
private boolean isConcealed(String internalName) {
- if (main.concealedPackages.isEmpty()) {
+ if (concealedPkgs.isEmpty()) {
return false;
}
int idx = internalName.lastIndexOf('/');
String pkgName = idx != -1 ? internalName.substring(0, idx).replace('/', '.') : "";
- return main.concealedPackages.contains(pkgName);
+ return concealedPkgs.contains(pkgName);
}
private void debug(String fmt, Object... args) {
if (DEBUG) System.err.format(fmt, args);
}
+
+ private void error(String msg) {
+ main.error(msg);
+ }
+
+ private void warn(String msg) {
+ main.warn(msg);
+ }
+
}
-
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Thu Jan 12 16:41:08 2017 -0800
@@ -66,23 +66,6 @@
Unexpected module descriptor {0}
error.module.descriptor.not.found=\
Module descriptor not found
-error.versioned.info.without.root=\
- module-info.class found in a versioned directory without module-info.class \
- in the root
-error.versioned.info.name.notequal=\
- module-info.class in a versioned directory contains incorrect name
-error.versioned.info.requires.transitive=\
- module-info.class in a versioned directory contains additional "requires transitive"
-error.versioned.info.requires.added=\
- module-info.class in a versioned directory contains additional "requires"
-error.versioned.info.requires.dropped=\
- module-info.class in a versioned directory contains missing "requires"
-error.versioned.info.exports.notequal=\
- module-info.class in a versioned directory contains different "exports"
-error.versioned.info.opens.notequal=\
- module-info.class in a versioned directory contains different "opens"
-error.versioned.info.provides.notequal=\
- module-info.class in a versioned directory contains different "provides"
error.invalid.versioned.module.attribute=\
Invalid module descriptor attribute {0}
error.missing.provider=\
@@ -113,6 +96,24 @@
entry: {0}, contains a class with different api from earlier version
error.validator.names.mismatch=\
entry: {0}, contains a class with internal name {1}, names do not match
+error.validator.info.name.notequal=\
+ module-info.class in a versioned directory contains incorrect name
+error.validator.info.requires.transitive=\
+ module-info.class in a versioned directory contains additional "requires transitive"
+error.validator.info.requires.added=\
+ module-info.class in a versioned directory contains additional "requires"
+error.validator.info.requires.dropped=\
+ module-info.class in a versioned directory contains missing "requires"
+error.validator.info.exports.notequal=\
+ module-info.class in a versioned directory contains different "exports"
+error.validator.info.opens.notequal=\
+ module-info.class in a versioned directory contains different "opens"
+error.validator.info.provides.notequal=\
+ module-info.class in a versioned directory contains different "provides"
+error.validator.info.version.notequal=\
+ {0}: module-info.class in a versioned directory contains different "version"
+error.validator.info.manclass.notequal=\
+ {0}: module-info.class in a versioned directory contains different "main-class"
warn.validator.identical.entry=\
Warning: entry {0} contains a class that\n\
is identical to an entry already in the jar
@@ -122,6 +123,8 @@
Warning: entry {0} is a public class\n\
in a concealed package, placing this jar on the class path will result\n\
in incompatible public interfaces
+warn.release.unexpected.versioned.entry=\
+ unexpected versioned entry {0}
out.added.manifest=\
added manifest
out.added.module-info=\
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=--module-version oder --hash-modules ohne module-info.class
error.unexpected.module-info=Unerwarteter Moduldeskriptor {0}
error.module.descriptor.not.found=Moduldeskriptor nicht gefunden
-error.versioned.info.without.root=module-info.class in einem versionierten Verzeichnis gefunden, in der Root ist module-info.class jedoch nicht vorhanden
-error.versioned.info.name.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt falschen Namen
-error.versioned.info.requires.public=module-info.class in einem versionierten Verzeichnis enth\u00E4lt zus\u00E4tzlichen "requires public"
-error.versioned.info.requires.added=module-info.class in einem versionierten Verzeichnis enth\u00E4lt zus\u00E4tzlichen "requires"
-error.versioned.info.requires.dropped=module-info.class in einem versionierten Verzeichnis enth\u00E4lt fehlenden "requires"
-error.versioned.info.exports.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt unterschiedliche "exports"
-error.versioned.info.provides.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt unterschiedliche "provides"
+error.validator.info.without.root=module-info.class in einem versionierten Verzeichnis gefunden, in der Root ist module-info.class jedoch nicht vorhanden
+error.validator.info.name.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt falschen Namen
+error.validator.info.requires.public=module-info.class in einem versionierten Verzeichnis enth\u00E4lt zus\u00E4tzlichen "requires public"
+error.validator.info.requires.added=module-info.class in einem versionierten Verzeichnis enth\u00E4lt zus\u00E4tzlichen "requires"
+error.validator.info.requires.dropped=module-info.class in einem versionierten Verzeichnis enth\u00E4lt fehlenden "requires"
+error.validator.info.exports.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt unterschiedliche "exports"
+error.validator.info.provides.notequal=module-info.class in einem versionierten Verzeichnis enth\u00E4lt unterschiedliche "provides"
error.invalid.versioned.module.attribute=Ung\u00FCltiges Moduldeskriptorattribut {0}
error.missing.provider=Serviceprovider nicht gefunden: {0}
error.release.value.notnumber=Release {0} nicht g\u00FCltig
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_es.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_es.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=Uno de --module-version o -hash-modules sin module-info.class
error.unexpected.module-info=Descriptor de m\u00F3dulo inesperado {0}
error.module.descriptor.not.found=No se ha encontrado el descriptor de m\u00F3dulo
-error.versioned.info.without.root=Se ha encontrado module-info.class en un directorio con versi\u00F3n sin module-info.class en la ra\u00EDz
-error.versioned.info.name.notequal=module-info.class en un directorio con versi\u00F3n contiene un nombre incorrecto
-error.versioned.info.requires.public=module-info.class en un directorio con versiones contiene "requires public" adicionales
-error.versioned.info.requires.added=module-info.class en un directorio con versi\u00F3n contiene "requires" adicionales
-error.versioned.info.requires.dropped=module-info.class en un directorio con versiones contiene "requires" que faltan
-error.versioned.info.exports.notequal=module-info.class en un directorio con versiones contiene "exports" diferentes
-error.versioned.info.provides.notequal=module-info.class en un directorio con versiones contiene "provides" diferentes
+error.validator.info.without.root=Se ha encontrado module-info.class en un directorio con versi\u00F3n sin module-info.class en la ra\u00EDz
+error.validator.info.name.notequal=module-info.class en un directorio con versi\u00F3n contiene un nombre incorrecto
+error.validator.info.requires.public=module-info.class en un directorio con versiones contiene "requires public" adicionales
+error.validator.info.requires.added=module-info.class en un directorio con versi\u00F3n contiene "requires" adicionales
+error.validator.info.requires.dropped=module-info.class en un directorio con versiones contiene "requires" que faltan
+error.validator.info.exports.notequal=module-info.class en un directorio con versiones contiene "exports" diferentes
+error.validator.info.provides.notequal=module-info.class en un directorio con versiones contiene "provides" diferentes
error.invalid.versioned.module.attribute=Atributo de descriptor de m\u00F3dulo no v\u00E1lido {0}
error.missing.provider=No se ha encontrado el proveedor de servicios: {0}
error.release.value.notnumber=versi\u00F3n {0} no v\u00E1lida
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_fr.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_fr.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=Une des options --module-version ou --hash-modules sans module-info.class
error.unexpected.module-info=Descripteur de module {0} inattendu
error.module.descriptor.not.found=Descripteur de module introuvable
-error.versioned.info.without.root=module-info.class a \u00E9t\u00E9 d\u00E9tect\u00E9 dans un r\u00E9pertoire avec num\u00E9ro de version sans module-info.class dans la racine
-error.versioned.info.name.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient un nom incorrect
-error.versioned.info.requires.public=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires public" suppl\u00E9mentaires
-error.versioned.info.requires.added=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires" suppl\u00E9mentaires
-error.versioned.info.requires.dropped=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires" manquants
-error.versioned.info.exports.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "exports" diff\u00E9rents
-error.versioned.info.provides.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "provides" diff\u00E9rents
+error.validator.info.without.root=module-info.class a \u00E9t\u00E9 d\u00E9tect\u00E9 dans un r\u00E9pertoire avec num\u00E9ro de version sans module-info.class dans la racine
+error.validator.info.name.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient un nom incorrect
+error.validator.info.requires.public=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires public" suppl\u00E9mentaires
+error.validator.info.requires.added=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires" suppl\u00E9mentaires
+error.validator.info.requires.dropped=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "requires" manquants
+error.validator.info.exports.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "exports" diff\u00E9rents
+error.validator.info.provides.notequal=module-info.class dans un r\u00E9pertoire avec num\u00E9ro de version contient des mots-cl\u00E9s "provides" diff\u00E9rents
error.invalid.versioned.module.attribute=Attribut de descripteur de module non valide {0}
error.missing.provider=Fournisseur de services introuvable : {0}
error.release.value.notnumber=version {0} non valide
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_it.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_it.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=Una delle opzioni --module-version o --hash-modules non contiene module-info.class
error.unexpected.module-info=Descrittore di modulo {0} imprevisto
error.module.descriptor.not.found=Descrittore di modulo non trovato
-error.versioned.info.without.root=module-info.class trovato in una directory con controllo delle versioni senza module-info.class nella radice
-error.versioned.info.name.notequal=module-info.class in una directory con controllo delle versioni contiene un nome errato
-error.versioned.info.requires.public=module-info.class in una directory con controllo delle versioni contiene valori "requires public" aggiuntivi
-error.versioned.info.requires.added=module-info.class in una directory con controllo delle versioni contiene valori "requires" aggiuntivi
-error.versioned.info.requires.dropped=module-info.class in una directory con controllo delle versioni contiene valori "requires" mancanti
-error.versioned.info.exports.notequal=module-info.class in una directory con controllo delle versioni contiene "exports" differenti
-error.versioned.info.provides.notequal=module-info.class in una directory con controllo delle versioni contiene valori "provides" differenti
+error.validator.info.without.root=module-info.class trovato in una directory con controllo delle versioni senza module-info.class nella radice
+error.validator.info.name.notequal=module-info.class in una directory con controllo delle versioni contiene un nome errato
+error.validator.info.requires.public=module-info.class in una directory con controllo delle versioni contiene valori "requires public" aggiuntivi
+error.validator.info.requires.added=module-info.class in una directory con controllo delle versioni contiene valori "requires" aggiuntivi
+error.validator.info.requires.dropped=module-info.class in una directory con controllo delle versioni contiene valori "requires" mancanti
+error.validator.info.exports.notequal=module-info.class in una directory con controllo delle versioni contiene "exports" differenti
+error.validator.info.provides.notequal=module-info.class in una directory con controllo delle versioni contiene valori "provides" differenti
error.invalid.versioned.module.attribute=Attributo descrittore del modulo {0} non valido.
error.missing.provider=Provider di servizi non trovato: {0}
error.release.value.notnumber=release {0} non valida
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=--module-version\u307E\u305F\u306F--hash-modules\u306E\u3044\u305A\u308C\u304B\u3067module-info.class\u304C\u3042\u308A\u307E\u305B\u3093
error.unexpected.module-info=\u4E88\u671F\u3057\u306A\u3044\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF{0}
error.module.descriptor.not.found=\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
-error.versioned.info.without.root=module-info.class\u304C\u3001\u30EB\u30FC\u30C8\u306Bmodule-info.class\u306E\u306A\u3044\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u898B\u3064\u304B\u308A\u307E\u3057\u305F
-error.versioned.info.name.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u6B63\u3057\u304F\u306A\u3044\u540D\u524D\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
-error.versioned.info.requires.public=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u8FFD\u52A0\u306E"requires public"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
-error.versioned.info.requires.added=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u8FFD\u52A0\u306E"requires"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
-error.versioned.info.requires.dropped=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u6B20\u843D\u3057\u3066\u3044\u308B"requires"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
-error.versioned.info.exports.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u7570\u306A\u308B"exports"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
-error.versioned.info.provides.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u7570\u306A\u308B"provides"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.without.root=module-info.class\u304C\u3001\u30EB\u30FC\u30C8\u306Bmodule-info.class\u306E\u306A\u3044\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u898B\u3064\u304B\u308A\u307E\u3057\u305F
+error.validator.info.name.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u6B63\u3057\u304F\u306A\u3044\u540D\u524D\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.requires.public=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u8FFD\u52A0\u306E"requires public"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.requires.added=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u8FFD\u52A0\u306E"requires"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.requires.dropped=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u6B20\u843D\u3057\u3066\u3044\u308B"requires"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.exports.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u7570\u306A\u308B"exports"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
+error.validator.info.provides.notequal=\u30D0\u30FC\u30B8\u30E7\u30CB\u30F3\u30B0\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306Emodule-info.class\u306B\u7570\u306A\u308B"provides"\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059
error.invalid.versioned.module.attribute=\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF\u5C5E\u6027{0}\u304C\u7121\u52B9\u3067\u3059
error.missing.provider=\u30B5\u30FC\u30D3\u30B9\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: {0}
error.release.value.notnumber=\u30EA\u30EA\u30FC\u30B9{0}\u306F\u6709\u52B9\u3067\u306F\u3042\u308A\u307E\u305B\u3093
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ko.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ko.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=module-info.class \uC5C6\uC774 --module-version \uB610\uB294 --hash-modules \uC911 \uD558\uB098
error.unexpected.module-info=\uC608\uC0C1\uCE58 \uC54A\uC740 \uBAA8\uB4C8 \uAE30\uC220\uC790 {0}
error.module.descriptor.not.found=\uBAA8\uB4C8 \uAE30\uC220\uC790\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC74C
-error.versioned.info.without.root=\uB8E8\uD2B8\uC5D0\uC11C module-info.class \uC5C6\uC774 \uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C module-info.class\uAC00 \uBC1C\uACAC\uB428
-error.versioned.info.name.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uC798\uBABB\uB41C \uC774\uB984\uC774 \uD3EC\uD568\uB428
-error.versioned.info.requires.public=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uCD94\uAC00 "requires public" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
-error.versioned.info.requires.added=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uCD94\uAC00 "requires" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
-error.versioned.info.requires.dropped=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB204\uB77D\uB41C "requires" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
-error.versioned.info.exports.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB2E4\uB978 "exports" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
-error.versioned.info.provides.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB2E4\uB978 "provides" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
+error.validator.info.without.root=\uB8E8\uD2B8\uC5D0\uC11C module-info.class \uC5C6\uC774 \uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C module-info.class\uAC00 \uBC1C\uACAC\uB428
+error.validator.info.name.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uC798\uBABB\uB41C \uC774\uB984\uC774 \uD3EC\uD568\uB428
+error.validator.info.requires.public=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uCD94\uAC00 "requires public" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
+error.validator.info.requires.added=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uCD94\uAC00 "requires" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
+error.validator.info.requires.dropped=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB204\uB77D\uB41C "requires" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
+error.validator.info.exports.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB2E4\uB978 "exports" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
+error.validator.info.provides.notequal=\uBC84\uC804 \uC9C0\uC815\uB41C \uB514\uB809\uD1A0\uB9AC\uC758 module-info.class\uC5D0 \uB2E4\uB978 "provides" \uD56D\uBAA9\uC774 \uD3EC\uD568\uB428
error.invalid.versioned.module.attribute=\uBD80\uC801\uD569\uD55C \uBAA8\uB4C8 \uAE30\uC220\uC790 \uC18D\uC131 {0}
error.missing.provider=\uC11C\uBE44\uC2A4 \uC81C\uACF5\uC790\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC74C: {0}
error.release.value.notnumber=\uB9B4\uB9AC\uC2A4 {0}\uC774(\uAC00) \uBD80\uC801\uD569\uD568
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_pt_BR.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_pt_BR.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=Um dentre --module-version ou --hash-modules est\u00E1 sem module-info.class
error.unexpected.module-info=Descritor de m\u00F3dulo inesperado {0}
error.module.descriptor.not.found=Descritor de m\u00F3dulo n\u00E3o encontrado
-error.versioned.info.without.root=module-info.class encontrado em um diret\u00F3rio com controle de vers\u00E3o sem module-info.class na raiz
-error.versioned.info.name.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m nome incorreto
-error.versioned.info.requires.public=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "requires public" adicional
-error.versioned.info.requires.added=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "requires" adicional
-error.versioned.info.requires.dropped=module-info.class em um diret\u00F3rio com controle de vers\u00E3o falta "requires"
-error.versioned.info.exports.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "exports" diferente
-error.versioned.info.provides.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "provides" diferente
+error.validator.info.without.root=module-info.class encontrado em um diret\u00F3rio com controle de vers\u00E3o sem module-info.class na raiz
+error.validator.info.name.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m nome incorreto
+error.validator.info.requires.public=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "requires public" adicional
+error.validator.info.requires.added=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "requires" adicional
+error.validator.info.requires.dropped=module-info.class em um diret\u00F3rio com controle de vers\u00E3o falta "requires"
+error.validator.info.exports.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "exports" diferente
+error.validator.info.provides.notequal=module-info.class em um diret\u00F3rio com controle de vers\u00E3o cont\u00E9m "provides" diferente
error.invalid.versioned.module.attribute=Atributo {0} de descritor de m\u00F3dulo inv\u00E1lido
error.missing.provider=Prestador de servi\u00E7os n\u00E3o encontrado: {0}
error.release.value.notnumber=release {0} n\u00E3o v\u00E1lida
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_sv.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_sv.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=--module-version eller --hash-modules utan module-info.class
error.unexpected.module-info=Ov\u00E4ntad moduldeskriptor, {0}
error.module.descriptor.not.found=Moduldeskriptorn hittades inte
-error.versioned.info.without.root=module-info.class hittades i en versionshanterad katalog utan module-info.class i roten
-error.versioned.info.name.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller ett felaktigt namn
-error.versioned.info.requires.public=module-info.class i en versionshanterad katalog inneh\u00E5ller fler "requires public"
-error.versioned.info.requires.added=module-info.class i en versionshanterad katalog inneh\u00E5ller fler "requires"
-error.versioned.info.requires.dropped=module-info.class i en versionshanterad katalog inneh\u00E5ller saknade "requires"
-error.versioned.info.exports.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller olika "exports"
-error.versioned.info.provides.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller olika "provides"
+error.validator.info.without.root=module-info.class hittades i en versionshanterad katalog utan module-info.class i roten
+error.validator.info.name.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller ett felaktigt namn
+error.validator.info.requires.public=module-info.class i en versionshanterad katalog inneh\u00E5ller fler "requires public"
+error.validator.info.requires.added=module-info.class i en versionshanterad katalog inneh\u00E5ller fler "requires"
+error.validator.info.requires.dropped=module-info.class i en versionshanterad katalog inneh\u00E5ller saknade "requires"
+error.validator.info.exports.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller olika "exports"
+error.validator.info.provides.notequal=module-info.class i en versionshanterad katalog inneh\u00E5ller olika "provides"
error.invalid.versioned.module.attribute=Ogiltigt attribut f\u00F6r moduldeskriptor, {0}
error.missing.provider=Tj\u00E4nsteleverant\u00F6ren hittades inte: {0}
error.release.value.notnumber=utg\u00E5va {0} \u00E4r inte giltig
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=--module-version \u6216 --hash-modules \u4E4B\u4E00\u6CA1\u6709 module-info.class
error.unexpected.module-info=\u610F\u5916\u7684\u6A21\u5757\u63CF\u8FF0\u7B26 {0}
error.module.descriptor.not.found=\u627E\u4E0D\u5230\u6A21\u5757\u63CF\u8FF0\u7B26
-error.versioned.info.without.root=\u5728\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u627E\u5230\u4E86 module-info.class, \u4F46\u6839\u4E2D\u6CA1\u6709 module-info.class
-error.versioned.info.name.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u6B63\u786E\u7684\u540D\u79F0
-error.versioned.info.requires.public=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u989D\u5916\u7684 "requires public"
-error.versioned.info.requires.added=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u989D\u5916\u7684 "requires"
-error.versioned.info.requires.dropped=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u7F3A\u5C11\u7684 "requires"
-error.versioned.info.exports.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "exports"
-error.versioned.info.provides.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "provides"
+error.validator.info.without.root=\u5728\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u627E\u5230\u4E86 module-info.class, \u4F46\u6839\u4E2D\u6CA1\u6709 module-info.class
+error.validator.info.name.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u6B63\u786E\u7684\u540D\u79F0
+error.validator.info.requires.public=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u989D\u5916\u7684 "requires public"
+error.validator.info.requires.added=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u989D\u5916\u7684 "requires"
+error.validator.info.requires.dropped=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u7F3A\u5C11\u7684 "requires"
+error.validator.info.exports.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "exports"
+error.validator.info.provides.notequal=\u7248\u672C\u5316\u76EE\u5F55\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "provides"
error.invalid.versioned.module.attribute=\u65E0\u6548\u7684\u6A21\u5757\u63CF\u8FF0\u7B26\u5C5E\u6027 {0}
error.missing.provider=\u672A\u627E\u5230\u670D\u52A1\u63D0\u4F9B\u65B9: {0}
error.release.value.notnumber=\u53D1\u884C\u7248 {0} \u65E0\u6548
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_TW.properties Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_TW.properties Thu Jan 12 16:41:08 2017 -0800
@@ -42,13 +42,13 @@
error.module.options.without.info=--module-version \u6216 --hash-modules \u5176\u4E2D\u4E00\u500B\u6C92\u6709 module-info.class
error.unexpected.module-info=\u672A\u9810\u671F\u7684\u6A21\u7D44\u63CF\u8FF0\u5340 {0}
error.module.descriptor.not.found=\u627E\u4E0D\u5230\u6A21\u7D44\u63CF\u8FF0\u5340
-error.versioned.info.without.root=\u5728\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u767C\u73FE module-info.class\uFF0C\u4F46\u662F\u6839\u4E2D\u6C92\u6709 module-info.class
-error.versioned.info.name.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u6B63\u78BA\u7684\u540D\u7A31
-error.versioned.info.requires.public=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u984D\u5916\u7684 "requires public"
-error.versioned.info.requires.added=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u984D\u5916\u7684 "requires"
-error.versioned.info.requires.dropped=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u907A\u6F0F\u7684 "requires"
-error.versioned.info.exports.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "exports"
-error.versioned.info.provides.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "provides"
+error.validator.info.without.root=\u5728\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u767C\u73FE module-info.class\uFF0C\u4F46\u662F\u6839\u4E2D\u6C92\u6709 module-info.class
+error.validator.info.name.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u6B63\u78BA\u7684\u540D\u7A31
+error.validator.info.requires.public=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u984D\u5916\u7684 "requires public"
+error.validator.info.requires.added=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u984D\u5916\u7684 "requires"
+error.validator.info.requires.dropped=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u907A\u6F0F\u7684 "requires"
+error.validator.info.exports.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "exports"
+error.validator.info.provides.notequal=\u5DF2\u555F\u52D5\u591A\u7248\u672C\u529F\u80FD\u76EE\u9304\u4E2D\u7684 module-info.class \u5305\u542B\u4E0D\u540C\u7684 "provides"
error.invalid.versioned.module.attribute=\u6A21\u7D44\u63CF\u8FF0\u5340\u5C6C\u6027 {0} \u7121\u6548
error.missing.provider=\u627E\u4E0D\u5230\u670D\u52D9\u63D0\u4F9B\u8005: {0}
error.release.value.notnumber=\u7248\u672C {0} \u7121\u6548
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/mmrjar/Basic.java Thu Jan 12 16:41:08 2017 -0800
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8146486 8172432
+ * @summary Fail to create a MR modular JAR with a versioned entry in
+ * base-versioned empty package
+ * @modules java.base/jdk.internal.module
+ * jdk.compiler
+ * jdk.jartool
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.FileUtils
+ * @run testng Basic
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Version;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import java.util.spi.ToolProvider;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipFile;
+
+import jdk.internal.module.ModuleInfoExtender;
+import jdk.testlibrary.FileUtils;
+
+public class Basic {
+ private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+ .orElseThrow(() -> new RuntimeException("jar tool not found"));
+ private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac")
+ .orElseThrow(() -> new RuntimeException("javac tool not found"));
+ private final String linesep = System.lineSeparator();
+ private final Path testsrc;
+ private final Path userdir;
+ private final ByteArrayOutputStream outbytes = new ByteArrayOutputStream();
+ private final PrintStream out = new PrintStream(outbytes, true);
+ private final ByteArrayOutputStream errbytes = new ByteArrayOutputStream();
+ private final PrintStream err = new PrintStream(errbytes, true);
+
+ public Basic() throws IOException {
+ testsrc = Paths.get(System.getProperty("test.src"));
+ userdir = Paths.get(System.getProperty("user.dir", "."));
+
+ // compile the classes directory
+ Path source = testsrc.resolve("src").resolve("classes");
+ Path destination = Paths.get("classes");
+ javac(source, destination);
+
+ // compile the mr9 directory including module-info.java
+ source = testsrc.resolve("src").resolve("mr9");
+ destination = Paths.get("mr9");
+ javac(source, destination);
+
+ // move module-info.class for later use
+ Files.move(destination.resolve("module-info.class"),
+ Paths.get("module-info.class"));
+ }
+
+ private void javac(Path source, Path destination) throws IOException {
+ String[] args = Stream.concat(
+ Stream.of("-d", destination.toString()),
+ Files.walk(source)
+ .map(Path::toString)
+ .filter(s -> s.endsWith(".java"))
+ ).toArray(String[]::new);
+ JAVAC_TOOL.run(System.out, System.err, args);
+ }
+
+ private int jar(String cmd) {
+ outbytes.reset();
+ errbytes.reset();
+ return JAR_TOOL.run(out, err, cmd.split(" +"));
+ }
+
+ @AfterClass
+ public void cleanup() throws IOException {
+ Files.walk(userdir, 1)
+ .filter(p -> !p.equals(userdir))
+ .forEach(p -> {
+ try {
+ if (Files.isDirectory(p)) {
+ FileUtils.deleteFileTreeWithRetry(p);
+ } else {
+ FileUtils.deleteFileIfExistsWithRetry(p);
+ }
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ }
+ });
+ }
+
+ // updates a valid multi-release jar with a new public class in
+ // versioned section and fails
+ @Test
+ public void test1() {
+ // successful build of multi-release jar
+ int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
+ Assert.assertEquals(rc, 0);
+
+ jar("-tf mmr.jar");
+
+ Set<String> actual = lines(outbytes);
+ Set<String> expected = Set.of(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "p/",
+ "p/Hi.class",
+ "META-INF/versions/9/p/Hi.class"
+ );
+ Assert.assertEquals(actual, expected);
+
+ // failed build because of new public class
+ rc = jar("-uf mmr.jar --release 9 -C mr9 p/internal/Bar.class");
+ Assert.assertEquals(rc, 1);
+
+ String s = new String(errbytes.toByteArray());
+ Assert.assertTrue(Message.NOT_FOUND_IN_BASE_ENTRY.match(s, "p/internal/Bar.class"));
+ }
+
+ // updates a valid multi-release jar with a module-info class and new
+ // concealed public class in versioned section and succeeds
+ @Test
+ public void test2() {
+ // successful build of multi-release jar
+ int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
+ Assert.assertEquals(rc, 0);
+
+ // successful build because of module-info and new public class
+ rc = jar("-uf mmr.jar module-info.class --release 9 -C mr9 p/internal/Bar.class");
+ Assert.assertEquals(rc, 0);
+
+ String s = new String(errbytes.toByteArray());
+ Assert.assertTrue(Message.NEW_CONCEALED_PACKAGE_WARNING.match(s, "p/internal/Bar.class"));
+
+ jar("-tf mmr.jar");
+
+ Set<String> actual = lines(outbytes);
+ Set<String> expected = Set.of(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "p/",
+ "p/Hi.class",
+ "META-INF/versions/9/p/Hi.class",
+ "META-INF/versions/9/p/internal/Bar.class",
+ "module-info.class"
+ );
+ Assert.assertEquals(actual, expected);
+ }
+
+ // jar tool fails building mmr.jar because of new public class
+ @Test
+ public void test3() {
+ int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 .");
+ Assert.assertEquals(rc, 1);
+
+ String s = new String(errbytes.toByteArray());
+ Assert.assertTrue(Message.NOT_FOUND_IN_BASE_ENTRY.match(s, "p/internal/Bar.class"));
+ }
+
+ // jar tool succeeds building mmr.jar because of concealed package
+ @Test
+ public void test4() {
+ int rc = jar("-cf mmr.jar module-info.class -C classes . " +
+ "--release 9 module-info.class -C mr9 .");
+ Assert.assertEquals(rc, 0);
+
+ String s = new String(errbytes.toByteArray());
+ Assert.assertTrue(Message.NEW_CONCEALED_PACKAGE_WARNING.match(s, "p/internal/Bar.class"));
+
+ jar("-tf mmr.jar");
+
+ Set<String> actual = lines(outbytes);
+ Set<String> expected = Set.of(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "module-info.class",
+ "META-INF/versions/9/module-info.class",
+ "p/",
+ "p/Hi.class",
+ "META-INF/versions/9/",
+ "META-INF/versions/9/p/",
+ "META-INF/versions/9/p/Hi.class",
+ "META-INF/versions/9/p/internal/",
+ "META-INF/versions/9/p/internal/Bar.class"
+ );
+ Assert.assertEquals(actual, expected);
+ }
+
+ // jar tool does two updates, no exported packages, all concealed
+ @Test
+ public void test5() throws IOException {
+ // compile the mr10 directory
+ Path source = testsrc.resolve("src").resolve("mr10");
+ Path destination = Paths.get("mr10");
+ javac(source, destination);
+
+ // create a directory for this tests special files
+ Files.createDirectory(Paths.get("test5"));
+
+ // create an empty module-info.java
+ String hi = "module hi {" + linesep + "}" + linesep;
+ Path modinfo = Paths.get("test5", "module-info.java");
+ Files.write(modinfo, hi.getBytes());
+
+ // and compile it
+ javac(modinfo, Paths.get("test5"));
+
+ int rc = jar("--create --file mr.jar -C classes .");
+ Assert.assertEquals(rc, 0);
+
+ rc = jar("--update --file mr.jar -C test5 module-info.class"
+ + " --release 9 -C mr9 .");
+ Assert.assertEquals(rc, 0);
+
+ jar("tf mr.jar");
+
+ Set<String> actual = lines(outbytes);
+ Set<String> expected = Set.of(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "p/",
+ "p/Hi.class",
+ "META-INF/versions/9/",
+ "META-INF/versions/9/p/",
+ "META-INF/versions/9/p/Hi.class",
+ "META-INF/versions/9/p/internal/",
+ "META-INF/versions/9/p/internal/Bar.class",
+ "module-info.class"
+ );
+ Assert.assertEquals(actual, expected);
+
+ jar("-d --file mr.jar");
+
+ actual = lines(outbytes);
+ expected = Set.of(
+ "hi",
+ "requires mandated java.base",
+ "contains p",
+ "contains p.internal"
+ );
+ Assert.assertEquals(actual, expected);
+
+ rc = jar("--update --file mr.jar --release 10 -C mr10 .");
+ Assert.assertEquals(rc, 0);
+
+ jar("tf mr.jar");
+
+ actual = lines(outbytes);
+ expected = Set.of(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "p/",
+ "p/Hi.class",
+ "META-INF/versions/9/",
+ "META-INF/versions/9/p/",
+ "META-INF/versions/9/p/Hi.class",
+ "META-INF/versions/9/p/internal/",
+ "META-INF/versions/9/p/internal/Bar.class",
+ "META-INF/versions/10/",
+ "META-INF/versions/10/p/",
+ "META-INF/versions/10/p/internal/",
+ "META-INF/versions/10/p/internal/bar/",
+ "META-INF/versions/10/p/internal/bar/Gee.class",
+ "module-info.class"
+ );
+ Assert.assertEquals(actual, expected);
+
+ jar("-d --file mr.jar");
+
+ actual = lines(outbytes);
+ expected = Set.of(
+ "hi",
+ "requires mandated java.base",
+ "contains p",
+ "contains p.internal",
+ "contains p.internal.bar"
+ );
+ Assert.assertEquals(actual, expected);
+ }
+
+ // root and versioned module-info entries have different main-class, version
+ // attributes
+ @Test
+ public void test6() throws IOException {
+ // create a directory for this tests special files
+ Files.createDirectory(Paths.get("test6"));
+ Files.createDirectory(Paths.get("test6-v9"));
+
+ // compile the classes directory
+ Path src = testsrc.resolve("src").resolve("classes");
+ Path dst = Paths.get("test6");
+ javac(src, dst);
+
+ byte[] mdBytes = Files.readAllBytes(Paths.get("module-info.class"));
+
+ ModuleInfoExtender mie = ModuleInfoExtender.newExtender(
+ new ByteArrayInputStream(mdBytes));
+
+ mie.mainClass("foo.main");
+ mie.version(Version.parse("1.0"));
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mie.write(baos);
+ Files.write(Paths.get("test6", "module-info.class"), baos.toByteArray());
+ Files.write(Paths.get("test6-v9", "module-info.class"), baos.toByteArray());
+
+ int rc = jar("--create --file mmr.jar -C test6 . --release 9 -C test6-v9 .");
+ Assert.assertEquals(rc, 0);
+
+
+ // different main-class
+ mie = ModuleInfoExtender.newExtender(new ByteArrayInputStream(mdBytes));
+ mie.mainClass("foo.main2");
+ mie.version(Version.parse("1.0"));
+ baos.reset();
+ mie.write(baos);
+ Files.write(Paths.get("test6-v9", "module-info.class"), baos.toByteArray());
+
+ rc = jar("--create --file mmr.jar -C test6 . --release 9 -C test6-v9 .");
+ Assert.assertEquals(rc, 1);
+
+ Assert.assertTrue(Message.CONTAINS_DIFFERENT_MAINCLASS.match(
+ new String(errbytes.toByteArray()),
+ "META-INF/versions/9/module-info.class"));
+
+ // different version
+ mie = ModuleInfoExtender.newExtender(new ByteArrayInputStream(mdBytes));
+ mie.mainClass("foo.main");
+ mie.version(Version.parse("2.0"));
+ baos.reset();
+ mie.write(baos);
+ Files.write(Paths.get("test6-v9", "module-info.class"), baos.toByteArray());
+
+ rc = jar("--create --file mmr.jar -C test6 . --release 9 -C test6-v9 .");
+ Assert.assertEquals(rc, 1);
+
+ Assert.assertTrue(Message.CONTAINS_DIFFERENT_VERSION.match(
+ new String(errbytes.toByteArray()),
+ "META-INF/versions/9/module-info.class"));
+
+ }
+
+ // versioned mmr without root module-info.class
+ @Test
+ public void test7() throws IOException {
+ // create a directory for this tests special files
+ Files.createDirectory(Paths.get("test7"));
+ Files.createDirectory(Paths.get("test7-v9"));
+ Files.createDirectory(Paths.get("test7-v10"));
+
+ // compile the classes directory
+ Path src = testsrc.resolve("src").resolve("classes");
+ Path dst = Paths.get("test7");
+ javac(src, dst);
+
+ // move module-info.class to v9 later use
+ Files.copy(Paths.get("module-info.class"),
+ Paths.get("test7-v9", "module-info.class"));
+
+ Files.copy(Paths.get("test7-v9", "module-info.class"),
+ Paths.get("test7-v10", "module-info.class"));
+
+ int rc = jar("--create --file mmr.jar --main-class=foo.main -C test7 . --release 9 -C test7-v9 . --release 10 -C test7-v10 .");
+
+System.out.println("-----------------------");
+System.out.println( new String(errbytes.toByteArray()));
+
+
+ Assert.assertEquals(rc, 0);
+
+
+ jar("-tf mmr.jar");
+
+System.out.println("-----------------------");
+System.out.println( new String(outbytes.toByteArray()));
+
+ Optional<String> exp = Optional.of("foo.main");
+ try (ZipFile zf = new ZipFile("mmr.jar")) {
+ Assert.assertTrue(zf.getEntry("module-info.class") == null);
+
+ ModuleDescriptor md = ModuleDescriptor.read(
+ zf.getInputStream(zf.getEntry("META-INF/versions/9/module-info.class")));
+ Assert.assertEquals(md.mainClass(), exp);
+
+ md = ModuleDescriptor.read(
+ zf.getInputStream(zf.getEntry("META-INF/versions/10/module-info.class")));
+ Assert.assertEquals(md.mainClass(), exp);
+ }
+ }
+
+ private static Set<String> lines(ByteArrayOutputStream baos) {
+ String s = new String(baos.toByteArray());
+ return Arrays.stream(s.split("\\R"))
+ .map(l -> l.trim())
+ .filter(l -> l.length() > 0)
+ .collect(Collectors.toSet());
+ }
+
+ static enum Message {
+ CONTAINS_DIFFERENT_MAINCLASS(
+ ": module-info.class in a versioned directory contains different \"main-class\""
+ ),
+ CONTAINS_DIFFERENT_VERSION(
+ ": module-info.class in a versioned directory contains different \"version\""
+ ),
+ NOT_FOUND_IN_BASE_ENTRY(
+ ", contains a new public class not found in base entries"
+ ),
+ NEW_CONCEALED_PACKAGE_WARNING(
+ " is a public class" +
+ " in a concealed package, placing this jar on the class path will result" +
+ " in incompatible public interfaces"
+ );
+
+ final String msg;
+ Message(String msg) {
+ this.msg = msg;
+ }
+
+ /*
+ * Test if the given output contains this message ignoring the line break.
+ */
+ boolean match(String output, String entry) {
+ System.out.println("Expected: " + entry + msg);
+ System.out.println("Found: " + output);
+ return Arrays.stream(output.split("\\R"))
+ .collect(Collectors.joining(" "))
+ .contains(entry + msg);
+ }
+ }
+}
--- a/jdk/test/tools/jar/mmrjar/ConcealedPackage.java Thu Jan 12 12:15:34 2017 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,339 +0,0 @@
-/*
- * Copyright (c) 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.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 8146486
- * @summary Fail to create a MR modular JAR with a versioned entry in
- * base-versioned empty package
- * @modules jdk.compiler
- * jdk.jartool
- * @library /lib/testlibrary
- * @build jdk.testlibrary.FileUtils
- * @run testng ConcealedPackage
- */
-
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.spi.ToolProvider;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import jdk.testlibrary.FileUtils;
-
-public class ConcealedPackage {
- private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
- .orElseThrow(() -> new RuntimeException("jar tool not found"));
- private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac")
- .orElseThrow(() -> new RuntimeException("javac tool not found"));
- private final String linesep = System.lineSeparator();
- private final Path testsrc;
- private final Path userdir;
- private final ByteArrayOutputStream outbytes = new ByteArrayOutputStream();
- private final PrintStream out = new PrintStream(outbytes, true);
- private final ByteArrayOutputStream errbytes = new ByteArrayOutputStream();
- private final PrintStream err = new PrintStream(errbytes, true);
-
- public ConcealedPackage() throws IOException {
- testsrc = Paths.get(System.getProperty("test.src"));
- userdir = Paths.get(System.getProperty("user.dir", "."));
-
- // compile the classes directory
- Path source = testsrc.resolve("src").resolve("classes");
- Path destination = Paths.get("classes");
- javac(source, destination);
-
- // compile the mr9 directory including module-info.java
- source = testsrc.resolve("src").resolve("mr9");
- destination = Paths.get("mr9");
- javac(source, destination);
-
- // move module-info.class for later use
- Files.move(destination.resolve("module-info.class"),
- Paths.get("module-info.class"));
- }
-
- private void javac(Path source, Path destination) throws IOException {
- String[] args = Stream.concat(
- Stream.of("-d", destination.toString()),
- Files.walk(source)
- .map(Path::toString)
- .filter(s -> s.endsWith(".java"))
- ).toArray(String[]::new);
- JAVAC_TOOL.run(System.out, System.err, args);
- }
-
- private int jar(String cmd) {
- outbytes.reset();
- errbytes.reset();
- return JAR_TOOL.run(out, err, cmd.split(" +"));
- }
-
- @AfterClass
- public void cleanup() throws IOException {
- Files.walk(userdir, 1)
- .filter(p -> !p.equals(userdir))
- .forEach(p -> {
- try {
- if (Files.isDirectory(p)) {
- FileUtils.deleteFileTreeWithRetry(p);
- } else {
- FileUtils.deleteFileIfExistsWithRetry(p);
- }
- } catch (IOException x) {
- throw new UncheckedIOException(x);
- }
- });
- }
-
- // updates a valid multi-release jar with a new public class in
- // versioned section and fails
- @Test
- public void test1() {
- // successful build of multi-release jar
- int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
- Assert.assertEquals(rc, 0);
-
- jar("-tf mmr.jar");
-
- Set<String> actual = lines(outbytes);
- Set<String> expected = Set.of(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "p/",
- "p/Hi.class",
- "META-INF/versions/9/p/Hi.class"
- );
- Assert.assertEquals(actual, expected);
-
- // failed build because of new public class
- rc = jar("-uf mmr.jar --release 9 -C mr9 p/internal/Bar.class");
- Assert.assertEquals(rc, 1);
-
- String s = new String(errbytes.toByteArray());
- Assert.assertTrue(Message.NOT_FOUND_IN_BASE_ENTRY.match(s, "p/internal/Bar.class"));
- }
-
- // updates a valid multi-release jar with a module-info class and new
- // concealed public class in versioned section and succeeds
- @Test
- public void test2() {
- // successful build of multi-release jar
- int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
- Assert.assertEquals(rc, 0);
-
- // successful build because of module-info and new public class
- rc = jar("-uf mmr.jar module-info.class --release 9 -C mr9 p/internal/Bar.class");
- Assert.assertEquals(rc, 0);
-
- String s = new String(errbytes.toByteArray());
- Assert.assertTrue(Message.NEW_CONCEALED_PACKAGE_WARNING.match(s, "p/internal/Bar.class"));
-
- jar("-tf mmr.jar");
-
- Set<String> actual = lines(outbytes);
- Set<String> expected = Set.of(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "p/",
- "p/Hi.class",
- "META-INF/versions/9/p/Hi.class",
- "META-INF/versions/9/p/internal/Bar.class",
- "module-info.class"
- );
- Assert.assertEquals(actual, expected);
- }
-
- // jar tool fails building mmr.jar because of new public class
- @Test
- public void test3() {
- int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 .");
- Assert.assertEquals(rc, 1);
-
- String s = new String(errbytes.toByteArray());
- Assert.assertTrue(Message.NOT_FOUND_IN_BASE_ENTRY.match(s, "p/internal/Bar.class"));
- }
-
- // jar tool succeeds building mmr.jar because of concealed package
- @Test
- public void test4() {
- int rc = jar("-cf mmr.jar module-info.class -C classes . " +
- "--release 9 module-info.class -C mr9 .");
- Assert.assertEquals(rc, 0);
-
- String s = new String(errbytes.toByteArray());
- Assert.assertTrue(Message.NEW_CONCEALED_PACKAGE_WARNING.match(s, "p/internal/Bar.class"));
-
- jar("-tf mmr.jar");
-
- Set<String> actual = lines(outbytes);
- Set<String> expected = Set.of(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "module-info.class",
- "META-INF/versions/9/module-info.class",
- "p/",
- "p/Hi.class",
- "META-INF/versions/9/p/",
- "META-INF/versions/9/p/Hi.class",
- "META-INF/versions/9/p/internal/",
- "META-INF/versions/9/p/internal/Bar.class"
- );
- Assert.assertEquals(actual, expected);
- }
-
- // jar tool does two updates, no exported packages, all concealed
- @Test
- public void test5() throws IOException {
- // compile the mr10 directory
- Path source = testsrc.resolve("src").resolve("mr10");
- Path destination = Paths.get("mr10");
- javac(source, destination);
-
- // create a directory for this tests special files
- Files.createDirectory(Paths.get("test5"));
-
- // create an empty module-info.java
- String hi = "module hi {" + linesep + "}" + linesep;
- Path modinfo = Paths.get("test5", "module-info.java");
- Files.write(modinfo, hi.getBytes());
-
- // and compile it
- javac(modinfo, Paths.get("test5"));
-
- int rc = jar("--create --file mr.jar -C classes .");
- Assert.assertEquals(rc, 0);
-
- rc = jar("--update --file mr.jar -C test5 module-info.class"
- + " --release 9 -C mr9 .");
- Assert.assertEquals(rc, 0);
-
- jar("tf mr.jar");
-
- Set<String> actual = lines(outbytes);
- Set<String> expected = Set.of(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "p/",
- "p/Hi.class",
- "META-INF/versions/9/p/",
- "META-INF/versions/9/p/Hi.class",
- "META-INF/versions/9/p/internal/",
- "META-INF/versions/9/p/internal/Bar.class",
- "module-info.class"
- );
- Assert.assertEquals(actual, expected);
-
- jar("-d --file mr.jar");
-
- actual = lines(outbytes);
- expected = Set.of(
- "hi",
- "requires mandated java.base",
- "contains p",
- "contains p.internal"
- );
- Assert.assertEquals(actual, expected);
-
- rc = jar("--update --file mr.jar --release 10 -C mr10 .");
- Assert.assertEquals(rc, 0);
-
- jar("tf mr.jar");
-
- actual = lines(outbytes);
- expected = Set.of(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "p/",
- "p/Hi.class",
- "META-INF/versions/9/p/",
- "META-INF/versions/9/p/Hi.class",
- "META-INF/versions/9/p/internal/",
- "META-INF/versions/9/p/internal/Bar.class",
- "META-INF/versions/10/p/",
- "META-INF/versions/10/p/internal/",
- "META-INF/versions/10/p/internal/bar/",
- "META-INF/versions/10/p/internal/bar/Gee.class",
- "module-info.class"
- );
- Assert.assertEquals(actual, expected);
-
- jar("-d --file mr.jar");
-
- actual = lines(outbytes);
- expected = Set.of(
- "hi",
- "requires mandated java.base",
- "contains p",
- "contains p.internal",
- "contains p.internal.bar"
- );
- Assert.assertEquals(actual, expected);
- }
-
- private static Set<String> lines(ByteArrayOutputStream baos) {
- String s = new String(baos.toByteArray());
- return Arrays.stream(s.split("\\R"))
- .map(l -> l.trim())
- .filter(l -> l.length() > 0)
- .collect(Collectors.toSet());
- }
-
- static enum Message {
- NOT_FOUND_IN_BASE_ENTRY(
- ", contains a new public class not found in base entries"
- ),
- NEW_CONCEALED_PACKAGE_WARNING(
- " is a public class" +
- " in a concealed package, placing this jar on the class path will result" +
- " in incompatible public interfaces"
- );
-
- final String msg;
- Message(String msg) {
- this.msg = msg;
- }
-
- /*
- * Test if the given output contains this message ignoring the line break.
- */
- boolean match(String output, String entry) {
- System.out.println("Expected: " + entry + msg);
- System.out.println("Found: " + output);
- return Arrays.stream(output.split("\\R"))
- .collect(Collectors.joining(" "))
- .contains(entry + msg);
- }
- }
-}
--- a/jdk/test/tools/jar/modularJar/Basic.java Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/test/tools/jar/modularJar/Basic.java Thu Jan 12 16:41:08 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -46,7 +46,7 @@
/*
* @test
- * @bug 8167328
+ * @bug 8167328 8171830
* @library /lib/testlibrary
* @modules jdk.compiler
* jdk.jartool
@@ -241,6 +241,11 @@
java(mp, FOO.moduleName + "/" + FOO.mainClass)
.assertSuccess()
+.resultChecker(r -> {
+ System.out.println("===================================");
+ System.out.println(r.output);
+ System.out.println("===================================");
+})
.resultChecker(r -> assertModuleData(r, FOO));
try (InputStream fis = Files.newInputStream(modularJar);
JarInputStream jis = new JarInputStream(fis)) {
@@ -417,6 +422,7 @@
jar("--update",
"--file=" + modularJar.toString(),
"--main-class=" + FOO.mainClass,
+ "--module-version=" + FOO.version,
"-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
"-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class")
.assertSuccess();
@@ -734,6 +740,25 @@
}
@Test
+ public void exportCreateWithMissingPkg() throws IOException {
+
+ Path foobar = TEST_SRC.resolve("src").resolve("foobar");
+ Path dst = Files.createDirectories(MODULE_CLASSES.resolve("foobar"));
+ javac(dst, null, sourceList(foobar));
+
+ Path mp = Paths.get("exportWithMissingPkg");
+ createTestDir(mp);
+ Path modClasses = dst;
+ Path modularJar = mp.resolve("foofoo.jar");
+
+ jar("--create",
+ "--file=" + modularJar.toString(),
+ "-C", modClasses.toString(), "module-info.class",
+ "-C", modClasses.toString(), "jdk/test/foo/Foo.class")
+ .assertFailure();
+ }
+
+ @Test
public void printModuleDescriptorFoo() throws IOException {
Path mp = Paths.get("printModuleDescriptorFoo");
createTestDir(mp);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/modularJar/src/foobar/Bar.java Thu Jan 12 16:41:08 2017 -0800
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.test.bar;
+
+public class Bar {
+ public static void main(String[] args) {}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/modularJar/src/foobar/Foo.java Thu Jan 12 16:41:08 2017 -0800
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.test.foo;
+
+public class Foo {
+ public static void main(String[] args) {}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/modularJar/src/foobar/module-info.java Thu Jan 12 16:41:08 2017 -0800
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module foo {
+ exports jdk.test.foo;
+ exports jdk.test.bar;
+ opens jdk.test.foo;
+ opens jdk.test.bar;
+}
--- a/jdk/test/tools/jar/multiRelease/Basic1.java Thu Jan 12 12:15:34 2017 -0800
+++ b/jdk/test/tools/jar/multiRelease/Basic1.java Thu Jan 12 16:41:08 2017 -0800
@@ -62,15 +62,20 @@
Path source = Paths.get(src, "data", test, "base", "version");
javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
- Path v9 = Paths.get("v9").resolve("META-INF").resolve("versions").resolve("9");
+ Path v9 = Paths.get("v9");
Files.createDirectories(v9);
source = Paths.get(src, "data", test, "v9", "version");
javac(v9, source.resolve("Version.java"));
- Path v10 = Paths.get("v10").resolve("META-INF").resolve("versions").resolve("10");
+ Path v10 = Paths.get("v10");
Files.createDirectories(v10);
source = Paths.get(src, "data", test, "v10", "version");
javac(v10, source.resolve("Version.java"));
+
+ Path v10_1 = Paths.get("v10_1").resolve("META-INF").resolve("versions").resolve("v10");
+ Files.createDirectories(v10_1);
+ source = Paths.get(src, "data", test, "v10", "version");
+ javac(v10_1, source.resolve("Version.java"));
}
@Test
@@ -95,28 +100,30 @@
new String[] {"classes", "base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
- new String[] {"v9", "META-INF", "versions", "9", "version", "Version.class"},
+ new String[] {"v9", "version", "Version.class"},
"META-INF/versions/10/version/Version.class",
- new String[] {"v10", "META-INF", "versions", "10", "version", "Version.class"}
+ new String[] {"v10", "version", "Version.class"}
);
compare(jarfile, names);
}
+
@Test
public void testFail() throws IOException {
String jarfile = "test.jar";
Path classes = Paths.get("classes");
- Path v9 = Paths.get("v9");
- Path v10 = Paths.get("v10");
+ Path v10 = Paths.get("v10_1");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
- "--release", "9", "-C", v10.toString(), ".")
+ "--release", "10", "-C", v10.toString(), ".")
.assertFailure()
.outputContains("unexpected versioned entry META-INF/versions/");
}
+
+
private void checkMultiRelease(String jarFile, boolean expected) throws IOException {
try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ,
JarFile.runtimeVersion())) {