diff --git a/src/main/java/org/rauschig/jarchivelib/Archiver.java b/src/main/java/org/rauschig/jarchivelib/Archiver.java index 97a1582..03f1137 100644 --- a/src/main/java/org/rauschig/jarchivelib/Archiver.java +++ b/src/main/java/org/rauschig/jarchivelib/Archiver.java @@ -35,7 +35,7 @@ public interface Archiver { *
* If the archive parameter has no file extension (e.g. "archive" instead of "archive.zip"), the concrete archiver * implementation should append it according to its file format (.zip, .tar, .tar.gz, ...). - * + * * @param archive the name of the archive to create * @param destination the destination directory where to place the created archive * @param source the input file or directory to archive @@ -65,7 +65,7 @@ public interface Archiver { * Extracts the given archive file into the given destination directory. *
* The destination is expected to be a writable directory. - * + * * @param archive the archive file to extract * @param destination the directory to which to extract the files * @throws IOException propagated I/O errors by {@code java.io} @@ -86,7 +86,7 @@ public interface Archiver { /** * Reads the given archive file as an {@link ArchiveStream} which is used to access individual {@link ArchiveEntry} * objects within the archive without extracting the archive onto the file system. - * + * * @param archive the archive file to stream * @return a new archive stream for the given archive * @throws IOException propagated I/O errors by {@code java.io} @@ -96,8 +96,15 @@ public interface Archiver { /** * Returns the filename extension that indicates the file format this archiver handles. E.g .tar" or ".zip". In case * of compressed archives, it will return the composite filename extensions, e.g. ".tar.gz" - * + * * @return a filename extension with a preceding dot */ String getFilenameExtension(); + + /** + * Returns the archiver's format + * + * @return archiver format + */ + ArchiveFormat getArchiveFormat(); } diff --git a/src/main/java/org/rauschig/jarchivelib/ArchiverCompressorDecorator.java b/src/main/java/org/rauschig/jarchivelib/ArchiverCompressorDecorator.java index 2a336f8..d79edfc 100644 --- a/src/main/java/org/rauschig/jarchivelib/ArchiverCompressorDecorator.java +++ b/src/main/java/org/rauschig/jarchivelib/ArchiverCompressorDecorator.java @@ -34,16 +34,16 @@ */ class ArchiverCompressorDecorator implements Archiver { - private CommonsArchiver archiver; + private Archiver archiver; private CommonsCompressor compressor; /** * Decorates the given Archiver with the given Compressor. - * + * * @param archiver the archiver to decorate * @param compressor the compressor used for compression */ - ArchiverCompressorDecorator(CommonsArchiver archiver, CommonsCompressor compressor) { + ArchiverCompressorDecorator(Archiver archiver, CommonsCompressor compressor) { this.archiver = archiver; this.compressor = compressor; } @@ -119,13 +119,18 @@ public String getFilenameExtension() { return archiver.getFilenameExtension() + compressor.getFilenameExtension(); } + @Override + public ArchiveFormat getArchiveFormat() { + return archiver.getArchiveFormat(); + } + /** * Returns a file name from the given archive name. The file extension suffix will be appended according to what is * already present. *
* E.g. if the compressor uses the file extension "gz", the archiver "tar", and passed argument is "archive.tar", * the returned value will be "archive.tar.gz". - * + * * @param archive the existing archive file name * @return the normalized archive file name including the correct file name extension */ diff --git a/src/main/java/org/rauschig/jarchivelib/ArchiverFactory.java b/src/main/java/org/rauschig/jarchivelib/ArchiverFactory.java index d1357c8..bec12d4 100644 --- a/src/main/java/org/rauschig/jarchivelib/ArchiverFactory.java +++ b/src/main/java/org/rauschig/jarchivelib/ArchiverFactory.java @@ -31,7 +31,7 @@ private ArchiverFactory() { * Probes the given {@link File} for its file type and creates an {@link Archiver} based on this file type. If the * File has a composite file extension such as ".tar.gz", the created {@link Archiver} will also handle ".gz" * compression. - * + * * @param archive the archive file to check. * @return a new Archiver instance (that may also handle compression) * @throws IllegalArgumentException if the given file is not a known archive @@ -49,7 +49,7 @@ public static Archiver createArchiver(File archive) throws IllegalArgumentExcept /** * Creates an Archiver that handles the given {@link FileType}. The Archiver may handle compression inherently, if * the {@link FileType} uses a compression type, such as ".tgz" might. - * + * * @param fileType the file type * @return a new Archiver instance (that may also handle compression) */ @@ -69,7 +69,7 @@ public static Archiver createArchiver(FileType fileType) { /** * Creates an Archiver for the given archive format that uses compression. - * + * * @param archiveFormat the archive format e.g. "tar" or "zip" * @param compression the compression algorithm name e.g. "gz" * @return a new Archiver instance that also handles compression @@ -88,13 +88,13 @@ public static Archiver createArchiver(String archiveFormat, String compression) /** * Creates an Archiver for the given archive format that uses compression. - * + * * @param archiveFormat the archive format * @param compression the compression algorithm * @return a new Archiver instance that also handles compression */ public static Archiver createArchiver(ArchiveFormat archiveFormat, CompressionType compression) { - CommonsArchiver archiver = new CommonsArchiver(archiveFormat); + Archiver archiver = createArchiver(archiveFormat); CommonsCompressor compressor = new CommonsCompressor(compression); return new ArchiverCompressorDecorator(archiver, compressor); @@ -102,7 +102,7 @@ public static Archiver createArchiver(ArchiveFormat archiveFormat, CompressionTy /** * Creates an Archiver for the given archive format. - * + * * @param archiveFormat the archive format e.g. "tar" or "zip" * @return a new Archiver instance * @throws IllegalArgumentException if the archive format is unknown @@ -117,7 +117,7 @@ public static Archiver createArchiver(String archiveFormat) throws IllegalArgume /** * Creates an Archiver for the given archive format. - * + * * @param archiveFormat the archive format * @return a new Archiver instance */ @@ -126,6 +126,8 @@ public static Archiver createArchiver(ArchiveFormat archiveFormat) { return new SevenZArchiver(); } else if (archiveFormat == ArchiveFormat.ZIP) { return new ZipFileArchiver(); + } else if (archiveFormat == ArchiveFormat.TAR) { + return new TarArchiver(); } return new CommonsArchiver(archiveFormat); } diff --git a/src/main/java/org/rauschig/jarchivelib/CommonsArchiver.java b/src/main/java/org/rauschig/jarchivelib/CommonsArchiver.java index 27f7558..0ed5dce 100644 --- a/src/main/java/org/rauschig/jarchivelib/CommonsArchiver.java +++ b/src/main/java/org/rauschig/jarchivelib/CommonsArchiver.java @@ -26,6 +26,7 @@ import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; /** * Implementation of an {@link Archiver} that uses {@link ArchiveStreamFactory} to generate archive streams by a given @@ -40,6 +41,7 @@ class CommonsArchiver implements Archiver { this.archiveFormat = archiveFormat; } + @Override public ArchiveFormat getArchiveFormat() { return archiveFormat; } @@ -120,7 +122,7 @@ public String getFilenameExtension() { /** * Returns a new ArchiveInputStream for reading archives. Subclasses can override this to return their own custom * implementation. - * + * * @param archive the archive file to stream from * @return a new ArchiveInputStream for the given archive file * @throws IOException propagated IO exceptions @@ -152,7 +154,7 @@ protected ArchiveInputStream createArchiveInputStream(InputStream archive) throw /** * Returns a new ArchiveOutputStream for creating archives. Subclasses can override this to return their own custom * implementation. - * + * * @param archiveFile the archive file to stream to * @return a new ArchiveOutputStream for the given archive file. * @throws IOException propagated IO exceptions @@ -167,7 +169,7 @@ protected ArchiveOutputStream createArchiveOutputStream(File archiveFile) throws /** * Asserts that the given File object is a readable file that can be used to extract from. - * + * * @param archive the file to check * @throws FileNotFoundException if the file does not exist * @throws IllegalArgumentException if the file is a directory or not readable @@ -185,7 +187,7 @@ protected void assertExtractSource(File archive) throws FileNotFoundException, I /** * Creates a new File in the given destination. The resulting name will always be "archive"."fileExtension". If the * archive name parameter already ends with the given file name extension, it is not additionally appended. - * + * * @param archive the name of the archive * @param extension the file extension (e.g. ".tar") * @param destination the parent path @@ -207,7 +209,7 @@ protected File createNewArchiveFile(String archive, String extension, File desti * Recursion entry point for {@link #writeToArchive(File, File[], ArchiveOutputStream)}. *
* Recursively writes all given source {@link File}s into the given {@link ArchiveOutputStream}. - * + * * @param sources the files to write in to the archive * @param archive the archive to write into * @throws IOException when an I/O error occurs @@ -220,14 +222,14 @@ protected void writeToArchive(File[] sources, ArchiveOutputStream archive) throw throw new FileNotFoundException(source.getPath() + " (Permission denied)"); } - writeToArchive(source.getParentFile(), new File[]{ source }, archive); + writeToArchive(source.getParentFile(), new File[]{source}, archive); } } /** * Recursively writes all given source {@link File}s into the given {@link ArchiveOutputStream}. The paths of the * sources in the archive will be relative to the given parent {@code File}. - * + * * @param parent the parent file node for computing a relative path (see {@link IOUtils#relativePath(File, File)}) * @param sources the files to write in to the archive * @param archive the archive to write into @@ -245,10 +247,18 @@ protected void writeToArchive(File parent, File[] sources, ArchiveOutputStream a } } + /** + * Process archive entry before it's finalized / stored + * @param entry archive entry + */ + protected void processArchiveEntry(ArchiveEntry entry) { + // For specialized archivers to override. Default implementation is noop. + } + /** * Creates a new {@link ArchiveEntry} in the given {@link ArchiveOutputStream}, and copies the given {@link File} * into the new entry. - * + * * @param file the file to add to the archive * @param entryName the name of the archive entry * @param archive the archive to write to @@ -256,7 +266,10 @@ protected void writeToArchive(File parent, File[] sources, ArchiveOutputStream a */ protected void createArchiveEntry(File file, String entryName, ArchiveOutputStream archive) throws IOException { ArchiveEntry entry = archive.createArchiveEntry(file, entryName); + // TODO #23: read permission from file, write it to the ArchiveEntry + + this.processArchiveEntry(entry); archive.putArchiveEntry(entry); if (!entry.isDirectory()) { diff --git a/src/main/java/org/rauschig/jarchivelib/CommonsStreamFactory.java b/src/main/java/org/rauschig/jarchivelib/CommonsStreamFactory.java index dfc0f63..e376c88 100644 --- a/src/main/java/org/rauschig/jarchivelib/CommonsStreamFactory.java +++ b/src/main/java/org/rauschig/jarchivelib/CommonsStreamFactory.java @@ -61,15 +61,15 @@ static ArchiveInputStream createArchiveInputStream(String archiverName, InputStr * @see {@link ArchiveStreamFactory#createArchiveInputStream(String, InputStream)} */ static ArchiveInputStream createArchiveInputStream(ArchiveFormat archiveFormat, InputStream in) - throws ArchiveException { + throws ArchiveException { return createArchiveInputStream(archiveFormat.getName(), in); } /** * @see {@link ArchiveStreamFactory#createArchiveInputStream(String, InputStream)} */ - static ArchiveInputStream createArchiveInputStream(CommonsArchiver archiver, InputStream in) - throws ArchiveException { + static ArchiveInputStream createArchiveInputStream(Archiver archiver, InputStream in) + throws ArchiveException { return createArchiveInputStream(archiver.getArchiveFormat(), in); } @@ -82,7 +82,7 @@ static ArchiveInputStream createArchiveInputStream(InputStream in) throws Archiv /** * Uses the {@link ArchiveStreamFactory} to create a new {@link ArchiveInputStream} for the given archive file. - * + * * @param archive the archive file * @return a new {@link ArchiveInputStream} for the given archive file * @throws IOException propagated IOException when creating the FileInputStream. @@ -96,19 +96,19 @@ static ArchiveInputStream createArchiveInputStream(File archive) throws IOExcept * @see {@link ArchiveStreamFactory#createArchiveOutputStream(String, OutputStream)}; */ static ArchiveOutputStream createArchiveOutputStream(String archiverName, OutputStream out) - throws ArchiveException { + throws ArchiveException { return archiveStreamFactory.createArchiveOutputStream(archiverName, out); } static ArchiveOutputStream createArchiveOutputStream(ArchiveFormat format, File archive) throws IOException, - ArchiveException { + ArchiveException { return createArchiveOutputStream(format.getName(), new FileOutputStream(archive)); } /** * Uses the {@link ArchiveStreamFactory} and the name of the given archiver to create a new * {@link ArchiveOutputStream} for the given archive {@link File}. - * + * * @param archiver the invoking archiver * @param archive the archive file to create the {@link ArchiveOutputStream} for * @return a new {@link ArchiveOutputStream} @@ -116,14 +116,14 @@ static ArchiveOutputStream createArchiveOutputStream(ArchiveFormat format, File * @throws ArchiveException if the archiver name is not known */ static ArchiveOutputStream createArchiveOutputStream(CommonsArchiver archiver, File archive) throws IOException, - ArchiveException { + ArchiveException { return createArchiveOutputStream(archiver.getArchiveFormat(), archive); } /** * Uses the {@link CompressorStreamFactory} to create a new {@link CompressorInputStream} for the given source * {@link File}. - * + * * @param source the file to create the {@link CompressorInputStream} for * @return a new {@link CompressorInputStream} * @throws IOException if an I/O error occurs @@ -136,14 +136,14 @@ static CompressorInputStream createCompressorInputStream(File source) throws IOE /** * Uses the {@link CompressorStreamFactory} to create a new {@link CompressorInputStream} for the compression type * and wraps the given source {@link File} with it. - * + * * @param source the file to create the {@link CompressorInputStream} for * @return a new {@link CompressorInputStream} * @throws IOException if an I/O error occurs * @throws CompressorException if the compressor name is not known */ static CompressorInputStream createCompressorInputStream(CompressionType type, File source) throws IOException, - CompressorException { + CompressorException { return createCompressorInputStream(type, new BufferedInputStream(new FileInputStream(source))); } @@ -151,7 +151,7 @@ static CompressorInputStream createCompressorInputStream(CompressionType type, F * @see {@link CompressorStreamFactory#createCompressorInputStream(String, java.io.InputStream)} */ static CompressorInputStream createCompressorInputStream(CompressionType compressionType, InputStream in) - throws CompressorException { + throws CompressorException { return compressorStreamFactory.createCompressorInputStream(compressionType.getName(), in); } @@ -163,14 +163,14 @@ static CompressorInputStream createCompressorInputStream(InputStream in) throws } static CompressorOutputStream createCompressorOutputStream(CompressionType compressionType, File destination) - throws IOException, CompressorException { + throws IOException, CompressorException { return createCompressorOutputStream(compressionType.getName(), new FileOutputStream(destination)); } /** * Uses the {@link CompressorStreamFactory} and the name of the given compressor to create a new * {@link CompressorOutputStream} for the given destination {@link File}. - * + * * @param compressor the invoking compressor * @param destination the file to create the {@link CompressorOutputStream} for * @return a new {@link CompressorOutputStream} @@ -178,7 +178,7 @@ static CompressorOutputStream createCompressorOutputStream(CompressionType compr * @throws CompressorException if the compressor name is not known */ static CompressorOutputStream createCompressorOutputStream(CommonsCompressor compressor, File destination) - throws IOException, CompressorException { + throws IOException, CompressorException { return createCompressorOutputStream(compressor.getCompressionType(), destination); } @@ -186,7 +186,7 @@ static CompressorOutputStream createCompressorOutputStream(CommonsCompressor com * @see {@link CompressorStreamFactory#createCompressorOutputStream(String, OutputStream)}; */ static CompressorOutputStream createCompressorOutputStream(String compressorName, OutputStream out) - throws CompressorException { + throws CompressorException { return compressorStreamFactory.createCompressorOutputStream(compressorName, out); } diff --git a/src/main/java/org/rauschig/jarchivelib/TarArchiver.java b/src/main/java/org/rauschig/jarchivelib/TarArchiver.java new file mode 100644 index 0000000..65baea7 --- /dev/null +++ b/src/main/java/org/rauschig/jarchivelib/TarArchiver.java @@ -0,0 +1,44 @@ +/** + * Copyright 2013 Thomas Rausch + * + * 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 org.rauschig.jarchivelib; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; + +/** + * Archiver to handle tar archives. + *

+ * Created by masc on 31.10.18. + */ +public class TarArchiver extends CommonsArchiver { + private static final int DEFAULT_EXECUTABLE_FILE_MODE = 0100755; + + TarArchiver() { + super(ArchiveFormat.TAR); + } + + @Override + protected void processArchiveEntry(ArchiveEntry entry) { + if (entry instanceof TarArchiveEntry) { + + TarArchiveEntry tarEntry = (TarArchiveEntry) entry; + + // Preserve executability of files + if (tarEntry.isFile() && tarEntry.getFile().canExecute()) + tarEntry.setMode(DEFAULT_EXECUTABLE_FILE_MODE); + } + } +} \ No newline at end of file