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

Java tutorial

Introduction

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

Source

/*Argus -- A Zip Installer for Circumventing Common Educational Web Blocks
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.argus.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.argus.Notifications;
import com.mattc.argus.util.Console;
import com.mattc.argus.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 <br />
 * what is found on most secondary school computers. <br />
 * <br />
 * Threaded to Allow Multiple Extractions at Once
 * 
 * @author Matthew Crocco
 */
public class ZipProcess implements DecompressProcess {

    /**Determined Appropriate Buffer Size*/
    public final int BUFFER_SIZE;

    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
    private final byte[] buffer; //Allocation Buffer
    private final AtomicBoolean running; //Thread Monitor Value

    public ZipProcess(File zipFile, String dest) {
        this(zipFile, new File(dest));
    }

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

        //Get Buffer Size and Create Byte Buffer
        BUFFER_SIZE = 8192; //Standard Cache Size
        buffer = new byte[BUFFER_SIZE];
        this.destDir = destDir;
        this.zipFile = zipFile;

        running = new AtomicBoolean(false);
    }

    public void run() {
        running.set(true);

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

        try {

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

            entries = 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
                File destination = new File(destDir, entry.getName());
                File dirs = new File(destination.getParent());
                dirs.mkdirs();
                Console.info("EXT: " + Utility.relativizePath(destination, destDir.getParentFile()));
                Notifications.updateLog("Extracting " + destination.getName()
                        + (entry.isDirectory() ? " as Directory" : " as File") + "...");

                /*
                 * 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()) {
                        destination.getParentFile().delete();
                        destination.getParentFile().mkdirs();
                    }
                    destination.createNewFile();
                } catch (IOException e) {
                    String errMsg = "Failure to Extract " + zipFile.getName();
                    String errPath = "PATH = " + destination.getCanonicalPath();
                    String errParent = "PARENT = " + destination.getParentFile().getCanonicalPath();
                    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);

                    //GUI Error Information. For End User.
                    String msg = errMsg + "\n" + errPath + "\n" + errParent + "\n" + errIsDir;
                    Notifications.exception(msg, e);

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

                }

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

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

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

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

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

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