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>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>

View File

@ -18,6 +18,7 @@ package io.spring.initializr.web.project;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@ -25,6 +26,9 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
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.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.AgentId;
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.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.HttpHeaders;
@ -221,26 +228,8 @@ public class MainController extends AbstractInitializrController {
@ResponseBody
public ResponseEntity<byte[]> springZip(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
File dir = result.getRootDirectory().toFile();
Path archive = this.projectGenerationInvoker.createDistributionFile(result.getRootDirectory(), ".zip");
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();
Path archive = createArchive(result, "zip", ZipArchiveOutputStream::new, ZipArchiveEntry::new,
(entry, mode) -> entry.setUnixMode(mode));
return upload(archive, result.getRootDirectory(), generateFileName(request, "zip"), "application/zip");
}
@ -248,31 +237,63 @@ public class MainController extends AbstractInitializrController {
@ResponseBody
public ResponseEntity<byte[]> springTgz(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
File dir = result.getRootDirectory().toFile();
Path download = this.projectGenerationInvoker.createDistributionFile(result.getRootDirectory(), ".tar.gz");
String wrapperScript = getWrapperScript(result.getProjectDescription());
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"),
Path archive = createArchive(result, "tar.gz", this::createTarArchiveOutputStream, TarArchiveEntry::new,
(entry, mode) -> entry.setMode(mode));
return upload(archive, result.getRootDirectory(), generateFileName(request, "tar.gz"),
"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) {
String candidate = (StringUtils.hasText(request.getArtifactId()) ? request.getArtifactId()
: this.metadataProvider.get().getArtifactId().getContent());

View File

@ -16,15 +16,19 @@
package io.spring.initializr.web;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode;
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.mapper.InitializrMetadataVersion;
import io.spring.initializr.web.support.InitializrMetadataUpdateStrategy;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.ExecTask;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.ant.taskdefs.Untar;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
@ -212,59 +217,45 @@ public abstract class AbstractInitializrIntegrationTests {
}
}
private void untar(Path archiveFile, Path project) {
if (!runningOnWindows()) {
createProjectDir(project);
ExecTask execTask = new ExecTask();
execTask.setProject(new Project());
execTask.setExecutable("tar");
execTask.createArg().setValue("-C");
execTask.createArg().setValue(project.toFile().getAbsolutePath());
execTask.createArg().setValue("-xf");
execTask.createArg().setValue(archiveFile.toFile().getAbsolutePath());
execTask.execute();
}
else {
Untar expand = new Untar();
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 untar(Path archiveFile, Path project) throws IOException {
try (TarArchiveInputStream input = new TarArchiveInputStream(
new GzipCompressorInputStream(Files.newInputStream(archiveFile)))) {
TarArchiveEntry entry = null;
while ((entry = input.getNextTarEntry()) != null) {
Path path = project.resolve(entry.getName());
if (entry.isDirectory()) {
Files.createDirectories(path);
}
else {
Files.createDirectories(path.getParent());
Files.write(path, StreamUtils.copyToByteArray(input));
}
Files.setPosixFilePermissions(path, getPosixFilePermissions(entry.getMode()));
}
}
}
private void createProjectDir(Path project) {
ExecTask execTask = new ExecTask();
execTask.setProject(new Project());
execTask.setExecutable("mkdir");
execTask.createArg().setValue(project.toFile().getAbsolutePath());
execTask.execute();
}
private void unzip(Path archiveFile, Path project) {
if (!runningOnWindows()) {
ExecTask execTask = new ExecTask();
execTask.setProject(new Project());
execTask.setExecutable("unzip");
execTask.createArg().setValue(archiveFile.toFile().getAbsolutePath());
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 void unzip(Path archiveFile, Path project) throws IOException {
try (ZipFile zip = new ZipFile(archiveFile.toFile())) {
Enumeration<? extends ZipArchiveEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
Path path = project.resolve(entry.getName());
if (entry.isDirectory()) {
Files.createDirectories(path);
}
else {
Files.createDirectories(path.getParent());
Files.write(path, StreamUtils.copyToByteArray(zip.getInputStream(entry)));
}
Files.setPosixFilePermissions(path, getPosixFilePermissions(entry.getUnixMode()));
}
}
}
private boolean runningOnWindows() {
return File.separatorChar == '\\';
private Set<PosixFilePermission> getPosixFilePermissions(int unixMode) {
return Arrays.stream(BitMaskFilePermission.values()).filter((permission) -> permission.permitted(unixMode))
.map(BitMaskFilePermission::getFilePermission).collect(Collectors.toSet());
}
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>
<main.basedir>${basedir}</main.basedir>
<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>
<maven.version>3.6.1</maven.version>
<maven-resolver.version>1.3.3</maven-resolver.version>
@ -105,6 +106,11 @@
<version>${revision}</version>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
@ -146,17 +152,6 @@
<artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version>
</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>
</dependencyManagement>