ch.admin.suis.msghandler.util.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for ch.admin.suis.msghandler.util.FileUtils.java

Source

/*
 * $Id: FileUtils.java 327 2014-01-27 13:07:13Z blaser $
 *
 * Copyright (C) 2006-2012 by Bundesamt fr Justiz, Fachstelle fr Rechtsinformatik
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */
package ch.admin.suis.msghandler.util;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.Validate;

import java.io.*;
import java.nio.channels.FileLock;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The
 * <code>FileUtils</code> class is a utility class to handle the file name transformations.
 *
 * @author Alexander Nikiforov
 * @author $Author: blaser $
 * @version $Revision: 327 $
 */
public final class FileUtils {

    /**
     * logger
     */
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FileUtils.class.getName());

    private static final int MAX_FILE_COUNT = 100;

    private static final int BUFFER_SIZE = 2048;

    /**
     * the counter to
     */
    private static final AtomicInteger COUNTER = new AtomicInteger(0);

    private static final String NOT_A_DIRECTORY = " is not a directory";

    private FileUtils() {
    }

    /**
     * Formats the name of the data file for the provided message ID.
     *
     * @param messageId The message ID
     * @return The formed name of the file
     */
    public static String getDataFilename(String messageId) {
        return "data_" + messageId + ".zip";
    }

    /**
     * Formats the name of the data file for the provided message ID.
     *
     * @param messageId The message ID
     * @return The formed name of the envelope
     */
    public static String getEnvelopeFilename(String messageId) {
        return "envl_" + messageId + ".xml";
    }

    /**
     * Returns a name of the file in the given directory. If the file with the provided name already exists in that
     * directory, this method returns a new name that is formed by adding the current timestamp value in milliseconds to
     * the provided name. Otherwise the name remains unchanged. This method returns the absolute filepath.
     *
     * @param inputDir where the file should be placed
     * @param name     current file name
     * @return the absolute path inclusive filename
     */
    public static String getFilename(final File inputDir, String name) {
        Validate.isTrue(inputDir.isDirectory(), inputDir.getAbsolutePath() + NOT_A_DIRECTORY);

        File local = new File(inputDir, name);
        if (local.exists()) {
            int count = COUNTER.incrementAndGet();
            count = count % MAX_FILE_COUNT; //

            int insertPos = name.lastIndexOf('.');
            if (insertPos >= 0) {
                // the dot is there, so replace it
                StringBuilder sb = new StringBuilder(name);
                String uniquePart = "-" + System.currentTimeMillis() + "-" + count;
                sb.insert(insertPos, uniquePart);
                return new File(inputDir, sb.toString()).getAbsolutePath();
            } else {
                // just add to the end
                return new File(inputDir, name + "-" + System.currentTimeMillis() + "-" + count).getAbsolutePath();
            }
        } else {
            // the file does not exist yet and can be written without any change to
            // its name
            return local.getAbsolutePath();
        }
    }

    /**
     * Moves the file to the given directory, renaming it if a file with the same name already exists there.
     *
     * @param srcFile The file to move
     * @param destDir The destination dir
     */
    public static String moveToDirectory(File srcFile, File destDir) throws IOException {
        Validate.isTrue(!srcFile.isDirectory(), srcFile.getAbsolutePath() + " is a directory");
        Validate.isTrue(destDir.isDirectory(), destDir.getAbsolutePath() + NOT_A_DIRECTORY);

        File destFile = new File(FileUtils.getFilename(destDir, srcFile.getName()));
        moveFile(srcFile, destFile);
        return destFile.getAbsolutePath();
    }

    /**
     * Copies a file into a directory, renaming it if a file with the same name already exists there.
     *
     * @param srcFile The file to move
     * @param destDir The destination dir
     */
    public static String copyIntoDirectory(File srcFile, File destDir) throws IOException {
        Validate.isTrue(!srcFile.isDirectory(), srcFile.getAbsolutePath() + " is a directory");
        Validate.isTrue(destDir.isDirectory(), destDir.getAbsolutePath() + NOT_A_DIRECTORY);

        File destFile = new File(FileUtils.getFilename(destDir, srcFile.getName()));
        copy(srcFile, destFile);
        return destFile.getAbsolutePath();
    }

    /**
     * Copies the source file to the destination file. Both parameters must be files and not directories. If the
     * destination exists, it will be overwritten.
     *
     * @param source the source file
     * @param dest   the destination file
     * @throws IOException              if something does not work
     * @throws IllegalArgumentException if the source is not a file
     */
    public static void copy(File source, File dest) throws IOException {
        Validate.isTrue(source.isFile(), source + " is not a file");
        Validate.isTrue(!dest.isDirectory(), dest + " is not a file");

        checkForFreeDiskSpace(source, dest); //Mantis: 0006311

        try (InputStream is = new BufferedInputStream(new FileInputStream(source));
                OutputStream os = new BufferedOutputStream(new FileOutputStream(dest))) {

            final byte[] data = new byte[BUFFER_SIZE];

            int countBytes;
            while ((countBytes = is.read(data, 0, BUFFER_SIZE)) != -1) {
                os.write(data, 0, countBytes);
            }
        } catch (IOException ex) {
            LOG.error("Unable to copy file.", ex);
            boolean isFileDeleted = false;
            if (dest.exists()) {
                isFileDeleted = dest.delete();
            }
            if (!isFileDeleted) {
                throw new IOException("Unable to delete file " + dest.getAbsolutePath());
            }
            throw ex;
        }
    }

    /**
     * Returns
     * <code>true</code>, if the given file can be read by the application. This methods tries to acquire a shared lock
     * over the specified file. This lock is then immediately released. if an error occurs while acuiring the lock, this
     * method returns
     * <code>false</code>.
     *
     * @param pathname The path to the file
     * @return boolean. True if the file can be read, false otherwise.
     */
    public static boolean canRead(File pathname) {
        if (!pathname.canRead()) {
            return false;
        }

        try (FileInputStream fis = new FileInputStream(pathname)) {
            FileLock lock = fis.getChannel().tryLock(0, Long.MAX_VALUE, true);

            if (lock != null) {
                // do not hold the lock
                lock.release();

                return true;
            } else {
                LOG.info("cannot lock the file " + pathname.getAbsolutePath()
                        + "; it is probably locked by another application");
                return false;
            }
        } catch (IOException e) {
            LOG.error("an exception occured while trying to acquire lock on the file " + pathname.getAbsolutePath(),
                    e);
            return false;
        }
    }

    /**
     * Returns a {@link File} object pointing to the absolute path for the supplied name. If the
     * <code>name</code> parameter already denotes an absolute path, then a {@link File} object is created solely for this
     * path. Otherwise a {@link File} object is created treating the
     * <code>parent</code> parameter as the parent directory and the
     * <code>name</code> parameter as a child path relative to that parent.
     *
     * @param parent parent directory
     * @param name   child path relative to that parent
     * @return The object pointing to the absolute path
     */
    public static File createPath(String parent, String name) {
        File nameFile = new File(name);
        if (nameFile.isAbsolute()) {
            return nameFile;
        } else if (StringUtils.isEmpty(parent)) { // parent == null || parent.isEmpty()
            return nameFile.getAbsoluteFile();
        } else {
            File concatFile = new File(new File(parent), name);
            return concatFile.isAbsolute() ? concatFile : concatFile.getAbsoluteFile();
        }
    }

    /**
     * Moves a file. If destination already exists, it will be delete before the move.<br />Complete logging implemented<p
     * />
     * Both parameters have to be files (not directories).
     *
     * @param src  Source File
     * @param dest Destination File
     */
    public static void moveFile(File src, File dest) throws IOException {

        checkForFreeDiskSpace(src, dest); //Mantis: 0006311
        boolean isFileDeleted;
        try {
            if (dest.exists()) {
                LOG.info("moveFile: " + dest.getAbsoluteFile() + " already exists. Will be overwritten with: "
                        + src.getAbsolutePath());
                isFileDeleted = dest.delete();
                if (!isFileDeleted) {
                    LOG.error("Unable to delete file: " + dest.getAbsolutePath());
                }
            }
            org.apache.commons.io.FileUtils.moveFile(src, dest);
            LOG.debug("File succesfull moved: " + src.getAbsolutePath() + " to: " + dest.getAbsolutePath());
        } catch (IOException ex) {
            isFileDeleted = false; // Assuming it went wrong here
            LOG.error("Unable to move file from: " + src.getAbsolutePath() + " to: " + dest.getAbsolutePath()
                    + ", reason: " + ex.getMessage(), ex);
            if (dest.exists()) {
                isFileDeleted = dest.delete();
            }
            if (!isFileDeleted) {
                throw new IOException("Unable to delete file " + dest.getAbsolutePath());
            }
            throw ex;
        }
    }

    /**
     * Checks if the path is an existing file.
     * <br />Used to validate configuration values.
     *
     * @param file The file
     */
    public static void isFile(String file, String helpText) throws ConfigurationException {
        if (file == null) {
            throwNullPointer(helpText);
        }
        isFile(new File(file), helpText);
    }

    /**
     * Checks if the path is an existing file. <br />Used to validate configuration values.
     *
     * @param file The file
     */
    public static void isFile(File file, String helpText) throws ConfigurationException {
        if (file == null) {
            throwNullPointer(helpText);
        }
        if (!file.exists() || file.isDirectory()) {
            throw new ConfigurationException("File: " + file.getAbsolutePath()
                    + " either not exist or is not a file. Param name: " + helpText);
        }
    }

    /**
     * Checks if the path is an existing directory. <br />Used to validate configuration values.
     *
     * @param path The path
     */
    public static void isDirectory(String path, String helpText) throws ConfigurationException {
        if (path == null) {
            throwNullPointer(helpText);
        }

        isDirectory(new File(path), helpText);
    }

    /**
     * Checks if the path is an existing directory.
     * <br />Used to validate configuration values.
     *
     * @param path The path
     */
    public static void isDirectory(File path, String helpText) throws ConfigurationException {
        if (path == null) {
            throwNullPointer(helpText);
        }

        if (!path.exists() || !path.isDirectory()) {
            throw new ConfigurationException("Directory: " + path.getAbsolutePath()
                    + " either not exist or is not a directory. Param Name: " + helpText);
        }
    }

    private static void throwNullPointer(String helpText) throws ConfigurationException {
        throw new ConfigurationException("A required configuration parameter is not set. Param Name: " + helpText);
    }

    /**
     * Reads all files from a directory. Will throw an UnhandledException if the directory doesn't exist or an IO
     * exception occurred. Use this method instead of File.listFiles(...)
     *
     * @param directory  The directory to list the files from
     * @param filefilter The file filter
     * @return A list of files (static array)
     * @Deprecated This should never, ever be used again. It is a very, very old method. See java.nio.file. This method
     * is not good when you're getting to big data.
     */
    public static File[] listFiles(File directory, FileFilter filefilter) {
        File[] result = directory.listFiles(filefilter);

        if (result == null) {
            throw new UnhandledException(
                    new IOException("This pathname does not denote a directory, or an I/O error occured: "
                            + directory.getAbsolutePath()));
        }

        return result;
    }

    /**
     * Reads all files from a directory. Will throw an UnhandledException if the directory doesn't exist or an IO
     * exception occurred. Use this method instead of File.listFiles(...)
     *
     * @param directory The directory to list the files from
     * @param filter    The file filter
     * @return A stream of files
     */
    public static DirectoryStream<Path> listFiles(File directory, DirectoryStream.Filter<Path> filter) {
        Path folder = Paths.get(directory.getAbsolutePath());

        try {
            return Files.newDirectoryStream(folder, filter);
        } catch (IOException e) {
            throw new UnhandledException(
                    new IOException("This pathname does not denote a directory, or an I/O error occured: "
                            + directory.getAbsolutePath()));
        }
    }

    /**
     * Returns the number of unallocated bytes in the partition named by this path name. If the parameter points to a
     * file, the free disk space from the current parent directory will be calculated.
     *
     * @param file The file
     * @return The unallocated bytes
     */
    public static long getFreeDiskSpace(File file) {
        if (file.isDirectory()) {
            return file.getFreeSpace();
        } else {
            File parent = file.getParentFile();
            if (parent.exists()) {
                return parent.getFreeSpace();
            } else {
                return -1;
            }
        }
    }

    /**
     * Mantis: 0006311. Checks for free diskspace before copy or move a file!
     *
     * @param src  The file to copy
     * @param dest The destination
     * @throws IOException Ouch, IO problem here. Probably not enough disk space
     */
    private static void checkForFreeDiskSpace(File src, File dest) throws IOException {
        long requiredBytes = src.length();
        long freeSpaceBytes = getFreeDiskSpace(dest);

        if (requiredBytes > freeSpaceBytes) {
            String msg = "Not enough free disk space. Required Bytes: " + requiredBytes + ", available Bytes: "
                    + freeSpaceBytes;
            LOG.error("Unable to copy file: src: " + src.getAbsolutePath() + " dest: " + dest.getAbsolutePath()
                    + ". Message: " + msg);
            throw new IOException(msg);
        }
    }
}