gov.nih.nci.ncicb.tcga.dcc.qclive.common.action.ArchiveExpander.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.ncicb.tcga.dcc.qclive.common.action.ArchiveExpander.java

Source

/*
 * Software License, Version 1.0 Copyright 2010 SRA International, Inc.
 * Copyright Notice.  The software subject to this notice and license includes both human
 * readable source code form and machine readable, binary, object code form (the "caBIG
 * Software").
 *
 * Please refer to the complete License text for full details at the root of the project.
 */

package gov.nih.nci.ncicb.tcga.dcc.qclive.common.action;

import gov.nih.nci.ncicb.tcga.dcc.ConstantValues;
import gov.nih.nci.ncicb.tcga.dcc.common.bean.Archive;
import gov.nih.nci.ncicb.tcga.dcc.common.util.FileUtil;
import gov.nih.nci.ncicb.tcga.dcc.qclive.common.QcContext;
import gov.nih.nci.ncicb.tcga.dcc.qclive.common.util.DirectoryListerImpl;
import org.apache.commons.io.IOUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * This takes an Archive and expands it into a directory in the same location
 * as the archive.  The files will be put in a directory given the name of the archive.
 *
 * @author Jessica Chen
 *         Last updated by: $Author: sfeirr $
 * @version $Rev: 3419 $
 */
public class ArchiveExpander extends AbstractProcessor<Archive, Archive> {

    /**
     * Expands the archive into a directory whose name is the archive name.  Puts the directory
     * in the same location as the archive.  Flattens the structure, so that all files are in the
     * same directory.  Will warn if there are nested or oddly-named directories, or if there is no
     * directory named after the archive, but will still succeed.
     *
     * @param archive the archive to expand
     * @return the same archive, which has been expanded
     * @throws gov.nih.nci.ncicb.tcga.dcc.qclive.common.action.Processor.ProcessorException
     *          if there is an unrecoverable error during expansion
     */
    protected Archive doWork(final Archive archive, final QcContext context) throws ProcessorException {
        try {
            final File expandDir = makeArchiveDir(archive);
            final String archiveName = archive.getArchiveFile().getName();
            if (archiveName.endsWith(ConstantValues.COMPRESSED_ARCHIVE_EXTENSION)
                    || archiveName.endsWith(ConstantValues.UNCOMPRESSED_ARCHIVE_EXTENSION)) {
                expandTarOrTarGz(expandDir, archive, context);
            } else {
                throw new ProcessorException(new StringBuilder().append("Archive is not correct type: '")
                        .append(ConstantValues.COMPRESSED_ARCHIVE_EXTENSION).append("' expected").toString());
            }
        } catch (IOException e) {
            throw new ProcessorException(
                    new StringBuilder().append("Error expanding archive: ").append(e.getMessage()).toString(), e);
        }
        try {
            final File[] theFilesInDirectoryList = DirectoryListerImpl
                    .getFilesInDir(archive.getExplodedArchiveDirectoryLocation());
            if ((theFilesInDirectoryList == null) || (theFilesInDirectoryList.length < 1)) {
                throw new ProcessorException("The expanded archive directory does not contain any files");
            }
            final File[] hiddenFiles = DirectoryListerImpl
                    .getHiddenFilesInDir(archive.getExplodedArchiveDirectoryLocation());
            if (hiddenFiles != null && hiddenFiles.length > 0) {
                final StringBuilder errorMessage = new StringBuilder();
                errorMessage.append("Archives may not contain hidden files, but found: ");
                for (int i = 0; i < hiddenFiles.length; i++) {
                    errorMessage.append(hiddenFiles[i].getName());
                    if (i < hiddenFiles.length - 1) {
                        errorMessage.append(", ");
                    }
                }
                throw new ProcessorException(errorMessage.toString());
            }
        } catch (IOException e) {
            throw new ProcessorException(
                    new StringBuilder().append("I/O error checking the expanded archive directory: ")
                            .append(e.getMessage()).toString(),
                    e);
        }
        archive.setExpanded();
        return archive;
    }

