8066272: pack200 must support Multi-Release Jars
authorksrini
Mon, 28 Sep 2015 08:42:06 -0700
changeset 34323 8e42083c7bc8
parent 33875 c1c71107d45f
child 34324 19a97dc4cb26
8066272: pack200 must support Multi-Release Jars Reviewed-by: jrose, sdrach
jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java
jdk/test/tools/pack200/MultiRelease.java
--- a/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Fri Nov 20 19:26:16 2015 +0100
+++ b/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Mon Sep 28 08:42:06 2015 -0700
@@ -272,22 +272,6 @@
 
         // (Done collecting options from props.)
 
-        boolean isClassFile(String name) {
-            if (!name.endsWith(".class"))  return false;
-            for (String prefix = name; ; ) {
-                if (passFiles.contains(prefix))  return false;
-                int chop = prefix.lastIndexOf('/');
-                if (chop < 0)  break;
-                prefix = prefix.substring(0, chop);
-            }
-            return true;
-        }
-
-        boolean isMetaInfFile(String name) {
-            return name.startsWith("/" + Utils.METAINF) ||
-                        name.startsWith(Utils.METAINF);
-        }
-
         // Get a new package, based on the old one.
         private void makeNextPackage() {
             pkg.reset();
@@ -332,6 +316,29 @@
             InFile(JarEntry je) {
                 this(null, je);
             }
+            boolean isClassFile() {
+                if (!name.endsWith(".class")) {
+                    return false;
+                }
+                for (String prefix = name;;) {
+                    if (passFiles.contains(prefix)) {
+                        return false;
+                    }
+                    int chop = prefix.lastIndexOf('/');
+                    if (chop < 0) {
+                        break;
+                    }
+                    prefix = prefix.substring(0, chop);
+                }
+                return true;
+            }
+            boolean isMetaInfFile() {
+                return name.startsWith("/" + Utils.METAINF)
+                        || name.startsWith(Utils.METAINF);
+            }
+            boolean mustProcess() {
+                return !isMetaInfFile() && isClassFile();
+            }
             long getInputLength() {
                 long len = (je != null)? je.getSize(): f.length();
                 assert(len >= 0) : this+".len="+len;
@@ -391,7 +398,7 @@
                 Package.File file = null;
                 // (5078608) : discount the resource files in META-INF
                 // from segment computation.
-                long inflen = (isMetaInfFile(name))
+                long inflen = (inFile.isMetaInfFile())
                               ? 0L
                               : inFile.getInputLength();
 
@@ -406,7 +413,7 @@
 
                 assert(je.isDirectory() == name.endsWith("/"));
 
-                if (isClassFile(name)) {
+                if (inFile.mustProcess()) {
                     file = readClass(name, bits.getInputStream());
                 }
                 if (file == null) {
@@ -429,7 +436,7 @@
             for (InFile inFile : inFiles) {
                 String name      = inFile.name;
                 // (5078608) : discount the resource files completely from segmenting
-                long inflen = (isMetaInfFile(name))
+                long inflen = (inFile.isMetaInfFile())
                                ? 0L
                                : inFile.getInputLength() ;
                 if ((segmentSize += inflen) > segmentLimit) {
@@ -447,7 +454,7 @@
                 if (verbose > 1)
                     Utils.log.fine("Reading " + name);
                 Package.File file = null;
-                if (isClassFile(name)) {
+                if (inFile.mustProcess()) {
                     file = readClass(name, strm);
                     if (file == null) {
                         strm.close();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/MultiRelease.java	Mon Sep 28 08:42:06 2015 -0700
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2015, 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).
+ *
+ r 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 java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * @test
+ * @bug 8066272
+ * @summary tests a simple multi-versioned jar file
+ * @compile -XDignore.symbol.file Utils.java MultiRelease.java
+ * @run main MultiRelease
+ * @author ksrini
+ */
+
+public class MultiRelease {
+    private static final File cwd = new File(".");
+    private static int pass = 0;
+    private static int fail = 0;
+    // specify alternate name via arguments to verify
+    // if permanent fix works
+
+    private static final String PropKey = "pack200.MultiRelease.META-INF";
+    private static final String MetaInfName = System.getProperty(PropKey, "META-INF");
+
+    public static void main(String... args) throws Exception {
+        new MultiRelease().run();
+    }
+
+    void run() throws Exception {
+        List<TestCase> testCases = new ArrayList<>();
+        testCases.add(new TestCase1());
+        testCases.add(new TestCase2());
+        for (TestCase tc : testCases) {
+            tc.run();
+        }
+        if (fail > 0) {
+            throw new Exception(fail + "/" + testCases.size() + " tests fails");
+        } else {
+            System.out.println("All tests(" + pass + ") passes");
+        }
+    }
+
+    /*
+     * An abstract class to eliminate test boiler plating.
+     */
+    static abstract class TestCase {
+        final File tcwd;
+        final File metaInfDir;
+        final File versionsDir;
+        final File manifestFile;
+
+        TestCase(String directory) throws IOException {
+            System.out.println("initializing directories");
+            tcwd = new File(cwd, directory);
+            metaInfDir = mkdir(new File(tcwd, MetaInfName));
+            versionsDir = mkdir(new File(metaInfDir, "versions"));
+            manifestFile = new File(tcwd, "manifest.tmp");
+            List<String> scratch = new ArrayList<>();
+            scratch.add("Multi-Release: true");
+            Utils.createFile(manifestFile, scratch);
+        }
+
+        File mkdir(File f) throws IOException {
+            if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite()) {
+                return f;
+            }
+            if (!f.mkdirs()) {
+               throw new IOException("mkdirs failed: " + f.getAbsolutePath());
+            }
+            return f;
+        }
+
+        abstract void emitClassFiles() throws Exception;
+
+        void run() {
+            try {
+                emitClassFiles();
+                // jar the file up
+                File testFile = new File(tcwd, "test" + Utils.JAR_FILE_EXT);
+                Utils.jar("cvfm",
+                        testFile.getAbsolutePath(),
+                        manifestFile.getAbsolutePath(),
+                        "-C",
+                        tcwd.getAbsolutePath(),
+                        ".");
+                File outFile = new File(tcwd, "test-repacked" + Utils.JAR_FILE_EXT);
+                List<String> cmdsList = new ArrayList<>();
+
+                cmdsList.add(Utils.getPack200Cmd());
+                cmdsList.add("-J-ea");
+                cmdsList.add("-J-esa");
+                cmdsList.add("-v");
+                cmdsList.add("--repack");
+                cmdsList.add(outFile.getAbsolutePath());
+                cmdsList.add(testFile.getAbsolutePath());
+                List<String> output = Utils.runExec(cmdsList);
+                Utils.doCompareVerify(testFile.getAbsoluteFile(), outFile.getAbsoluteFile());
+                pass++;
+            } catch (Exception e) {
+                e.printStackTrace(System.err);
+                fail++;
+            }
+        }
+    }
+
+    static class TestCase1 extends TestCase {
+        private TestCase1(String directory) throws IOException {
+            super(directory);
+        }
+
+        public TestCase1() throws Exception {
+            this("case1");
+        }
+
+        @Override
+        void emitClassFiles() throws Exception {
+            emitClassFile("");
+            emitClassFile("7");
+            emitClassFile("8");
+            emitClassFile("9");
+        }
+
+        /*
+         * Adds different variants of types
+         */
+        void emitClassFile(String version) throws IOException {
+            final File outDir = mkdir(version.isEmpty()
+                    ? tcwd
+                    : new File(versionsDir, version));
+
+            final File srcDir = mkdir(version.isEmpty()
+                            ? new File(tcwd, "src")
+                            : new File(new File(versionsDir, version), "src"));
+
+            final String fname = "Foo";
+            final File srcFile = new File(srcDir, fname + Utils.JAVA_FILE_EXT);
+            List<String> scratch = new ArrayList<>();
+
+            scratch.add("package pkg;");
+            switch (version) {
+                case "7":
+                    scratch.add("public class Foo {");
+                    scratch.add("public static final class Bar {}");
+                    break;
+                case "8":
+                    scratch.add("public abstract class Foo {");
+                    scratch.add("public final class Bar {}");
+                    break;
+                case "9":
+                    scratch.add("public interface Foo {");
+                    scratch.add("public final class Bar {}");
+                    break;
+                default:
+                    scratch.add("public class Foo {");
+                    scratch.add("public final class Bar {}");
+                    break;
+            }
+            scratch.add("}");
+
+            Utils.createFile(srcFile, scratch);
+            Utils.compiler("-d",
+                    outDir.getAbsolutePath(),
+                    srcFile.getAbsolutePath());
+        }
+    }
+
+    static class TestCase2 extends TestCase {
+        private TestCase2(String directory) throws IOException {
+            super(directory);
+        }
+
+        TestCase2() throws Exception {
+            this("case2");
+        }
+
+        @Override
+        void emitClassFiles() throws Exception {
+            emitClassFile("");
+            emitClassFile("8");
+        }
+
+        /*
+         * Adds different variants of types and tries to invoke an
+         * interface or concrete method defined by them.
+         */
+        void emitClassFile(String version) throws IOException {
+
+            final File outDir = mkdir(version.isEmpty()
+                    ? tcwd
+                    : new File(versionsDir, version));
+
+            final File srcDir = mkdir(version.isEmpty()
+                    ? new File(tcwd, "src")
+                    : new File(new File(versionsDir, version), "src"));
+
+            List<String> scratch = new ArrayList<>();
+            final String fname1 = "Ab";
+            final File srcFile1 = new File(srcDir, fname1 + Utils.JAVA_FILE_EXT);
+
+            final String fname2 = "AbNormal";
+            final File srcFile2 = new File(srcDir, fname2 + Utils.JAVA_FILE_EXT);
+            switch (version) {
+                case "8":
+                    scratch.clear();
+                    scratch.add("import java.io.IOException;");
+                    scratch.add("public interface " + fname1 + "{");
+                    scratch.add("    public abstract void close() throws IOException ;");
+                    scratch.add("}");
+                    Utils.createFile(srcFile1, scratch);
+                    break;
+                default:
+                    scratch.clear();
+                    scratch.add("import java.io.IOException;");
+                    scratch.add("public abstract class " + fname1 + "{");
+                    scratch.add("    public abstract void close() throws IOException ;");
+                    scratch.add("}");
+                    Utils.createFile(srcFile1, scratch);
+            }
+
+            scratch.clear();
+            scratch.add("import java.io.IOException;");
+            scratch.add("public class " + fname2 + "{");
+            scratch.add("    public void doSomething(Ab ab) throws IOException {");
+            scratch.add("       ab.close();");
+            scratch.add("    }");
+            scratch.add("}");
+
+            Utils.createFile(srcFile2, scratch);
+            Utils.compiler("-d",
+                    outDir.getAbsolutePath(),
+                    srcFile1.getAbsolutePath(),
+                    srcFile2.getAbsolutePath());
+        }
+    }
+}