This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new e23b7e1166 [filesystem] Fix nested META-INF/versions in filesystem 
impl jars (#8913)
e23b7e1166 is described below

commit e23b7e116640cafcf4cbc371d044c9ea1a25f1c9
Author: Dapeng Sun(孙大鹏) <[email protected]>
AuthorDate: Thu Jul 30 11:59:20 2026 +0800

    [filesystem] Fix nested META-INF/versions in filesystem impl jars (#8913)
---
 paimon-filesystems/paimon-azure-impl/pom.xml       |  7 +++-
 paimon-filesystems/paimon-cosn-impl/pom.xml        |  7 +++-
 paimon-filesystems/paimon-gs-impl/pom.xml          |  7 +++-
 paimon-filesystems/paimon-oss-impl/pom.xml         |  7 +++-
 paimon-filesystems/paimon-s3-impl/pom.xml          |  7 +++-
 .../tools/ci/licensecheck/JarFileChecker.java      | 40 ++++++++++++---------
 .../tools/ci/licensecheck/JarFileCheckerTest.java  | 41 ++++++++++++++++++++++
 7 files changed, 94 insertions(+), 22 deletions(-)

diff --git a/paimon-filesystems/paimon-azure-impl/pom.xml 
b/paimon-filesystems/paimon-azure-impl/pom.xml
index a4cfcfd181..828fca5929 100644
--- a/paimon-filesystems/paimon-azure-impl/pom.xml
+++ b/paimon-filesystems/paimon-azure-impl/pom.xml
@@ -146,6 +146,12 @@
                                     <fileset 
dir="${project.build.directory}/temporary">
                                         <include name="*"/>
                                     </fileset>
+                                    <patternset>
+                                        <!-- exclude the jar's own META-INF, 
otherwise multi-release
+                                             metadata gets nested under 
META-INF/versions/11 and tools
+                                             like JaCoCo fail on duplicate 
class names -->
+                                        <exclude name="META-INF/**"/>
+                                    </patternset>
                                 </unzip>
                             </target>
                         </configuration>
@@ -177,7 +183,6 @@
                                         <exclude>mime.types</exclude>
                                         <exclude>mozilla/**</exclude>
                                         <exclude>META-INF/maven/**</exclude>
-                                        
<exclude>META-INF/versions/11/META-INF/maven/**</exclude>
                                         <exclude>META-INF/LICENSE.txt</exclude>
                                     </excludes>
                                 </filter>
diff --git a/paimon-filesystems/paimon-cosn-impl/pom.xml 
b/paimon-filesystems/paimon-cosn-impl/pom.xml
index 838e1a62a1..df94305a2b 100644
--- a/paimon-filesystems/paimon-cosn-impl/pom.xml
+++ b/paimon-filesystems/paimon-cosn-impl/pom.xml
@@ -155,6 +155,12 @@
                                     <fileset 
dir="${project.build.directory}/temporary">
                                         <include name="*"/>
                                     </fileset>
+                                    <patternset>
+                                        <!-- exclude the jar's own META-INF, 
otherwise multi-release
+                                             metadata gets nested under 
META-INF/versions/11 and tools
+                                             like JaCoCo fail on duplicate 
class names -->
+                                        <exclude name="META-INF/**"/>
+                                    </patternset>
                                 </unzip>
                             </target>
                         </configuration>
@@ -187,7 +193,6 @@
                                         <exclude>mime.types</exclude>
                                         <exclude>mozilla/**</exclude>
                                         <exclude>META-INF/maven/**</exclude>
-                                        
<exclude>META-INF/versions/11/META-INF/maven/**</exclude>
                                         <exclude>META-INF/LICENSE.txt</exclude>
                                     </excludes>
                                 </filter>
diff --git a/paimon-filesystems/paimon-gs-impl/pom.xml 
b/paimon-filesystems/paimon-gs-impl/pom.xml
index 5bcc99520f..41c00859d1 100644
--- a/paimon-filesystems/paimon-gs-impl/pom.xml
+++ b/paimon-filesystems/paimon-gs-impl/pom.xml
@@ -272,6 +272,12 @@
                                     <fileset 
dir="${project.build.directory}/temporary">
                                         <include name="*"/>
                                     </fileset>
+                                    <patternset>
+                                        <!-- exclude the jar's own META-INF, 
otherwise multi-release
+                                             metadata gets nested under 
META-INF/versions/11 and tools
+                                             like JaCoCo fail on duplicate 
class names -->
+                                        <exclude name="META-INF/**"/>
+                                    </patternset>
                                 </unzip>
                             </target>
                         </configuration>
@@ -303,7 +309,6 @@
                                         <exclude>mime.types</exclude>
                                         <exclude>mozilla/**</exclude>
                                         <exclude>META-INF/maven/**</exclude>
-                                        
<exclude>META-INF/versions/11/META-INF/maven/**</exclude>
                                         <exclude>META-INF/LICENSE.txt</exclude>
                                     </excludes>
                                 </filter>
diff --git a/paimon-filesystems/paimon-oss-impl/pom.xml 
b/paimon-filesystems/paimon-oss-impl/pom.xml
index 9a0a28f45a..308cfe3f6f 100644
--- a/paimon-filesystems/paimon-oss-impl/pom.xml
+++ b/paimon-filesystems/paimon-oss-impl/pom.xml
@@ -162,6 +162,12 @@
                                     <fileset 
dir="${project.build.directory}/temporary">
                                         <include name="*"/>
                                     </fileset>
+                                    <patternset>
+                                        <!-- exclude the jar's own META-INF, 
otherwise multi-release
+                                             metadata gets nested under 
META-INF/versions/11 and tools
+                                             like JaCoCo fail on duplicate 
class names -->
+                                        <exclude name="META-INF/**"/>
+                                    </patternset>
                                 </unzip>
                             </target>
                         </configuration>
@@ -194,7 +200,6 @@
                                         <exclude>mime.types</exclude>
                                         <exclude>mozilla/**</exclude>
                                         <exclude>META-INF/maven/**</exclude>
-                                        
<exclude>META-INF/versions/11/META-INF/maven/**</exclude>
                                         <exclude>META-INF/LICENSE.txt</exclude>
                                     </excludes>
                                 </filter>
diff --git a/paimon-filesystems/paimon-s3-impl/pom.xml 
b/paimon-filesystems/paimon-s3-impl/pom.xml
index 886af231f4..761b0f6d90 100644
--- a/paimon-filesystems/paimon-s3-impl/pom.xml
+++ b/paimon-filesystems/paimon-s3-impl/pom.xml
@@ -260,6 +260,12 @@
                                     <fileset 
dir="${project.build.directory}/temporary">
                                         <include name="*"/>
                                     </fileset>
+                                    <patternset>
+                                        <!-- exclude the jar's own META-INF, 
otherwise multi-release
+                                             metadata gets nested under 
META-INF/versions/11 and tools
+                                             like JaCoCo fail on duplicate 
class names -->
+                                        <exclude name="META-INF/**"/>
+                                    </patternset>
                                 </unzip>
                             </target>
                         </configuration>
@@ -291,7 +297,6 @@
                                         <exclude>mime.types</exclude>
                                         <exclude>mozilla/**</exclude>
                                         <exclude>META-INF/maven/**</exclude>
-                                        
<exclude>META-INF/versions/11/META-INF/maven/**</exclude>
                                         <exclude>META-INF/LICENSE.txt</exclude>
                                     </excludes>
                                 </filter>
diff --git 
a/tools/ci/paimon-ci-tools/src/main/java/org/apache/paimon/tools/ci/licensecheck/JarFileChecker.java
 
b/tools/ci/paimon-ci-tools/src/main/java/org/apache/paimon/tools/ci/licensecheck/JarFileChecker.java
index ac5785ec10..06f33273e2 100644
--- 
a/tools/ci/paimon-ci-tools/src/main/java/org/apache/paimon/tools/ci/licensecheck/JarFileChecker.java
+++ 
b/tools/ci/paimon-ci-tools/src/main/java/org/apache/paimon/tools/ci/licensecheck/JarFileChecker.java
@@ -81,6 +81,8 @@ public class JarFileChecker {
                     getNumLicenseFilesOutsideMetaInfDirectory(file, 
fileSystem.getPath("/"));
 
             numSevereIssues += getFilesWithIncompatibleLicenses(file, 
fileSystem.getPath("/"));
+
+            numSevereIssues += getNumNestedMetaInfDirectories(file, 
fileSystem.getPath("/"));
         }
         return numSevereIssues;
     }
@@ -215,7 +217,6 @@ public class JarFileChecker {
                             path ->
                                     !pathStartsWith(
                                             path, 
"/META-INF/maven/javax.xml.bind/jaxb-api"))
-                    .filter(path -> !isJavaxManifest(jar, path))
                     // dual-licensed under GPL 2 and EPL 2.0
                     // contained in sql-avro-confluent-registry
                     .filter(path -> !pathStartsWith(path, 
"/org/glassfish/jersey/internal"))
@@ -306,6 +307,27 @@ public class JarFileChecker {
         }
     }
 
+    private static int getNumNestedMetaInfDirectories(Path jar, Path jarRoot) 
throws IOException {
+        // a nested META-INF below META-INF/versions indicates that another 
jar was
+        // unpacked/shaded as-is into the multi-release directory; the nested 
multi-release
+        // metadata is not resolvable at runtime and breaks tools scanning the 
unpacked
+        // classes (e.g., JaCoCo fails on duplicate class names)
+        final Pattern nestedMetaInf = 
Pattern.compile("^/?META-INF/versions/[^/]+/META-INF/?$");
+        try (Stream<Path> files = Files.walk(jarRoot)) {
+            final List<String> filesWithIssues =
+                    files.map(Path::toString)
+                            .filter(path -> 
nestedMetaInf.matcher(path).matches())
+                            .collect(Collectors.toList());
+            for (String fileWithIssue : filesWithIssues) {
+                LOG.error(
+                        "Jar file {} contains a nested META-INF directory in a 
multi-release directory: {}",
+                        jar,
+                        fileWithIssue);
+            }
+            return filesWithIssues.size();
+        }
+    }
+
     private static String getFileName(Path path) {
         return path.getFileName().toString().toLowerCase();
     }
@@ -314,26 +336,10 @@ public class JarFileChecker {
         return file.startsWith(file.getFileSystem().getPath(path));
     }
 
-    private static boolean equals(Path file, String path) {
-        return file.equals(file.getFileSystem().getPath(path));
-    }
-
     private static boolean isNoClassFile(Path file) {
         return !getFileName(file).endsWith(".class");
     }
 
-    private static boolean isJavaxManifest(Path jar, Path 
potentialManifestFile) {
-        try {
-            return equals(potentialManifestFile, 
"/META-INF/versions/11/META-INF/MANIFEST.MF")
-                    && 
readFile(potentialManifestFile).contains("Specification-Title: jaxb-api");
-        } catch (IOException e) {
-            throw new RuntimeException(
-                    String.format(
-                            "Error while reading file %s from jar %s.", 
potentialManifestFile, jar),
-                    e);
-        }
-    }
-
     private static String readFile(Path file) throws IOException {
         return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
     }
diff --git 
a/tools/ci/paimon-ci-tools/src/test/java/org/apache/paimon/tools/ci/licensecheck/JarFileCheckerTest.java
 
b/tools/ci/paimon-ci-tools/src/test/java/org/apache/paimon/tools/ci/licensecheck/JarFileCheckerTest.java
index 2b3165b0c8..7ace8b7a05 100644
--- 
a/tools/ci/paimon-ci-tools/src/test/java/org/apache/paimon/tools/ci/licensecheck/JarFileCheckerTest.java
+++ 
b/tools/ci/paimon-ci-tools/src/test/java/org/apache/paimon/tools/ci/licensecheck/JarFileCheckerTest.java
@@ -298,6 +298,47 @@ class JarFileCheckerTest {
                 .isEqualTo(0);
     }
 
+    @Test
+    void testRejectedOnNestedMetaInfInMultiReleaseDirectory(@TempDir Path 
tempDir)
+            throws Exception {
+        assertThat(
+                        JarFileChecker.checkJar(
+                                createJar(
+                                        tempDir,
+                                        Entry.fileEntry(VALID_NOTICE_CONTENTS, 
VALID_NOTICE_PATH),
+                                        
Entry.fileEntry(VALID_LICENSE_CONTENTS, VALID_LICENSE_PATH),
+                                        Entry.fileEntry(
+                                                "Manifest-Version: 1.0",
+                                                Arrays.asList(
+                                                        "META-INF",
+                                                        "versions",
+                                                        "11",
+                                                        "META-INF",
+                                                        "MANIFEST.MF")))))
+                .isEqualTo(1);
+    }
+
+    @Test
+    void testAcceptedOnRegularMultiReleaseContent(@TempDir Path tempDir) 
throws Exception {
+        assertThat(
+                        JarFileChecker.checkJar(
+                                createJar(
+                                        tempDir,
+                                        Entry.fileEntry(VALID_NOTICE_CONTENTS, 
VALID_NOTICE_PATH),
+                                        
Entry.fileEntry(VALID_LICENSE_CONTENTS, VALID_LICENSE_PATH),
+                                        Entry.fileEntry(
+                                                "some java 11 resource",
+                                                Arrays.asList(
+                                                        "META-INF",
+                                                        "versions",
+                                                        "11",
+                                                        "javax",
+                                                        "xml",
+                                                        "bind",
+                                                        
"Messages.properties")))))
+                .isEqualTo(0);
+    }
+
     private static class Entry {
         final String contents;
         final List<String> path;

Reply via email to