Migrate from Ant to Commons Compress

Closes gh-951
This commit is contained in:
Andy Wilkinson 2019-07-11 11:43:52 +01:00
parent aad9ce07d7
commit b769c8d6a0
4 changed files with 158 additions and 112 deletions

View File

@ -41,8 +41,8 @@
<artifactId>spring-hateoas</artifactId> <artifactId>spring-hateoas</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.ant</groupId> <groupId>org.apache.commons</groupId>
<artifactId>ant</artifactId> <artifactId>commons-compress</artifactId>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -18,6 +18,7 @@ package io.spring.initializr.web.project;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -25,6 +26,9 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import io.spring.initializr.generator.buildsystem.BuildSystem; import io.spring.initializr.generator.buildsystem.BuildSystem;
import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem; import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem;
@ -43,13 +47,16 @@ import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import io.spring.initializr.web.support.Agent; import io.spring.initializr.web.support.Agent;
import io.spring.initializr.web.support.Agent.AgentId; import io.spring.initializr.web.support.Agent.AgentId;
import io.spring.initializr.web.support.CommandLineHelpGenerator; import io.spring.initializr.web.support.CommandLineHelpGenerator;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Tar;
import org.apache.tools.ant.taskdefs.Zip;
import org.apache.tools.ant.types.TarFileSet;
import org.apache.tools.ant.types.ZipFileSet;
import org.springframework.http.CacheControl; import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -221,26 +228,8 @@ public class MainController extends AbstractInitializrController {
@ResponseBody @ResponseBody
public ResponseEntity<byte[]> springZip(ProjectRequest request) throws IOException { public ResponseEntity<byte[]> springZip(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request); ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
File dir = result.getRootDirectory().toFile(); Path archive = createArchive(result, "zip", ZipArchiveOutputStream::new, ZipArchiveEntry::new,
Path archive = this.projectGenerationInvoker.createDistributionFile(result.getRootDirectory(), ".zip"); (entry, mode) -> entry.setUnixMode(mode));
String wrapperScript = getWrapperScript(result.getProjectDescription());
Zip zip = new Zip();
zip.setProject(new Project());
zip.setDefaultexcludes(false);
ZipFileSet set = new ZipFileSet();
set.setDir(dir);
set.setFileMode("755");
set.createInclude().setName(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
set = new ZipFileSet();
set.setDir(dir);
set.setIncludes("**,");
set.createExclude().setName(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
zip.setDestFile(archive.toFile().getCanonicalFile());
zip.execute();
return upload(archive, result.getRootDirectory(), generateFileName(request, "zip"), "application/zip"); return upload(archive, result.getRootDirectory(), generateFileName(request, "zip"), "application/zip");
} }
@ -248,31 +237,63 @@ public class MainController extends AbstractInitializrController {
@ResponseBody @ResponseBody
public ResponseEntity<byte[]> springTgz(ProjectRequest request) throws IOException { public ResponseEntity<byte[]> springTgz(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request); ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
File dir = result.getRootDirectory().toFile(); Path archive = createArchive(result, "tar.gz", this::createTarArchiveOutputStream, TarArchiveEntry::new,
Path download = this.projectGenerationInvoker.createDistributionFile(result.getRootDirectory(), ".tar.gz"); (entry, mode) -> entry.setMode(mode));
String wrapperScript = getWrapperScript(result.getProjectDescription()); return upload(archive, result.getRootDirectory(), generateFileName(request, "tar.gz"),
Tar zip = new Tar();
zip.setProject(new Project());
zip.setDefaultexcludes(false);
TarFileSet set = zip.createTarFileSet();
set.setDir(dir);
set.setFileMode("755");
set.createInclude().setName(wrapperScript);
set.setDefaultexcludes(false);
set = zip.createTarFileSet();
set.setDir(dir);
set.setIncludes("**,");
set.createExclude().setName(wrapperScript);
set.setDefaultexcludes(false);
zip.setDestFile(download.toFile().getCanonicalFile());
Tar.TarCompressionMethod method = new Tar.TarCompressionMethod();
method.setValue("gzip");
zip.setCompression(method);
zip.execute();
return upload(download, result.getRootDirectory(), generateFileName(request, "tar.gz"),
"application/x-compress"); "application/x-compress");
} }
private TarArchiveOutputStream createTarArchiveOutputStream(OutputStream output) {
try {
return new TarArchiveOutputStream(new GzipCompressorOutputStream(output));
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
private <T extends ArchiveEntry> Path createArchive(ProjectGenerationResult result, String fileExtension,
Function<OutputStream, ? extends ArchiveOutputStream> archiveOutputStream,
BiFunction<File, String, T> archiveEntry, BiConsumer<T, Integer> setMode) throws IOException {
Path archive = this.projectGenerationInvoker.createDistributionFile(result.getRootDirectory(),
"." + fileExtension);
String wrapperScript = getWrapperScript(result.getProjectDescription());
try (ArchiveOutputStream output = archiveOutputStream.apply(Files.newOutputStream(archive))) {
Files.walk(result.getRootDirectory()).filter((path) -> !result.getRootDirectory().equals(path))
.forEach((path) -> {
try {
String entryName = getEntryName(result.getRootDirectory(), path);
T entry = archiveEntry.apply(path.toFile(), entryName);
setMode.accept(entry, getUnixMode(wrapperScript, entryName, path));
output.putArchiveEntry(entry);
if (!Files.isDirectory(path)) {
Files.copy(path, output);
}
output.closeArchiveEntry();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
});
}
return archive;
}
private String getEntryName(Path root, Path path) {
String entryName = root.relativize(path).toString().replace('\\', '/');
if (Files.isDirectory(path)) {
entryName += "/";
}
return entryName;
}
private int getUnixMode(String wrapperScript, String entryName, Path path) {
if (Files.isDirectory(path)) {
return UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
}
return UnixStat.FILE_FLAG | (entryName.equals(wrapperScript) ? 0755 : UnixStat.DEFAULT_FILE_PERM);
}
private String generateFileName(ProjectRequest request, String extension) { private String generateFileName(ProjectRequest request, String extension) {
String candidate = (StringUtils.hasText(request.getArtifactId()) ? request.getArtifactId() String candidate = (StringUtils.hasText(request.getArtifactId()) ? request.getArtifactId()
: this.metadataProvider.get().getArtifactId().getContent()); : this.metadataProvider.get().getArtifactId().getContent());

View File

@ -16,15 +16,19 @@
package io.spring.initializr.web; package io.spring.initializr.web;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -32,10 +36,11 @@ import io.spring.initializr.generator.spring.test.ProjectAssert;
import io.spring.initializr.web.AbstractInitializrIntegrationTests.Config; import io.spring.initializr.web.AbstractInitializrIntegrationTests.Config;
import io.spring.initializr.web.mapper.InitializrMetadataVersion; import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import io.spring.initializr.web.support.InitializrMetadataUpdateStrategy; import io.spring.initializr.web.support.InitializrMetadataUpdateStrategy;
import org.apache.tools.ant.Project; import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.tools.ant.taskdefs.ExecTask; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.tools.ant.taskdefs.Expand; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.tools.ant.taskdefs.Untar; import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -212,59 +217,45 @@ public abstract class AbstractInitializrIntegrationTests {
} }
} }
private void untar(Path archiveFile, Path project) { private void untar(Path archiveFile, Path project) throws IOException {
if (!runningOnWindows()) { try (TarArchiveInputStream input = new TarArchiveInputStream(
createProjectDir(project); new GzipCompressorInputStream(Files.newInputStream(archiveFile)))) {
ExecTask execTask = new ExecTask(); TarArchiveEntry entry = null;
execTask.setProject(new Project()); while ((entry = input.getNextTarEntry()) != null) {
execTask.setExecutable("tar"); Path path = project.resolve(entry.getName());
execTask.createArg().setValue("-C"); if (entry.isDirectory()) {
execTask.createArg().setValue(project.toFile().getAbsolutePath()); Files.createDirectories(path);
execTask.createArg().setValue("-xf"); }
execTask.createArg().setValue(archiveFile.toFile().getAbsolutePath()); else {
execTask.execute(); Files.createDirectories(path.getParent());
} Files.write(path, StreamUtils.copyToByteArray(input));
else { }
Untar expand = new Untar(); Files.setPosixFilePermissions(path, getPosixFilePermissions(entry.getMode()));
expand.setProject(new Project()); }
expand.setDest(project.toFile());
expand.setSrc(archiveFile.toFile());
Untar.UntarCompressionMethod method = new Untar.UntarCompressionMethod();
method.setValue("gzip");
expand.setCompression(method);
expand.execute();
} }
} }
private void createProjectDir(Path project) { private void unzip(Path archiveFile, Path project) throws IOException {
ExecTask execTask = new ExecTask(); try (ZipFile zip = new ZipFile(archiveFile.toFile())) {
execTask.setProject(new Project()); Enumeration<? extends ZipArchiveEntry> entries = zip.getEntries();
execTask.setExecutable("mkdir"); while (entries.hasMoreElements()) {
execTask.createArg().setValue(project.toFile().getAbsolutePath()); ZipArchiveEntry entry = entries.nextElement();
execTask.execute(); Path path = project.resolve(entry.getName());
} if (entry.isDirectory()) {
Files.createDirectories(path);
private void unzip(Path archiveFile, Path project) { }
if (!runningOnWindows()) { else {
ExecTask execTask = new ExecTask(); Files.createDirectories(path.getParent());
execTask.setProject(new Project()); Files.write(path, StreamUtils.copyToByteArray(zip.getInputStream(entry)));
execTask.setExecutable("unzip"); }
execTask.createArg().setValue(archiveFile.toFile().getAbsolutePath()); Files.setPosixFilePermissions(path, getPosixFilePermissions(entry.getUnixMode()));
execTask.createArg().setValue("-d"); }
execTask.createArg().setValue(project.toFile().getAbsolutePath());
execTask.execute();
}
else {
Expand expand = new Expand();
expand.setProject(new Project());
expand.setDest(project.toFile());
expand.setSrc(archiveFile.toFile());
expand.execute();
} }
} }
private boolean runningOnWindows() { private Set<PosixFilePermission> getPosixFilePermissions(int unixMode) {
return File.separatorChar == '\\'; return Arrays.stream(BitMaskFilePermission.values()).filter((permission) -> permission.permitted(unixMode))
.map(BitMaskFilePermission::getFilePermission).collect(Collectors.toSet());
} }
protected Path writeArchive(byte[] body) throws IOException { protected Path writeArchive(byte[] body) throws IOException {
@ -320,4 +311,43 @@ public abstract class AbstractInitializrIntegrationTests {
} }
private enum BitMaskFilePermission {
OWNER_READ(0400),
OWNER_WRITE(0200),
OWNER_EXECUTE(0100),
GROUP_READ(0040),
GROUP_WRITE(0020),
GROUP_EXECUTE(0010),
OTHERS_READ(0004),
OTHERS_WRITE(0002),
OTHERS_EXECUTE(0001);
private int mask;
private PosixFilePermission filePermission;
BitMaskFilePermission(int mask) {
this.mask = mask;
this.filePermission = PosixFilePermission.valueOf(this.name());
}
boolean permitted(int unixMode) {
return (this.mask & unixMode) == this.mask;
}
PosixFilePermission getFilePermission() {
return this.filePermission;
}
}
} }

17
pom.xml
View File

@ -41,6 +41,7 @@
<java.version>1.8</java.version> <java.version>1.8</java.version>
<main.basedir>${basedir}</main.basedir> <main.basedir>${basedir}</main.basedir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<commons-compress.version>1.18</commons-compress.version>
<junit-jupiter.version>5.4.2</junit-jupiter.version> <junit-jupiter.version>5.4.2</junit-jupiter.version>
<maven.version>3.6.1</maven.version> <maven.version>3.6.1</maven.version>
<maven-resolver.version>1.3.3</maven-resolver.version> <maven-resolver.version>1.3.3</maven-resolver.version>
@ -105,6 +106,11 @@
<version>${revision}</version> <version>${revision}</version>
<type>test-jar</type> <type>test-jar</type>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.maven</groupId> <groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId> <artifactId>maven-resolver-provider</artifactId>
@ -146,17 +152,6 @@
<artifactId>android-json</artifactId> <artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version> <version>0.0.20131108.vaadin1</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>