ch.sdi.core.impl.ftp.FTPClientExample.java Source code

Java tutorial

Introduction

Here is the source code for ch.sdi.core.impl.ftp.FTPClientExample.java

Source

/**
 * Copyright (c) 2014 by the original author or authors.
 *
 * This code is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package ch.sdi.core.impl.ftp;

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.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPHTTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
import org.apache.commons.net.util.TrustManagerUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.util.StringUtils;

import ch.sdi.core.exc.SdiException;
import ch.sdi.core.impl.ftp.PrintCommandToLoggerListener;

/**
 * The base of this example is copied from
 * (http://commons.apache.org/proper/commons-net/examples/ftp/FTPClientExample.java) and adapted for
 * using it embedded.<p>
 *
 * This is an example program demonstrating how to use the FTPClient class.
 * This program connects to an FTP server and retrieves the specified
 * file. If the -s flag is used, it stores the local file at the FTP server.
 * Just so you can see what's happening, all reply strings are printed.
 * If the -b flag is used, a binary transfer is assumed (default is ASCII).
 * See below for further options.
 */
public final class FTPClientExample {

    /** logger for this class */
    private static Logger myLog = LogManager.getLogger(FTPClientExample.class);

    private boolean myStoreFile = false;
    private boolean myBinaryTransfer = false;
    private boolean myListFiles = false;
    private boolean myListNames = false;
    private boolean myHidden = false;
    private boolean myLocalActive = false;
    private boolean myUseEpsvWithIPv4 = false;
    private boolean myFeat = false;
    private boolean myPrintHash = false;
    private boolean myMlst = false;
    private boolean myMlsd = false;
    private boolean myLenient = false;
    private long myKeepAliveTimeout = -1;
    private int myControlKeepAliveReplyTimeout = -1;
    private String myProtocol = null; // SSL protocol
    private String myDoCommand = null;
    private String myTrustmgr = null;
    private String myProxyHost = null;
    private int myProxyPort = 80;
    private String myProxyUser = null;
    private String myProxyPassword = null;
    private String myUsername = null;
    private String myPassword = null;
    private String myRemote;
    private String myLocal;
    private int myPort;
    private String myServer;
    private FTPClient myFtp;
    private PrintCommandToLoggerListener myPrintCommandToLoggerListener;

    public static final String USAGE = "Usage: ftp [options] <hostname> <username> <password> [<remote file> [<local file>]]\n"
            + "\nDefault behavior is to download a file and use ASCII transfer mode.\n"
            + "\t-a - use local active mode (default is local passive)\n"
            + "\t-A - anonymous login (omit username and password parameters)\n"
            + "\t-b - use binary transfer mode\n"
            + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n"
            + "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n"
            + "\t-e - use EPSV with IPv4 (default false)\n"
            + "\t-f - issue FEAT command (remote and local files are ignored)\n"
            + "\t-h - list hidden files (applies to -l and -n only)\n"
            + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n"
            + "\t-l - list files using LIST (remote is used as the pathname if provided)\n"
            + "\t     Files are listed twice: first in raw mode, then as the formatted parsed data.\n"
            + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n"
            + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n"
            + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n"
            + "\t-s - store file on server (upload)\n"
            + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n"
            + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n"
            + "\t-T  all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n"
            + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n"
            + "\t-PrU user - HTTP Proxy server username\n" + "\t-PrP password - HTTP Proxy server password\n"
            + "\t-# - add hash display during transfers\n";

    public static void main(String[] aArgs) throws UnknownHostException {
        List<String> args = new ArrayList<String>(Arrays.asList(aArgs));

        args.add("-s"); // store file on sesrver
        args.add("-b"); // binary transfer mode
        args.add("-#");

        args.add("192.168.99.1");
        args.add("heri"); // user
        args.add("heri"); // pw
        args.add("/var/www/log4j2.xml");

        URL url = ClassLoader.getSystemResource("sdimain_test.properties");
        // URL url = ClassLoader.getSystemResource( "log4j2.xml" );
        args.add(url.getFile());

        FTPClientExample example = new FTPClientExample();
        try {
            example.init(args.toArray(new String[args.size()]));
            example.run();
        } catch (Throwable t) {
            myLog.error("Exception caught", t);
            myLog.info(USAGE);
            System.exit(1);
        }

    } // end main

