org.waarp.openr66.protocol.utils.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.openr66.protocol.utils.FileUtils.java

Source

/**
 * This file is part of Waarp Project.
 * 
 * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
 * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
 * 
 * All Waarp Project 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 3 of
 * the License, or (at your option) any later version.
 * 
 * Waarp 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 Waarp . If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.waarp.openr66.protocol.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.waarp.common.command.exception.CommandAbstractException;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.digest.FilesystemBasedDigest.DigestAlgo;
import org.waarp.common.file.filesystembased.FilesystemBasedFileParameterImpl;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.utility.DetectionUtils;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.filesystem.R66Dir;
import org.waarp.openr66.context.filesystem.R66File;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;

/**
 * File Utils
 * 
 * @author Frederic Bregier
 * 
 */
public class FileUtils {

    /**
     * Copy one file to another one
     * 
     * @param from
     * @param to
     * @param move
     *            True if the copy is in fact a move operation
     * @param append
     *            True if the copy is in append
     * @throws OpenR66ProtocolSystemException
     */
    public static void copy(File from, File to, boolean move, boolean append)
            throws OpenR66ProtocolSystemException {
        if (from == null || to == null) {
            throw new OpenR66ProtocolSystemException("Source or Destination is null");
        }
        File directoryTo = to.getParentFile();
        if (createDir(directoryTo)) {
            if (move && from.renameTo(to)) {
                return;
            }
            FileChannel fileChannelIn = getFileChannel(from, false, false);
            if (fileChannelIn == null) {
                throw new OpenR66ProtocolSystemException("Cannot read source file");
                // return false;
            }
            FileChannel fileChannelOut = getFileChannel(to, true, append);
            if (fileChannelOut == null) {
                try {
                    fileChannelIn.close();
                } catch (IOException e) {
                }
                throw new OpenR66ProtocolSystemException("Cannot write destination file");
            }
            if (write(fileChannelIn, fileChannelOut) > -1) {
                if (move) {
                    // do not test the delete
                    from.delete();
                }
                return;
            }
            throw new OpenR66ProtocolSystemException("Cannot copy");
        }
        throw new OpenR66ProtocolSystemException("Cannot access to parent dir of destination");
    }

    /**
     * Copy a group of files to a directory
     * 
     * @param from
     * @param directoryTo
     * @param move
     *            True if the copy is in fact a move operation
     * @return the group of copy files or null (partially or totally) if an error occurs
     * @throws OpenR66ProtocolSystemException
     */
    public static File[] copy(File[] from, File directoryTo, boolean move) throws OpenR66ProtocolSystemException {
        if (from == null || directoryTo == null) {
            return null;
        }
        File[] to = null;
        if (createDir(directoryTo)) {
            to = new File[from.length];
            for (int i = 0; i < from.length; i++) {
                try {
                    to[i] = copyToDir(from[i], directoryTo, move);
                } catch (OpenR66ProtocolSystemException e) {
                    throw e;
                }
            }
        }
        return to;
    }

    /**
     * Copy one file to a directory
     * 
     * @param from
     * @param directoryTo
     * @param move
     *            True if the copy is in fact a move operation
     * @return The copied file or null if an error occurs
     * @throws OpenR66ProtocolSystemException
     */
    public static File copyToDir(File from, File directoryTo, boolean move) throws OpenR66ProtocolSystemException {
        if (from == null || directoryTo == null) {
            throw new OpenR66ProtocolSystemException("Source or Destination is null");
        }
        if (createDir(directoryTo)) {
            File to = new File(directoryTo, from.getName());
            if (move && from.renameTo(to)) {
                return to;
            }
            FileChannel fileChannelIn = getFileChannel(from, false, false);
            if (fileChannelIn == null) {
                throw new OpenR66ProtocolSystemException("Cannot read source file");
            }
            FileChannel fileChannelOut = getFileChannel(to, true, false);
            if (fileChannelOut == null) {
                try {
                    fileChannelIn.close();
                } catch (IOException e) {
                }
                throw new OpenR66ProtocolSystemException("Cannot write destination file");
            }
            if (write(fileChannelIn, fileChannelOut) > 0) {
                if (move) {
                    // do not test the delete
                    from.delete();
                }
                return to;
            }
            throw new OpenR66ProtocolSystemException("Cannot write destination file");
        }
        throw new OpenR66ProtocolSystemException("Cannot access to parent dir of destination");
    }

