package io.arivera.oss.embedded.rabbitmq.extract; import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig; import io.arivera.oss.embedded.rabbitmq.apache.commons.lang3.StopWatch; import io.arivera.oss.embedded.rabbitmq.util.ArchiveType; 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.utils.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tukaani.xz.XZInputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.zip.GZIPInputStream; class BasicExtractor implements Extractor { private static final Logger LOGGER = LoggerFactory.getLogger(BasicExtractor.class); private final EmbeddedRabbitMqConfig config; BasicExtractor(EmbeddedRabbitMqConfig config) { this.config = config; } @Override public void run() throws ExtractionException { Runnable extractor = getExtractor(config); extractor.run(); } CompressedExtractor getExtractor(EmbeddedRabbitMqConfig config) { String downloadedFilename = config.getDownloadTarget().toString(); if (ArchiveType.TAR_GZ.matches(downloadedFilename)) { return new TarGzExtractor(config); } else if (ArchiveType.TAR_XZ.matches(downloadedFilename)) { return new TarXzExtractor(config); } else if (ArchiveType.ZIP.matches(downloadedFilename)) { return new ZipExtractor(config); } else { throw new IllegalStateException("Could not determine compression format for file: " + downloadedFilename); } } abstract static class CompressedExtractor implements Runnable { protected final EmbeddedRabbitMqConfig config; CompressedExtractor(EmbeddedRabbitMqConfig config) { this.config = config; } protected static void createNewFile(File destPath) { try { boolean newFile = destPath.createNewFile(); if (!newFile) { LOGGER.warn("File '{}' already exists. Will attempt to continue...", destPath); } } catch (IOException e) { LOGGER.warn("Could not extract file '" + destPath + "'. Will attempt to continue...", e); } } protected static void makeDirectory(File destPath) { boolean mkdirs = destPath.mkdirs(); if (!mkdirs) { LOGGER.warn("Directory '{}' could not be created. Will attempt to continue...", destPath); } } protected static void extractFile(InputStream archive, File destPath, String fileName) { BufferedOutputStream output = null; try { LOGGER.debug("Extracting '{}'...", fileName); output = new BufferedOutputStream(new FileOutputStream(destPath)); IOUtils.copy(archive, output); } catch (IOException e) { throw new ExtractionException("Error extracting file '" + fileName + "' ", e); } finally { IOUtils.closeQuietly(output); } } } abstract static class AbstractTarExtractor extends CompressedExtractor { AbstractTarExtractor(EmbeddedRabbitMqConfig config) { super(config); } @Override public void run() throws ExtractionException { String downloadedFile = config.getDownloadTarget().toString(); TarArchiveInputStream archive; try { BufferedInputStream bufferedFileInput = new BufferedInputStream(new FileInputStream(config.getDownloadTarget())); InputStream compressedInputStream = getCompressedInputStream(downloadedFile, bufferedFileInput); archive = new TarArchiveInputStream(compressedInputStream); } catch (IOException e) { throw new ExtractionException( "Download file '" + config.getDownloadTarget() + "' was not found or is not accessible.", e); } try { LOGGER.info("Extracting '{}' to '{}'", config.getDownloadTarget(), config.getExtractionFolder()); StopWatch stopWatch = new StopWatch(); stopWatch.start(); extractTar(archive); stopWatch.stop(); LOGGER.info("Finished extracting files in {}ms", stopWatch.getTime()); } finally { IOUtils.closeQuietly(archive); } } protected abstract InputStream getCompressedInputStream(String downloadedFile, BufferedInputStream bufferedFileInput) throws IOException; private void extractTar(TarArchiveInputStream archive) { TarArchiveEntry fileToExtract; try { fileToExtract = archive.getNextTarEntry(); } catch (IOException e) { throw new ExtractionException("Could not extract files from file '" + config.getDownloadTarget() + "' due to: " + e.getLocalizedMessage(), e); } while (fileToExtract != null) { File destPath = new File(config.getExtractionFolder(), fileToExtract.getName()); if (fileToExtract.isDirectory()) { makeDirectory(destPath); } else if (fileToExtract.isLink()) { createLink(fileToExtract, destPath); } else { createNewFile(destPath); int mode = fileToExtract.getMode(); // example: 764 int ownerBits = mode >> 2; // owner bits: 7 int isExecutable = ownerBits & 1; // bits: RWX, where X = executable bit boolean madeExecutable = destPath.setExecutable(isExecutable == 1); if (!madeExecutable) { LOGGER.warn("File '{}' (original mode {}) could not be made executable probably due to permission issues.", fileToExtract.getName(), mode); } boolean madeReadable = destPath.setReadable(true); if (!madeReadable) { LOGGER.warn("File '{}' (original mode {}) could not be made readable probably due to permission issues.", fileToExtract.getName(), mode); } extractFile(archive, destPath, fileToExtract.getName()); } try { fileToExtract = archive.getNextTarEntry(); } catch (IOException e) { LOGGER.error("Could not find next file to extract.", e); break; } } File extractionFolder = config.getExtractionFolder(); boolean madeReadable = extractionFolder.setReadable(true); if (!madeReadable) { LOGGER.warn("File '{}' could not be made readable probably due to permission issues.", extractionFolder); } } private void createLink(TarArchiveEntry fileToExtract, File destPath) { Path link = Paths.get(destPath.toURI()); Path existingFile = Paths.get(config.getExtractionFolder().toString(), fileToExtract.getLinkName()); try { LOGGER.debug("Extracting '{}'...", destPath); Files.createLink(link, existingFile); } catch (IOException e) { LOGGER.warn("Could not create link '{}' to '{}'", link, existingFile, e); } } } private static class TarGzExtractor extends AbstractTarExtractor { public TarGzExtractor(EmbeddedRabbitMqConfig config) { super(config); } @Override protected InputStream getCompressedInputStream(String downloadedFile, BufferedInputStream bufferedFileInput) throws IOException { return new GZIPInputStream(bufferedFileInput); } } private static class TarXzExtractor extends AbstractTarExtractor { public TarXzExtractor(EmbeddedRabbitMqConfig config) { super(config); } protected InputStream getCompressedInputStream(String downloadedFile, BufferedInputStream bufferedFileInput) throws IOException { return new XZInputStream(bufferedFileInput); } } private static class ZipExtractor extends CompressedExtractor { public ZipExtractor(EmbeddedRabbitMqConfig config) { super(config); } @Override public void run() throws ExtractionException { ZipFile zipFile; try { zipFile = new ZipFile(config.getDownloadTarget()); } catch (IOException e) { throw new ExtractionException( "Download file '" + config.getDownloadTarget() + "' was not found or is not accessible.", e); } try { LOGGER.info("Extracting '{}' to '{}'", config.getDownloadTarget(), config.getExtractionFolder()); StopWatch stopWatch = new StopWatch(); stopWatch.start(); extractZip(zipFile); stopWatch.stop(); LOGGER.info("Finished extracting files in {}ms", stopWatch.getTime()); } finally { IOUtils.closeQuietly(zipFile); } } private void extractZip(ZipFile zipFile) { Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); while (entries.hasMoreElements()) { ZipArchiveEntry entry = entries.nextElement(); String fileName = entry.getName(); File outputFile = new File(config.getExtractionFolder(), fileName); if (entry.isDirectory()) { makeDirectory(outputFile); } else { createNewFile(outputFile); try { InputStream inputStream = zipFile.getInputStream(entry); extractFile(inputStream, outputFile, fileName); } catch (IOException e) { throw new ExtractionException("Error extracting file '" + fileName + "' " + "from downloaded file: " + config.getDownloadTarget(), e); } } } } } }