org.waarp.openr66.context.filesystem.R66File.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.openr66.context.filesystem.R66File.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.context.filesystem;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;

import io.netty.channel.ChannelFuture;

import org.waarp.common.command.exception.CommandAbstractException;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.exception.FileEndOfTransferException;
import org.waarp.common.exception.FileTransferException;
import org.waarp.common.file.DataBlock;
import org.waarp.common.file.filesystembased.FilesystemBasedDirImpl;
import org.waarp.common.file.filesystembased.FilesystemBasedFileImpl;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.openr66.context.ErrorCode;
import org.waarp.openr66.context.R66Result;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
import org.waarp.openr66.protocol.localhandler.RetrieveRunner;
import org.waarp.openr66.protocol.utils.ChannelUtils;
import org.waarp.openr66.protocol.utils.FileUtils;

/**
 * File representation
 * 
 * @author frederic bregier
 * 
 */
public class R66File extends FilesystemBasedFileImpl {
    /**
     * Internal Logger
     */
    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(R66File.class);

    /**
     * Does the current file is external (i.e. out of R66 base directory)
     */
    private boolean isExternal = false;

    /**
     * @param session
     * @param dir
     * @param path
     * @param append
     * @throws CommandAbstractException
     */
    public R66File(R66Session session, R66Dir dir, String path, boolean append) throws CommandAbstractException {
        super(session, dir, path, append);
    }

    /**
     * This constructor is for External file
     * 
     * @param session
     * @param dir
     * @param path
     */
    public R66File(R66Session session, R66Dir dir, String path) {
        super(session, dir, path);
        isExternal = true;
    }

