org.geoserver.wps.ppio.ZipArchivePPIO.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.wps.ppio.ZipArchivePPIO.java

Source

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
 * (c) 2001 - 2013 OpenPlans
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.wps.ppio;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.geoserver.data.util.IOUtils;
import org.geotools.util.logging.Logging;

/**
 * Handles input and output of feature collections as zipped files.
 * 
 * @author "Alessio Fabiani - alessio.fabiani@geo-solutions.it"
 * @author Simone Giannecchini, GeoSolutions SAS
 */
public class ZipArchivePPIO extends BinaryPPIO {

    public static final String ZIP = "zip";

    private final static Logger LOGGER = Logging.getLogger(ZipArchivePPIO.class);

    /** Parameter indicating the compression level to use */
    private int compressionLevel;

    /**
     * Instantiates a new zip archive ppio.
     * 
     * @param resources the resources
     */
    public ZipArchivePPIO(int compressionLevel) {
        super(File.class, File.class, "application/zip");
        if (compressionLevel < ZipOutputStream.STORED || compressionLevel > ZipOutputStream.DEFLATED) {
            throw new IllegalArgumentException("Invalid Compression Level: " + compressionLevel);
        }
        this.compressionLevel = compressionLevel;
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "Using compression level " + compressionLevel);
        }
    }

    /**
     * Default constructor using ZipOutputStream.STORED compression level.
     * 
     */
    public ZipArchivePPIO() {
        this(ZipOutputStream.STORED);
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "Using compression level " + ZipOutputStream.STORED);
        }
    }

    /**
     * Encodes the output file.
     * 
     * @param output the output
     * @param os the os
     * @throws Exception the exception
     */
    @SuppressWarnings("rawtypes")
    @Override
    public void encode(final Object output, OutputStream os) throws Exception {
        // avoid double zipping
        if (output instanceof File && isZpFile((File) output)) {
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.log(Level.FINE, "File is already a zip, we have only to copy it");
            }
            FileUtils.copyFile((File) output, os);
            return;
        }

        ZipOutputStream zipout = new ZipOutputStream(os);
        zipout.setLevel(compressionLevel);

        // directory
        if (output instanceof File) {
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.log(Level.FINE, "Zipping the file");
            }
            final File file = ((File) output);
            if (file.isDirectory()) {
                IOUtils.zipDirectory(file, zipout, FileFilterUtils.trueFileFilter());
            } else {
                // check if is a zip file already
                zipFile(file, zipout);
            }
        } else {
            // list of files
            if (output instanceof Collection) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.log(Level.FINE, "Zipping the collection");
                }
                // create temp dir
                final Collection collection = (Collection) output;
                for (Object obj : collection) {
                    if (obj instanceof File) {
                        // convert to file and add to zip
                        final File file = ((File) obj);
                        if (file.isDirectory()) {
                            IOUtils.zipDirectory(file, zipout, FileFilterUtils.trueFileFilter());
                        } else {
                            // check if is a zip file already
                            zipFile(file, zipout);
                        }
                    } else {
                        if (LOGGER.isLoggable(Level.INFO)) {
                            LOGGER.info("Skipping object -->" + obj.toString());
                        }
                    }
                }
            } else {
                // error
                throw new IllegalArgumentException("Unable to zip provided output. Output-->" + output != null
                        ? output.getClass().getCanonicalName()
                        : "null");
            }
        }
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "Finished to zip");
        }
        zipout.finish();
    }

    /**
     * Gets the file extension.
     * 
     * @return the file extension
     */
    @Override
    public String getFileExtension() {
        return ZIP;
    }

    /**
     * This method zip the provided file to the provided {@link ZipOutputStream}.
     * 
     * <p>
     * It throws {@link IllegalArgumentException} in case the provided file does not exists or is not a readable file.
     * 
     * @param file the {@link File} to zip
     * @param zipout the {@link ZipOutputStream} to write to
     * @throws IOException in case something bad happen
     */
    public static void zipFile(File file, ZipOutputStream zipout) throws IOException {
        // copy file by reading 4k at a time (faster than buffered reading)
        byte[] buffer = new byte[4096];
        zipFileInternal(file, zipout, buffer);

    }

    /**
     * This method tells us if the provided {@link File} is a Zip File.
     * 
     * <p>
     * It throws {@link IllegalArgumentException} in case the provided file does not exists or is not a readable file.
     * 
     * @param file the {@link File} to check for zip
     * @throws IOException in case something bad happen
     */
    public static boolean isZpFile(File file) {
        if (file == null || !file.exists() || !file.canRead()) {
            throw new IllegalArgumentException(
                    "Provided File is not valid and/or reqadable! --> File:" + file != null ? file.getAbsolutePath()
                            : "null");
        }
        // Check if the file is a directory
        if (file.isDirectory()) {
            return false;
        }
        // Check on the path length
        if (file.length() < 4) {
            return false;
        }
        // Check on the first Integer
        DataInputStream in = null;
        try {
            in = new DataInputStream(new FileInputStream(file));

            int test = in.readInt();
            return test == 0x504b0304;
        } catch (IOException e) {
            if (LOGGER.isLoggable(Level.SEVERE)) {
                LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
            }
            return false;
        } finally {
            if (in != null) {
                org.apache.commons.io.IOUtils.closeQuietly(in);
            }
        }
    }

    /**
     * This method zip the provided file to the provided {@link ZipOutputStream}.
     * 
     * <p>
     * It throws {@link IllegalArgumentException} in case the provided file does not exists or is not a readable file.
     * 
     * @param file the {@link File} to zip
     * @param zipout the {@link ZipOutputStream} to write to
     * @param buffer the buffer to use for reading/writing
     * @throws IOException in case something bad happen
     */
    private static void zipFileInternal(File file, ZipOutputStream zipout, byte[] buffer) throws IOException {
        if (file == null || !file.exists() || !file.canRead()) {
            throw new IllegalArgumentException(
                    "Provided File is not valid and/or reqadable! --> File:" + file != null ? file.getAbsolutePath()
                            : "null");
        }

        final ZipEntry entry = new ZipEntry(FilenameUtils.getName(file.getAbsolutePath()));
        zipout.putNextEntry(entry);

        // copy over the file
        InputStream in = null;
        try {
            int c;
            in = new FileInputStream(file);
            while (-1 != (c = in.read(buffer))) {
                zipout.write(buffer, 0, c);
            }
            zipout.closeEntry();
        } finally {
            // close the input stream
            if (in != null) {
                org.apache.commons.io.IOUtils.closeQuietly(in);
            }
        }
        zipout.flush();
    }

    @Override
    public Object decode(InputStream input) throws Exception {
        throw new UnsupportedOperationException("Decode unsupported");
    }
}