    /**
     * Create the directory associated with the File as path
     * 
     * @param directory
     * @return True if created, False else.
     */
    public final static boolean createDir(File directory) {
        if (directory == null) {
            return false;
        }
        if (directory.isDirectory()) {
            return true;
        }
        return directory.mkdirs();
    }

    /**
     * Delete physically the file
     * 
     * @param file
     * @return True if OK, else if not (or if the file never exists).
     */
    public final static boolean delete(File file) {
        if (!file.exists()) {
            return true;
        }
        return file.delete();
    }

    /**
     * Delete the directory associated with the File as path if empty
     * 
     * @param directory
     * @return True if deleted, False else.
     */
    public final static boolean deleteDir(File directory) {
        if (directory == null) {
            return true;
        }
        if (!directory.exists()) {
            return true;
        }
        if (!directory.isDirectory()) {
            return false;
        }
        return directory.delete();
    }

    /**
     * Delete physically the file but when the JVM exits (useful for temporary file)
     * 
     * @param file
     */
    public final static void deleteOnExit(File file) {
        if (!file.exists()) {
            return;
        }
        file.deleteOnExit();
    }

    /**
     * Delete the directory and its subdirs associated with the File as path if empty
     * 
     * @param directory
     * @return True if deleted, False else.
     */
    public static boolean deleteRecursiveDir(File directory) {
        if (directory == null) {
            return true;
        }
        boolean retour = true;
        if (!directory.exists()) {
            return true;
        }
        if (!directory.isDirectory()) {
            return false;
        }
        File[] list = directory.listFiles();
        if (list == null || list.length == 0) {
            list = null;
            retour = directory.delete();
            return retour;
        }
        int len = list.length;
        for (int i = 0; i < len; i++) {
            if (list[i].isDirectory()) {
                if (!deleteRecursiveFileDir(list[i])) {
                    retour = false;
                }
            } else {
                retour = false;
            }
        }
        list = null;
        if (retour) {
            retour = directory.delete();
        }
        return retour;
    }

    /**
     * Delete the directory and its subdirs associated with the File dir if empty
     * 
     * @param dir
     * @return True if deleted, False else.
     */
    private static boolean deleteRecursiveFileDir(File dir) {
        if (dir == null) {
            return true;
        }
        boolean retour = true;
        if (!dir.exists()) {
            return true;
        }
        File[] list = dir.listFiles();
        if (list == null || list.length == 0) {
            list = null;
            return dir.delete();
        }
        int len = list.length;
        for (int i = 0; i < len; i++) {
            if (list[i].isDirectory()) {
                if (!deleteRecursiveFileDir(list[i])) {
                    retour = false;
                }
            } else {
                retour = false;
                list = null;
                return retour;
            }
        }
        list = null;
        if (retour) {
            retour = dir.delete();
        }
        return retour;
    }

