Java tutorial
/*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(); } }