/** * 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.ftp.filesystembased; import java.util.concurrent.locks.ReentrantLock; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import org.waarp.common.command.exception.CommandAbstractException; import org.waarp.common.exception.FileEndOfTransferException; import org.waarp.common.exception.FileTransferException; import org.waarp.common.file.DataBlock; import org.waarp.common.file.filesystembased.FilesystemBasedFileImpl; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.ftp.core.exception.FtpNoConnectionException; import org.waarp.ftp.core.file.FtpFile; import org.waarp.ftp.core.session.FtpSession; /** * Filesystem implementation of a FtpFile * * @author Frederic Bregier * */ public abstract class FilesystemBasedFtpFile extends FilesystemBasedFileImpl implements FtpFile { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(FilesystemBasedFtpFile.class); /** * Retrieve lock to ensure only one call at a time for one file */ private final ReentrantLock retrieveLock = new ReentrantLock(); /** * @param session * @param dir * It is not necessary the directory that owns this file. * @param path * @param append * @throws CommandAbstractException */ public FilesystemBasedFtpFile(FtpSession session, FilesystemBasedFtpDir dir, String path, boolean append) throws CommandAbstractException { super(session, dir, path, append); } @Override public long length() throws CommandAbstractException { long length = super.length(); if (((FtpSession) getSession()).getDataConn() .isFileStreamBlockAsciiImage()) { long block = (long) Math.ceil((double) length / (double) getSession().getBlockSize()); length += (block + 3) * 3; } return length; } /** * Launch retrieve operation (internal method, should not be called directly) * */ public void trueRetrieve() { retrieveLock.lock(); try { if (!isReady) { return; } // First check if ready to run from Control try { ((FtpSession) session).getDataConn().getFtpTransferControl() .waitForDataNetworkHandlerReady(); } catch (InterruptedException e) { // bad thing logger.warn("DataNetworkHandler was not ready", e); return; } Channel channel = null; try { channel = ((FtpSession) session).getDataConn().getCurrentDataChannel(); } catch (FtpNoConnectionException e) { if (this.isInReading()) { logger.error("Should not be", e); ((FtpSession) session).getDataConn().getFtpTransferControl() .setTransferAbortedFromInternal(true); } logger.debug("Possible call while channel was on going to be closed once transfer was done", e); closeFile(); ((FtpSession) session).getDataConn().getFtpTransferControl() .setPreEndOfTransfer(); return; } DataBlock block = null; try { block = readDataBlock(); } catch (FileEndOfTransferException e) { // Last block (in fact, previous block was the last one, // but it could be aligned with the block size so not // detected) closeFile(); ((FtpSession) session).getDataConn().getFtpTransferControl() .setPreEndOfTransfer(); return; } if (block == null) { // Last block (in fact, previous block was the last one, // but it could be aligned with the block size so not // detected) closeFile(); ((FtpSession) session).getDataConn().getFtpTransferControl() .setPreEndOfTransfer(); return; } // While not last block ChannelFuture future = null; while (block != null && !block.isEOF()) { future = channel.writeAndFlush(block); try { future.await(); } catch (InterruptedException e) { } if (!future.isSuccess()) { closeFile(); throw new FileTransferException("File transfer in error"); } try { block = readDataBlock(); } catch (FileEndOfTransferException e) { closeFile(); // Wait for last write if (future.isSuccess()) { ((FtpSession) session).getDataConn() .getFtpTransferControl().setPreEndOfTransfer(); } else { throw new FileTransferException("File transfer in error"); } return; } } // Last block closeFile(); if (block != null) { logger.debug("Write " + block.getByteCount()); future = channel.writeAndFlush(block); } // Wait for last write if (future != null) { try { future.await(); } catch (InterruptedException e) { } if (future.isSuccess()) { ((FtpSession) session).getDataConn().getFtpTransferControl() .setPreEndOfTransfer(); } else { throw new FileTransferException("Write is not successful"); } } } catch (FileTransferException e) { // An error occurs! ((FtpSession) session).getDataConn().getFtpTransferControl() .setTransferAbortedFromInternal(true); } catch (CommandAbstractException e) { logger.error("Should not be", e); ((FtpSession) session).getDataConn().getFtpTransferControl() .setTransferAbortedFromInternal(true); } finally { retrieveLock.unlock(); } } }