    /**
     * Change or create the R66File associated with the context
     * 
     * @param logger
     * @param session
     * @param filenameSrc
     *            new filename
     * @param isPreStart
     * @param isSender
     * @param isThrough
     * @param file
     *            old R66File if any (might be null)
     * @return the R66File
     * @throws OpenR66RunnerErrorException
     */
    public final static R66File getFile(WaarpLogger logger, R66Session session, String filenameSrc,
            boolean isPreStart, boolean isSender, boolean isThrough, R66File file)
            throws OpenR66RunnerErrorException {
        String filename;
        logger.debug("PreStart: " + isPreStart);
        logger.debug("Dir is: " + session.getDir().getFullPath());
        logger.debug("File is: " + filenameSrc);
        if (isPreStart) {
            filename = R66Dir.normalizePath(filenameSrc);
            if (filename.startsWith("file:/")) {
                if (DetectionUtils.isWindows()) {
                    filename = filename.substring("file:/".length());
                } else {
                    filename = filename.substring("file:".length());
                }
                if (filename.contains("%")) {
                    try {
                        filename = URLDecoder.decode(filename, WaarpStringUtils.UTF8.name());
                    } catch (UnsupportedEncodingException e) {
                        logger.warn("Filename convertion to UTF8 cannot be read: " + filename);
                    }
                }
            }
            logger.debug("File becomes: " + filename);
        } else {
            filename = filenameSrc;
        }
        if (isSender) {
            try {
                if (file == null) {
                    try {
                        file = (R66File) session.getDir().setFile(filename, false);
                    } catch (CommandAbstractException e) {
                        logger.warn("File not placed in normal directory", e);
                        // file is not under normal base directory, so is external
                        // File should already exist but can be using special code ('*?')
                        file = session.getDir().setFileNoCheck(filename);
                    }
                }
                if (isThrough) {
                    // no test on file since it does not really exist
                    logger.debug("File is in through mode: {}", file);
                } else if (!file.canRead()) {
                    logger.debug("File {} cannot be read, so try external from " + filename, file);
                    // file is not under normal base directory, so is external
                    // File should already exist but cannot use special code ('*?')
                    R66File file2 = new R66File(session, session.getDir(), filename);
                    if (!file2.canRead()) {
                        throw new OpenR66RunnerErrorException(
                                "File cannot be read: " + file.getTrueFile().getAbsolutePath());
                    }
                    file = file2;
                }
            } catch (CommandAbstractException e) {
                throw new OpenR66RunnerErrorException(e);
            }
        } else {
            // not sender so file is just registered as is but no test of existence
            file = new R66File(session, session.getDir(), filename);
        }
        return file;
    }

    /**
     * @param _FileName
     * @param _Path
     * @return true if the file exist in the specified path
     */
    public final static boolean fileExist(String _FileName, String _Path) {
        boolean exist = false;
        String fileString = _Path + File.separator + _FileName;
        File file = new File(fileString);
        if (file.exists()) {
            exist = true;
        }
        return exist;
    }

    /**
     * Returns the FileChannel in Out MODE (if isOut is True) or in In MODE (if isOut is False)
     * associated with the file. In out MODE, it can be in append MODE.
     * 
     * @param isOut
     * @param append
     * @return the FileChannel (OUT or IN)
     * @throws OpenR66ProtocolSystemException
     */
    private static FileChannel getFileChannel(File file, boolean isOut, boolean append)
            throws OpenR66ProtocolSystemException {
        FileChannel fileChannel = null;
        try {
            if (isOut) {
                @SuppressWarnings("resource")
                FileOutputStream fileOutputStream = new FileOutputStream(file.getPath(), append);
                fileChannel = fileOutputStream.getChannel();
                if (append) {
                    // Bug in JVM since it does not respect the API (position
                    // should be set as length)
                    try {
                        fileChannel.position(file.length());
                    } catch (IOException e) {
                    }
                }
            } else {
                if (!file.exists()) {
                    throw new OpenR66ProtocolSystemException("File does not exist");
                }
                @SuppressWarnings("resource")
                FileInputStream fileInputStream = new FileInputStream(file.getPath());
                fileChannel = fileInputStream.getChannel();
            }
        } catch (FileNotFoundException e) {
            throw new OpenR66ProtocolSystemException("File not found", e);
        }
        return fileChannel;
    }

    /**
     * Get the list of files from a given directory
     * 
     * @param directory
     * @return the list of files (as an array)
     */
    public final static File[] getFiles(File directory) {
        if (directory == null || !directory.isDirectory()) {
            return null;
        }
        return directory.listFiles();
    }