    /**
     * @param aArgs
     */
    private void init(String[] aArgs) throws Throwable {
        int base = 0;
        int minParams = 5; // listings require 3 params
        for (base = 0; base < aArgs.length; base++) {
            if (aArgs[base].equals("-s")) {
                myStoreFile = true;
            } else if (aArgs[base].equals("-a")) {
                myLocalActive = true;
            } else if (aArgs[base].equals("-A")) {
                myPassword = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName();
                myUsername = "anonymous";
            } else if (aArgs[base].equals("-b")) {
                myBinaryTransfer = true;
            } else if (aArgs[base].equals("-c")) {
                myDoCommand = aArgs[++base];
                minParams = 3;
            } else if (aArgs[base].equals("-d")) {
                myMlsd = true;
                minParams = 3;
            } else if (aArgs[base].equals("-e")) {
                myUseEpsvWithIPv4 = true;
            } else if (aArgs[base].equals("-f")) {
                myFeat = true;
                minParams = 3;
            } else if (aArgs[base].equals("-h")) {
                myHidden = true;
            } else if (aArgs[base].equals("-k")) {
                myKeepAliveTimeout = Long.parseLong(aArgs[++base]);
            } else if (aArgs[base].equals("-l")) {
                myListFiles = true;
                minParams = 3;
            } else if (aArgs[base].equals("-L")) {
                myLenient = true;
            } else if (aArgs[base].equals("-n")) {
                myListNames = true;
                minParams = 3;
            } else if (aArgs[base].equals("-p")) {
                myProtocol = aArgs[++base];
            } else if (aArgs[base].equals("-t")) {
                myMlst = true;
                minParams = 3;
            } else if (aArgs[base].equals("-w")) {
                myControlKeepAliveReplyTimeout = Integer.parseInt(aArgs[++base]);
            } else if (aArgs[base].equals("-T")) {
                myTrustmgr = aArgs[++base];
            } else if (aArgs[base].equals("-PrH")) {
                myProxyHost = aArgs[++base];
                String parts[] = myProxyHost.split(":");
                if (parts.length == 2) {
                    myProxyHost = parts[0];
                    myProxyPort = Integer.parseInt(parts[1]);
                }
            } else if (aArgs[base].equals("-PrU")) {
                myProxyUser = aArgs[++base];
            } else if (aArgs[base].equals("-PrP")) {
                myProxyPassword = aArgs[++base];
            } else if (aArgs[base].equals("-#")) {
                myPrintHash = true;
            } else {
                break;
            }
        }

        int remain = aArgs.length - base;
        if (myUsername != null) {
            minParams -= 2;
        }
        if (remain < minParams) // server, user, pass, remote, local [protocol]
        {
            throw new Exception("Error: Too less params");
        }

        myServer = aArgs[base++];
        myPort = 0;
        String parts[] = myServer.split(":");
        if (parts.length == 2) {
            myServer = parts[0];
            myPort = Integer.parseInt(parts[1]);
        }
        if (myUsername == null) {
            myUsername = aArgs[base++];
            myPassword = aArgs[base++];
        }

        myRemote = null;
        if (aArgs.length - base > 0) {
            myRemote = aArgs[base++];
        }

        myLocal = null;
        if (aArgs.length - base > 0) {
            myLocal = aArgs[base++];
        }

    }

