sos.net.SOSFTP.java Source code

Java tutorial

Introduction

Here is the source code for sos.net.SOSFTP.java

Source

/**
 * Copyright (C) 2014 BigLoupe http://bigloupe.github.io/SoS-JobScheduler/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
/********************************************************* begin of preamble
**
** Copyright (C) 2003-2012 Software- und Organisations-Service GmbH. 
** All rights reserved.
**
** This file may be used under the terms of either the 
**
**   GNU General Public License version 2.0 (GPL)
**
**   as published by the Free Software Foundation
**   http://www.gnu.org/licenses/gpl-2.0.txt and appearing in the file
**   LICENSE.GPL included in the packaging of this file. 
**
** or the
**  
**   Agreement for Purchase and Licensing
**
**   as offered by Software- und Organisations-Service GmbH
**   in the respective terms of supply that ship with this file.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
********************************************************** end of preamble*/
package sos.net;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Vector;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ProtocolCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

import sos.util.SOSLogger;

import com.sos.JSHelper.Exceptions.JobSchedulerException;

//import com.sos.JSHelper.Exceptions.JobSchedulerException;

/**
 * <p>Title: FTP-Client</p>
 * <p>Description: a wrapper around the Jakarta Commons FTP</p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: SOS GmbH</p>
 * @author <a href="mailto:ghassan.beydoun@sos-berlin.com">Ghassan Beydoun</a>
 * @version $Id: SOSFTP.java 19067 2013-02-07 17:31:45Z kb $
 * @resource commons-net-1.3.0.jar, 
 *           oro.jar fr den Aufruf FTPFile (=download=http://jakarta.apache.org/site/downloads/downloads_oro.cgi)
 */

public class SOSFTP extends FTPClient implements SOSFileTransfer {

    private final String conClassName = "SOSFTP";
    private final Logger objLogger = Logger.getLogger(SOSFTP.class);
    protected static final String conRegExpBackslash = "\\\\";

    private ProtocolCommandListener listener = null;

    public SOSFTP() {
    }

    /**
     * SOSFTP constructor: there is no need to call the connect-Method
     * @param host the remote ftp server
     * @throws java.net.SocketException
     * @throws java.io.IOException
     * @throws java.net.UnknownHostException
     */
    public SOSFTP(final String host) throws SocketException, IOException, UnknownHostException {
        connect(host);
    }

    /**
     * SOSFTP constructor: there is no need to call the connect-Method
     * @param host the remote ftp server
     * @param port the port number of the remote server
     * @throws java.net.SocketException
     * @throws java.io.IOException
     * @throws java.net.UnknownHostException
     */
    public SOSFTP(final String host, final int port) throws SocketException, IOException, UnknownHostException {
        connect(host, port);
    }

