org.jasig.ssp.util.importer.job.tasklet.BatchFinalizer.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.ssp.util.importer.job.tasklet.BatchFinalizer.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you 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 org.jasig.ssp.util.importer.job.tasklet;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jasig.ssp.util.importer.job.util.ZipDirectory;
import org.jasig.ssp.util.importer.job.validation.map.metadata.validation.violation.TableViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class BatchFinalizer implements JobExecutionListener {

    private Resource archiveDirectory;
    private Resource inputDirectory;
    private Resource processDirectory;
    private Resource upsertDirectory;
    private String batchTitle = "csv_import";
    private ArchiveType archiveFiles = ArchiveType.UNIQUE;
    private Boolean retainInputFiles = false;
    Logger logger = LoggerFactory.getLogger(BatchFinalizer.class);

    private FilenameFilter csvFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            String lowercaseName = name.toLowerCase();
            if (lowercaseName.endsWith(".csv")) {
                return true;
            } else {
                return false;
            }
        }
    };

    public void setArchiveDirectory(Resource archiveDirectory) throws Exception {
        this.archiveDirectory = archiveDirectory;
        createDirectory(this.archiveDirectory);
    }

    public void setInputDirectory(Resource inputDirectory) {
        this.inputDirectory = inputDirectory;
    }

    public void setProcessDirectory(Resource processDirectory) {
        this.processDirectory = processDirectory;
    }

    public void setUpsertDirectory(Resource upsertDirectory) {
        this.upsertDirectory = upsertDirectory;
    }

    public void setBatchTitle(String batchTitle) {
        this.batchTitle = batchTitle;
    }

    //For testing purposes only
    public void setArchiveFiles(ArchiveType archiveFiles) {
        this.archiveFiles = archiveFiles;
    }

    //For testing purposes only
    public void setRetainInputFiles(Boolean retainInputFiles) {
        this.retainInputFiles = retainInputFiles;
    }

    @Override
    public void beforeJob(JobExecution jobExecution) {

    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        List<Throwable> failureExceptions = jobExecution.getAllFailureExceptions();

        if (failureExceptions != null) {
            for (Throwable failureException : failureExceptions) {
                if (ExceptionUtils.indexOfThrowable(failureException, JobInterruptedException.class) >= 0) {
                    return;
                }
            }
        }
        logger.info("Files deleted and archived");
        Long diff = TimeUnit.MILLISECONDS
                .toMinutes(jobExecution.getEndTime().getTime() - jobExecution.getStartTime().getTime());

        logger.info("Job Duration in minutes: " + diff.toString());

        try {
            if (!archiveFiles.equals(ArchiveType.NONE)) {
                try {
                    archive();
                } catch (Exception e) {
                    logger.error("Error Archiving. Proceeding with file cleanup anyway.", e);
                }
            }
            // Always want to at least attempt clean up. Else behavior of next upload probably isn't what you expect.
            cleanDirectoryQuietly(processDirectory.getFile());
            cleanDirectoryQuietly(upsertDirectory.getFile());
            if (!retainInputFiles) {
                cleanCsvFilesQuietly(inputDirectory.getFile());
            }
        } catch (Exception e) {
            logger.error("Error Delete Process, Upsert And Input Directory", e);
        }
    }

    private void archive() throws IOException {
        try {
            if (archiveFiles.equals(ArchiveType.UNIQUE)) {
                logger.info("Deleting duplicate files prior to archiving");
                if (!retainInputFiles) {
                    removeDuplicates(processDirectory.getFile(), inputDirectory.getFile());
                    removeDuplicates(inputDirectory.getFile(), upsertDirectory.getFile());
                }
                removeDuplicates(processDirectory.getFile(), upsertDirectory.getFile());
            }

            final List<File> filesToZip = Arrays.asList(inputDirectory.getFile(), processDirectory.getFile(),
                    upsertDirectory.getFile());
            // zipIt() will freak out if filesToZip is empty or consists entirely of empty directories.
            if (hasAnyNonDirectoryFiles(filesToZip)) {
                logger.info("Archiving [{}]", filesToZip);
                ZipDirectory zippy = new ZipDirectory(generateFileArchive());
                zippy.zipIt(filesToZip, true);
            } else {
                logger.info("No files to archive after removing duplicates from [{}], [{}], and [{}]",
                        new Object[] { inputDirectory, processDirectory, upsertDirectory });
            }
        } catch (IOException e) {
            throw e;
        }
    }

    private boolean hasAnyNonDirectoryFiles(List<File> files) {
        if (files == null || files.isEmpty()) {
            return false;
        }
        for (File file : files) {
            if (!(file.exists())) {
                continue;
            }
            if (file.isFile()) {
                return true;
            }
            if (file.isDirectory()) {
                final Collection<File> regularFiles = FileUtils.listFiles(file, FileFileFilter.FILE,
                        TrueFileFilter.INSTANCE);
                if (regularFiles != null && !(regularFiles.isEmpty())) {
                    return true;
                }
            }
        }
        return false;
    }

    private void removeDuplicates(File srcDirectory, File destDirectory) {
        if (!destDirectory.exists() || !srcDirectory.exists())
            return;

        File[] destFiles = destDirectory.listFiles(csvFilter);
        File[] srcFiles = srcDirectory.listFiles(csvFilter);
        for (File srcFile : srcFiles) {
            for (File destFile : destFiles) {
                if (srcFile.getName().equals(destFile.getName()) && srcFile.length() == destFile.length()) {
                    srcFile.delete();
                    break;
                }
            }
        }
    }

    private File generateFileArchive() throws IOException {
        Date processDate = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE_MMM_dd_hh_mm_ss_a_yyyy_");
        return new File(archiveDirectory.getFile(),
                dateFormat.format(processDate) + "_importJOB_" + batchTitle + ".zip");
    }

    private void cleanCsvFilesQuietly(File directory) {
        final File[] files;
        if (directory.exists()) {
            try {
                files = directory.listFiles(csvFilter);
            } catch (Exception e) {
                logger.error("Unable to list CSV files in [{}]", directory, e);
                return;
            }

            for (File file : files) {
                if (!(file.delete())) {
                    logger.error("Failed to delete file [{}]", file);
                }
            }
        } else {
            logger.info("Attempting to empty directory [{}], directory does not exist.", directory);
        }
    }

    private void cleanDirectoryQuietly(File directory) {
        if (directory.exists()) {
            try {
                logger.info("Emptying directory [{}]", directory);
                FileUtils.cleanDirectory(directory);
            } catch (Exception e) {
                logger.error("Failed to empty directory [{}]", directory, e);
            }
        } else {
            logger.info("Attempting to empty directory [{}], directory does not exist.", directory);
        }
    }

    private File createDirectory(Resource directory) throws Exception {
        File dir = directory.getFile();
        if (!dir.exists())
            if (!dir.mkdirs())
                throw new Exception("Archive directory not created mkdirs failed. Attempted location: {}"
                        + dir.getAbsolutePath());
        return dir;
    }

}