changeset 59219:4c6190f99f3a

8240910: jmod rejects duplicate entries in --class-path jars Reviewed-by: alanb, lancea
author mchung
date Fri, 08 May 2020 08:23:35 -0700
parents dd1034e6350f
children 8cb6bb7de14c
files src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java test/jdk/tools/jmod/JmodTest.java
diffstat 3 files changed, 59 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java	Fri May 08 15:34:14 2020 +0200
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java	Fri May 08 08:23:35 2020 -0700
@@ -34,6 +34,10 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 import jdk.internal.jmod.JmodFile;
@@ -44,6 +48,8 @@
  * Output stream to write to JMOD file
  */
 class JmodOutputStream extends OutputStream implements AutoCloseable {
+    private final Map<Section, Set<String>> entries = new HashMap<>();
+
     /**
      * This method creates (or overrides, if exists) the JMOD file,
      * returning the the output stream to write to the JMOD file.
@@ -110,13 +116,22 @@
         zos.closeEntry();
     }
 
-    private ZipEntry newEntry(Section section, String path) {
+    private ZipEntry newEntry(Section section, String path) throws IOException {
+        if (contains(section, path)) {
+            throw new IOException("duplicate entry: " + path + " in section " + section);
+        }
         String prefix = section.jmodDir();
         String name = Paths.get(prefix, path).toString()
                            .replace(File.separatorChar, '/');
+        entries.get(section).add(path);
         return new ZipEntry(name);
     }
 
+    public boolean contains(Section section, String path) {
+        Set<String> set = entries.computeIfAbsent(section, k -> new HashSet<>());
+        return set.contains(path);
+    }
+
     @Override
     public void write(int b) throws IOException {
         zos.write(b);
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Fri May 08 15:34:14 2020 +0200
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Fri May 08 08:23:35 2020 -0700
@@ -757,21 +757,17 @@
                         throws IOException
                     {
                         Path relPath = path.relativize(file);
-                        if (relPath.toString().equals(MODULE_INFO)
-                                && !Section.CLASSES.equals(section))
-                            warning("warn.ignore.entry", MODULE_INFO, section);
-
-                        if (!relPath.toString().equals(MODULE_INFO)
-                                && !matches(relPath, excludes)) {
-                            try (InputStream in = Files.newInputStream(file)) {
-                                out.writeEntry(in, section, relPath.toString());
-                            } catch (IOException x) {
-                                if (x.getMessage().contains("duplicate entry")) {
-                                    warning("warn.ignore.duplicate.entry",
-                                            relPath.toString(), section);
-                                    return FileVisitResult.CONTINUE;
+                        String name = relPath.toString();
+                        if (name.equals(MODULE_INFO)) {
+                            if (!Section.CLASSES.equals(section))
+                                warning("warn.ignore.entry", name, section);
+                        } else if (!matches(relPath, excludes)) {
+                            if (out.contains(section, name)) {
+                                warning("warn.ignore.duplicate.entry", name, section);
+                            } else {
+                                try (InputStream in = Files.newInputStream(file)) {
+                                    out.writeEntry(in, section, name);
                                 }
-                                throw x;
                             }
                         }
                         return FileVisitResult.CONTINUE;
@@ -808,7 +804,14 @@
             public boolean test(JarEntry je) {
                 String name = je.getName();
                 // ## no support for excludes. Is it really needed?
-                return !name.endsWith(MODULE_INFO) && !je.isDirectory();
+                if (name.endsWith(MODULE_INFO) || je.isDirectory()) {
+                    return false;
+                }
+                if (out.contains(Section.CLASSES, name)) {
+                    warning("warn.ignore.duplicate.entry", name, Section.CLASSES);
+                    return false;
+                }
+                return true;
             }
         }
     }
--- a/test/jdk/tools/jmod/JmodTest.java	Fri May 08 15:34:14 2020 +0200
+++ b/test/jdk/tools/jmod/JmodTest.java	Fri May 08 08:23:35 2020 -0700
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2020, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8142968 8166568 8166286 8170618 8168149
+ * @bug 8142968 8166568 8166286 8170618 8168149 8240910
  * @summary Basic test for jmod
  * @library /test/lib
  * @modules jdk.compiler
@@ -60,6 +60,10 @@
         .orElseThrow(() ->
             new RuntimeException("jmod tool not found")
         );
+    static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
 
     static final String TEST_SRC = System.getProperty("test.src", ".");
     static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
@@ -437,6 +441,25 @@
     }
 
     @Test
+    public void testDuplicateEntriesFromJarFile() throws IOException {
+        String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
+        Path jar = Paths.get("foo.jar");
+        Path jmod = MODS_DIR.resolve("testDuplicates.jmod");
+        FileUtils.deleteFileIfExistsWithRetry(jar);
+        FileUtils.deleteFileIfExistsWithRetry(jmod);
+        // create JAR file
+        assertTrue(JAR_TOOL.run(System.out, System.err, "cf", jar.toString(), "-C", cp, ".") == 0);
+
+        jmod("create",
+             "--class-path", jar.toString() + pathSeparator + jar.toString(),
+             jmod.toString())
+             .assertSuccess()
+             .resultChecker(r ->
+                 assertContains(r.output, "Warning: ignoring duplicate entry")
+             );
+    }
+
+    @Test
     public void testIgnoreModuleInfoInOtherSections() throws IOException {
         Path jmod = MODS_DIR.resolve("testIgnoreModuleInfoInOtherSections.jmod");
         FileUtils.deleteFileIfExistsWithRetry(jmod);