    public SOSFTP(final String host, final int port, final SOSLogger logger) {
        try {
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // connect( host, port);
    }

    public SOSFTP(final String ftpHost, final int ftpPort, final String proxyHost, final int proxyPort)
            throws SocketException, IOException, UnknownHostException {
        // this.setProxyHost(proxyHost);
        // this.setProxyPort(proxyPort);
        this.connect(ftpHost, ftpPort);
    }

    /*
     * @param host the remote ftp server
     * @param port the port number of the remote server
     * @throws java.net.SocketException
     * @throws java.io.IOException
     * @throws java.net.UnknownHostException
     * @see org.apache.commons.net.SocketClient#connect(java.lang.String, int)
     */
    @Override
    public void connect(final String host, final int port)
            throws SocketException, IOException, UnknownHostException {
        if (!isConnected()) {

            listener = new PrintCommandListener(new PrintWriter(System.out));
            String strAddFTPProtocol = System.getenv("AddFTPProtocol");
            if (strAddFTPProtocol != null && strAddFTPProtocol.equalsIgnoreCase("true")) {
                this.addProtocolCommandListener(listener);
            }

            super.connect(host, port);
            LogReply();
        }

    }

    /*
     * @param host the remote ftp server
     * @param port the port number of the remote server
     * @throws java.net.SocketException
     * @throws java.io.IOException
     * @throws java.net.UnknownHostException
     * @see org.apache.commons.net.SocketClient#connect(java.lang.String)
     */
    @Override
    public void connect(final String hostname) throws SocketException, IOException, UnknownHostException {

        if (!isConnected())
            super.connect(hostname);
        LogReply();

    }

    /**
     * Creates a new subdirectory on the FTP server in the current directory .
     * @param pathname The pathname of the directory to create.
     * @return True if successfully completed, false if not.
     * @throws java.lang.IOException
     */
    @Override
    public boolean mkdir(final String pathname) throws IOException {
        int intPosixPermissions = 484;
        return mkdir(pathname, intPosixPermissions);
    }

    @Override
    public boolean mkdir(final String pathname, final int pintPosixPermissions) throws IOException {

        boolean flgRet = makeDirectory(pathname);
        this.sendCommand("CHMOD " + Integer.toOctalString(pintPosixPermissions) + " " + pathname);
        return flgRet;
    }

    /**
     * Removes a directory on the FTP server (if empty).
     * @param pathname The pathname of the directory to remove.
     * @return True if successfully completed, false if not.
     * @throws java.lang.IOException
     */
    public boolean rmdir(final String pathname) throws IOException {
        return removeDirectory(pathname);
    }

    /**
     * turn passive transfer mode on.
     *
     * @return The reply code received from the server.
     */
    public int passive() throws IOException {
        return pasv();
    }

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine
     * @param pathname on remote machine
     * @return a listing of the contents of a directory on the remote machine
     *
     * @exception Exception
     * @see #dir()
     */
    @Override
    public Vector<String> nList(final String pathname) throws Exception {
        return getFilenames(pathname);
    } // nList

    private Vector<String> getFilenames(final String pathname) throws IOException {
        return getFilenames(pathname, false);
    }

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine (without subdirectory)
     * 
     * @param pstrPathName on remote machine
     * @return a listing of the contents of a directory on the remote machine
     * @throws IOException 
     *
     * @exception Exception
     * @see #dir()
     */
    private Vector<String> getFilenames(final String pstrPathName, final boolean flgRecurseSubFolders)
            throws IOException {
        Vector<String> vecListFileItems = new Vector<String>();
        // setListHiddenFiles(true);
        String[] fileList = null;
        String strCurrentDirectoryName = DoPWD();
        String lstrPathName = pstrPathName.trim();

        if (lstrPathName.length() <= 0) {
            lstrPathName = ".";
        }

        if (lstrPathName.equals(".")) {
            lstrPathName = strCurrentDirectoryName;
        }

        objLogger.debug("nlist with " + lstrPathName);
        if (1 == 1) {
            fileList = listNames(lstrPathName);
        } else {
            FTPFile[] objFtpFiles = listFiles(lstrPathName);
            if (objFtpFiles != null) {
                int i = 0;
                for (FTPFile ftpFile : objFtpFiles) {
                    fileList[i++] = ftpFile.getName();
                }
            }
        }
        if (fileList == null) {
            return vecListFileItems;
        }

        for (String strCurrentFileName : fileList) {
            if (isNotHiddenFile(strCurrentFileName)) {
                DoCD(strCurrentFileName); // is this file-entry a subfolder?
                if (isNegativeCommandCompletion()) {
                    /**
                     * kb 2011-04-27  add Directory-name if flgRecurseSubFolders == true
                     */
                    if (flgRecurseSubFolders && strCurrentFileName.startsWith(strCurrentDirectoryName) == false) {
                        if (strCurrentDirectoryName.length() > 0) {
                            strCurrentFileName = strCurrentDirectoryName + "/" + strCurrentFileName;
                        }
                    }
                    /**
                     * \change
                     * kb: 2011-06-17
                     * bei sBroker gibt der FTP-Server (oder wer auch immer) einen backslash vor dem 
                     * Dateinamen an, der Pfad selbst ist mit "/" maskiert.
                     * beispiel:  /a/b/c\dateiname.pdf
                     * siehe auch http://www.mail-archive.com/gnu-arch-users@gnu.org/msg02884.html
                     * 
                     * wird ein solcher Pfad an den Server gesendet, um die Datei zu holen, so gibt
                     * es die Meldung, da eine solche Datei nicht vorhanden ist. Das scheint eine konfig-sache
                     * zu sein. 8of9 - liefert auch den backslash - erlaubt die angabe beider Pfadtrenner und zickt nicht rum.
                     * 
                     * Um dies zu vermeiden wird hier ein eventueller backslash in einen normalen
                     * Schrgstrich umgewandelt.
                     */
                    strCurrentFileName = strCurrentFileName.replaceAll(conRegExpBackslash, "/");
                    vecListFileItems.add(strCurrentFileName);
                } else { // yes, it's a subfolder. undo the cd now
                    // String[] strSubFolders = strCurrentFile.split("/"); // get number of subfolders in path
                    // for (int s = 0; s < strSubFolders.length; s++) {
                    // DoCDUP();
                    // }
                    DoCDUP();
                    if (flgRecurseSubFolders) {
                        Vector<String> vecNames = getFilenames(strCurrentFileName);
                        if (vecNames != null) {
                            vecListFileItems.addAll(vecNames);
                        }
                    }
                }
            }
        }

        objLogger.debug("strCurrentDirectory = " + strCurrentDirectoryName);
        DoCD(strCurrentDirectoryName);
        DoPWD();
        return vecListFileItems;

    } // nList

    public String DoPWD() {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::DoPWD";
        String strCurrentPathName = "";

        try {
            //         objLogger.debug("Try pwd.");

            pwd();
            strCurrentPathName = getReplyString();
            int idx = strCurrentPathName.indexOf('"');
            if (idx >= 0) {
                strCurrentPathName = strCurrentPathName.substring(idx + 1, strCurrentPathName.length() - idx + 1);
                idx = strCurrentPathName.indexOf('"');
                if (idx > 0) {
                    strCurrentPathName = strCurrentPathName.substring(0, idx);
                }
            }
            LogReply();
        } catch (IOException e) {
            RaiseException("Problems with pwd", e);
            //         objLogger.error("Problems with pwd", e);
        }

        return strCurrentPathName;
    } // private int DoPWD

    private int DoCDUP() {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::DoPWD";

        try {
            objLogger.debug("Try cdup .");

            cdup();
            LogReply();
            DoPWD();

        } catch (IOException e) {
            // objLogger.error("Problems with CDUP", e);
        }

        return 0;
    } // private int DoPWD

    private int DoCD(final String strFolderName) {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::DoCD";

        int x = 0;
        try {
            /**
             * kb 2011-03-18
             * den objLogger rausgenommen, weil i.d.R. keine konfiguration fr log4j
             * vorgefunden wird. Dann wird alles das an das Ende des Protokolls
             * als [info] geschrieben und blht das Protokoll unntig auf.
             */
            // objLogger.debug("Try cd with '" + strFolderName + "'.");
            x = cd(strFolderName);
            // LogReply();
            String strReply = getReplyString();
        } catch (IOException e) {
        }

        return x;
    } // private int DoCD

    private boolean LogReply() {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::LogReply";

        String strReply = getReplyString();
        objLogger.debug(strReply);

        return true;
    } // private boolean LogReply

    private boolean isNegativeCommandCompletion() {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::isNegativeCommandCompletion";

        int x = getReplyCode();

        return x > 300;
    } // private boolean isNegativeCommandCompletion

    private boolean isPositiveCommandCompletion() {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::isPositiveCommandCompletion";

        int x = getReplyCode();

        return x <= 300;
    } // private boolean isPositiveCommandCompletion

    public boolean isNotHiddenFile(final String strFileName) {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::isNotHiddenFile";

        if (strFileName != ".." && strFileName != ".") {
            return true; // not a hidden file
        }

        return false; // it is a hidden-file
    } // private boolean isNotHiddenFile

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine
     * @param pathname on remote machine
     * @return a listing of the contents of a directory on the remote machine
     *
     * @exception Exception
     * @see #dir()
     */
    @Override
    public Vector<String> nList(final String pathname, final boolean flgRecurseSubFolder) throws Exception {

        return getFilenames(pathname, flgRecurseSubFolder);
    } // nList

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine
     *
     * @return a listing of the contents of a directory on the remote machine
     *
     * @exception Exception
     * @see #nList( String )
     * @see #dir()
     * @see #dir( String )
     */
    @Override
    public Vector<String> nList() throws Exception {
        return getFilenames();
    } // nList

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine (without subdirectory)
     *
     * @return a listing of the contents of a directory on the remote machine
     *
     * @exception Exception
     * @see #nList( String )
     * @see #dir()
     * @see #dir( String )
     */
    private Vector<String> getFilenames() throws Exception {
        return getFilenames("", false);
    } // getFilenames

    private Vector<String> getFilenames(final boolean flgRecurseSubFolders) throws Exception {
        return getFilenames("", flgRecurseSubFolders);
    } // getFilenames

    /**
     * return a listing of the contents of a directory in short format on
     * the remote machine
     *
     * @return a listing of the contents of a directory on the remote machine
     *
     * @exception Exception
     * @see #nList( String )
     * @see #dir()
     * @see #dir( String )
     */

    @Override
    public Vector<String> nList(final boolean recursive) throws Exception {
        return getFilenames(recursive);
    } // nList

    /**
     * return a listing of the files in a directory in long format on
     * the remote machine
     * @param pathname on remote machine
     * @return a listing of the contents of a directory on the remote machine
     * @exception Exception
     * @see #nList()
     * @see #nList( String )
     * @see #dir()
     */

    public Vector<String> dir(final String pathname) throws Exception {
        return getFilenames(pathname);
    }

    /**
     * return a listing of a directory in long format on
     * the remote machine
     * @param pathname on remote machine
     * @return a listing of the contents of a directory on the remote machine
     * @exception Exception
     * @see #nList()
     * @see #nList( String )
     * @see #dir()
     * @deprecated
     */
    @Deprecated
    public Vector<String> dir(final String pathname, final int flag) throws Exception {

        Vector<String> fileList = new Vector<String>();
        FTPFile[] listFiles = listFiles(pathname);
        for (FTPFile listFile : listFiles) {
            if (flag > 0 && listFile.isDirectory()) {
                fileList.addAll(this.dir(pathname + "/" + listFile.toString(), flag >= 1024 ? flag : flag + 1024));
            } else {
                if (flag >= 1024) {
                    fileList.add(pathname + "/" + listFile.toString());
                } else {
                    fileList.add(listFile.toString());
                }
            }
        }
        return fileList;
    }

    /**
     * return a listing of the files of the current directory in long format on
     * the remote machine
     * @return a listing of the contents of the current directory on the remote machine
     * @exception Exception
     * @see #nList()
     * @see #nList( String )
     * @see #dir( String )
     */
    public Vector<String> dir() throws Exception {
        return dir(".");
    }

    /**
     * @return The entire text from the last FTP response as a String.
     */
    public String getResponse() {
        return this.getReplyString();
    }

    /**
     * return the size of remote-file on the remote machine on success, otherwise -1
     * @param remoteFile the file on remote machine
     * @return the size of remote-file on remote machine
     */
    @Override
    public long size(final String remoteFile) throws Exception {

        this.sendCommand("SIZE " + remoteFile);
        if (this.getReplyCode() == FTPReply.CODE_213)
            return Long.parseLong(trimResponseCode(this.getReplyString()));
        else
            return -1L;
    }

    /**
     * trim the response code at the beginning
     * @param response
     * @return the response string without response code
     * @throws Exception
     */
    private String trimResponseCode(final String response) throws Exception {
        if (response.length() < 5)
            return response;
        return response.substring(4).trim();
    }

    /**
     * Retrieves a named file from the ftp server.
     *
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @exception Exception
     * @see #getFile( String, String )
     */
    public boolean get(final String remoteFile, final String localFile) throws Exception {
        FileOutputStream out = null;
        boolean rc = false;
        try {
            out = new FileOutputStream(localFile);
            rc = retrieveFile(remoteFile, out);
            return rc;
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                if (out != null)
                    out.close();
            } catch (Exception e) {
            }
        }
    } // get

