Skip to content

Commit

Permalink
Initial impl of unarchiving
Browse files Browse the repository at this point in the history
  • Loading branch information
jjlauer committed Dec 12, 2024
1 parent 475077a commit 859f51a
Show file tree
Hide file tree
Showing 17 changed files with 483 additions and 1 deletion.
88 changes: 88 additions & 0 deletions blaze-archive/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<artifactId>blaze-archive</artifactId>
<packaging>jar</packaging>

<parent>
<groupId>com.fizzed</groupId>
<artifactId>blaze</artifactId>
<version>1.6.2-SNAPSHOT</version>
</parent>

<properties>
<main.java.package>com.fizzed.blaze.archive</main.java.package>
</properties>

<dependencies>

<dependency>
<groupId>com.fizzed</groupId>
<artifactId>blaze-core</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.27.1</version>
</dependency>

<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.10</version>
</dependency>

<dependency>
<groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId>
<version>1.5.6-4</version>
</dependency>

<!-- apache unfortunately uses commons-logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>

<!-- testing -->

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fizzed</groupId>
<artifactId>crux-util</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.fizzed.blaze.archive;

public class ArchiveFormat {

final private String compressMethod;
final private String archiveMethod;
final private String[] extensions;

public ArchiveFormat(String archiveMethod, String compressMethod, String... extensions) {
this.compressMethod = compressMethod;
this.archiveMethod = archiveMethod;
this.extensions = extensions;
}

public String getCompressMethod() {
return compressMethod;
}

public String getArchiveMethod() {
return archiveMethod;
}

public String[] getExtensions() {
return extensions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fizzed.blaze.archive;

import java.util.List;

import static java.util.Arrays.asList;

public class ArchiveFormats {

static public final List<ArchiveFormat> ALL = asList(
new ArchiveFormat("zip", null, ".zip"),
new ArchiveFormat("tar", null, ".tar"),
new ArchiveFormat("tar", "gz", ".tar.gz", ".tgz"),
new ArchiveFormat("tar", "bzip2", ".tar.bz2"),
new ArchiveFormat("tar", "xz", ".tar.xz"),
new ArchiveFormat("tar", "zstd", ".tar.zst"),
new ArchiveFormat(null, "gz", ".gz")
);

static public ArchiveFormat detectByFileName(String fileName) {
final String n = fileName.toLowerCase();

// find the longest matching extension
String matchedExtension = null;
ArchiveFormat matchedFormat = null;

for (ArchiveFormat af : ALL) {
for (String ext : af.getExtensions()) {
if (n.endsWith(ext) && (matchedExtension == null || ext.length() > matchedExtension.length())) {
matchedExtension = ext;
matchedFormat = af;
}
}
}

return matchedFormat;
}

}
209 changes: 209 additions & 0 deletions blaze-archive/src/main/java/com/fizzed/blaze/archive/Unarchive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright 2015 Fizzed, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fizzed.blaze.archive;

import com.fizzed.blaze.Context;
import com.fizzed.blaze.core.*;
import com.fizzed.blaze.util.ObjectHelper;
import com.fizzed.blaze.util.Timer;
import com.fizzed.blaze.util.VerboseLogger;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.compressors.CompressorException;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.commons.compress.compressors.FileNameUtil;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;

public class Unarchive extends Action<Unarchive.Result,Void> implements VerbosityMixin<Unarchive> {

static public class Result extends com.fizzed.blaze.core.Result<Unarchive,Void,Result> {

private int dirsCreated;
private int filesCopied;
private int filesOverwritten;

Result(Unarchive action, Void value) {
super(action, value);
}

public int getDirsCreated() {
return dirsCreated;
}

public int getFilesCopied() {
return filesCopied;
}

public int getFilesOverwritten() {
return filesOverwritten;
}

}

private final VerboseLogger log;
private final Path source;
private Path target;
//private boolean force;
private Verbosity verbosity;

public Unarchive(Context context, Path source) {
super(context);
this.log = new VerboseLogger(this);
this.source = source;
//this.force = false;
}

public VerboseLogger getVerboseLogger() {
return this.log;
}

public Unarchive target(String path) {
ObjectHelper.requireNonNull(path, "path cannot be null");
return this.target(Paths.get(path));
}

public Unarchive target(File path) {
ObjectHelper.requireNonNull(path, "path cannot be null");
return this.target(path.toPath());
}

public Unarchive target(Path path) {
ObjectHelper.requireNonNull(path, "path cannot be null");
this.target = path;
return this;
}

/*public Unarchive force() {
this.force = true;
return this;
}
public Unarchive force(boolean force) {
this.force = force;
return this;
}*/

@Override
protected Unarchive.Result doRun() throws BlazeException {
/*if (this.sources.isEmpty() && !this.force) {
throw new BlazeException("Copy requires at least 1 source path (and force is disabled)");
}*/

// the source must all exist (we should check this first before we do anything)
if (!Files.exists(this.source)) {
throw new FileNotFoundException("Source file " + source + " not found");
}

// target is current directory by default, or the provided target
final Path destDir = this.target != null ? this.target : Paths.get(".");

if (!Files.exists(destDir)) {
log.debug("Creating target directory: {}", destDir);
try {
Files.createDirectories(destDir);
} catch (IOException e) {
throw new BlazeException("Unable to create target directory", e);
}
}


final Result result = new Result(this, null);
final Timer timer = new Timer();

final ArchiveFormat archiveFormat = ArchiveFormats.detectByFileName(this.source.getFileName().toString());

if (archiveFormat == null) {
throw new BlazeException("Unable to detect archive format (or its unsupported)");
}

log.debug("Unarchiving {} -> {}", this.source, this.target);
log.debug("Detected archive format: archiveMethod={}, compressMethod={}", archiveFormat.getArchiveMethod(), archiveFormat.getCompressMethod());

try (InputStream fin = Files.newInputStream(this.source)) {
// we need it buffered so we can auto-detect the format with mark/reset
try (InputStream bin = new BufferedInputStream(fin)) {
// is this file compressed?
InputStream uncompressedIn = bin;
if (archiveFormat.getCompressMethod() != null) {
try {
uncompressedIn = CompressorStreamFactory.getSingleton().createCompressorInputStream(archiveFormat.getCompressMethod(), bin);
} catch (CompressorException e) {
throw new BlazeException("Unable to uncompress source", e);
}
}

try {
// is this file archived?
if (archiveFormat.getArchiveMethod() != null) {
try {
ArchiveInputStream<? extends ArchiveEntry> ais = ArchiveStreamFactory.DEFAULT.createArchiveInputStream(archiveFormat.getArchiveMethod(), uncompressedIn);

ArchiveEntry entry = ais.getNextEntry();
while (entry != null) {
final Path file = entry.resolveIn(destDir);

if (entry.isDirectory()) {
Files.createDirectories(file);
} else {
log.debug(entry.getName());
Files.createDirectories(file.getParent());
Files.copy(ais, file, StandardCopyOption.REPLACE_EXISTING);
}

entry = ais.getNextEntry();
}
} catch (ArchiveException e) {
throw new BlazeException("Unable to unarchive source", e);
}
}
} finally {
if (uncompressedIn != null) {
uncompressedIn.close();
}
}
}



/*ArchiveInputStream ain = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.ZIP, is);
ZipArchiveEntry entry = (ZipArchiveEntry) in.getNextEntry();
OutputStream out = Files.newOutputStream(dir.toPath().resolve(entry.getName()));
IOUtils.copy(in, out);
out.close();
in.close();final InputStream is = Files.newInputStream(input.toPath());
ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.ZIP, is);
ZipArchiveEntry entry = (ZipArchiveEntry) in.getNextEntry();
OutputStream out = Files.newOutputStream(dir.toPath().resolve(entry.getName()));
IOUtils.copy(in, out);
out.close();
in.close();*/
} catch (IOException e) {
throw new BlazeException("Unable to copy", e);
}

//log.debug("Copied {} files, overwrote {} files, created {} dirs (in {})", result.filesCopied, result.filesOverwritten, result.dirsCreated, timer);

return new Unarchive.Result(this, null);
}

}
Loading

0 comments on commit 859f51a

Please sign in to comment.