diff --git a/io/build-release-8 b/io/build-release-8
new file mode 100644
index 00000000..e69de29b
diff --git a/io/build-test-java10 b/io/build-test-java10
new file mode 100644
index 00000000..e69de29b
diff --git a/io/build-test-java8 b/io/build-test-java8
new file mode 100644
index 00000000..e69de29b
diff --git a/io/build-test-java9 b/io/build-test-java9
new file mode 100644
index 00000000..e69de29b
diff --git a/io/pom.xml b/io/pom.xml
index 5c085c8c..af78bec7 100644
--- a/io/pom.xml
+++ b/io/pom.xml
@@ -13,6 +13,36 @@
smallrye-common-io
SmallRye Common: IO
+
+
+ org.jboss.shrinkwrap
+ shrinkwrap-depchain
+ pom
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 3.0.0-M5
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
coverage
diff --git a/io/src/main/java/io/smallrye/common/io/jar/JarEntries.java b/io/src/main/java/io/smallrye/common/io/jar/JarEntries.java
new file mode 100644
index 00000000..470f4b23
--- /dev/null
+++ b/io/src/main/java/io/smallrye/common/io/jar/JarEntries.java
@@ -0,0 +1,13 @@
+package io.smallrye.common.io.jar;
+
+import java.util.jar.JarEntry;
+
+public class JarEntries {
+ /**
+ * Returns the real name of this {@link JarEntry}. On Java 8, it returns the {@link JarEntry#getName()}
+ * On Java 10+, a getRealName() method was added
+ */
+ public static String getRealName(JarEntry jarEntry) {
+ return jarEntry.getName();
+ }
+}
diff --git a/io/src/main/java/io/smallrye/common/io/JarFiles.java b/io/src/main/java/io/smallrye/common/io/jar/JarFiles.java
similarity index 66%
rename from io/src/main/java/io/smallrye/common/io/JarFiles.java
rename to io/src/main/java/io/smallrye/common/io/jar/JarFiles.java
index 369dc6d1..69b12b7a 100644
--- a/io/src/main/java/io/smallrye/common/io/JarFiles.java
+++ b/io/src/main/java/io/smallrye/common/io/jar/JarFiles.java
@@ -1,8 +1,10 @@
-package io.smallrye.common.io;
+package io.smallrye.common.io.jar;
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.util.jar.JarFile;
+import java.util.jar.Manifest;
/**
* Java 8 variant of a JDK-specific class for working with {@code JarFile}s.
@@ -39,4 +41,21 @@ public static JarFile create(File file) throws IOException {
public static JarFile create(File file, boolean verify) throws IOException {
return new JarFile(file, verify);
}
+
+ /**
+ * Returns true if this {@link JarFile} is a multi-release jar. On Java 8 this is done by browsing the manifest.
+ * On Java 9+, there is a isMultiRelease method
+ */
+ public static boolean isMultiRelease(JarFile jarFile) {
+ String value = null;
+ try {
+ Manifest manifest = jarFile.getManifest();
+ if (manifest != null) {
+ value = manifest.getMainAttributes().getValue("Multi-Release");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Cannot read manifest attributes", e);
+ }
+ return Boolean.parseBoolean(value);
+ }
}
diff --git a/io/src/main/java10/io/smallrye/common/io/jar/JarEntries.java b/io/src/main/java10/io/smallrye/common/io/jar/JarEntries.java
new file mode 100644
index 00000000..d054c7ac
--- /dev/null
+++ b/io/src/main/java10/io/smallrye/common/io/jar/JarEntries.java
@@ -0,0 +1,13 @@
+package io.smallrye.common.io.jar;
+
+import java.util.jar.JarEntry;
+
+public class JarEntries {
+ /**
+ * Returns the real name of this {@link JarEntry}. On Java 8, it returns the {@link JarEntry#getName()}
+ * On Java 10+, a getRealName() method was added
+ */
+ public static String getRealName(JarEntry jarEntry) {
+ return jarEntry.getRealName();
+ }
+}
diff --git a/io/src/main/java9/io/smallrye/common/io/jar/JarEntries.java b/io/src/main/java9/io/smallrye/common/io/jar/JarEntries.java
new file mode 100644
index 00000000..3697d6ba
--- /dev/null
+++ b/io/src/main/java9/io/smallrye/common/io/jar/JarEntries.java
@@ -0,0 +1,36 @@
+package io.smallrye.common.io.jar;
+
+import java.lang.reflect.Method;
+import java.util.jar.JarEntry;
+
+public class JarEntries {
+
+ private static final Method REAL_NAME_METHOD;
+
+ static {
+ Method method;
+ try {
+ method = Class.forName("java.util.jar.JarFile$JarFileEntry").getDeclaredMethod("realName");
+ method.setAccessible(true);
+ } catch (NoSuchMethodException | ClassNotFoundException e) {
+ method = null;
+ }
+ REAL_NAME_METHOD = method;
+ }
+
+ /**
+ * Returns the real name of this {@link JarEntry}. On Java 8, it returns the {@link JarEntry#getName()}
+ * On Java 10+, a getRealName() method was added
+ */
+ public static String getRealName(JarEntry jarEntry) {
+ if (REAL_NAME_METHOD != null) {
+ try {
+ return REAL_NAME_METHOD.invoke(jarEntry).toString();
+ } catch (Exception e) {
+ // This should never happen
+ }
+ }
+ // As a safe net, fallback to the original value
+ return jarEntry.getName();
+ }
+}
diff --git a/io/src/main/java9/io/smallrye/common/io/JarFiles.java b/io/src/main/java9/io/smallrye/common/io/jar/JarFiles.java
similarity index 83%
rename from io/src/main/java9/io/smallrye/common/io/JarFiles.java
rename to io/src/main/java9/io/smallrye/common/io/jar/JarFiles.java
index 70839cca..6d6fb733 100644
--- a/io/src/main/java9/io/smallrye/common/io/JarFiles.java
+++ b/io/src/main/java9/io/smallrye/common/io/jar/JarFiles.java
@@ -1,4 +1,4 @@
-package io.smallrye.common.io;
+package io.smallrye.common.io.jar;
import java.io.File;
import java.io.IOException;
@@ -40,4 +40,13 @@ public static JarFile create(File file) throws IOException {
public static JarFile create(File file, boolean verify) throws IOException {
return new JarFile(file, verify, ZipFile.OPEN_READ, JarFile.runtimeVersion());
}
+
+ /**
+ * Returns true if this {@link JarFile} is a multi-release jar. On Java 8 this is done by browsing the manifest.
+ * On Java 9+, there is a isMultiRelease method
+ */
+ public static boolean isMultiRelease(JarFile jarFile) {
+ return jarFile.isMultiRelease();
+ }
+
}
diff --git a/io/src/test/java/io/smallrye/common/io/jar/JarEntriesIT.java b/io/src/test/java/io/smallrye/common/io/jar/JarEntriesIT.java
new file mode 100644
index 00000000..d4811ba5
--- /dev/null
+++ b/io/src/test/java/io/smallrye/common/io/jar/JarEntriesIT.java
@@ -0,0 +1,36 @@
+package io.smallrye.common.io.jar;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnJre;
+import org.junit.jupiter.api.condition.EnabledOnJre;
+import org.junit.jupiter.api.condition.JRE;
+
+// This needs to be run as an integration-test
+public class JarEntriesIT {
+
+ @Test
+ @DisabledOnJre(JRE.JAVA_8)
+ public void shouldUseMultiReleaseName() throws IOException {
+ File tmpFile = JarGenerator.generateMultiReleaseJar();
+ JarFile jarFile = JarFiles.create(tmpFile);
+ JarEntry jarEntry = jarFile.getJarEntry("foo.txt");
+ assertEquals("META-INF/versions/9/foo.txt", JarEntries.getRealName(jarEntry));
+ }
+
+ @Test
+ @EnabledOnJre(JRE.JAVA_8)
+ public void shouldUseName() throws IOException {
+ File tmpFile = JarGenerator.generateMultiReleaseJar();
+ JarFile jarFile = JarFiles.create(tmpFile);
+ JarEntry jarEntry = jarFile.getJarEntry("foo.txt");
+ assertEquals("foo.txt", JarEntries.getRealName(jarEntry));
+ }
+
+}
diff --git a/io/src/test/java/io/smallrye/common/io/jar/JarFilesTest.java b/io/src/test/java/io/smallrye/common/io/jar/JarFilesTest.java
new file mode 100644
index 00000000..d5ab8128
--- /dev/null
+++ b/io/src/test/java/io/smallrye/common/io/jar/JarFilesTest.java
@@ -0,0 +1,28 @@
+package io.smallrye.common.io.jar;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.jar.JarFile;
+
+import org.junit.jupiter.api.Test;
+
+public class JarFilesTest {
+
+ @Test
+ public void shouldReadPlainJars() throws IOException {
+ File tmpFile = JarGenerator.generatePlainJar();
+ JarFile jarFile = JarFiles.create(tmpFile);
+ assertFalse(JarFiles.isMultiRelease(jarFile));
+ }
+
+ @Test
+ public void shouldReadMultiReleaseJars() throws IOException {
+ File tmpFile = JarGenerator.generateMultiReleaseJar();
+ JarFile jarFile = JarFiles.create(tmpFile);
+ assertTrue(JarFiles.isMultiRelease(jarFile));
+ }
+
+}
diff --git a/io/src/test/java/io/smallrye/common/io/jar/JarGenerator.java b/io/src/test/java/io/smallrye/common/io/jar/JarGenerator.java
new file mode 100644
index 00000000..d3e2baba
--- /dev/null
+++ b/io/src/test/java/io/smallrye/common/io/jar/JarGenerator.java
@@ -0,0 +1,29 @@
+package io.smallrye.common.io.jar;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+
+public class JarGenerator {
+ public static File generatePlainJar() throws IOException {
+ JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
+ .addAsResource(new StringAsset("Original"), "foo.txt");
+ File tmpFile = File.createTempFile("tmp", ".tmp");
+ jar.as(ZipExporter.class).exportTo(tmpFile, true);
+ return tmpFile;
+ }
+
+ public static File generateMultiReleaseJar() throws IOException {
+ JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
+ .addAsManifestResource(new StringAsset("Multi-Release: true\n"), "MANIFEST.MF")
+ .addAsResource(new StringAsset("Original"), "foo.txt")
+ .addAsManifestResource(new StringAsset("MultiRelease"), "versions/9/foo.txt");
+ File tmpFile = File.createTempFile("tmp", ".tmp");
+ jar.as(ZipExporter.class).exportTo(tmpFile, true);
+ return tmpFile;
+ }
+}
diff --git a/pom.xml b/pom.xml
index c6f2596d..a039d0e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,13 @@
+
+ org.jboss.shrinkwrap
+ shrinkwrap-bom
+ 1.2.6
+ pom
+ import
+