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;