ch.admin.suis.msghandler.util.ZipUtils.java Source code

Java tutorial

Introduction

Here is the source code for ch.admin.suis.msghandler.util.ZipUtils.java

Source

/*
 * $Id: ZipUtils.java 340 2015-08-16 14:51:19Z sasha $
 *
 * Copyright (C) 2006-2012 by Bundesamt fr Justiz, Fachstelle fr Rechtsinformatik
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

package ch.admin.suis.msghandler.util;

import org.apache.commons.lang.Validate;

import java.io.*;
import java.nio.channels.FileLock;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Utility class to help compress and decompress the files for the message
 * handler.
 *
 * @author Alexander Nikiforov
 * @author $Author: sasha $
 * @version $Revision: 340 $
 */
public final class ZipUtils {
    /**
     * logger
     */
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ZipUtils.class.getName());

    private static final int BUFFER_SIZE = 2048;

    private ZipUtils() {

    }

    /**
     * Decompress the given file to the specified directory.
     *
     * @param zipFile the ZIP file to decompress
     * @param toDir   the directory where the files from the archive must be placed; the
     *                file will be replaced if it already exists
     * @return a list of files that were extracted into the destination directory
     * @throws IllegalArgumentException if the provided file does not exist or the specified destination
     *                                  is not a directory
     * @throws IOException              if an IO error has occured (probably, a corrupted ZIP file?)
     */
    public static List<File> decompress(File zipFile, File toDir) throws IOException {
        Validate.isTrue(zipFile.exists(), "ZIP file does not exist", zipFile.getAbsolutePath());
        Validate.isTrue(toDir.isDirectory(), toDir.getAbsolutePath() + " is not a directory");

        final ArrayList<File> files = new ArrayList<>();

        try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)))) {

            // read the entries
            ZipEntry entry;
            while (null != (entry = zis.getNextEntry())) {
                if (entry.isDirectory()) {
                    LOG.error(MessageFormat.format(
                            "cannot extract the entry {0} from the {1}. because it is a directory", entry.getName(),
                            zipFile.getAbsolutePath()));
                    continue;
                }

                // extract the file to the provided destination
                // we have to watch out for a unique name of the file to be extracted:
                // it can happen, that several at the same time incoming messages have a file with the same name
                File extracted = new File(FileUtils.getFilename(toDir, entry.getName()));
                if (!extracted.getParentFile().mkdirs()) {
                    LOG.debug("cannot make all the necessary directories for the file "
                            + extracted.getAbsolutePath() + " or " + "the path is already created ");
                }

                try (BufferedOutputStream dest = new BufferedOutputStream(new FileOutputStream(extracted),
                        BUFFER_SIZE)) {
                    byte[] data = new byte[BUFFER_SIZE];
                    int count;
                    while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) {
                        dest.write(data, 0, count);
                    }

                    files.add(extracted);
                }
            }

        }

        return files;
    }

    /**
     * Creates a new unique ZIP file in the destination directory and adds to it
     * the provided collection of files.
     *
     * @param toDir The name of the file created
     * @param files The files to compress
     * @return A Zipped File
     * @throws IOException if the file cannot be created because of a IO error
     */
    public static File compress(File toDir, Collection<File> files) throws IOException {
        final File zipFile = File.createTempFile("data", ".zip", toDir);

        // was there an exception?
        boolean exceptionThrown = false;

        try (ZipOutputStream zout = new ZipOutputStream(
                new BufferedOutputStream(new FileOutputStream(zipFile), BUFFER_SIZE))) {

            byte[] data = new byte[BUFFER_SIZE];

            for (File file : files) {
                // create the entry
                zout.putNextEntry(new ZipEntry(file.getName()));

                FileInputStream in = new FileInputStream(file);

                try (FileLock lock = in.getChannel().tryLock(0, Long.MAX_VALUE, true)) {
                    isInValid(lock, in);
                    int len;

                    // write the file to the entry
                    while ((len = in.read(data)) > 0) {
                        zout.write(data, 0, len);
                    }
                    lock.release();
                } finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        LOG.error("cannot properly close the opened file " + file.getAbsolutePath(), e);
                    }
                }

            }
        } catch (IOException e) {
            LOG.error("error while creating the ZIP file " + zipFile.getAbsolutePath(), e);
            // mark for the finally block
            exceptionThrown = true;
            // rethrow - the finally block is only for the first exception
            throw e;
        } finally {
            // remove the file in case of an exception
            if (exceptionThrown && !zipFile.delete()) {
                LOG.error("cannot delete the file " + zipFile.getAbsolutePath());
            }
        }

        return zipFile;
    }

    private static void isInValid(FileLock lock, FileInputStream in) throws IOException {
        if (lock == null) {
            // the lock cannot be taken, report an Exception
            // First, we need to close the FileInputStream, otherwise it's gonna mess things around
            in.close(); // Could throw an IOException but it's pretty much what's going to happen here.
            throw new IOException(
                    "cannot apply lock to the file {0}, the file is probably locked by another application");
        }
    }
}