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