edu.umd.cs.marmoset.modelClasses.ZipFileAggregator.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.marmoset.modelClasses.ZipFileAggregator.java

Source

/**
 * Marmoset: a student project snapshot, submission, testing and code review
 * system developed by the Univ. of Maryland, College Park
 * 
 * Developed as part of Jaime Spacco's Ph.D. thesis work, continuing effort led
 * by William Pugh. See http://marmoset.cs.umd.edu/
 * 
 * Copyright 2005 - 2011, Univ. of Maryland
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 */

package edu.umd.cs.marmoset.modelClasses;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.CopyUtils;

/**
 * Take a number of smaller zip files and aggregate them
 * into a single large zip file.
 *
 * @author David Hovemeyer
 */
public class ZipFileAggregator {
    /**
     * This kind of exception is thrown if we try to
     * add an invalid zip file to the aggregate.
     */
    public static class BadInputZipFileException extends Exception {
        public BadInputZipFileException(String msg, Throwable e) {
            super(msg, e);
        }
    }

    //   /**
    //    * Delegating InputStream that never closes the underlying
    //    * stream.
    //    */
    //   private static class NoCloseInputStream extends DataInputStream {
    //      public NoCloseInputStream(InputStream in) {
    //         super(in);
    //      }
    //
    //      public void close() {
    //         // do nothing
    //      }
    //   }

    // Fields
    private ZipOutputStream zipOutput;

    /**
     * Constructor.
     * @param out the OutputStream to write aggregated zip output to
     */
    public ZipFileAggregator(OutputStream out) {
        this.zipOutput = new ZipOutputStream(out);
    }

    /**
     * Close the zip output stream.
     * This needs to be called after all input zip files
     * have been added.
     *
     * @throws IOException
     */
    public void close() throws IOException {
        if (zipOutput != null) {
            zipOutput.close();
        }
    }

    public void addPlainFileFromBytes(String name, byte[] bytes) throws IOException, BadInputZipFileException {
        // Create output entry
        ZipEntry outputEntry = new ZipEntry(name);
        zipOutput.putNextEntry(outputEntry);

        zipOutput.write(bytes);
        zipOutput.closeEntry();
    }

    public void addFileFromBytes(String dirName, byte[] bytes) throws IOException, BadInputZipFileException {
        addFileFromBytes(dirName, -1, bytes);
    }

