8231093: Document the ZIP FS properties noCompression and releaseVersion
Reviewed-by: clanger, martin, alanb
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Fri Oct 04 15:51:17 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Sun Oct 06 13:08:58 2019 -0400
@@ -233,7 +233,7 @@
}
public void setMultiReleaseValue(String multiReleaseValue) {
- fsEnv = Collections.singletonMap("multi-release", multiReleaseValue);
+ fsEnv = Collections.singletonMap("releaseVersion", multiReleaseValue);
}
private boolean contains(Collection<Path> searchPath, Path file) throws IOException {
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Fri Oct 04 15:51:17 2019 -0400
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Sun Oct 06 13:08:58 2019 -0400
@@ -60,18 +60,20 @@
JarFileSystem(ZipFileSystemProvider provider, Path zfpath, Map<String,?> env) throws IOException {
super(provider, zfpath, env);
- if (isMultiReleaseJar()) {
+ Object o = getRuntimeVersion(env);
+ if (isMultiReleaseJar() && (o != null)) {
int version;
- Object o = env.get("multi-release");
if (o instanceof String) {
String s = (String)o;
if (s.equals("runtime")) {
version = Runtime.version().feature();
+ } else if (s.matches("^[1-9][0-9]*$")) {
+ version = Version.parse(s).feature();
} else {
- version = Integer.parseInt(s);
+ throw new IllegalArgumentException("Invalid runtime version");
}
} else if (o instanceof Integer) {
- version = (Integer)o;
+ version = Version.parse(((Integer)o).toString()).feature();
} else if (o instanceof Version) {
version = ((Version)o).feature();
} else {
@@ -83,6 +85,23 @@
}
}
+ /**
+ * Utility method to get the release version for a multi-release JAR. It
+ * first checks the documented property {@code releaseVersion} and if not
+ * found checks the original property {@code multi-release}
+ * @param env ZIP FS map
+ * @return release version or null if it is not specified
+ */
+ private Object getRuntimeVersion(Map<String, ?> env) {
+ Object o = null;
+ if (env.containsKey(ZipFileSystemProvider.PROPERTY_RELEASE_VERSION)) {
+ o = env.get(ZipFileSystemProvider.PROPERTY_RELEASE_VERSION);
+ } else {
+ o = env.get(ZipFileSystemProvider.PROPERTY_MULTI_RELEASE);
+ }
+ return o;
+ }
+
private boolean isMultiReleaseJar() throws IOException {
try (InputStream is = newInputStream(getBytes("/META-INF/MANIFEST.MF"))) {
String multiRelease = new Manifest(is).getMainAttributes()
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Fri Oct 04 15:51:17 2019 -0400
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Sun Oct 06 13:08:58 2019 -0400
@@ -81,13 +81,19 @@
(PrivilegedAction<Boolean>)()->System.getProperty("os.name")
.startsWith("Windows"));
private static final byte[] ROOTPATH = new byte[] { '/' };
- private static final String OPT_POSIX = "enablePosixFileAttributes";
- private static final String OPT_DEFAULT_OWNER = "defaultOwner";
- private static final String OPT_DEFAULT_GROUP = "defaultGroup";
- private static final String OPT_DEFAULT_PERMISSIONS = "defaultPermissions";
+ private static final String PROPERTY_POSIX = "enablePosixFileAttributes";
+ private static final String PROPERTY_DEFAULT_OWNER = "defaultOwner";
+ private static final String PROPERTY_DEFAULT_GROUP = "defaultGroup";
+ private static final String PROPERTY_DEFAULT_PERMISSIONS = "defaultPermissions";
private static final Set<PosixFilePermission> DEFAULT_PERMISSIONS =
PosixFilePermissions.fromString("rwxrwxrwx");
+ // Property used to specify the compression mode to use
+ private static final String PROPERTY_COMPRESSION_METHOD = "compressionMethod";
+ // Value specified for compressionMethod property to compress Zip entries
+ private static final String COMPRESSION_METHOD_DEFLATED = "DEFLATED";
+ // Value specified for compressionMethod property to not compress Zip entries
+ private static final String COMPRESSION_METHOD_STORED = "STORED";
private final ZipFileSystemProvider provider;
private final Path zfpath;
@@ -124,8 +130,8 @@
this.noExtt = "false".equals(env.get("zipinfo-time"));
this.useTempFile = isTrue(env, "useTempFile");
this.forceEnd64 = isTrue(env, "forceZIP64End");
- this.defaultCompressionMethod = isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED;
- this.supportPosix = isTrue(env, OPT_POSIX);
+ this.defaultCompressionMethod = getDefaultCompressionMethod(env);
+ this.supportPosix = isTrue(env, PROPERTY_POSIX);
this.defaultOwner = initOwner(zfpath, env);
this.defaultGroup = initGroup(zfpath, env);
this.defaultPermissions = initPermissions(env);
@@ -163,6 +169,50 @@
this.zfpath = zfpath;
}
+ /**
+ * Return the compression method to use (STORED or DEFLATED). If the
+ * property {@code commpressionMethod} is set use its value to determine
+ * the compression method to use. If the property is not set, then the
+ * default compression is DEFLATED unless the property {@code noCompression}
+ * is set which is supported for backwards compatibility.
+ * @param env Zip FS map of properties
+ * @return The Compression method to use
+ */
+ private int getDefaultCompressionMethod(Map<String, ?> env) {
+ int result =
+ isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED;
+ if (env.containsKey(PROPERTY_COMPRESSION_METHOD)) {
+ Object compressionMethod = env.get(PROPERTY_COMPRESSION_METHOD);
+ if (compressionMethod != null) {
+ if (compressionMethod instanceof String) {
+ switch (((String) compressionMethod).toUpperCase()) {
+ case COMPRESSION_METHOD_STORED:
+ result = METHOD_STORED;
+ break;
+ case COMPRESSION_METHOD_DEFLATED:
+ result = METHOD_DEFLATED;
+ break;
+ default:
+ throw new IllegalArgumentException(String.format(
+ "The value for the %s property must be %s or %s",
+ PROPERTY_COMPRESSION_METHOD, COMPRESSION_METHOD_STORED,
+ COMPRESSION_METHOD_DEFLATED));
+ }
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "The Object type for the %s property must be a String",
+ PROPERTY_COMPRESSION_METHOD));
+ }
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "The value for the %s property must be %s or %s",
+ PROPERTY_COMPRESSION_METHOD, COMPRESSION_METHOD_STORED,
+ COMPRESSION_METHOD_DEFLATED));
+ }
+ }
+ return result;
+ }
+
// returns true if there is a name=true/"true" setting in env
private static boolean isTrue(Map<String, ?> env, String name) {
return "true".equals(env.get(name)) || TRUE.equals(env.get(name));
@@ -173,7 +223,7 @@
// be determined, we try to go with system property "user.name". If that's not
// accessible, we return "<zipfs_default>".
private UserPrincipal initOwner(Path zfpath, Map<String, ?> env) throws IOException {
- Object o = env.get(OPT_DEFAULT_OWNER);
+ Object o = env.get(PROPERTY_DEFAULT_OWNER);
if (o == null) {
try {
PrivilegedExceptionAction<UserPrincipal> pa = ()->Files.getOwner(zfpath);
@@ -193,7 +243,7 @@
if (o instanceof String) {
if (((String)o).isEmpty()) {
throw new IllegalArgumentException("Value for property " +
- OPT_DEFAULT_OWNER + " must not be empty.");
+ PROPERTY_DEFAULT_OWNER + " must not be empty.");
}
return ()->(String)o;
}
@@ -201,7 +251,7 @@
return (UserPrincipal)o;
}
throw new IllegalArgumentException("Value for property " +
- OPT_DEFAULT_OWNER + " must be of type " + String.class +
+ PROPERTY_DEFAULT_OWNER + " must be of type " + String.class +
" or " + UserPrincipal.class);
}
@@ -210,7 +260,7 @@
// If this is not possible/unsupported, we will return a group principal going by
// the same name as the default owner.
private GroupPrincipal initGroup(Path zfpath, Map<String, ?> env) throws IOException {
- Object o = env.get(OPT_DEFAULT_GROUP);
+ Object o = env.get(PROPERTY_DEFAULT_GROUP);
if (o == null) {
try {
PosixFileAttributeView zfpv = Files.getFileAttributeView(zfpath, PosixFileAttributeView.class);
@@ -232,7 +282,7 @@
if (o instanceof String) {
if (((String)o).isEmpty()) {
throw new IllegalArgumentException("Value for property " +
- OPT_DEFAULT_GROUP + " must not be empty.");
+ PROPERTY_DEFAULT_GROUP + " must not be empty.");
}
return ()->(String)o;
}
@@ -240,14 +290,14 @@
return (GroupPrincipal)o;
}
throw new IllegalArgumentException("Value for property " +
- OPT_DEFAULT_GROUP + " must be of type " + String.class +
+ PROPERTY_DEFAULT_GROUP + " must be of type " + String.class +
" or " + GroupPrincipal.class);
}
// Initialize the default permissions for files inside the zip archive.
// If not specified in env, it will return 777.
private Set<PosixFilePermission> initPermissions(Map<String, ?> env) {
- Object o = env.get(OPT_DEFAULT_PERMISSIONS);
+ Object o = env.get(PROPERTY_DEFAULT_PERMISSIONS);
if (o == null) {
return DEFAULT_PERMISSIONS;
}
@@ -256,7 +306,7 @@
}
if (!(o instanceof Set)) {
throw new IllegalArgumentException("Value for property " +
- OPT_DEFAULT_PERMISSIONS + " must be of type " + String.class +
+ PROPERTY_DEFAULT_PERMISSIONS + " must be of type " + String.class +
" or " + Set.class);
}
Set<PosixFilePermission> perms = new HashSet<>();
@@ -264,7 +314,7 @@
if (o2 instanceof PosixFilePermission) {
perms.add((PosixFilePermission)o2);
} else {
- throw new IllegalArgumentException(OPT_DEFAULT_PERMISSIONS +
+ throw new IllegalArgumentException(PROPERTY_DEFAULT_PERMISSIONS +
" must only contain objects of type " + PosixFilePermission.class);
}
}
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java Fri Oct 04 15:51:17 2019 -0400
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java Sun Oct 06 13:08:58 2019 -0400
@@ -53,6 +53,11 @@
*/
public class ZipFileSystemProvider extends FileSystemProvider {
+ // Property used to specify the entry version to use for a multi-release JAR
+ static final String PROPERTY_RELEASE_VERSION = "releaseVersion";
+ // Original property used to specify the entry version to use for a
+ // multi-release JAR which is kept for backwards compatibility.
+ static final String PROPERTY_MULTI_RELEASE = "multi-release";
private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
public ZipFileSystemProvider() {}
@@ -104,20 +109,7 @@
if (filesystems.containsKey(realPath))
throw new FileSystemAlreadyExistsException();
}
- ZipFileSystem zipfs;
- try {
- if (env.containsKey("multi-release")) {
- zipfs = new JarFileSystem(this, path, env);
- } else {
- zipfs = new ZipFileSystem(this, path, env);
- }
- } catch (ZipException ze) {
- String pname = path.toString();
- if (pname.endsWith(".zip") || pname.endsWith(".jar"))
- throw ze;
- // assume NOT a zip/jar file
- throw new UnsupportedOperationException();
- }
+ ZipFileSystem zipfs = getZipFileSystem(path, env);
if (realPath == null) { // newly created
realPath = path.toRealPath();
}
@@ -131,20 +123,25 @@
throws IOException
{
ensureFile(path);
+ return getZipFileSystem(path, env);
+ }
+
+ private ZipFileSystem getZipFileSystem(Path path, Map<String, ?> env) throws IOException {
+ ZipFileSystem zipfs;
try {
- ZipFileSystem zipfs;
- if (env.containsKey("multi-release")) {
+ if (env.containsKey(PROPERTY_RELEASE_VERSION) ||
+ env.containsKey(PROPERTY_MULTI_RELEASE)) {
zipfs = new JarFileSystem(this, path, env);
} else {
zipfs = new ZipFileSystem(this, path, env);
}
- return zipfs;
} catch (ZipException ze) {
String pname = path.toString();
if (pname.endsWith(".zip") || pname.endsWith(".jar"))
throw ze;
throw new UnsupportedOperationException();
}
+ return zipfs;
}
@Override
--- a/src/jdk.zipfs/share/classes/module-info.java Fri Oct 04 15:51:17 2019 -0400
+++ b/src/jdk.zipfs/share/classes/module-info.java Sun Oct 06 13:08:58 2019 -0400
@@ -147,7 +147,7 @@
* <tbody>
* <tr>
* <th scope="row">create</th>
- * <td>java.lang.String</td>
+ * <td>{@link java.lang.String} or {@link java.lang.Boolean}</td>
* <td>false</td>
* <td>
* If the value is {@code true}, the Zip file system provider
@@ -156,7 +156,7 @@
* </tr>
* <tr>
* <th scope="row">encoding</th>
- * <td>java.lang.String</td>
+ * <td>{@link java.lang.String}</td>
* <td>UTF-8</td>
* <td>
* The value indicates the encoding scheme for the
@@ -164,8 +164,8 @@
* </td>
* </tr>
* <tr>
- * <td scope="row">enablePosixFileAttributes</td>
- * <td>java.lang.String</td>
+ * <th scope="row">enablePosixFileAttributes</th>
+ * <td>{@link java.lang.String} or {@link java.lang.Boolean}</td>
* <td>false</td>
* <td>
* If the value is {@code true}, the Zip file system will support
@@ -173,8 +173,9 @@
* </td>
* </tr>
* <tr>
- * <td scope="row">defaultOwner</td>
- * <td>{@link java.nio.file.attribute.UserPrincipal UserPrincipal}<br> or java.lang.String</td>
+ * <th scope="row">defaultOwner</th>
+ * <td>{@link java.nio.file.attribute.UserPrincipal UserPrincipal}<br> or
+ * {@link java.lang.String}</td>
* <td>null/unset</td>
* <td>
* Override the default owner for entries in the Zip file system.<br>
@@ -182,8 +183,9 @@
* </td>
* </tr>
* <tr>
- * <td scope="row">defaultGroup</td>
- * <td>{@link java.nio.file.attribute.GroupPrincipal GroupPrincipal}<br> or java.lang.String</td>
+ * <th scope="row">defaultGroup</th>
+ * <td>{@link java.nio.file.attribute.GroupPrincipal GroupPrincipal}<br> or
+ * {@link java.lang.String}</td>
* <td>null/unset</td>
* <td>
* Override the the default group for entries in the Zip file system.<br>
@@ -191,9 +193,9 @@
* </td>
* </tr>
* <tr>
- * <td scope="row">defaultPermissions</td>
+ * <th scope="row">defaultPermissions</th>
* <td>{@link java.util.Set Set}<{@link java.nio.file.attribute.PosixFilePermission PosixFilePermission}><br>
- * or java.lang.String</td>
+ * or {@link java.lang.String}</td>
* <td>null/unset</td>
* <td>
* Override the default Set of permissions for entries in the Zip file system.<br>
@@ -201,7 +203,66 @@
* a String that is parsed by {@link java.nio.file.attribute.PosixFilePermissions#fromString PosixFilePermissions::fromString}
* </td>
* </tr>
- * </tbody>
+ * <tr>
+ * <th scope="row">compressionMethod</th>
+ * <td>{@link java.lang.String}</td>
+ * <td>"DEFLATED"</td>
+ * <td>
+ * The value representing the compression method to use when writing entries
+ * to the Zip file system.
+ * <ul>
+ * <li>
+ * If the value is {@code "STORED"}, the Zip file system provider will
+ * not compress entries when writing to the Zip file system.
+ * </li>
+ * <li>
+ * If the value is {@code "DEFLATED"} or the property is not set,
+ * the Zip file system provider will use data compression when
+ * writing entries to the Zip file system.
+ * </li>
+ * <li>
+ * If the value is not {@code "STORED"} or {@code "DEFLATED"}, an
+ * {@code IllegalArgumentException} will be thrown when the Zip
+ * filesystem is created.
+ * </li>
+ * </ul>
+ * </td>
+ * </tr>
+ * <tr>
+ * <th scope="row">releaseVersion</th>
+ * <td>{@link java.lang.String} or {@link java.lang.Integer}</td>
+ * <td>null/unset</td>
+ * <td>
+ * A value representing the version entry to use when accessing a
+ * <a href=="{@docRoot}/../specs/jar/jar.html#multi-release-jar-files">
+ * multi-release JAR</a>. If the JAR is not a
+ * <a href=="{@docRoot}/../specs/jar/jar.html#multi-release-jar-files">
+ * multi-release JAR</a>, the value will be ignored and the JAR will be
+ * considered un-versioned.
+ * <p>
+ * The value must be either the string "runtime" or represent a valid
+ * {@linkplain Runtime.Version Java SE Platform version number},
+ * such as {@code 9} or {@code 14}, in order to determine the version entry.
+ *
+ * <ul>
+ * <li>
+ * If the value is {@code null} or the property is not set,
+ * then the JAR will be treated as an un-versioned JAR.
+ * </li>
+ * <li>
+ * If the value is {@code "runtime"}, the
+ * version entry will be determined by invoking
+ * {@linkplain Runtime.Version#feature() Runtime.Version.feature()}.
+ * </li>
+ * <li>
+ * If the value does not represent a valid
+ * {@linkplain Runtime.Version Java SE Platform version number},
+ * an {@code IllegalArgumentException} will be thrown.
+ * </li>
+ * </ul>
+ * </td>
+ * </tr>
+ * </tbody>
* </table>
*
* <h2>Examples:</h2>
@@ -223,7 +284,7 @@
* <pre>
* {@code
*
- * FileSystem zipfs = FileSystems.newFileSystem(Path.of("helloworld.jar"), null);
+ * FileSystem zipfs = FileSystems.newFileSystem(Path.of("helloworld.jar"));
* Path rootDir = zipfs.getPath("/");
* Files.walk(rootDir)
* .forEach(System.out::println);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/nio/zipfs/CompressionModeTest.java Sun Oct 06 13:08:58 2019 -0400
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import org.testng.annotations.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static java.lang.String.format;
+import static java.util.stream.Collectors.joining;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8231093
+ * @summary Test Zip FS compressionMethod property
+ * @modules jdk.zipfs
+ * @run testng CompressionModeTest
+ */
+public class CompressionModeTest {
+
+ private static final Path HERE = Path.of(".");
+
+ /**
+ * Number of ZIP entries to create
+ */
+ private static final int ENTRIES = 5;
+
+ /**
+ * Value used for creating the required entries in a ZIP or JAR file
+ */
+ private static final String ZIP_FILE_VALUE = "US Open 2019";
+ private static final byte[] ZIP_FILE_ENTRY =
+ ZIP_FILE_VALUE.getBytes(StandardCharsets.UTF_8);
+
+ private static final SecureRandom random = new SecureRandom();
+
+ /**
+ * Validate that you can create a ZIP file with and without compression
+ * and that entries are created with the specified compression method.
+ *
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param compression Indicates whether the files are DEFLATED(default)
+ * or STORED
+ * @throws Exception If an error occurs during the creation, verification or
+ * deletion of the ZIP file
+ */
+ @Test(dataProvider = "validCompressionMethods", enabled = true)
+ public void testValidCompressionMehods(Map<String, String> env,
+ int compression) throws Exception {
+
+ System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ",
+ formatMap(env), compression);
+
+ Path zipfile = generatePath(HERE, "test", ".zip");
+ Files.deleteIfExists(zipfile);
+ createZipFile(zipfile, env, ENTRIES);
+ verify(zipfile, compression, ENTRIES, 0);
+ Files.deleteIfExists(zipfile);
+ }
+
+ /**
+ * Validate that an IllegalArgumentException is thrown when an invalid
+ * value is specified for the compressionMethod property.
+ *
+ * @param env Properties used for creating the ZIP Filesystem
+ * @throws Exception if an error occurs other than the expected
+ * IllegalArgumentException
+ */
+ @Test(dataProvider = "invalidCompressionMethod")
+ public void testInvalidCompressionMethod(Map<String, String> env) throws Exception {
+ System.out.printf("ZIP FS Map = %s%n ", formatMap(env));
+ Path zipfile = generatePath(HERE, "test", ".zip");
+ Files.deleteIfExists(zipfile);
+ assertThrows(IllegalArgumentException.class, () ->
+ createZipFile(zipfile, env, ENTRIES));
+ Files.deleteIfExists(zipfile);
+ }
+
+ /**
+ * Create a ZIP File System using the specified properties and a ZIP file
+ * with the specified number of entries
+ *
+ * @param zipFile Path to the ZIP File to create
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param entries Number of entries to add to the ZIP File
+ * @throws IOException If an error occurs while creating the ZIP file
+ */
+ private void createZipFile(Path zipFile, Map<String, String> env,
+ int entries) throws IOException {
+ System.out.printf("Creating file = %s%n", zipFile);
+ try (FileSystem zipfs =
+ FileSystems.newFileSystem(zipFile, env)) {
+
+ for (int i = 0; i < entries; i++) {
+ Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE);
+ }
+ }
+ }
+
+ /**
+ * DataProvider used to validate that you can create a ZIP file with and
+ * without compression.
+ */
+ @DataProvider(name = "validCompressionMethods")
+ private Object[][] validCompressionMethods() {
+ return new Object[][]{
+ {Map.of("create", "true"), ZipEntry.DEFLATED},
+ {Map.of("create", "true", "noCompression", "true"),
+ ZipEntry.STORED},
+ {Map.of("create", "true", "noCompression", "false"),
+ ZipEntry.DEFLATED},
+ {Map.of("create", "true", "compressionMethod", "STORED"),
+ ZipEntry.STORED},
+ {Map.of("create", "true", "compressionMethod", "DEFLATED"),
+ ZipEntry.DEFLATED},
+ {Map.of("create", "true", "compressionMethod", "stored"),
+ ZipEntry.STORED},
+ {Map.of("create", "true", "compressionMethod", "deflated"),
+ ZipEntry.DEFLATED}
+ };
+ }
+
+ /**
+ * DataProvider used to validate that an IllegalArgumentException is thrown
+ * for an invalid value for the compressionMethod property.
+ */
+ @DataProvider(name = "invalidCompressionMethod")
+ private Object[][] invalidCompressionMethod() {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("create", "true");
+ map.put("compressionMethod", null);
+ return new Object[][]{
+ {map},
+ {Map.of("create", "true", "compressionMethod", "")},
+ {Map.of("create", "true", "compressionMethod",
+ Integer.parseInt("5"))},
+ {Map.of("create", "true", "compressionMethod", "invalid")}
+ };
+ }
+
+ /**
+ * Verify that the given path is a ZIP file containing the
+ * expected entries.
+ *
+ * @param zipfile ZIP file to be validated
+ * @param method Expected Compression method: STORED or DEFLATED
+ * @param entries Number of expected entries
+ * @param start Starting number for verifying entries
+ * @throws Exception If an error occurs while examining the ZIP file
+ */
+ private static void verify(Path zipfile, int method, int entries,
+ int start) throws Exception {
+ // check entries with ZIP API
+ try (ZipFile zf = new ZipFile(zipfile.toFile())) {
+ // check entry count
+ assertEquals(entries, zf.size());
+
+ // check compression method and content of each entry
+ for (int i = start; i < entries; i++) {
+ ZipEntry ze = zf.getEntry("Entry-" + i);
+ assertNotNull(ze);
+ assertEquals(method, ze.getMethod());
+ try (InputStream is = zf.getInputStream(ze)) {
+ byte[] bytes = is.readAllBytes();
+ assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY));
+ }
+ }
+ }
+ // check entries with FileSystem API
+ try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
+
+ // check entry count
+ Path top = fs.getPath("/");
+ long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) ->
+ attrs.isRegularFile() || (attrs.isDirectory() &&
+ path.getFileName() != null &&
+ path.getFileName().toString().equals("META-INF")))
+ .count();
+ assertEquals(entries, count);
+
+ // check content of each entry
+ for (int i = start; i < entries; i++) {
+ Path file = fs.getPath("Entry-" + i);
+ byte[] bytes = Files.readAllBytes(file);
+ assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY));
+ }
+ }
+ }
+
+ /**
+ * Generate a temporary file Path
+ *
+ * @param dir Directory used to create the path
+ * @param prefix The prefix string used to create the path
+ * @param suffix The suffix string used to create the path
+ * @return Path that was generated
+ */
+ private static Path generatePath(Path dir, String prefix, String suffix) {
+ long n = random.nextLong();
+ String s = prefix + Long.toUnsignedString(n) + suffix;
+ Path name = dir.getFileSystem().getPath(s);
+ // the generated name should be a simple file name
+ if (name.getParent() != null)
+ throw new IllegalArgumentException("Invalid prefix or suffix");
+ return dir.resolve(name);
+ }
+
+ /**
+ * Utility method to return a formatted String of the key:value entries for
+ * a Map
+ *
+ * @param env Map to format
+ * @return Formatted string of the Map entries
+ */
+ private static String formatMap(Map<String, String> env) {
+ return env.entrySet().stream()
+ .map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
+ .collect(joining(", "));
+ }
+}
--- a/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java Fri Oct 04 15:51:17 2019 -0400
+++ b/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java Sun Oct 06 13:08:58 2019 -0400
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8144355 8144062 8176709 8194070 8193802
+ * @bug 8144355 8144062 8176709 8194070 8193802 8231093
* @summary Test aliasing additions to ZipFileSystem for multi-release jar files
* @library /lib/testlibrary/java/util/jar
* @modules jdk.compiler
@@ -40,6 +40,7 @@
import java.lang.Runtime.Version;
import java.net.URI;
import java.nio.file.*;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -88,8 +89,7 @@
public Object[][] createStrings() {
return new Object[][]{
{"runtime", MAJOR_VERSION},
- {"-20", 8},
- {"0", 8},
+ {null, 8},
{"8", 8},
{"9", 9},
{Integer.toString(MAJOR_VERSION), MAJOR_VERSION},
@@ -101,8 +101,7 @@
@DataProvider(name="integers")
public Object[][] createIntegers() {
return new Object[][] {
- {Integer.valueOf(-5), 8},
- {Integer.valueOf(0), 8},
+ {null, 8},
{Integer.valueOf(8), 8},
{Integer.valueOf(9), 9},
{Integer.valueOf(MAJOR_VERSION), MAJOR_VERSION},
@@ -114,6 +113,7 @@
@DataProvider(name="versions")
public Object[][] createVersions() {
return new Object[][] {
+ {null, 8},
{Version.parse("8"), 8},
{Version.parse("9"), 9},
{Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION},
@@ -122,6 +122,20 @@
};
}
+ @DataProvider(name="invalidVersions")
+ public Object[][] invalidVersions() {
+ return new Object[][] {
+ {Map.of("releaseVersion", "")},
+ {Map.of("releaseVersion", "invalid")},
+ {Map.of("releaseVersion", "0")},
+ {Map.of("releaseVersion", "-1")},
+ {Map.of("releaseVersion", "11.0.1")},
+ {Map.of("releaseVersion", new ArrayList<Long>())},
+ {Map.of("releaseVersion", Integer.valueOf(0))},
+ {Map.of("releaseVersion", Integer.valueOf(-1))}
+ };
+ }
+
// Not the best test but all I can do since ZipFileSystem and JarFileSystem
// are not public, so I can't use (fs instanceof ...)
@Test
@@ -131,7 +145,7 @@
try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) {
Assert.assertTrue(readAndCompare(fs, 8));
}
- env.put("multi-release", "runtime");
+ env.put("releaseVersion", "runtime");
// a configuration and jar file is multi-release
try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) {
Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION));
@@ -150,30 +164,67 @@
@Test(dataProvider="strings")
public void testStrings(String value, int expected) throws Throwable {
+ stringEnv.put("releaseVersion", value);
+ runTest(stringEnv, expected);
+ }
+
+ @Test(dataProvider="integers")
+ public void testIntegers(Integer value, int expected) throws Throwable {
+ integerEnv.put("releaseVersion", value);
+ runTest(integerEnv, expected);
+ }
+
+ @Test(dataProvider="versions")
+ public void testVersions(Version value, int expected) throws Throwable {
+ versionEnv.put("releaseVersion", value);
+ runTest(versionEnv, expected);
+ }
+
+ @Test
+ public void testShortJar() throws Throwable {
+ integerEnv.put("releaseVersion", Integer.valueOf(MAJOR_VERSION));
+ runTest(smruri, integerEnv, MAJOR_VERSION);
+ integerEnv.put("releaseVersion", Integer.valueOf(9));
+ runTest(smruri, integerEnv, 8);
+ }
+
+ /**
+ * Validate that an invalid value for the "releaseVersion" property throws
+ * an {@code IllegalArgumentException}
+ * @param env Zip FS map
+ * @throws Throwable Exception thrown for anything other than the expected
+ * IllegalArgumentException
+ */
+ @Test(dataProvider="invalidVersions")
+ public void testInvalidVersions(Map<String,?> env) throws Throwable {
+ Assert.assertThrows(IllegalArgumentException.class, () ->
+ FileSystems.newFileSystem(Path.of(userdir,
+ "multi-release.jar"), env));
+ }
+
+ // The following tests are for backwards compatibility to validate that
+ // the original property still works
+ @Test(dataProvider="strings")
+ public void testMRStrings(String value, int expected) throws Throwable {
+ stringEnv.clear();
stringEnv.put("multi-release", value);
runTest(stringEnv, expected);
}
@Test(dataProvider="integers")
- public void testIntegers(Integer value, int expected) throws Throwable {
+ public void testMRIntegers(Integer value, int expected) throws Throwable {
+ integerEnv.clear();
integerEnv.put("multi-release", value);
runTest(integerEnv, expected);
}
@Test(dataProvider="versions")
- public void testVersions(Version value, int expected) throws Throwable {
+ public void testMRVersions(Version value, int expected) throws Throwable {
+ versionEnv.clear();
versionEnv.put("multi-release", value);
runTest(versionEnv, expected);
}
- @Test
- public void testShortJar() throws Throwable {
- integerEnv.put("multi-release", Integer.valueOf(MAJOR_VERSION));
- runTest(smruri, integerEnv, MAJOR_VERSION);
- integerEnv.put("multi-release", Integer.valueOf(9));
- runTest(smruri, integerEnv, 8);
- }
-
private void runTest(Map<String,?> env, int expected) throws Throwable {
runTest(mruri, env, expected);
}
@@ -213,7 +264,7 @@
JarBuilder jb = new JarBuilder(jfname);
jb.addAttribute("Multi-Release", "true");
jb.build();
- Map<String,String> env = Map.of("multi-release", "runtime");
+ Map<String,String> env = Map.of("releaseVersion", "runtime");
try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
Assert.assertTrue(true);
}
@@ -228,7 +279,7 @@
creator.buildCustomMultiReleaseJar(fileName, value, Map.of(),
/*addEntries*/true);
- Map<String,String> env = Map.of("multi-release", "runtime");
+ Map<String,String> env = Map.of("releaseVersion", "runtime");
Path filePath = Paths.get(userdir, fileName);
String ssp = filePath.toUri().toString();
URI customJar = new URI("jar", ssp , null);