diff options
author | Harald Musum <musum@verizonmedia.com> | 2019-06-12 14:08:51 +0200 |
---|---|---|
committer | Harald Musum <musum@verizonmedia.com> | 2019-06-12 14:08:51 +0200 |
commit | 01024988e4d4d2fdfa76d2d446706e75cc9cdcf2 (patch) | |
tree | afde1e1d8daa6ebe3872c6e098de8b88ab2f0aa7 /configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java | |
parent | a7fe82abe533cf8633af17a0e914fdc89d6db231 (diff) |
Move to a more suitable package
Diffstat (limited to 'configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java')
-rw-r--r-- | configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java new file mode 100644 index 00000000000..c94f739b958 --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStream.java @@ -0,0 +1,129 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.application; + +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; +import com.yahoo.log.LogLevel; +import com.yahoo.vespa.config.server.http.BadRequestException; +import com.yahoo.vespa.config.server.http.InternalServerException; +import com.yahoo.vespa.config.server.http.v2.ApplicationApiHandler; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; + +import java.io.*; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; + +/** + * A compressed application points to an application package that can be decompressed. + * + * @author Ulf Lilleengen + */ +public class CompressedApplicationInputStream implements AutoCloseable { + + private static final Logger log = Logger.getLogger(CompressedApplicationInputStream.class.getPackage().getName()); + private final ArchiveInputStream ais; + + /** + * Create an instance of a compressed application from an input stream. + * + * @param is the input stream containing the compressed files. + * @param contentType the content type for determining what kind of compressed stream should be used. + * @return An instance of an unpacked application. + */ + public static CompressedApplicationInputStream createFromCompressedStream(InputStream is, String contentType) { + try { + ArchiveInputStream ais = getArchiveInputStream(is, contentType); + return createFromCompressedStream(ais); + } catch (IOException e) { + throw new InternalServerException("Unable to create compressed application stream", e); + } + } + + static CompressedApplicationInputStream createFromCompressedStream(ArchiveInputStream ais) { + return new CompressedApplicationInputStream(ais); + } + + private static ArchiveInputStream getArchiveInputStream(InputStream is, String contentTypeHeader) throws IOException { + ArchiveInputStream ais; + switch (contentTypeHeader) { + case ApplicationApiHandler.APPLICATION_X_GZIP: + ais = new TarArchiveInputStream(new GZIPInputStream(is)); + break; + case ApplicationApiHandler.APPLICATION_ZIP: + ais = new ZipArchiveInputStream(is); + break; + default: + throw new BadRequestException("Unable to decompress"); + } + return ais; + } + + private CompressedApplicationInputStream(ArchiveInputStream ais) { + this.ais = ais; + } + + /** + * Close this stream. + * @throws IOException if the stream could not be closed + */ + public void close() throws IOException { + ais.close(); + } + + File decompress() throws IOException { + return decompress(Files.createTempDir()); + } + + public File decompress(File dir) throws IOException { + decompressInto(dir); + dir = findActualApplicationDir(dir); + return dir; + } + + private void decompressInto(File application) throws IOException { + log.log(LogLevel.DEBUG, "Application is in " + application.getAbsolutePath()); + int entries = 0; + ArchiveEntry entry; + while ((entry = ais.getNextEntry()) != null) { + log.log(LogLevel.DEBUG, "Unpacking " + entry.getName()); + File outFile = new File(application, entry.getName()); + // FIXME/TODO: write more tests that break this logic. I have a feeling it is not very robust. + if (entry.isDirectory()) { + if (!(outFile.exists() && outFile.isDirectory())) { + log.log(LogLevel.DEBUG, "Creating dir: " + outFile.getAbsolutePath()); + boolean res = outFile.mkdirs(); + if (!res) { + log.log(LogLevel.WARNING, "Could not create dir " + entry.getName()); + } + } + } else { + log.log(LogLevel.DEBUG, "Creating output file: " + outFile.getAbsolutePath()); + + // Create parent dir if necessary + String parent = outFile.getParent(); + new File(parent).mkdirs(); + + FileOutputStream fos = new FileOutputStream(outFile); + ByteStreams.copy(ais, fos); + fos.close(); + } + entries++; + } + if (entries == 0) { + log.log(LogLevel.WARNING, "Not able to read any entries from " + application.getName()); + } + } + + private File findActualApplicationDir(File application) { + // If application is in e.g. application/, use that as root for UnpackedApplication + // TODO: Vespa 8: Remove application/ directory support + File[] files = application.listFiles(); + if (files != null && files.length == 1 && files[0].isDirectory()) { + application = files[0]; + } + return application; + } +} |