    public void addFileFromBytes(String dirName, long time, byte[] bytes)
            throws IOException, BadInputZipFileException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        if (isValidZipfile(bytes)) {
            addZipFileFromInputStream(dirName, time, bais);
        } else {
            addFileFromInputStream(dirName, time, bais);
        }
    }

    private void addFileFromInputStream(String dirName, long time, InputStream inputStream) throws IOException {
        // Prepend directory name
        String name = dirName + "/nonzipfile";

        // Create output entry
        ZipEntry outputEntry = new ZipEntry(name);
        if (time > 0L)
            outputEntry.setTime(time); // only add valid times
        zipOutput.putNextEntry(outputEntry);

        // Copy zip input to output
        CopyUtils.copy(inputStream, zipOutput);

        zipOutput.closeEntry();
    }

    /**
     * Adds a zipfile from an inputStream to the aggregate zipfile.
     *
     * @param dirName name of the top-level directory that will be
     * @param inputStream the inputStream to the zipfile created in the aggregate zip file
     * @throws IOException
     * @throws BadInputZipFileException
     */
    private void addZipFileFromInputStream(String dirName, long time, InputStream inputStream)
            throws IOException, BadInputZipFileException {
        // First pass: just scan through the contents of the
        // input file to make sure it's really valid.
        ZipInputStream zipInput = null;
        try {
            zipInput = new ZipInputStream(new BufferedInputStream(inputStream));
            ZipEntry entry;
            while ((entry = zipInput.getNextEntry()) != null) {
                zipInput.closeEntry();
            }
        } catch (IOException e) {
            throw new BadInputZipFileException("Input zip file seems to be invalid", e);
        } finally {
            if (zipInput != null)
                zipInput.close();
        }

        // FIXME: It is probably wrong to call reset() on any input stream; for my application the inputStream will only ByteArrayInputStream or FileInputStream
        inputStream.reset();

        // Second pass: read each entry from the input zip file,
        // writing it to the output file.
        zipInput = null;
        try {
            // add the root directory with the correct timestamp
            if (time > 0L) {
                // Create output entry
                ZipEntry outputEntry = new ZipEntry(dirName + "/");
                outputEntry.setTime(time);
                zipOutput.closeEntry();
            }

            zipInput = new ZipInputStream(new BufferedInputStream(inputStream));
            ZipEntry entry;
            while ((entry = zipInput.getNextEntry()) != null) {
                try {
                    String name = entry.getName();
                    // Convert absolute paths to relative
                    if (name.startsWith("/")) {
                        name = name.substring(1);
                    }

                    // Prepend directory name
                    name = dirName + "/" + name;

                    // Create output entry
                    ZipEntry outputEntry = new ZipEntry(name);
                    if (time > 0L)
                        outputEntry.setTime(time);
                    zipOutput.putNextEntry(outputEntry);

                    // Copy zip input to output
                    CopyUtils.copy(zipInput, zipOutput);
                } catch (Exception zex) {
                    // ignore it
                } finally {
                    zipInput.closeEntry();
                    zipOutput.closeEntry();
                }
            }
        } finally {
            if (zipInput != null) {
                try {
                    zipInput.close();
                } catch (IOException ignore) {
                    // Ignore
                }
            }
        }
    }

    /**
     * Add a zip file to be added to the aggregate.
     *
     * @param dirName   name of the top-level directory that will be
     *                  created in the aggregate zip file
     * @param inputFile the zip file to be added
     *
     * @throws BadInputZipFileException if the input file seems
     *                     to be invalid; in this case,
     *                     we do <em>not</em> write any
     *                     output to the output file, and
     *                     writing of other zip files to
     *                     the aggregate may continue
     * @throws IOException if an error occurs while copying the
     *                     input entries to the aggregate output;
     *                     in this case, the aggregate output is
     *                     probably corrupted or incomplete
     */
    public void addFile(String dirName, File inputFile) throws IOException, BadInputZipFileException {
        long time = -1L;

        // safely check the time in the file
        try {
            time = inputFile.lastModified();
        } catch (Exception e) {
        }

        if (time <= 0L)
            time = -1L;

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(inputFile);
            addZipFileFromInputStream(dirName, time, fis);
        } finally {
            if (fis != null)
                fis.close();
        }
    }

    // Just for testing.
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("inputfile1.zip");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        CopyUtils.copy(fis, baos);

        byte[] byteArr = baos.toByteArray();
        fis.close();

        fis = new FileInputStream("inputfile2.zip");
        baos = new ByteArrayOutputStream();
        CopyUtils.copy(fis, baos);

        byteArr = baos.toByteArray();
        fis.close();

        //      if (args.length < 2) {
        //         System.err.println("Usage: " +
        //               ZipFileAggregator.class.getName() +
        //               " <output file> <file1, file2...>");
        //         System.exit(1);
        //      }
        //
        //      ZipFileAggregator agg=null;
        //      try {
        //         agg=new ZipFileAggregator(new FileOutputStream(args[0]));
        //         for (int i = 1; i < args.length; ++i) {
        //            File inputFile = new File(args[i]);
        //            System.out.println("Adding " + args[i]);
        //            try {
        //               agg.addFile(args[i], inputFile);
        //            } catch (ZipFileAggregator.BadInputZipFileException e) {
        //               System.out.println("Recoverable exception: " + e);
        //               System.out.println("Continuing...");
        //            }
        //         }
        //      } finally {
        //         if (agg != null) agg.close();
        //      }
        //      System.out.println("Done!");
    }

    public static boolean isValidZipfile(byte[] byteArr) {
        ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(byteArr));
        boolean areBytesAZipfile = false;
        try {
            while (true) {
                ZipEntry entry = zipIn.getNextEntry();
                if (entry == null)
                    break;
                // We need to read at least one valid entry for this to be a valid zipfile
                areBytesAZipfile = true;
            }
        } catch (Throwable e) {
            return false;
        }
        return areBytesAZipfile;
    }

}