Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/mkr/archivefs/archive/Archive.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.mkr.archivefs.archive;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
Expand All @@ -8,7 +9,7 @@
* An archive is package of directories and files and their associated metadata. Classes implementing this interface
* provide non-sequential ("random") access to the contents of an archive.
*/
public interface Archive {
public interface Archive extends Closeable {

/**
* Returns metadata items for all entries in the archive
Expand Down
16 changes: 3 additions & 13 deletions src/main/java/io/mkr/archivefs/archive/ArchiveFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
Expand Down Expand Up @@ -66,26 +65,17 @@ public static boolean supportedFromStream(String mime) {
* @param mimeType The mime type to create the archive for
* @param file the {@link File}
* @return the {@link Archive} or null if the type is not supported
* @throws IOException If creating the archive fails
*/
public Archive archiveForFile(String mimeType, final File file) {
public Archive archiveForFile(String mimeType, final File file) throws IOException {
switch (mimeType) {
case MIME_ZIP:
case MIME_JAR:
return new CCZipFileArchive(file);
case MIME_RAR:
return new RarFileArchive(file);
case MIME_7Z:
// The 7Z file implementation does not allow random-access, map it to an in-memory archive
return archiveForInputStream(mimeType, new Creator<InputStream>() {
@Override
public CCSeven7ArchiveInputStream create() {
try {
return new CCSeven7ArchiveInputStream(new SevenZFile(file));
} catch (IOException e) {
throw new IllegalArgumentException("Error accessing file", e);
}
}
});
return new CCSevenZArchive(file);
case MIME_LZH1:
case MIME_LZH2:
case MIME_LZH3:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ public Exception getReadException() {
return readException;
}

@Override
public void close() throws IOException {

}
}
4 changes: 4 additions & 0 deletions src/main/java/io/mkr/archivefs/archive/StreamingArchive.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@ public InputStream contentFor(ArchiveItem entry) throws IOException {
return null;
}

@Override
public void close() throws IOException {

}
}
85 changes: 85 additions & 0 deletions src/main/java/io/mkr/archivefs/archive/cc/CCSevenZArchive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.mkr.archivefs.archive.cc;

import io.mkr.archivefs.archive.Archive;
import io.mkr.archivefs.archive.ArchiveItem;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Wrapper for 7z archives. Commons-compress does support random acces on 7z archives since 1.20.
*/
public class CCSevenZArchive implements Archive {

private SevenZFile sevenZFile;

private boolean closed;

/**
* Constructor for a new 7z archive.
* @param file File which is a 7z archive.
*/
public CCSevenZArchive(final File file) throws IOException {
this(file.toPath());
}

/**
* Constructor for a new 7z archive.
* @param file Path to file which is a 7z archive.
*/
public CCSevenZArchive(final Path file) throws IOException {
this.sevenZFile = new SevenZFile(Files.newByteChannel(file));
}

/**
* {@inheritDoc}
*/
@Override
public Collection<ArchiveItem> getItems() throws IOException {
checkArchiveClosed();
final List<ArchiveItem> entriesMeta = new ArrayList<>();
for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
entriesMeta.add(new CCArchiveEntryItem(entry));
}
return entriesMeta;
}

/**
* {@inheritDoc}
*/
@Override
public InputStream contentFor(ArchiveItem entry) throws IOException {
checkArchiveClosed();
return sevenZFile.getInputStream((SevenZArchiveEntry) ((CCArchiveEntryItem) entry).getDelegate());
}

/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
sevenZFile.close();
sevenZFile = null;
}
}

/**
* Checks if the archive is already closed. An {@link IOException} is thrown if the archive is already closed.
* @throws IOException if the archive is already closed
*/
private void checkArchiveClosed() throws IOException {
if (closed) {
throw new IOException("Archive is already closed!");
}
}
}
73 changes: 73 additions & 0 deletions src/test/java/io/mkr/archivefs/archive/cc/FlatArchiveTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.mkr.archivefs.archive.cc;

import io.mkr.archivefs.archive.util.ArchiveConstants;
import io.mkr.archivefs.archive.util.ArchiveCreator;
import io.mkr.archivefs.archive.util.SevenZArchiveCreator;
import io.mkr.archivefs.fs.ArchiveFilesystemProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class FlatArchiveTest {

private static final ArchiveFilesystemProvider afsp = new ArchiveFilesystemProvider();

@TempDir
Path tempDir;

private Path testFile;

private static Stream<ArchiveCreator> archiveCreators() {
return Stream.of(
new SevenZArchiveCreator()
);
}

@BeforeEach
void createTempDirectory() {
testFile = tempDir.resolve("testfile");
}

@ParameterizedTest
@MethodSource("archiveCreators")
void readContentFromFlatArchive(final ArchiveCreator archiveCreator) throws IOException {
archiveCreator.createFlatArchive(testFile);
assertTrue(Files.exists(testFile));

try (final FileSystem afs = afsp.newFileSystem(testFile, new HashMap<>())) {
final Path testFileInArchive = afs.getPath("/" + ArchiveConstants.TEST_FILE_NAME);
assertAll(() -> assertTrue(Files.exists(testFileInArchive)),
() -> assertArrayEquals(ArchiveConstants.TEST_FILE_CONTENT.getBytes(StandardCharsets.UTF_8),
Files.readAllBytes(testFileInArchive)));
}
}

@ParameterizedTest
@MethodSource("archiveCreators")
void accessFileSystemAfterClose(final ArchiveCreator archiveCreator) throws IOException {
archiveCreator.createFlatArchive(testFile);
assertTrue(Files.exists(testFile));

final FileSystem afs = afsp.newFileSystem(testFile, new HashMap<>());
final Path testFileInArchive = afs.getPath("/" + ArchiveConstants.TEST_FILE_NAME);
afs.close();

assertNotNull(afs.getPath("/" + ArchiveConstants.TEST_FILE_NAME));
assertThrows(IOException.class, () -> Files.readAllBytes(testFileInArchive));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.mkr.archivefs.archive.util;

public class ArchiveConstants {

public static final String TEST_FILE_CONTENT = "Test";

public static final String TEST_FILE_NAME = "test.txt";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.mkr.archivefs.archive.util;

import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class ArchiveCreationHelper {

/**
* Create a 7z file.
* @param path Path where the 7z file should be created
* @throws IOException If creating the 7z file fails
*/
public static void createSevenZArchive(final Path path) throws IOException {
try (final SevenZOutputFile out = new SevenZOutputFile(path.toFile())) {
final File inputFile = Files.createTempFile("SevenZBaseTemp", "").toFile();

SevenZArchiveEntry entry = out.createArchiveEntry(inputFile, ArchiveConstants.TEST_FILE_NAME);
out.putArchiveEntry(entry);
out.write(ArchiveConstants.TEST_FILE_CONTENT.getBytes(StandardCharsets.UTF_8));
out.closeArchiveEntry();

Files.deleteIfExists(inputFile.toPath());
}
}
}
10 changes: 10 additions & 0 deletions src/test/java/io/mkr/archivefs/archive/util/ArchiveCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.mkr.archivefs.archive.util;

import java.io.IOException;
import java.nio.file.Path;

public interface ArchiveCreator {

void createFlatArchive(Path path) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.mkr.archivefs.archive.util;

import java.io.IOException;
import java.nio.file.Path;

public class SevenZArchiveCreator implements ArchiveCreator {

@Override
public void createFlatArchive(final Path path) throws IOException {
ArchiveCreationHelper.createSevenZArchive(path);
}
}