com.fizzed.stork.deploy.Archive.java Source code

Java tutorial

Introduction

Here is the source code for com.fizzed.stork.deploy.Archive.java

Source

/*
 * Copyright 2016 Fizzed, Inc.
 *
 * 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 com.fizzed.stork.deploy;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
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.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Archive {
    static private final Logger log = LoggerFactory.getLogger(Archive.class);

    private final Path file;
    private final String format;

    public Archive(Path file) {
        this.file = file;
        this.format = format(file);
    }

    public Path getFile() {
        return this.file;
    }

    public String getName() {
        return this.file.getFileName().toString();
    }

    public String getNameWithNoExtension() {
        String name = getName();
        return name.substring(0, name.length() - this.format.length() - 1);
    }

    /**
     * Returns the format of the archive such as "tar.gz" or ".zip"
     * @return 
     */
    public String getFormat() {
        return this.format;
    }

    public Path unpack(Path unpackDir) throws IOException {
        Files.createDirectories(unpackDir);

        log.info("Unpacking {} to {}", file, unpackDir);

        // we need to know the top-level dir(s) created by unpack
        final Set<Path> firstLevelPaths = new LinkedHashSet<>();
        final AtomicInteger count = new AtomicInteger();

        try (ArchiveInputStream ais = newArchiveInputStream(file)) {
            ArchiveEntry entry;
            while ((entry = ais.getNextEntry()) != null) {
                try {
                    Path entryFile = Paths.get(entry.getName());
                    Path resolvedFile = unpackDir.resolve(entryFile);

                    firstLevelPaths.add(entryFile.getName(0));

                    log.debug("{}", resolvedFile);

                    if (entry.isDirectory()) {
                        Files.createDirectories(resolvedFile);
                    } else {
                        unpackEntry(ais, resolvedFile);
                        count.incrementAndGet();
                    }
                } catch (IOException | IllegalStateException | IllegalArgumentException e) {
                    log.error("", e);
                    throw new RuntimeException(e);
                }
            }
        }

        if (firstLevelPaths.size() != 1) {
            throw new IOException("Only archives with a single top-level directory are supported!");
        }

        Path assemblyDir = unpackDir.resolve(firstLevelPaths.iterator().next());

        log.info("Unpacked {} files to {}", count, assemblyDir);

        return assemblyDir;
    }

    static public String format(Path file) {
        String name = file.getFileName().toString();
        if (name.endsWith(".tar.gz")) {
            return "tar.gz";
        } else if (name.endsWith(".zip")) {
            return "zip";
        } else {
            return null;
        }
    }

    static private ArchiveInputStream newArchiveInputStream(Path file) throws IOException {
        String name = file.getFileName().toString();

        if (name.endsWith(".tar.gz")) {
            return new TarArchiveInputStream(new GzipCompressorInputStream(Files.newInputStream(file), true));
        } else if (name.endsWith(".zip")) {
            return new ZipArchiveInputStream(Files.newInputStream(file));
        } else {
            throw new IOException("Unsupported archive file type (we support .tar.gz and .zip)");
        }
    }

    static private void unpackEntry(ArchiveInputStream ais, Path target) throws IOException {
        // always make sure parent dir exists
        Files.createDirectories(target.getParent());

        try (OutputStream os = Files.newOutputStream(target, StandardOpenOption.CREATE,
                StandardOpenOption.TRUNCATE_EXISTING)) {
            try (BufferedOutputStream bos = new BufferedOutputStream(os)) {
                int len = 0;
                byte[] BUFFER = new byte[1024];
                while ((len = ais.read(BUFFER)) != -1) {
                    bos.write(BUFFER, 0, len);
                }
            }
        }
    }

    static public Archive pack(Path unpackedDir, String format) throws IOException {
        String name = unpackedDir.getFileName().toString();
        Path archiveFile = unpackedDir.resolveSibling(name + "." + format);
        return pack(unpackedDir, archiveFile, format);
    }

    static public Archive pack(Path unpackedDir, Path archiveFile, String format) throws IOException {
        log.info("Packing {} to {}", unpackedDir, archiveFile);

        try (ArchiveOutputStream aos = newArchiveOutputStream(archiveFile, format)) {
            packEntry(aos, unpackedDir, unpackedDir.getFileName().toString(), false);
        }

        return new Archive(archiveFile);
    }

    static private ArchiveOutputStream newArchiveOutputStream(Path file, String format) throws IOException {
        switch (format) {
        case "tar.gz":
            TarArchiveOutputStream tgzout = new TarArchiveOutputStream(
                    new GzipCompressorOutputStream(Files.newOutputStream(file)));
            tgzout.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
            tgzout.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            return tgzout;
        case "zip":
            return new ZipArchiveOutputStream(Files.newOutputStream(file));
        default:
            throw new IOException("Unsupported archive file type (we support .tar.gz and .zip)");
        }
    }

    static public void packEntry(ArchiveOutputStream aos, Path dirOrFile, String base, boolean appendName)
            throws IOException {
        String entryName = base;

        if (appendName) {
            if (!entryName.equals("")) {
                if (!entryName.endsWith("/")) {
                    entryName += "/" + dirOrFile.getFileName();
                } else {
                    entryName += dirOrFile.getFileName();
                }
            } else {
                entryName += dirOrFile.getFileName();
            }
        }

        ArchiveEntry entry = aos.createArchiveEntry(dirOrFile.toFile(), entryName);

        if (Files.isRegularFile(dirOrFile)) {
            if (entry instanceof TarArchiveEntry && Files.isExecutable(dirOrFile)) {
                // -rwxr-xr-x
                ((TarArchiveEntry) entry).setMode(493);
            } else {
                // keep default mode
            }
        }

        aos.putArchiveEntry(entry);

        if (Files.isRegularFile(dirOrFile)) {
            Files.copy(dirOrFile, aos);
            aos.closeArchiveEntry();
        } else {
            aos.closeArchiveEntry();
            List<Path> children = Files.list(dirOrFile).collect(Collectors.toList());
            if (children != null) {
                for (Path childFile : children) {
                    packEntry(aos, childFile, entryName + "/", true);
                }
            }
        }
    }

}