com.mattc.argus2.concurrent.ZipProcess.java Source code

Java tutorial

Introduction

Here is the source code for com.mattc.argus2.concurrent.ZipProcess.java

Source

/*
 * Argus Installer v2 -- A Better School Zip Alternative Copyright (C) 2014 Matthew
 * Crocco
 * 
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */
package com.mattc.argus2.concurrent;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipException;

// Apache Commons accounts much better for LARGE Zip Archives
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;

import com.mattc.argus2.annotations.Decompressor;
import com.mattc.argus2.util.Console;
import com.mattc.argus2.util.IOUtils;
import com.mattc.argus2.util.Utility;

/**
 * 
 * Thread to manage Unzipping Zip Archives in an Appropriate Manner. <br />
 * <br />
 * Unzip's Archives using a Buffer to accelerate extraction speeds to far beyond what
 * is found on most secondary school computers. <br />
 * <br />
 * Threaded to Allow Multiple Extractions at Once
 * 
 * @author Matthew Crocco
 */
@Decompressor("zip")
public class ZipProcess implements DecompressProcess {

    private final File zipFile; // Zip Archive to Extract
    private final File destDir; // Root Destination Directory
    private ZipFile zip;
    private FileOutputStream fos; // Output Stream to Extract With

    // Allocation Buffer
    private final byte[] buffer = new byte[Decompressors.BUFFER_SIZE];
    // Thread Monitor Value
    private final AtomicBoolean running = new AtomicBoolean(false);

    // For Initialization
    protected ZipProcess() {
        this.destDir = null;
        this.zipFile = null;
    }

    public ZipProcess(File destDir, File zipFile) {
        if (!zipFile.exists())
            throw new IllegalArgumentException("The desired ZipFile Does Not Exist");
        if (!destDir.exists()) {
            destDir.mkdirs();
        }

        this.destDir = destDir;
        this.zipFile = zipFile;
    }

    @Override
    public void run() {
        this.running.set(true);

        // Zip Archive Entry Input Stream
        InputStream zis = null;

        try {

            this.zip = new ZipFile(this.zipFile);
            ZipArchiveEntry entry;
            Enumeration<ZipArchiveEntry> entries;

            entries = this.zip.getEntriesInPhysicalOrder();

            // While there are still Entries to process, iterate
            while (entries.hasMoreElements()) {
                entry = entries.nextElement();
                // Create the Empty File to Extract to and its Parent Directory
                // Notify Console and ProgressLog that a File is Being Extracted
                final File destination = new File(this.destDir, entry.getName());
                final File dirs = new File(destination.getParent());
                dirs.mkdirs();
                Console.info("EXT: " + Utility.relativizePath(destination, this.destDir.getParentFile()));

                /*
                 * IMPORTANT
                 * 
                 * Ensures that All Files have a Directory to go to.
                 * 
                 * If a Folder is for some reason created as a File, this will detect
                 * that. It will delete the file and re-create it as a Directory so
                 * that installation can continue.
                 * 
                 * If all else fails, print debug information and stop extracting. It
                 * is unlikely the installed application will work without all files
                 * being extracted.
                 */
                try {
                    if (!destination.getParentFile().isDirectory()) {
                        if (!destination.getParentFile().delete())
                            throw new IOException(
                                    "Failure to Delete Directory! - " + destination.getCanonicalPath());
                        destination.getParentFile().mkdirs();
                    }
                    if (!destination.createNewFile() && !destination.exists())
                        throw new IOException("Failure to create Destination! - " + destination.getPath());
                } catch (final IOException e) {
                    final String errMsg = "Failure to Extract " + this.zipFile.getName();
                    final String errPath = "PATH = " + destination.getCanonicalPath();
                    final String errParent = "PARENT = " + destination.getParentFile().getCanonicalPath();
                    final String errIsDir = "PARENT_IS_DIRECTORY = " + destination.getParentFile().isDirectory();

                    // Standard IO Error Information
                    e.printStackTrace(System.err);
                    System.err.println(errMsg);
                    System.err.println(errPath);
                    System.err.println(errParent);
                    System.err.println(errIsDir);

                    // Log File Error Information (Possibly Standard IO if DEBUG
                    // = True)
                    Console.exception(e);
                    Console.error(errMsg);
                    Console.error(errPath);
                    Console.error(errParent);
                    Console.error(errIsDir);

                    // Attempt to Delete the Partially Unzipped and
                    // Non-functional install Directory
                    if (!IOUtils.deleteDirectory(this.destDir)) {
                        Console.error("Although extraction failed, the erroneous directory could not be removed!");
                    } else {
                        Console.info("Erroneous Directory Deleted Successfully!");
                    }

                }

                // Establish a Stream to output data to the destination file
                zis = this.zip.getInputStream(entry);
                this.fos = new FileOutputStream(destination);

                // Read Zip Entry data into buffer, output buffer to
                // installation file
                for (int c = zis.read(this.buffer); c > 0; c = zis.read(this.buffer)) {
                    this.fos.write(this.buffer, 0, c);
                }

                // Close Current Entry and Destination OutputStream
                zis.close();
                this.fos.close();
            }
        } catch (final ZipException e) {
            Console.exception(e);
        } catch (final IOException e) {
            Console.exception(e);
        } finally { // Ensure that All Streams Are Closed to prevent Memory
            // Leaks
            IOUtils.closeSilently(zis);
            IOUtils.closeSilently(this.fos);
            IOUtils.closeSilently(this.zip);
            this.running.set(false);
        }
    }

    public boolean isRunning() {
        return this.running.get();
    }

    @Override
    public String getArchiveName() {
        return this.zipFile.getName();
    }

    @Override
    public String getDestination() {
        return this.destDir.getAbsolutePath();
    }

    @Override
    public boolean isAcceptableFile(File file) {
        final String suffix = IOUtils.getSuffix(file).toLowerCase();
        return suffix.equals("zip");
    }

    @Override
    public String toString() {
        return "[ZipDecompress " + getArchiveName() + " to " + getDestination() + "]";
    }
}