de.langmi.spring.batch.examples.readers.file.archive.ArchiveMultiResourceItemReader.java Source code

Java tutorial

Introduction

Here is the source code for de.langmi.spring.batch.examples.readers.file.archive.ArchiveMultiResourceItemReader.java

Source

/**
 * Copyright 2011 Michael R. Lange <michael.r.lange@langmi.de>.
 *
 * 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 de.langmi.spring.batch.examples.readers.file.archive;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileInputStream;
import de.schlichtherle.truezip.fs.FsSyncException;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;

/**
 * ArchiveMultiResourceItemReader works with TAR, TAR.GZ, ZIP, B2ZIP, WAR, EAR and JAR 
 * Files. Falls back to normal MultiResourceItemReader behaviour if no archive is set.
 *
 * @author Michael R. Lange <michael.r.lange@langmi.de>
 */
public class ArchiveMultiResourceItemReader<T> extends MultiResourceItemReader<T> {

    private static final Logger LOG = LoggerFactory.getLogger(ArchiveMultiResourceItemReader.class);
    private Resource[] archives;
    private TFile[] wrappedArchives;
    private FilenameFilter filenameFilter = new DefaultArchiveFileNameFilter();

    /**
     * Tries to extract all files in the archives and adds them as resources to
     * the normal MultiResourceItemReader. Overwrites the Comparator from
     * the super class to get it working with itemstreams.
     *
     * @param executionContext
     * @throws ItemStreamException 
     */
    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        // really used with archives?
        if (archives != null) {
            // overwrite the comparator to use description instead of filename
            this.setComparator(new Comparator<Resource>() {

                /** Compares resource descriptions. */
                @Override
                public int compare(Resource r1, Resource r2) {
                    return r1.getDescription().compareTo(r2.getDescription());
                }
            });
            // get the inputStreams from all files inside the archives
            wrappedArchives = new TFile[archives.length];
            List<Resource> extractedResources = new ArrayList<Resource>();
            try {
                for (int i = 0; i < archives.length; i++) {
                    wrappedArchives[i] = new TFile(archives[i].getFile());
                    // iterate over each TFile and get the file list                
                    // extract only the files, ignore directories
                    List<TFile> fileList = new ArrayList<TFile>();
                    runNestedDirs(wrappedArchives[i], fileList, filenameFilter);
                    for (TFile tFile : fileList) {
                        extractedResources
                                .add(new InputStreamResource(new TFileInputStream(tFile), tFile.getName()));
                        LOG.info("using extracted file:" + tFile.getName());
                    }
                }
            } catch (Exception ex) {
                throw new ItemStreamException(ex);
            }
            // propagate extracted resources
            this.setResources(extractedResources.toArray(new Resource[extractedResources.size()]));
        }
        super.open(executionContext);
    }

    /**
     * Calls super.close() and tries to unmount all used archive files afterwards.
     *
     * @throws ItemStreamException 
     */
    @Override
    public void close() throws ItemStreamException {
        super.close();
        if (wrappedArchives != null) {
            for (int i = 0; i < wrappedArchives.length; i++) {
                // release all
                try {
                    TFile.umount(wrappedArchives[i]);
                } catch (FsSyncException ex) {
                    throw new ItemStreamException(ex);
                }
            }
        }
    }

    /**
     * Runs recursively through the rootFile.listFiles() and adds each file to the 
     * fileList. Logs each step - even for directories - to show the archive
     * file contents.
     * 
     * @param rootFile
     * @param fileList 
     * @param filenameFilter
     */
    private static void runNestedDirs(TFile rootFile, List<TFile> fileList, FilenameFilter filenameFilter) {
        TFile files[] = rootFile.listFiles(filenameFilter);
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                TFile tFile = files[i];
                LOG.info("extracting:" + tFile.getAbsolutePath());
                if (tFile.isDirectory()) {
                    runNestedDirs(tFile, fileList, filenameFilter);
                } else {
                    fileList.add(tFile);
                }
            }
        }
    }

    /**
     * Set archive files with normal Spring resources pattern, if not set, the
     * class will fallback to normal MultiResourceItemReader behaviour.
     *
     * @param archives 
     */
    public void setArchives(Resource[] archives) {
        this.archives = archives;
    }

    /**
     * Set a {@link FilenameFilter} to control used files from the archives.
     *
     * @param filenameFilter 
     */
    public void setFilenameFilter(FilenameFilter filenameFilter) {
        this.filenameFilter = filenameFilter;
    }
}