8231093: Document the ZIP FS properties noCompression and releaseVersion
authorlancea
Sun, 06 Oct 2019 13:08:58 -0400
changeset 58467 72ef2c0faf47
parent 58465 ff45c1bf8129
child 58468 97cd0aa39787
8231093: Document the ZIP FS properties noCompression and releaseVersion Reviewed-by: clanger, martin, alanb
src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java
src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java
src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java
src/jdk.zipfs/share/classes/module-info.java
test/jdk/jdk/nio/zipfs/CompressionModeTest.java
test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java
--- 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}&lt;{@link java.nio.file.attribute.PosixFilePermission PosixFilePermission}&gt;<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);