    /**
     * Retrieves a named file from the ftp server.
     *
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @return  The total number of bytes retrieved.
     * @see #get( String, String )
     * @exception Exception
     */
    @Override
    public long getFile(final String remoteFile, final String localFile) throws Exception {
        final boolean flgAppendLocalFile = false;
        return getFile(remoteFile, localFile, flgAppendLocalFile);
    }

    /**
     * Retrieves a named file from the ftp server.
     *
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @param append Appends the remote file to the local file.
     * @return  The total number of bytes retrieved.
     * @see #get( String, String )
     * @exception Exception
     */
    @Override
    public long getFile(final String remoteFile, final String localFile, final boolean append) throws Exception {
        InputStream in = null;
        OutputStream out = null;
        long totalBytes = 0;
        try {
            in = retrieveFileStream(remoteFile);
            // boolean test = retrieveFile(remoteFile, new FileOutputStream( new File(localFile+"test"), append ));

            if (in == null) {
                throw new JobSchedulerException("Could not open stream for " + remoteFile
                        + ". Perhaps the file does not exist. Reply from ftp server: " + getReplyString());
            }

            if (isPositiveCommandCompletion() == false) {
                throw new JobSchedulerException(
                        "..error occurred in getFile() [retrieveFileStream] on the FTP server for file ["
                                + remoteFile + "]: " + getReplyString());
            }

            // TODO Buffersize must be an Option
            byte[] buffer = new byte[4096];
            out = new FileOutputStream(new File(localFile), append);
            // TODO get progress info

            int bytes_read = 0;
            synchronized (this) {
                while ((bytes_read = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytes_read);
                    out.flush();
                    totalBytes += bytes_read;
                }
            }
            in.close();
            out.close();
            if (!completePendingCommand()) {
                logout();
                disconnect();
                throw new JobSchedulerException("File transfer failed.");
            }
            if (isNegativeCommandCompletion()) {
                throw new JobSchedulerException("..error occurred in getFile() on the FTP server for file ["
                        + remoteFile + "]: " + getReplyString());
            }
            if (totalBytes > 0)
                return totalBytes;
            else
                return -1L;
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Exception e) {
            }
            try {
                if (out != null)
                    out.flush();
            } catch (Exception e) {
            }
            try {
                if (out != null)
                    out.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * Stores a file on the server using the given name.
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @return True if successfully completed, false if not.
     * @exception Exception
     * @see #putFile( String, String )
     */
    @Override
    public boolean put(final String localFile, final String remoteFile) throws Exception {
        FileInputStream in = null;
        boolean rc = false;
        try {
            in = new FileInputStream(localFile);
            // TODO get progress info
            rc = storeFile(remoteFile, in);
            return rc;
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * Stores a file on the server using the given name.
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @return The total number of bytes written.
     * @exception Exception
     * @see #put( String, String )
     */
    @Override
    public long putFile(final String localFile, final String remoteFile) throws Exception {
        java.io.OutputStream outputStream = storeFileStream(remoteFile);
        if (isNegativeCommandCompletion()) {
            throw new JobSchedulerException("..error occurred in storeFileStream() on the FTP server for file ["
                    + remoteFile + "]: " + getReplyString());
        }
        return putFile(localFile, outputStream);
    } // putFile

    /**
     *
     * @param localfile The name of the local file.
     * @param an OutputStream through which data can be 
     * written to store a file on the server using the given name.
     * @return The total number of bytes written.
     * @exception Exception
     */
    @Override
    public long putFile(final String localFile, final OutputStream out) throws Exception {
        FileInputStream in = null;
        long totalBytes = 0;
        try {

            if (out == null)
                throw new JobSchedulerException("output stream has null value.");
            // TODO Buffersize must be an Option
            byte[] buffer = new byte[4096];
            in = new FileInputStream(new File(localFile));
            // TODO get progress info
            int bytesWritten;
            synchronized (this) {
                while ((bytesWritten = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesWritten);
                    totalBytes += bytesWritten;
                }
            }

            in.close();
            out.close();
            if (!completePendingCommand()) {
                logout();
                disconnect();
                throw new JobSchedulerException("File transfer failed.");
            }
            if (isNegativeCommandCompletion()) {
                throw new JobSchedulerException("..error occurred in putFile() on the FTP server for file ["
                        + localFile + "]: " + getReplyString());
            }
            return totalBytes;
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Exception e) {
            }
            try {
                out.flush();
            } catch (Exception e) {
            }
            try {
                if (out != null)
                    out.close();
            } catch (Exception e) {
            }
        }
    } // putFile

    /**
     * append a local file to the remote one on the server
     * 
     * @param localFile The name of the local file.
     * @param remoteFile The name of the remote file.
     * @return The total number of bytes appended.
     * @exception Exception
     * @see #put( String, String )
     * @see #putFile( String, String )
     */

    public long appendFile(final String localFile, final String remoteFile) throws Exception {
        return putFile(localFile, appendFileStream(remoteFile));
    } // appendFile

    /**
     * Using ASCII mode for file transfers
     * @return True if successfully completed, false if not.
     * @throws IOException If an I/O error occurs while either sending a
     * command to the server or receiving a reply from the server.
     */
    public boolean ascii() throws IOException {
        return setFileType(FTP.ASCII_FILE_TYPE);
    }

    /**
     * Using Binary mode for file transfers
     * @return True if successfully completed, false if not.
     * @throws IOException If an I/O error occurs while either sending a
     * command to the server or receiving a reply from the server.
     */
    public boolean binary() throws IOException {
        return setFileType(FTP.BINARY_FILE_TYPE);
    }

    /**
     *
     * @param directory The new working directory.
     * @return The reply code received from the server.
     * @throws IOException If an I/O error occurs while either sending a
     * command to the server or receiving a reply from the server.
     */
    public int cd(final String directory) throws IOException {
        return cwd(directory);
    }

    /**
     * Deletes a file on the FTP server.
     * @param The pathname of the file to be deleted.
     * @return True if successfully completed, false if not.
     * @throws IOException If an I/O error occurs while either sending a
     * command to the server or receiving a reply from the server.
     */
    @Override
    public boolean delete(final String pathname) throws IOException {
        return deleteFile(pathname);
    }

    @Override
    public boolean login(final String strUserName, final String strPassword) {

        @SuppressWarnings("unused")
        final String conMethodName = conClassName + "::login";

        try {
            super.login(strUserName, strPassword);
        } catch (IOException e) {
            e.printStackTrace();
        }
        LogReply();

        return true;
    } // private boolean login

    protected void RaiseException(final Exception e, final String pstrM) {
        objLogger.error(pstrM);
        if (e != null) {
            e.printStackTrace(System.err);
            throw new JobSchedulerException(pstrM, e);
        } else {
            throw new JobSchedulerException(pstrM);
        }
    }

    protected void RaiseException(final String pstrM, final Exception e) {
        RaiseException(e, pstrM);
    }

    protected void RaiseException(final String pstrM) {
        RaiseException(null, pstrM);
    }

}