    /**
    *
    */
    private void run() throws Throwable {
        if (myProtocol == null) {
            if (myProxyHost != null) {
                myLog.debug("Using HTTP proxy server: " + myProxyHost);
                myFtp = new FTPHTTPClient(myProxyHost, myProxyPort, myProxyUser, myProxyPassword);
            } else {
                myFtp = new FTPClient();
            }
        } else {
            FTPSClient ftps;
            if (myProtocol.equals("true")) {
                ftps = new FTPSClient(true);
            } else if (myProtocol.equals("false")) {
                ftps = new FTPSClient(false);
            } else {
                String prot[] = myProtocol.split(",");
                if (prot.length == 1) { // Just protocol
                    ftps = new FTPSClient(myProtocol);
                } else { // protocol,true|false
                    ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
                }
            }
            myFtp = ftps;
            if ("all".equals(myTrustmgr)) {
                ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
            } else if ("valid".equals(myTrustmgr)) {
                ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
            } else if ("none".equals(myTrustmgr)) {
                ftps.setTrustManager(null);
            }
        }

        if (myPrintHash) {
            myFtp.setCopyStreamListener(createListener());
        }
        if (myKeepAliveTimeout >= 0) {
            myFtp.setControlKeepAliveTimeout(myKeepAliveTimeout);
        }
        if (myControlKeepAliveReplyTimeout >= 0) {
            myFtp.setControlKeepAliveReplyTimeout(myControlKeepAliveReplyTimeout);
        }
        myFtp.setListHiddenFiles(myHidden);

        // intercept commands and write it to our logger
        myPrintCommandToLoggerListener = new PrintCommandToLoggerListener(myLog);
        PrintWriter writer = myPrintCommandToLoggerListener.getPrintWriter();
        myFtp.addProtocolCommandListener(new PrintCommandListener(writer, true, '\n', true));
        //        myFtp.addProtocolCommandListener( new PrintCommandListener( new PrintWriter( System.out ), true ) );

        try {
            int reply;
            if (myPort > 0) {
                myFtp.connect(myServer, myPort);
            } else {
                myFtp.connect(myServer);
            }

            myLog.debug("Connected to " + myServer + " on " + (myPort > 0 ? myPort : myFtp.getDefaultPort()));

            // After connection attempt, you should check the reply code to verify
            // success.
            reply = myFtp.getReplyCode();

            if (!FTPReply.isPositiveCompletion(reply)) {
                myFtp.disconnect();
                throw new Exception("FTP server refused connection.");
            }
        } catch (IOException e) {
            if (myFtp.isConnected()) {
                try {
                    myFtp.disconnect();
                } catch (IOException f) {
                    // do nothing
                }
            }
            throw new Exception("Could not connect to server.", e);
        }

        try {
            if (!myFtp.login(myUsername, myPassword)) {
                myFtp.logout();
                throw createFtpException("Problems on login");
            }

            myLog.debug("Remote system is " + myFtp.getSystemType());

            if (myBinaryTransfer) {
                myFtp.setFileType(FTP.BINARY_FILE_TYPE);
            } else {
                // in theory this should not be necessary as servers should default to ASCII
                // but they don't all do so - see NET-500
                myFtp.setFileType(FTP.ASCII_FILE_TYPE);
            }

            // Use passive mode as default because most of us are
            // behind firewalls these days.
            if (myLocalActive) {
                myFtp.enterLocalActiveMode();
            } else {
                myFtp.enterLocalPassiveMode();
            }

            myFtp.setUseEPSVwithIPv4(myUseEpsvWithIPv4);

            if (myStoreFile) {
                InputStream input;

                input = new FileInputStream(myLocal);

                myFtp.storeFile(myRemote, input);

                input.close();
            } else if (myListFiles) {
                if (myLenient) {
                    FTPClientConfig config = new FTPClientConfig();
                    config.setLenientFutureDates(true);
                    myFtp.configure(config);
                }

                for (FTPFile f : myFtp.listFiles(myRemote)) {
                    myLog.debug(f.getRawListing());
                    myLog.debug(f.toFormattedString());
                }
            } else if (myMlsd) {
                for (FTPFile f : myFtp.mlistDir(myRemote)) {
                    myLog.debug(f.getRawListing());
                    myLog.debug(f.toFormattedString());
                }
            } else if (myMlst) {
                FTPFile f = myFtp.mlistFile(myRemote);
                if (f != null) {
                    myLog.debug(f.toFormattedString());
                }
            } else if (myListNames) {
                for (String s : myFtp.listNames(myRemote)) {
                    myLog.debug(s);
                }
            } else if (myFeat) {
                // boolean feature check
                if (myRemote != null) { // See if the command is present
                    if (myFtp.hasFeature(myRemote)) {
                        myLog.debug("Has feature: " + myRemote);
                    } else {
                        if (FTPReply.isPositiveCompletion(myFtp.getReplyCode())) {
                            myLog.debug("FEAT " + myRemote + " was not detected");
                        } else {
                            throw createFtpException("Command failed");
                        }
                    }

                    // Strings feature check
                    String[] features = myFtp.featureValues(myRemote);
                    if (features != null) {
                        for (String f : features) {
                            myLog.debug("FEAT " + myRemote + "=" + f + ".");
                        }
                    } else {
                        if (FTPReply.isPositiveCompletion(myFtp.getReplyCode())) {
                            myLog.warn("FEAT " + myRemote + " is not present");
                        } else {
                            throw createFtpException("Command failed");
                        }
                    }
                } else {
                    if (myFtp.features()) {
                        // Command listener has already printed the output
                    } else {
                        throw createFtpException("Command failed");
                    }
                }
            } else if (myDoCommand != null) {
                if (myFtp.doCommand(myDoCommand, myRemote)) {
                    // Command listener has already printed the output
                } else {
                    throw createFtpException("Command failed");
                }
            } else {
                OutputStream output;

                output = new FileOutputStream(myLocal);

                myFtp.retrieveFile(myRemote, output);

                output.close();
            }

            myFtp.noop(); // check that control connection is working OK

            myFtp.logout();
        } catch (FTPConnectionClosedException e) {
            throw createFtpException("Server closed connection.");
        } catch (IOException e) {
            throw createFtpException("IOException caught");
        } finally {
            myPrintCommandToLoggerListener.flushRest();

            if (myFtp.isConnected()) {
                try {
                    myFtp.disconnect();
                } catch (IOException f) {
                    // do nothing
                }
            }
        }

    }

    private CopyStreamListener createListener() {
        return new CopyStreamListener() {

            private long megsTotal = 0;

            @Override
            public void bytesTransferred(CopyStreamEvent event) {
                bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(),
                        event.getStreamSize());
            }

            @Override
            public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
                // ??? what does this algo? Print a # for each transferred MB?
                long megs = totalBytesTransferred / 1000000;
                for (long l = megsTotal; l < megs; l++) {
                    myLog.warn("#");
                }
                megsTotal = megs;
            }
        };
    }

    private SdiException createFtpException(String aMessage) {
        return createFtpException(aMessage, null);

    }

    private SdiException createFtpException(String aMessage, Throwable aThrowable) {
        StringBuilder sb = new StringBuilder(aMessage);
        if (myFtp != null) {
            sb.append("ReplyCode: ").append(myFtp.getReplyCode());
            sb.append("\n    Reply-Message:");
            sb.append("\n        ").append(
                    StringUtils.collectionToDelimitedString(Arrays.asList(myFtp.getReplyStrings()), "\n        "));
        } // if myFtp != null

        return new SdiException(sb.toString(), aThrowable, SdiException.EXIT_CODE_FTP_ERROR);

    }

}