/*
* 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.ngrinder.common.util;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import static org.ngrinder.common.util.ExceptionUtils.processException;
import static org.ngrinder.common.util.Preconditions.checkNotNull;
/**
* Compression utility.
*
* @author JunHo Yoon
*/
@SuppressWarnings("All")
public abstract class CompressionUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class);
/**
* Unzip the given zipped file with given character set.
*
* @param zippedFile zipped file
* @param charsetName character set
*/
public static void unzip(File zippedFile, String charsetName) {
unzip(zippedFile, zippedFile.getParentFile(), charsetName);
}
/**
* Unzip the given zipped file into destination directory.
*
* @param zippedFile zipped file
* @param destDir destination directory
*/
public static void unzip(File zippedFile, File destDir) {
try {
unzip(new FileInputStream(zippedFile), destDir, Charset.defaultCharset().name());
} catch (FileNotFoundException e) {
throw processException(e);
}
}
/**
* Unzip the given zipped file into destination directory with the given
* character set.
*
* @param zippedFile zipped file
* @param destDir destination directory
* @param charsetName character set name
*/
public static void unzip(File zippedFile, File destDir, String charsetName) {
try {
unzip(new FileInputStream(zippedFile), destDir, charsetName);
} catch (FileNotFoundException e) {
throw processException(e);
}
}
/**
* Unzip the given input stream into destination directory with the default
* character set.
*
* @param is input stream
* @param destDir destination directory
*/
public static void unzip(InputStream is, File destDir) {
unzip(is, destDir, Charset.defaultCharset().name());
}
/**
* Unzip the given input stream into destination directory with the given
* character set.
*
* @param is input stream
* @param destDir destination directory
* @param charsetName character set name
*/
public static void unzip(InputStream is, File destDir, String charsetName) {
byte[] buffer = new byte[1024];
ZipInputStream zis = null;
FileOutputStream fos = null;
try {
File folder = destDir;
if (!folder.exists()) {
folder.mkdir();
}
zis = new ZipInputStream(is);
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(destDir.getAbsolutePath(), fileName);
if (newFile.getPath().contains("..")) {
throw new IllegalArgumentException("zip entry should not contain .. in the path.");
}
if (ze.isDirectory()) {
newFile.mkdirs();
} else {
fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
IOUtils.closeQuietly(fos);
}
ze = zis.getNextEntry();
}
} catch (Exception e) {
throw processException(e);
} finally {
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(zis);
}
}
/**
* Compresses the given file(or dir) and creates new file under the same
* directory.
*
* @param src file or directory
* @throws IOException IOException
*/
public static void zip(File src) throws IOException {
zip(src, Charset.defaultCharset().name(), true);
}
/**
* Zips the given file(or dir) and create.
*
* @param src file or directory to compress
* @param includeSrc if true and src is directory, then src is not included in the
* compression. if false, src is included.
* @throws IOException IOException
*/
public static void zip(File src, boolean includeSrc) throws IOException {
zip(src, Charset.defaultCharset().name(), includeSrc);
}
/**
* Compresses the given src file (or directory) with the given encoding.
*
* @param src src
* @param charSetName character set
* @param includeSrc true if sub-directory will be zipped as well.
* @throws IOException IOException
*/
public static void zip(File src, String charSetName, boolean includeSrc) throws IOException {
zip(src, src.getParentFile(), charSetName, includeSrc);
}
/**
* Compresses the given src file(or directory) and writes to the given
* output stream with sub directory.
*
* @param src src
* @param os output stream
* @throws IOException IOException
*/
public static void zip(File src, OutputStream os) throws IOException {
zip(src, os, Charset.defaultCharset().name(), true);
}
/**
* Compresses the given src file(or directory) and create the compressed
* file under the given destDir.
*
* @param src src to be zipped.
* @param destDir destination directory
* @param charSetName character set to be used
* @param includeSrc true if sub-directory will be zipped as well.
* @throws IOException IOException
*/
public static void zip(File src, File destDir, String charSetName, boolean includeSrc) throws IOException {
String fileName = src.getName();
if (!src.isDirectory()) {
int pos = fileName.lastIndexOf(".");
if (pos > 0) {
fileName = fileName.substring(0, pos);
}
}
fileName += ".zip";
File zippedFile = new File(destDir, fileName);
if (!zippedFile.exists()) {
zippedFile.createNewFile();
}
FileOutputStream os = null;
try {
os = new FileOutputStream(zippedFile);
zip(src, os, charSetName, includeSrc);
} finally {
IOUtils.closeQuietly(os);
}
}
/**
* Zip the given src into the given output stream.
*
* @param src src to be zipped
* @param os output stream
* @param charsetName character set to be used
* @param includeSrc true if src will be included.
* @throws IOException IOException
*/
public static void zip(File src, OutputStream os, String charsetName, boolean includeSrc) throws IOException {
ZipArchiveOutputStream zos = new ZipArchiveOutputStream(os);
zos.setEncoding(charsetName);
FileInputStream fis = null;
int length;
ZipArchiveEntry ze;
byte[] buf = new byte[8 * 1024];
String name;
Stack<File> stack = new Stack<File>();
File root;
if (src.isDirectory()) {
if (includeSrc) {
stack.push(src);
root = src.getParentFile();
} else {
File[] fs = checkNotNull(src.listFiles());
for (int i = 0; i < fs.length; i++) {
stack.push(fs[i]);
}
root = src;
}
} else {
stack.push(src);
root = src.getParentFile();
}
while (!stack.isEmpty()) {
File f = stack.pop();
name = toPath(root, f);
if (f.isDirectory()) {
File[] fs = checkNotNull(f.listFiles());
for (int i = 0; i < fs.length; i++) {
if (fs[i].isDirectory()) {
stack.push(fs[i]);
} else {
stack.add(0, fs[i]);
}
}
} else {
ze = new ZipArchiveEntry(name);
zos.putArchiveEntry(ze);
try {
fis = new FileInputStream(f);
while ((length = fis.read(buf, 0, buf.length)) >= 0) {
zos.write(buf, 0, length);
}
} finally {
IOUtils.closeQuietly(fis);
}
zos.closeArchiveEntry();
}
}
zos.close();
}
private static String toPath(File root, File dir) {
String path = dir.getAbsolutePath();
path = path.substring(root.getAbsolutePath().length()).replace(File.separatorChar, '/');
if (path.startsWith("/")) {
path = path.substring(1);
}
if (dir.isDirectory() && !path.endsWith("/")) {
path += "/";
}
return path;
}
public static final List<String> EXECUTABLE_EXTENSION = Arrays.asList("bat", "sh");
/**
* Untar an input file into an output file.
* The output file is created in the output folder, having the same name as
* the input file, minus the '.tar' extension.
*
* @param inFile the input .tar file
* @param outputDir the output directory file.
* @return The {@link List} of {@link File}s with the untared content.
*/
@SuppressWarnings("resource")
public static List<File> untar(final File inFile, final File outputDir) {
final List<File> untaredFiles = new LinkedList<File>();
InputStream is = null;
TarArchiveInputStream debInputStream = null;
try {
is = new FileInputStream(inFile);
debInputStream = (TarArchiveInputStream) new ArchiveStreamFactory().createArchiveInputStream("tar", is);
TarArchiveEntry entry;
while ((entry = (TarArchiveEntry) debInputStream.getNextEntry()) != null) {
final File outputFile = new File(outputDir, entry.getName());
if (entry.isDirectory()) {
if (!outputFile.exists()) {
if (!outputFile.mkdirs()) {
throw new IllegalStateException(String.format("Couldn't create directory %s.",
outputFile.getAbsolutePath()));
}
}
} else {
File parentFile = outputFile.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
final OutputStream outputFileStream = new FileOutputStream(outputFile);
try {
IOUtils.copy(debInputStream, outputFileStream);
} finally {
IOUtils.closeQuietly(outputFileStream);
}
if (FilenameUtils.isExtension(outputFile.getName(), EXECUTABLE_EXTENSION)) {
outputFile.setExecutable(true, true);
}
outputFile.setReadable(true);
outputFile.setWritable(true, true);
}
untaredFiles.add(outputFile);
}
} catch (Exception e) {
throw processException("Error while untar file", e);
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(debInputStream);
}
return untaredFiles;
}
/**
* Ungzip the given file.
*
* @param inFile file
* @param outFile to
* @return ungzipped file.
*/
public static File ungzip(final File inFile, final File outFile) {
FileInputStream fin = null;
BufferedInputStream in = null;
FileOutputStream fout = null;
GzipCompressorInputStream gzIn = null;
try {
fin = new FileInputStream(inFile);
in = new BufferedInputStream(fin);
gzIn = new GzipCompressorInputStream(in);
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
fout = new FileOutputStream(outFile);
final byte[] buffer = new byte[4048];
int n;
while (-1 != (n = gzIn.read(buffer))) {
fout.write(buffer, 0, n);
}
} catch (Exception e) {
throw processException("Error while ungzip file", e);
} finally {
IOUtils.closeQuietly(fin);
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(fout);
IOUtils.closeQuietly(gzIn);
}
return outFile;
}
/**
* Process each jar entry in the given jar file.
*
* @param jarFile jar file
* @param processor jar file entry predicate
* @throws IOException thrown when having IO problem.
*/
public static void processJarEntries(File jarFile, ZipEntryProcessor processor) {
try {
JarFile jarfile = new JarFile(jarFile);
Enumeration<java.util.jar.JarEntry> enu = jarfile.entries();
while (enu.hasMoreElements()) {
JarEntry je = enu.nextElement();
if (je.isDirectory()) {
continue;
}
processor.process(jarfile, je);
}
} catch (IOException e) {
throw processException("Error while extracting jar file", e);
}
}
/**
* Add a file into tar.
*
* @param tarStream TarArchive outputStream
* @param file file
* @param path relative path to append
* @throws IOException thrown when having IO problem.
*/
public static void addFileToTar(TarArchiveOutputStream tarStream, File file, String path) throws IOException {
int mode = file.isDirectory() ? TarArchiveEntry.DEFAULT_DIR_MODE : TarArchiveEntry.DEFAULT_FILE_MODE;
addFileToTar(tarStream, file, path, mode);
}
/**
* Add a folder into tar.
*
* @param tarStream TarArchive outputStream
* @param path path to append
* @throws IOException thrown when having IO problem.
*/
public static void addFolderToTar(TarArchiveOutputStream tarStream, String path) throws IOException {
TarArchiveEntry archiveEntry = new TarArchiveEntry(path);
archiveEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
tarStream.putArchiveEntry(archiveEntry);
tarStream.closeArchiveEntry();
}
/**
* Add the given input stream into tar.
*
* @param tarStream TarArchive outputStream
* @param inputStream input stream
* @param path relative path to append
* @param size size of stream
* @param mode mode for this entry
* @throws IOException thrown when having IO problem.
*/
public static void addInputStreamToTar(TarArchiveOutputStream tarStream, InputStream inputStream, String path,
long size, int mode) throws IOException {
TarArchiveEntry entry = new TarArchiveEntry(path);
entry.setSize(size);
entry.setMode(mode);
try {
tarStream.putArchiveEntry(entry);
IOUtils.copy(inputStream, tarStream);
} catch (IOException e) {
throw processException("Error while adding File to Tar file", e);
} finally {
tarStream.closeArchiveEntry();
}
}
/**
* Add a file into tar.
*
* @param tarStream TarArchive outputStream
* @param file file
* @param path relative path to append
* @param mode mode for this entry
* @throws IOException thrown when having IO problem.
*/
public static void addFileToTar(TarArchiveOutputStream tarStream, File file, String path, int mode) throws IOException {
TarArchiveEntry entry = new TarArchiveEntry(path);
entry.setSize(file.length());
entry.setMode(mode);
BufferedInputStream bis = null;
try {
tarStream.putArchiveEntry(entry);
bis = new BufferedInputStream(new FileInputStream(file));
IOUtils.copy(bis, tarStream);
} catch (IOException e) {
throw processException("Error while adding File to Tar file", e);
} finally {
IOUtils.closeQuietly(bis);
tarStream.closeArchiveEntry();
}
}
public interface ZipEntryProcessor {
public void process(ZipFile zipFile, ZipEntry je) throws IOException;
}
public interface FilePredicate {
public boolean evaluate(Object object);
}
}