Skip to content

Commit 94051ac

Browse files
committed
Add support for ZIP64 format
This change update introduces usage of Apache Commons Compress to support jars in ZIP64 format. Previous plugin version had a hard limit of 64K entries in the executable jar. After this change, limit for entries is 2^64-1 files which should be enough for most of the cases.
1 parent b0570cc commit 94051ac

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ To use it, add a plugin to your pom like
7878

7979
Changes:
8080

81+
2.0.0 - support ZIP64 format
82+
8183
1.4.0 - require Java 7, change code to use JDK7 APIs
8284
- Support Windows
8385
- Don't suppress errors

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
<groupId>org.skife.maven</groupId>
2424
<artifactId>really-executable-jar-maven-plugin</artifactId>
25-
<version>1.4.2-SNAPSHOT</version>
25+
<version>2.0.0-SNAPSHOT</version>
2626
<packaging>maven-plugin</packaging>
2727

2828
<name>Really Executable Jar Maven Plugin</name>
@@ -87,6 +87,12 @@
8787
<version>${dep.plexus-utils.version}</version>
8888
</dependency>
8989

90+
<dependency>
91+
<groupId>org.apache.commons</groupId>
92+
<artifactId>commons-compress</artifactId>
93+
<version>1.22</version>
94+
</dependency>
95+
9096
<dependency>
9197
<groupId>org.apache.maven</groupId>
9298
<artifactId>maven-artifact</artifactId>

src/main/java/org/skife/waffles/ReallyExecutableJarMojo.java

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
*/
1414
package org.skife.waffles;
1515

16+
import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
17+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
18+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntryPredicate;
19+
import org.apache.commons.compress.archivers.zip.ZipFile;
1620
import org.apache.maven.artifact.Artifact;
1721
import org.apache.maven.plugin.AbstractMojo;
1822
import org.apache.maven.plugin.MojoExecutionException;
@@ -25,10 +29,11 @@
2529
import org.codehaus.plexus.util.FileUtils;
2630
import org.codehaus.plexus.util.IOUtil;
2731

32+
import java.io.ByteArrayOutputStream;
2833
import java.io.File;
29-
import java.io.FileOutputStream;
3034
import java.io.IOException;
3135
import java.io.InputStream;
36+
import java.net.URI;
3237
import java.net.URL;
3338
import java.net.URLClassLoader;
3439
import java.nio.file.Files;
@@ -37,6 +42,12 @@
3742
import java.util.ArrayList;
3843
import java.util.List;
3944

45+
import static java.lang.String.format;
46+
import static java.nio.charset.StandardCharsets.US_ASCII;
47+
import static java.nio.charset.StandardCharsets.UTF_8;
48+
import static java.nio.file.Files.readAllBytes;
49+
import static org.apache.commons.compress.archivers.zip.Zip64Mode.AlwaysWithCompatibility;
50+
4051
/**
4152
* Make an artifact generated by the build really executable. The resulting artifact
4253
* can be run directly from the command line (Java must be installed and in the
@@ -162,39 +173,52 @@ private void makeExecutable(File file)
162173
{
163174
getLog().debug("Making " + file.getAbsolutePath() + " executable");
164175

165-
Path original = Paths.get(file.getAbsolutePath() + ".rx-orig");
176+
Path original = Paths.get(file.getAbsolutePath() + ".original");
166177
Files.move(file.toPath(), original);
167-
try (final FileOutputStream out = new FileOutputStream(file);
168-
final InputStream in = Files.newInputStream(original)) {
178+
try (JarArchiveOutputStream jar = new JarArchiveOutputStream(Files.newOutputStream(file.toPath()))) {
179+
jar.writePreamble(getPreamble(original.toUri()));
180+
jar.setUseZip64(AlwaysWithCompatibility);
181+
jar.setEncoding(UTF_8.name());
182+
183+
try (ZipFile zip = new ZipFile(original.toFile())) {
184+
zip.copyRawEntries(jar, zipArchiveEntry -> true);
185+
}
186+
jar.flush();
187+
jar.finish();
188+
}
189+
finally {
190+
Files.deleteIfExists(original);
191+
}
192+
193+
if (!file.setExecutable(true, false)) {
194+
throw new MojoExecutionException(format("Could not make JAR [%s] executable", file.getAbsolutePath()));
195+
}
196+
getLog().info(format("Successfully made JAR [%s] executable", file.getAbsolutePath()));
197+
}
169198

199+
private byte[] getPreamble(URI uri) throws MojoExecutionException
200+
{
201+
try
202+
{
170203
if (scriptFile == null) {
171-
out.write(("#!/bin/sh\n\nexec java " + flags + " -jar \"$0\" \"$@\"\n\n").getBytes("ASCII"));
204+
return ("#!/bin/sh\n\nexec java " + flags + " -jar \"$0\" \"$@\"\n\n").getBytes(UTF_8);
172205
}
173206
else if (Files.exists(Paths.get(scriptFile))) {
174-
getLog().debug(String.format("Loading file[%s] from filesystem", scriptFile));
175-
176-
byte[] script = Files.readAllBytes(Paths.get(scriptFile));
177-
out.write(script);
178-
out.write(new byte[]{'\n', '\n'});
207+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
208+
outputStream.write(readAllBytes(Paths.get(scriptFile)));
209+
outputStream.write("\n\n".getBytes(US_ASCII));
210+
return outputStream.toByteArray();
179211
}
180-
else {
181-
getLog().debug(String.format("Loading file[%s] from jar[%s]", scriptFile, original));
182-
183-
try (final URLClassLoader loader = new URLClassLoader(new URL[]{original.toUri().toURL()}, null);
184-
final InputStream scriptIn = loader.getResourceAsStream(scriptFile)) {
185212

186-
out.write(IOUtil.toString(scriptIn).getBytes("ASCII"));
187-
out.write("\n\n".getBytes("ASCII"));
188-
}
213+
try (final URLClassLoader loader = new URLClassLoader(new URL[]{uri.toURL()}, null); final InputStream scriptIn = loader.getResourceAsStream(scriptFile)) {
214+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
215+
outputStream.write(IOUtil.toString(scriptIn).getBytes(UTF_8));
216+
outputStream.write("\n\n".getBytes(UTF_8));
217+
return outputStream.toByteArray();
189218
}
190-
IOUtil.copy(in, out);
191219
}
192-
finally {
193-
Files.deleteIfExists(original);
220+
catch (IOException e) {
221+
throw new MojoExecutionException(e.getMessage());
194222
}
195-
196-
file.setExecutable(true, false);
197-
198-
getLog().info(String.format("Successfully made JAR [%s] executable", file.getAbsolutePath()));
199223
}
200224
}

0 commit comments

Comments
 (0)