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 +