    private File makeArchiveDir(final Archive archive) throws IOException, ProcessorException {
        File explodedDir = new File(archive.getExplodedArchiveDirectoryLocation());
        if (explodedDir.exists() && explodedDir.isDirectory()) {
            // clean up if exists
            boolean isDeleteSuccesfull = FileUtil.deleteDir(explodedDir);
            if (!isDeleteSuccesfull) {
                // something must be holding a lock , bail out
                throw new ProcessorException(new StringBuilder().append(" Unable to delete ")
                        .append(archive.getExplodedArchiveDirectoryLocation())
                        .append(" while expanding the archive: ").append(archive.getArchiveName()).toString());
            }
        }

        final File archiveDir = FileUtil.makeDir(archive.getExplodedArchiveDirectoryLocation());
        if (!archiveDir.exists()) {
            throw new ProcessorException(
                    new StringBuilder().append("Error expanding '").append(archive.getArchiveFile().getName())
                            .append("': could not create archive directory").toString());
        }
        if (!archiveDir.canWrite()) {
            throw new ProcessorException(
                    new StringBuilder().append("Error expanding '").append(archive.getArchiveFile().getName())
                            .append("': can't write to ").append(archiveDir.getAbsolutePath()).toString());
        }
        return archiveDir;
    }

    /**
     * Expands the given {@link Archive} provided that it is a tar or tar.gz archive.
     *
     * @param expandDir the directory under which to expand the archive
     * @param archive the archive
     * @param context the context
     * @throws IOException
     */
    private void expandTarOrTarGz(final File expandDir, final Archive archive, final QcContext context)
            throws IOException {

        try {
            FileUtil.explodeTarOrTarGz(expandDir, archive.fullArchivePathAndName());
        } catch (final IOException ie) {
            final StringTokenizer stk = new StringTokenizer(ie.getMessage(), "\n");
            while (stk.hasMoreTokens()) {
                context.addWarning(stk.nextToken());
            }
        }
    }

    /**
     * @return the name of this process step
     */
    public String getName() {
        return "archive expander";
    }

    private void expandZip(final File expandDir, final Archive archive, final QcContext context)
            throws IOException {

        ZipFile zipFile = null;
        BufferedOutputStream out = null;
        try {
            zipFile = new ZipFile(archive.fullArchivePathAndName());
            // write each entry to the archive directory
            final Enumeration zipEnum = zipFile.entries();
            boolean foundArchiveDir = false;
            while (zipEnum.hasMoreElements()) {
                final ZipEntry entry = (ZipEntry) zipEnum.nextElement();
                if (!entry.isDirectory()) {
                    String entryName = entry.getName();
                    final File entryFile = new File(entryName);
                    // extract out just the filename of the entry
                    if (entryFile.getCanonicalPath().lastIndexOf(File.separator) != -1) {
                        entryName = entryFile.getCanonicalPath()
                                .substring(entryFile.getCanonicalPath().lastIndexOf(File.separator));
                    }
                    // the file to write is the archive dir plus just the filename
                    final File archiveFile = new File(expandDir, entryName);
                    InputStream in = zipFile.getInputStream(entry);
                    FileOutputStream fout = new FileOutputStream(archiveFile);
                    //noinspection IOResourceOpenedButNotSafelyClosed
                    out = new BufferedOutputStream(fout);
                    copyInputStream(in, out);
                    in = null;
                    out = null;
                    fout.close();
                    fout = null;

                } else {
                    // if find a directory that isn't the archive name, add warning for weird archive structure
                    if (!entry.getName().equals(archive.getArchiveName())
                            && !entry.getName().equals(archive.getArchiveName() + "/")) {
                        context.addWarning(new StringBuilder().append("Archive '")
                                .append("' has a non-standard directory '").append(entry.getName()).append("'.")
                                .append("Archive files should be contained inside a single directory with the archive name as its name.")
                                .toString());
                    } else {
                        foundArchiveDir = true;
                    }
                }
            }
            if (!foundArchiveDir) {
                context.addWarning(
                        "Archive files should be contained inside a single directory with the archive name as its name.");
            }
        } finally {
            IOUtils.closeQuietly(out);

            if (zipFile != null) {
                zipFile.close();
            }
        }
    }

    /*
     * Copies what is read from in into out.
     */

    private void copyInputStream(final InputStream in, final OutputStream out) throws IOException {
        final byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) >= 0) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.flush();
        out.close();

    }

}