    /**
     * Start the retrieve (send to the remote host the local file)
     * 
     * @param running
     *            When false, should stop the runner
     * @throws OpenR66RunnerErrorException
     * @throws OpenR66ProtocolSystemException
     */
    public void retrieveBlocking(AtomicBoolean running)
            throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
        boolean retrieveDone = false;
        LocalChannelReference localChannelReference = getSession().getLocalChannelReference();
        FilesystemBasedDigest digest = null;
        logger.debug("File to retrieve: " + this.toString());
        try {
            if (!isReady) {
                return;
            }
            DataBlock block = null;
            try {
                block = readDataBlock();
            } catch (FileEndOfTransferException e) {
                // Last block (in fact, no data to read)
                retrieveDone = true;
                return;
            }
            if (block == null) {
                // Last block (in fact, no data to read)
                retrieveDone = true;
                return;
            }
            if (Configuration.configuration.isGlobalDigest()) {
                try {
                    digest = new FilesystemBasedDigest(Configuration.configuration.getDigest());
                } catch (NoSuchAlgorithmException e2) {
                    // ignore
                }
            }
            ChannelFuture future1 = null, future2 = null;
            if ((block != null && (running.get()))) {
                block.getBlock().retain();
                future1 = RetrieveRunner.writeWhenPossible(block, localChannelReference);
                if (Configuration.configuration.isGlobalDigest()) {
                    FileUtils.computeGlobalHash(digest, block.getBlock());
                }
            }
            // While not last block
            while (block != null && (!block.isEOF()) && (running.get())) {
                try {
                    future1.await();
                } catch (InterruptedException e) {
                }
                if (!future1.isSuccess()) {
                    return;
                }
                try {
                    block = readDataBlock();
                } catch (FileEndOfTransferException e) {
                    // Wait for last write
                    try {
                        future1.await();
                    } catch (InterruptedException e1) {
                    }
                    if (future1.isSuccess()) {
                        retrieveDone = true;
                    }
                    return;
                }
                block.getBlock().retain();
                future2 = RetrieveRunner.writeWhenPossible(block, localChannelReference);
                if (Configuration.configuration.isGlobalDigest()) {
                    FileUtils.computeGlobalHash(digest, block.getBlock());
                }
                future1 = future2;
            }
            if (!running.get()) {
                // stopped
                return;
            }
            // Wait for last write
            if (future1 != null) {
                try {
                    future1.await();
                } catch (InterruptedException e) {
                }
                if (!future1.isSuccess()) {
                    return;
                }
            }
            if (block != null) {
                block.getBlock().release();
                block.clear();
            }
            retrieveDone = true;
            return;
        } catch (FileTransferException e) {
            // An error occurs!
            getSession().setFinalizeTransfer(false, new R66Result(new OpenR66ProtocolSystemException(e),
                    getSession(), false, ErrorCode.TransferError, getSession().getRunner()));
        } catch (OpenR66ProtocolPacketException e) {
            // An error occurs!
            getSession().setFinalizeTransfer(false,
                    new R66Result(e, getSession(), false, ErrorCode.Internal, getSession().getRunner()));
        } finally {
            if (retrieveDone) {
                String hash = null;
                if (digest != null) {
                    hash = FilesystemBasedDigest.getHex(digest.Final());
                }
                try {
                    if (hash == null) {
                        ChannelUtils.writeEndTransfer(localChannelReference);
                    } else {
                        ChannelUtils.writeEndTransfer(localChannelReference, hash);
                    }
                } catch (OpenR66ProtocolPacketException e) {
                    // An error occurs!
                    getSession().setFinalizeTransfer(false,
                            new R66Result(e, getSession(), false, ErrorCode.Internal, getSession().getRunner()));
                }
            } else {
                // An error occurs!
                getSession().setFinalizeTransfer(false,
                        new R66Result(new OpenR66ProtocolSystemException("Transfer in error"), getSession(), false,
                                ErrorCode.TransferError, getSession().getRunner()));
            }
        }
    }

    /**
     * This method is a good to have in a true FileInterface implementation.
     * 
     * @return the File associated with the current FileInterface operation
     */
    public File getTrueFile() {
        if (isExternal) {
            return new File(currentFile);
        }
        try {
            return getFileFromPath(getFile());
        } catch (CommandAbstractException e) {
            logger.warn("Exception while getting file: " + this, e);
            return null;
        }
    }

    /**
     * 
     * @return the basename of the current file
     */
    public String getBasename() {
        return getBasename(currentFile);
    }

    /**
     * 
     * @param path
     * @return the basename from the given path
     */
    public static String getBasename(String path) {
        int pos = path.lastIndexOf('/');
        int pos2 = path.lastIndexOf('\\');
        if (pos2 > pos) {
            pos = pos2;
        }
        if (pos > 0) {
            return path.substring(pos + 1);
        }
        return path;
    }

    @Override
    public R66Session getSession() {
        return (R66Session) session;
    }

    @Override
    public boolean canRead() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            logger.debug("Final File: " + file + " CanRead: " + file.canRead());
            return file.canRead();
        }
        return super.canRead();
    }

    @Override
    public boolean canWrite() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            if (file.exists()) {
                return file.canWrite();
            }
            return file.getParentFile().canWrite();
        }
        return super.canWrite();
    }

    @Override
    public boolean delete() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            checkIdentify();
            if (!isReady) {
                return false;
            }
            if (!file.exists()) {
                return true;
            }
            closeFile();
            return file.delete();
        }
        return super.delete();
    }

    @Override
    public boolean exists() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            return file.exists();
        }
        return super.exists();
    }

    @Override
    protected FileChannel getFileChannel() {
        if (!isExternal) {
            return super.getFileChannel();
        }
        if (!isReady) {
            return null;
        }
        File trueFile = getTrueFile();
        FileChannel fileChannel;
        try {
            @SuppressWarnings("resource")
            FileInputStream fileInputStream = new FileInputStream(trueFile);
            fileChannel = fileInputStream.getChannel();
            if (getPosition() > 0) {
                fileChannel = fileChannel.position(getPosition());
            }
        } catch (FileNotFoundException e) {
            logger.error("FileInterface not found in getFileChannel:", e);
            return null;
        } catch (IOException e) {
            logger.error("Change position in getFileChannel:", e);
            return null;
        }
        return fileChannel;
    }

    @Override
    protected RandomAccessFile getRandomFile() {
        if (!isExternal) {
            return super.getRandomFile();
        }
        if (!isReady) {
            return null;
        }
        File trueFile = getTrueFile();
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(trueFile, "rw");
            raf.seek(getPosition());
        } catch (FileNotFoundException e) {
            logger.error("File not found in getRandomFile:", e);
            return null;
        } catch (IOException e) {
            logger.error("Change position in getRandomFile:", e);
            return null;
        }
        return raf;
    }

    /**
     * Returns the FileOutputStream in Out mode associated with the current file.
     * 
     * @param append
     *            True if the FileOutputStream should be in append mode
     * @return the FileOutputStream (OUT)
     */
    protected FileOutputStream getFileOutputStream(boolean append) {
        if (!isExternal) {
            return super.getFileOutputStream(append);
        }
        if (!isReady) {
            return null;
        }
        File trueFile = getTrueFile();
        if (getPosition() > 0) {
            if (trueFile.length() < getPosition()) {
                logger.error(
                        "Cannot Change position in getFileOutputStream: file is smaller than required position");
                return null;
            }
            RandomAccessFile raf = getRandomFile();
            try {
                raf.setLength(getPosition());
                raf.close();
            } catch (IOException e) {
                logger.error("Change position in getFileOutputStream:", e);
                return null;
            }
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(trueFile, append);
        } catch (FileNotFoundException e) {
            logger.error("File not found in getRandomFile:", e);
            return null;
        }
        return fos;
    }

    @Override
    public boolean isDirectory() throws CommandAbstractException {
        if (isExternal) {
            File dir = new File(currentFile);
            return dir.isDirectory();
        }
        return super.isDirectory();
    }

    @Override
    public boolean isFile() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            return file.isFile();
        }
        return super.isFile();
    }

    @Override
    public long length() throws CommandAbstractException {
        if (isExternal) {
            File file = new File(currentFile);
            if (file.canRead()) {
                return file.length();
            } else {
                return -1;
            }
        }
        return super.length();
    }

    protected String getFullInDir() {
        DbTaskRunner runner = getSession().getRunner();
        if (runner != null) {
            R66Dir dir = new R66Dir(getSession());
            try {
                dir.changeDirectory(runner.getRule().getRecvPath());
                return dir.getFullPath();
            } catch (CommandAbstractException e) {
            }
        }
        return null;
    }

    @Override
    public boolean renameTo(String path) throws CommandAbstractException {
        if (!isExternal) {
            return super.renameTo(path);
        }
        checkIdentify();
        if (!isReady) {
            logger.warn("File not ready: {}", this);
            return false;
        }
        File file = getTrueFile();
        if (file.canRead()) {
            File newFile = getFileFromPath(path);
            File parentFile = newFile.getParentFile();
            if (parentFile == null) {
                String dir = getFullInDir();
                if (dir != null) {
                    newFile = new File(dir, newFile.getPath());
                    parentFile = newFile.getParentFile();
                }
            }
            if (newFile.exists()) {
                logger.warn("Target file already exists: " + newFile.getAbsolutePath());
                return false;
            }
            if (newFile.getAbsolutePath().equals(file.getAbsolutePath())) {
                // already in the right position
                isReady = true;
                return true;
            }
            if (parentFile != null && parentFile.canWrite()) {
                if (!file.renameTo(newFile)) {
                    FileOutputStream fileOutputStream = null;
                    try {
                        try {
                            fileOutputStream = new FileOutputStream(newFile);
                        } catch (FileNotFoundException e) {
                            logger.warn("Cannot find file: " + newFile.getName(), e);
                            return false;
                        }
                        FileChannel fileChannelOut = fileOutputStream.getChannel();
                        if (get(fileChannelOut)) {
                            delete();
                        } else {
                            try {
                                fileChannelOut.close();
                            } catch (IOException e) {
                            }
                            logger.error("Cannot write file: {}", newFile);
                            return false;
                        }
                    } finally {
                        if (fileOutputStream != null) {
                            try {
                                fileOutputStream.close();
                            } catch (IOException e) {
                            }
                        }
                    }
                }
                currentFile = getRelativePath(newFile);
                isExternal = false;
                isReady = true;
                logger.debug("File renamed to: {} and real position: {}", this, newFile);
                return true;
            }
        }
        logger.warn("Cannot read file: {}", file);
        return false;
    }

    /**
     * Move the current file to the path as destination
     * 
     * @param path
     * @param external
     *            if True, the path is outside authentication control
     * @return True if the operation is done
     * @throws CommandAbstractException
     */
    public boolean renameTo(String path, boolean external) throws CommandAbstractException {
        if (!external) {
            return renameTo(path);
        }
        checkIdentify();
        if (!isReady) {
            return false;
        }
        File file = getTrueFile();
        if (file.canRead()) {
            File newFile = new File(path);
            File parentFile = newFile.getParentFile();
            if (parentFile == null) {
                String dir = getFullInDir();
                if (dir != null) {
                    newFile = new File(dir, newFile.getPath());
                    parentFile = newFile.getParentFile();
                }
            }
            if (newFile.exists()) {
                logger.warn("Target file already exists: " + newFile.getAbsolutePath());
                return false;
            }
            if (newFile.getAbsolutePath().equals(file.getAbsolutePath())) {
                // already in the right position
                isReady = true;
                return true;
            }
            if (parentFile != null && parentFile.canWrite()) {
                if (!file.renameTo(newFile)) {
                    FileOutputStream fileOutputStream = null;
                    try {
                        try {
                            fileOutputStream = new FileOutputStream(newFile);
                        } catch (FileNotFoundException e) {
                            logger.warn("Cannot find file: " + newFile.getName(), e);
                            return false;
                        }
                        FileChannel fileChannelOut = fileOutputStream.getChannel();
                        if (get(fileChannelOut)) {
                            delete();
                        } else {
                            try {
                                fileChannelOut.close();
                            } catch (IOException e) {
                            }
                            logger.error("Cannot write file: {}", newFile);
                            return false;
                        }
                    } finally {
                        if (fileOutputStream != null) {
                            try {
                                fileOutputStream.close();
                            } catch (IOException e) {
                            }
                        }
                    }
                }
                currentFile = FilesystemBasedDirImpl.normalizePath(newFile.getAbsolutePath());
                isExternal = true;
                isReady = true;
                return true;
            }
            logger.error("Cannot write to parent directory: {}", newFile.getParent());
        }
        logger.error("Cannot read file: {}", file);
        return false;
    }

    /**
     * Replace the current file with the new filename after closing the previous one.
     * 
     * @param filename
     * @param isExternal
     * @throws CommandAbstractException
     */
    public void replaceFilename(String filename, boolean isExternal) throws CommandAbstractException {
        closeFile();
        currentFile = filename;
        this.isExternal = isExternal;
        isReady = true;
    }

    @Override
    public boolean closeFile() throws CommandAbstractException {
        boolean status = super.closeFile();
        // FORCE re-open file
        isReady = true;
        return status;
    }

    /**
     * 
     * @return True if this file is outside OpenR66 Base directory
     */
    public boolean isExternal() {
        return isExternal;
    }

    @Override
    public String toString() {
        return "File: " + currentFile + " Ready " + isReady + " isExternal " + isExternal + " " + getPosition();
    }
}