Skip to content

Commit

Permalink
Merge pull request #4801 from jaikiran/qk-4782
Browse files Browse the repository at this point in the history
Work around JDK bug to produce JAR files with proper CRC32 entries
  • Loading branch information
gsmet authored Oct 23, 2019
2 parents 6600381 + 82ad42b commit a225ca1
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.deployment.pkg.steps;

import static io.quarkus.bootstrap.util.ZipUtils.wrapForJDK8232879;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
Expand Down Expand Up @@ -368,7 +370,7 @@ private void copyCommonContent(FileSystem runnerZipFs, Map<String, List<byte[]>>
for (TransformedClassesBuildItem.TransformedClass i : transformed) {
Path target = runnerZipFs.getPath(i.getFileName());
handleParent(runnerZipFs, i.getFileName(), seen);
try (OutputStream out = Files.newOutputStream(target)) {
try (final OutputStream out = wrapForJDK8232879(Files.newOutputStream(target))) {
out.write(i.getData());
}
seen.put(i.getFileName(), "Current Application");
Expand All @@ -382,7 +384,7 @@ private void copyCommonContent(FileSystem runnerZipFs, Map<String, List<byte[]>>
if (Files.exists(target)) {
continue;
}
try (OutputStream os = Files.newOutputStream(target)) {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(target))) {
os.write(i.getClassData());
}
}
Expand All @@ -396,7 +398,7 @@ private void copyCommonContent(FileSystem runnerZipFs, Map<String, List<byte[]>>
if (i.getName().startsWith("META-INF/services")) {
services.computeIfAbsent(i.getName(), (u) -> new ArrayList<>()).add(i.getClassData());
} else {
try (OutputStream os = Files.newOutputStream(target)) {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(target))) {
os.write(i.getClassData());
}
}
Expand All @@ -405,7 +407,7 @@ private void copyCommonContent(FileSystem runnerZipFs, Map<String, List<byte[]>>
copyFiles(appArchives.getRootArchive().getArchiveRoot(), runnerZipFs, services);

for (Map.Entry<String, List<byte[]>> entry : services.entrySet()) {
try (OutputStream os = Files.newOutputStream(runnerZipFs.getPath(entry.getKey()))) {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(runnerZipFs.getPath(entry.getKey())))) {
for (byte[] i : entry.getValue()) {
os.write(i);
os.write('\n');
Expand Down Expand Up @@ -483,7 +485,7 @@ private void generateManifest(FileSystem runnerZipFs, final String classPath, Pa
}
}
attributes.put(Attributes.Name.MAIN_CLASS, config.mainClass);
try (OutputStream os = Files.newOutputStream(manifestPath)) {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(manifestPath))) {
manifest.write(os);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.bootstrap.util;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.DirectoryStream;
Expand Down Expand Up @@ -168,4 +169,46 @@ public static FileSystem newFileSystem(final Path path) throws IOException {
throw new IOException("Failed to create a new filesystem for " + path, ioe);
}
}

/**
* This is a hack to get past the <a href="https://bugs.openjdk.java.net/browse/JDK-8232879">JDK-8232879</a>
* issue which causes CRC errors when writing out data to (jar) files using ZipFileSystem.
* TODO: Get rid of this method as soon as JDK-8232879 gets fixed and released in a public version
*
* @param original The original outputstream which will be wrapped into a new outputstream
* that delegates to this one.
* @return
*/
public static OutputStream wrapForJDK8232879(final OutputStream original) {
return new OutputStream() {
@Override
public void write(final byte[] b) throws IOException {
original.write(b);
}

@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
original.write(b, off, len);
}

@Override
public void flush() throws IOException {
original.flush();
}

@Override
public void close() throws IOException {
original.close();
}

@Override
public void write(final int b) throws IOException {
// we call the 3 arg write(...) method here, instead
// of the single arg one to bypass the JDK-8232879 issue
final byte[] buf = new byte[1];
buf[0] = (byte) (b & 0xff);
this.write(buf, 0, 1);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.maven.shared.invoker.MavenInvocationException;
import org.assertj.core.util.Arrays;
import org.junit.Assert;
import org.junit.jupiter.api.Test;

import io.quarkus.maven.it.verifier.MavenProcessInvocationResult;
Expand Down Expand Up @@ -79,6 +85,58 @@ public void testCustomPackaging()
jarNames);
}

/**
* Tests that the uber runner jar created by Quarkus has valid CRC entries. The verification
* is pretty trivial and involves opening and closing the ZipEntry entries that are part of the
* runner jar. That internally triggers the CRC checks.
*
* @throws Exception
* @see <a href="https://github.com/quarkusio/quarkus/issues/4782"/>
*/
@Test
public void testRunnerUberJarHasValidCRC() throws Exception {
testDir = initProject("projects/uberjar-check", "projects/project-uberjar-true");

running = new RunningInvoker(testDir, false);
final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"),
Collections.singletonMap("QUARKUS_PACKAGE_TYPES", "uber-jar"));
assertThat(result.getProcess().waitFor()).isEqualTo(0);

final File targetDir = getTargetDir();
assertThat(getNumberOfFilesEndingWith(targetDir, ".jar")).isEqualTo(1);
assertThat(getNumberOfFilesEndingWith(targetDir, ".original")).isEqualTo(1);

final Path runnerJar = targetDir.toPath().resolve("acme-1.0-SNAPSHOT-runner.jar");
Assert.assertTrue("Runner jar " + runnerJar + " is missing", Files.exists(runnerJar));
assertZipEntriesCanBeOpenedAndClosed(runnerJar);
}

/**
* Tests that the runner jar created by Quarkus has valid CRC entries. The verification
* is pretty trivial and involves opening and closing the ZipEntry entries that are part of the
* runner jar. That internally triggers the CRC checks.
*
* @throws Exception
* @see <a href="https://github.com/quarkusio/quarkus/issues/4782"/>
*/
@Test
public void testRunnerJarHasValidCRC() throws Exception {
testDir = initProject("projects/uberjar-check", "projects/project-uberjar-false");

running = new RunningInvoker(testDir, false);
final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"),
Collections.singletonMap("QUARKUS_PACKAGE_TYPES", "thin-jar"));

assertThat(result.getProcess().waitFor()).isEqualTo(0);

final File targetDir = getTargetDir();
assertThat(getNumberOfFilesEndingWith(targetDir, ".jar")).isEqualTo(2);

final Path runnerJar = targetDir.toPath().resolve("acme-1.0-SNAPSHOT-runner.jar");
Assert.assertTrue("Runner jar " + runnerJar + " is missing", Files.exists(runnerJar));
assertZipEntriesCanBeOpenedAndClosed(runnerJar);
}

private int getNumberOfFilesEndingWith(File dir, String suffix) {
final File[] files = dir.listFiles((d, name) -> name.endsWith(suffix));
return files != null ? files.length : 0;
Expand All @@ -87,4 +145,14 @@ private int getNumberOfFilesEndingWith(File dir, String suffix) {
private File getTargetDir() {
return new File(testDir.getAbsoluteFile() + "/target");
}

private void assertZipEntriesCanBeOpenedAndClosed(final Path jar) throws Exception {
try (final InputStream is = Files.newInputStream(jar)) {
final ZipInputStream zis = new ZipInputStream(is);
ZipEntry e = null;
while ((e = zis.getNextEntry()) != null) {
zis.closeEntry();
}
}
}
}

0 comments on commit a225ca1

Please sign in to comment.