Java tutorial
/* * ------------------------------------------------------------------------------ * Hermes FTP Server * Copyright (c) 2005-2014 Lars Behnke * ------------------------------------------------------------------------------ * * This file is part of Hermes FTP Server. * * Hermes FTP Server 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. * * Hermes FTP Server 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 Hermes FTP Server; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ------------------------------------------------------------------------------ */ package com.apporiented.hermesftp.cmd; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.util.zip.DeflaterOutputStream; import com.apporiented.hermesftp.common.FtpConstants; import com.apporiented.hermesftp.exception.FtpCmdException; import com.apporiented.hermesftp.exception.FtpPermissionException; import com.apporiented.hermesftp.exception.FtpQuotaException; import com.apporiented.hermesftp.streams.BlockModeOutputStream; import com.apporiented.hermesftp.streams.RecordOutputStream; import com.apporiented.hermesftp.streams.RecordWriteSupport; import com.apporiented.hermesftp.streams.TextOutputStream; import com.apporiented.hermesftp.utils.TransferRateLimiter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Abstract base class for RETR command implementations. * * @author Lars Behnke */ public abstract class AbstractFtpCmdRetr extends AbstractFtpCmd implements FtpConstants { private static Log log = LogFactory.getLog(AbstractFtpCmdRetr.class); private TransferRateLimiter transferRateLimiter = new TransferRateLimiter(); private long fileSize; private long completed; private boolean abortRequested; /** * Checks availability and access rights for the current folder and passed file. The methods * acts as a primitive operation that is called by the template method * <code>execute(boolean)</code>; * * @param file The destination file. * @throws IOException Thrown if one of the following conditions occurred: (1) IO failed or (3) * access rights have been violated or (3) resource limits have been reached. */ protected abstract void doPerformAccessChecks(File file) throws IOException; /** * Retrieves record based data. Since native files generally do not support records, the * assumption is made that each line of a text file corresponds to a record. The method acts as * a primitive operation that is called by the template method <code>execute()</code>; * Futhermore, text record data must be encoded by an 1-byte character set (ACII, ANSI or * EBCDIC). * * @param out The output stream. * @param file The source file. * @param fileOffset The file offset. * @throws IOException Thrown if IO fails or if a resource limit has been reached. */ protected abstract void doRetrieveRecordData(RecordWriteSupport out, File file, long fileOffset) throws IOException; /** * Retrieves file based data. The method acts as a primitive operation that is called by the * template method <code>execute()</code>; * * @param out The output stream. * @param file The source file. * @param fileOffset The file offset. * @throws IOException Thrown if IO fails or if a resource limit has been reached. */ protected abstract void doRetrieveFileData(OutputStream out, File file, long fileOffset) throws IOException; /** * {@inheritDoc} */ public boolean handleAsyncCmd(String req) { boolean result; if (req == null || isResponded()) { result = false; } else if (req.toUpperCase().startsWith("STAT")) { String stat = "STAT: " + getCompleted() + " from " + getFileSize() + " completed"; log.info(stat); // TODO Return statistics response. result = true; } else if (req.toUpperCase().startsWith("ABOR")) { abortRequested = true; result = true; } else { result = false; } return result; } /** * {@inheritDoc} */ public void execute() throws FtpCmdException { /* Get relevant information from context */ File file = new File(getPathArg()); int mode = getCtx().getTransmissionMode(); int struct = getCtx().getStorageStructure(); int type = getCtx().getDataType(); String charset = type == DT_ASCII || type == DT_EBCDIC ? getCtx().getCharset() : null; long fileOffset = getAndResetFileOffset(); getTransferRateLimiter().init(getCtx().getMaxDownloadRate()); try { /* Check availability and access rights */ doPerformAccessChecks(file); msgOut(MSG150); /* Wrap outbound data stream and call handler method */ Socket dataSocket = getCtx().getDataSocketProvider().provideSocket(); OutputStream dataOut = dataSocket.getOutputStream(); if (struct == STRUCT_RECORD) { RecordWriteSupport recordOut = createRecOutputStream(dataOut, mode, charset); doRetrieveRecordData(recordOut, file, fileOffset); } else if (struct == STRUCT_FILE) { OutputStream fileOut = createOutputStream(dataOut, mode, charset); doRetrieveFileData(fileOut, file, fileOffset); } else { log.error("Unknown data type"); msgOut(MSG550, "Unsupported data type"); //dataSocket.shutdownOutput(); return; } // TODO delegate event to FtpEventListener dataOut.flush(); } catch (FtpQuotaException e) { msgOut(MSG550, e.getMessage()); log.warn(e.getMessage()); } catch (FtpPermissionException e) { msgOut(MSG550_PERM); } catch (UnsupportedEncodingException e) { msgOut(MSG550, "Unsupported Encoding: " + charset); log.error(e.toString()); } catch (IOException e) { msgOut(MSG550); log.error(e.toString()); } catch (RuntimeException e) { msgOut(MSG550); log.error(e.toString()); } finally { getCtx().closeSockets(); } } private OutputStream createOutputStream(OutputStream dataOut, int mode, String charset) throws UnsupportedEncodingException { OutputStream result = null; if (mode == MODE_BLOCK) { result = new BlockModeOutputStream(dataOut); } else if (mode == MODE_STREAM) { result = dataOut; } else if (mode == MODE_ZIP) { result = new DeflaterOutputStream(dataOut); } else { log.error("Unsupported file mode: " + mode); } if (charset != null) { result = new TextOutputStream(result, charset); } return result; } private RecordWriteSupport createRecOutputStream(OutputStream dataOut, int mode, String charset) throws UnsupportedEncodingException { RecordWriteSupport result = null; if (mode == MODE_BLOCK) { result = new BlockModeOutputStream(dataOut); } else if (mode == MODE_STREAM) { result = new RecordOutputStream(dataOut); } else if (mode == MODE_ZIP) { result = new RecordOutputStream(new DeflaterOutputStream(dataOut)); } else { log.error("Unsupported record mode: " + mode); } if (charset != null) { result = new TextOutputStream((OutputStream) result, charset); } return result; } /** * @return True, if transfer has been aborted. */ protected boolean isAbortRequested() { return abortRequested; } /** * Getter method for the java bean <code>completed</code>. * * @return Returns the value of the java bean <code>completed</code>. */ public synchronized long getCompleted() { return completed; } /** * Setter method for the java bean <code>completed</code>. * * @param completed The value of completed to set. */ public synchronized void incCompleted(long completed) { this.completed += completed; } /** * Getter method for the java bean <code>fileSize</code>. * * @return Returns the value of the java bean <code>fileSize</code>. */ public long getFileSize() { return fileSize; } /** * Setter method for the java bean <code>fileSize</code>. * * @param fileSize The value of fileSize to set. */ public void setFileSize(long fileSize) { this.fileSize = fileSize; } /** * Getter Methode fuer die Eigenschaft <code>transferRateLimiter</code>. * * @return Wert der Eigenschaft <code>transferRateLimiter</code>. */ public TransferRateLimiter getTransferRateLimiter() { return transferRateLimiter; } /** * @param transferRateLimiter the transferRateLimiter to set */ public void setTransferRateLimiter(TransferRateLimiter transferRateLimiter) { this.transferRateLimiter = transferRateLimiter; } }