# HG changeset patch # User sdrach # Date 1468947922 25200 # Node ID 28c2c63fc09fef6a69eaeb9e2f188120bceeef2d # Parent 5f3e350ee13e4345da0508eaa5bcb35aadb35e6e 8157524: Revert JarFile methods "entries" and "stream" to Java 8 behavior Reviewed-by: alanb, psandoz, redestad diff -r 5f3e350ee13e -r 28c2c63fc09f jdk/src/java.base/share/classes/java/lang/module/ModulePath.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Tue Jul 19 16:13:01 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Tue Jul 19 10:05:22 2016 -0700 @@ -52,6 +52,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -420,7 +421,7 @@ // scan the entries in the JAR file to locate the .class and service // configuration file Map> map = - jf.stream() + versionedStream(jf) .map(JarEntry::getName) .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX))) .collect(Collectors.partitioningBy(s -> s.endsWith(".class"), @@ -503,8 +504,21 @@ return mn; } + private Stream versionedStream(JarFile jf) { + if (jf.isMultiRelease()) { + // a stream of JarEntries whose names are base names and whose + // contents are from the corresponding versioned entries in + // a multi-release jar file + return jf.stream().map(JarEntry::getName) + .filter(name -> !name.startsWith("META-INF/versions/")) + .map(jf::getJarEntry); + } else { + return jf.stream(); + } + } + private Set jarPackages(JarFile jf) { - return jf.stream() + return versionedStream(jf) .filter(e -> e.getName().endsWith(".class")) .map(e -> toPackageName(e.getName())) .filter(pkg -> pkg.length() > 0) // module-info diff -r 5f3e350ee13e -r 28c2c63fc09f jdk/src/java.base/share/classes/java/util/jar/JarFile.java --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue Jul 19 16:13:01 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue Jul 19 10:05:22 2016 -0700 @@ -141,7 +141,6 @@ private boolean verify; private final Runtime.Version version; // current version private final int versionMajor; // version.major() - private boolean notVersioned; // legacy constructor called private boolean isMultiRelease; // is jar multi-release? // indicates if Class-Path attribute present @@ -290,7 +289,6 @@ */ public JarFile(File file, boolean verify, int mode) throws IOException { this(file, verify, mode, BASE_VERSION); - this.notVersioned = true; } /** @@ -496,42 +494,14 @@ Iterator { final Enumeration e = JarFile.super.entries(); - ZipEntry ze; public boolean hasNext() { - if (notVersioned) { - return e.hasMoreElements(); - } - if (ze != null) { - return true; - } - return findNext(); - } - - private boolean findNext() { - while (e.hasMoreElements()) { - ZipEntry ze2 = e.nextElement(); - if (!ze2.getName().startsWith(META_INF_VERSIONS)) { - ze = ze2; - return true; - } - } - return false; + return e.hasMoreElements(); } public JarEntry next() { - ZipEntry ze2; - - if (notVersioned) { - ze2 = e.nextElement(); - return new JarFileEntry(ze2.getName(), ze2); - } - if (ze != null || findNext()) { - ze2 = ze; - ze = null; - return new JarFileEntry(ze2); - } - throw new NoSuchElementException(); + ZipEntry ze = e.nextElement(); + return new JarFileEntry(ze.getName(), ze); } public boolean hasMoreElements() { @@ -548,19 +518,7 @@ } /** - * Returns an enumeration of the jar file entries. The set of entries - * returned depends on whether or not the jar file is a multi-release jar - * file, and on the constructor used to create the {@code JarFile}. If the - * jar file is not a multi-release jar file, all entries are returned, - * regardless of how the {@code JarFile} is created. If the constructor - * does not take a {@code Release} argument, all entries are returned. - * If the jar file is a multi-release jar file and the constructor takes a - * {@code Release} argument, then the set of entries returned is equivalent - * to the set of entries that would be returned if the set was built by - * invoking {@link JarFile#getEntry(String)} or - * {@link JarFile#getJarEntry(String)} with the name of each base entry in - * the jar file. A base entry is an entry whose path name does not start - * with "META-INF/versions/". + * Returns an enumeration of the jar file entries. * * @return an enumeration of the jar file entries * @throws IllegalStateException @@ -571,24 +529,26 @@ } /** - * Returns an ordered {@code Stream} over all the jar file entries. + * Returns an ordered {@code Stream} over the jar file entries. * Entries appear in the {@code Stream} in the order they appear in - * the central directory of the jar file. The set of entries - * returned depends on whether or not the jar file is a multi-release jar - * file, and on the constructor used to create the {@code JarFile}. If the - * jar file is not a multi-release jar file, all entries are returned, - * regardless of how the {@code JarFile} is created. If the constructor - * does not take a {@code Release} argument, all entries are returned. - * If the jar file is a multi-release jar file and the constructor takes a - * {@code Release} argument, then the set of entries returned is equivalent - * to the set of entries that would be returned if the set was built by - * invoking {@link JarFile#getEntry(String)} or - * {@link JarFile#getJarEntry(String)} with the name of each base entry in - * the jar file. A base entry is an entry whose path name does not start - * with "META-INF/versions/". + * the central directory of the jar file. + * * @return an ordered {@code Stream} of entries in this jar file * @throws IllegalStateException if the jar file has been closed * @since 1.8 + * + * @apiNote A versioned view of the stream obtained from a {@code JarFile} + * configured to process a multi-release jar file can be created with code + * similar to the following: + *
+     * {@code
+     *     Stream versionedStream(JarFile jf) {
+     *         return jf.stream().map(JarEntry::getName)
+     *                  .filter(name -> !name.startsWith("META-INF/versions/"))
+     *                  .map(jf::getJarEntry);
+     *     }
+     * }
+     * 
*/ public Stream stream() { return StreamSupport.stream(Spliterators.spliterator( diff -r 5f3e350ee13e -r 28c2c63fc09f jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarIterators.java --- a/jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarIterators.java Tue Jul 19 16:13:01 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2015, 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 8132734 8144062 - * @summary Test the extended API and the aliasing additions in JarFile that - * support multi-release jar files - * @library /lib/testlibrary/java/util/jar - * @build Compiler JarBuilder CreateMultiReleaseTestJars - * @run testng MultiReleaseJarIterators - */ - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.stream.Collectors; -import java.util.zip.ZipFile; - -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - - -public class MultiReleaseJarIterators { - - static final int MAJOR_VERSION = Runtime.version().major(); - - String userdir = System.getProperty("user.dir", "."); - File unversioned = new File(userdir, "unversioned.jar"); - File multirelease = new File(userdir, "multi-release.jar"); - Map uvEntries = new HashMap<>(); - Map mrEntries = new HashMap<>(); - Map baseEntries = new HashMap<>(); - Map v9Entries = new HashMap<>(); - Map v10Entries = new HashMap<>(); - - @BeforeClass - public void initialize() throws Exception { - CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); - creator.compileEntries(); - creator.buildUnversionedJar(); - creator.buildMultiReleaseJar(); - - try (JarFile jf = new JarFile(multirelease)) { - for (Enumeration e = jf.entries(); e.hasMoreElements(); ) { - JarEntry je = e.nextElement(); - String name = je.getName(); - mrEntries.put(name, je); - if (name.startsWith("META-INF/versions/")) { - if (name.startsWith("META-INF/versions/9/")) { - v9Entries.put(name.substring(20), je); - } else if (name.startsWith("META-INF/versions/10/")) { - v10Entries.put(name.substring(21), je); - } - } else { - baseEntries.put(name, je); - } - } - } - Assert.assertEquals(mrEntries.size(), 14); - Assert.assertEquals(baseEntries.size(), 6); - Assert.assertEquals(v9Entries.size(), 5); - Assert.assertEquals(v10Entries.size(), 3); - - try (JarFile jf = new JarFile(unversioned)) { - jf.entries().asIterator().forEachRemaining(je -> uvEntries.put(je.getName(), je)); - } - Assert.assertEquals(uvEntries.size(), 6); - } - - @AfterClass - public void close() throws IOException { - Files.delete(unversioned.toPath()); - Files.delete(multirelease.toPath()); - } - - @Test - public void testMultiReleaseJar() throws IOException { - try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ)) { - testEnumeration(jf, mrEntries); - testStream(jf, mrEntries); - } - - try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, JarFile.baseVersion())) { - testEnumeration(jf, baseEntries); - testStream(jf, baseEntries); - } - - try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) { - testEnumeration(jf, v9Entries); - testStream(jf, v9Entries); - } - - try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.version())) { - Map expectedEntries; - switch (MAJOR_VERSION) { - case 9: - expectedEntries = v9Entries; - break; - case 10: // won't get here until JDK 10 - expectedEntries = v10Entries; - break; - default: - expectedEntries = baseEntries; - break; - } - - testEnumeration(jf, expectedEntries); - testStream(jf, expectedEntries); - } - } - - @Test - public void testUnversionedJar() throws IOException { - try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ)) { - testEnumeration(jf, uvEntries); - testStream(jf, uvEntries); - } - - try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, JarFile.baseVersion())) { - testEnumeration(jf, uvEntries); - testStream(jf, uvEntries); - } - - try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) { - testEnumeration(jf, uvEntries); - testStream(jf, uvEntries); - } - - try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.version())) { - testEnumeration(jf, uvEntries); - testStream(jf, uvEntries); - } - } - - private void testEnumeration(JarFile jf, Map expectedEntries) { - Map actualEntries = new HashMap<>(); - for (Enumeration e = jf.entries(); e.hasMoreElements(); ) { - JarEntry je = e.nextElement(); - actualEntries.put(je.getName(), je); - } - - testEntries(jf, actualEntries, expectedEntries); - } - - - private void testStream(JarFile jf, Map expectedEntries) { - Map actualEntries = jf.stream().collect(Collectors.toMap(je -> je.getName(), je -> je)); - - testEntries(jf, actualEntries, expectedEntries); - } - - private void testEntries(JarFile jf, Map actualEntries, Map expectedEntries) { - /* For multi-release jar files constructed with a Release object, - * actualEntries contain versionedEntries that are considered part of the - * public API. They have a 1-1 correspondence with baseEntries, - * so entries that are not part of the public API won't be present, - * i.e. those entries with a name that starts with version/PackagePrivate - * in this particular jar file (multi-release.jar) - */ - - Map entries; - if (expectedEntries == mrEntries) { - Assert.assertEquals(actualEntries.size(), mrEntries.size()); - entries = mrEntries; - } else if (expectedEntries == uvEntries) { - Assert.assertEquals(actualEntries.size(), uvEntries.size()); - entries = uvEntries; - } else { - Assert.assertEquals(actualEntries.size(), baseEntries.size()); // this is correct - entries = baseEntries; - } - - entries.keySet().forEach(name -> { - JarEntry ee = expectedEntries.get(name); - if (ee == null) ee = entries.get(name); - JarEntry ae = actualEntries.get(name); - try { - compare(jf, ae, ee); - } catch (IOException x) { - throw new RuntimeException(x); - } - }); - } - - private void compare(JarFile jf, JarEntry actual, JarEntry expected) throws IOException { - byte[] abytes; - byte[] ebytes; - - try (InputStream is = jf.getInputStream(actual)) { - abytes = is.readAllBytes(); - } - - try (InputStream is = jf.getInputStream(expected)) { - ebytes = is.readAllBytes(); - } - - Assert.assertEquals(abytes, ebytes); - } -}