    /**
     * Get the list of files from a given directory and a filter
     * 
     * @param directory
     * @param filter
     * @return the list of files (as an array)
     */
    public final static File[] getFiles(File directory, FilenameFilter filter) {
        if (directory == null || !directory.isDirectory()) {
            return null;
        }
        return directory.listFiles(filter);
    }

    /**
     * Calculates and returns the hash of the contents of the given file.
     * 
     * @param f
     *            FileInterface to hash
     * @return the hash from the given file
     * @throws OpenR66ProtocolSystemException
     **/
    public final static String getHash(File f) throws OpenR66ProtocolSystemException {
        try {
            return FilesystemBasedDigest.getHex(FilesystemBasedDigest.getHash(f,
                    FilesystemBasedFileParameterImpl.useNio, Configuration.configuration.getDigest()));
        } catch (IOException e) {
            throw new OpenR66ProtocolSystemException(e);
        }
    }

    /**
     * 
     * @param buffer
     * @return the hash from the given Buffer
     */
    public final static ByteBuf getHash(ByteBuf buffer, DigestAlgo algo) {
        byte[] newkey;
        try {
            newkey = FilesystemBasedDigest.getHash(buffer, algo);
        } catch (IOException e) {
            return Unpooled.EMPTY_BUFFER;
        }
        return Unpooled.wrappedBuffer(newkey);
    }

    /**
     * Compute global hash (if possible)
     * 
     * @param digest
     * @param buffer
     */
    public static void computeGlobalHash(FilesystemBasedDigest digest, ByteBuf buffer) {
        if (digest == null) {
            return;
        }
        digest.Update(buffer);
    }

    /**
     * Compute global hash (if possible) from a file but up to length
     * 
     * @param digest
     * @param file
     * @param length
     */
    public static void computeGlobalHash(FilesystemBasedDigest digest, File file, int length) {
        if (digest == null) {
            return;
        }
        byte[] bytes = new byte[65536];
        int still = length;
        int len = still > 65536 ? 65536 : still;
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            while (inputStream.read(bytes, 0, len) > 0) {
                digest.Update(bytes, 0, len);
                still -= length;
                if (still <= 0) {
                    break;
                }
                len = still > 65536 ? 65536 : still;
            }
        } catch (FileNotFoundException e) {
            // error
        } catch (IOException e) {
            // error
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * Write one fileChannel to another one. Close the fileChannels
     * 
     * @param fileChannelIn
     *            source of file
     * @param fileChannelOut
     *            destination of file
     * @return The size of copy if is OK
     * @throws AtlasIoException
     */
    private static long write(FileChannel fileChannelIn, FileChannel fileChannelOut)
            throws OpenR66ProtocolSystemException {
        if (fileChannelIn == null) {
            if (fileChannelOut != null) {
                try {
                    fileChannelOut.close();
                } catch (IOException e) {
                }
            }
            throw new OpenR66ProtocolSystemException("FileChannelIn is null");
        }
        if (fileChannelOut == null) {
            try {
                fileChannelIn.close();
            } catch (IOException e) {
            }
            throw new OpenR66ProtocolSystemException("FileChannelOut is null");
        }
        long size = 0;
        long transfert = 0;
        try {
            transfert = fileChannelOut.position();
            size = fileChannelIn.size();
            long chunkSize = size;
            while (transfert < size) {
                chunkSize = size - transfert;
                transfert += fileChannelOut.transferFrom(fileChannelIn, transfert, chunkSize);
            }
        } catch (IOException e) {
            try {
                fileChannelOut.close();
                fileChannelIn.close();
            } catch (IOException e1) {
            }
            throw new OpenR66ProtocolSystemException("An error during copy occurs", e);
        }
        try {
            fileChannelOut.close();
            fileChannelIn.close();
        } catch (IOException e) {// Close error can be ignored
        }
        boolean retour = size == transfert;
        if (!retour) {
            throw new OpenR66ProtocolSystemException(
                    "Copy is not complete: " + transfert + " bytes instead of " + size + " original bytes");
        }
        return size;
    }
}