/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.
 */
package org.opengion.fukurou.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.Map;
import java.util.LinkedHashMap ;

import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)
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.PrintCommandListener;
import org.apache.commons.net.ftp.FTPReply;

import org.opengion.fukurou.system.Closer;							// 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
import org.opengion.fukurou.system.ThrowUtil;				// 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system

/**
 * FTPConnect.java は、共通的に使用される FTP関連の基本機能を実装した、クラスです。
 *
 * これは、org.apache.commons.net.ftp.FTPClient をベースに開発されています。
 * このクラスの実行には、commons-net-ftp-2.0.jar が必要です。
 *
 * -host=FTPサーバー -user=ユーザー -passwd=パスワード -remoteFile=FTP先のファイル名 を必須設定します。
 * -localFile=ローカルのファイル名は、必須ではありませんが、-command=DEL の場合にのみ不要であり、
 * それ以外の command の場合は、必要です。
 *
 * -mode=[ASCII/BINARY] は、ファイル転送時に、ファイルの種類によって指定します。
 * ASCIIモードは、OS毎に異なる改行コードを変換して転送します。BINARYモードは、そのままの姿でやり取りします。
 *
 * -command=[GET/PUT/DEL] は、FTPサーバーに対しての処理の方法を指定します。
 *   GET:FTPサーバーからローカルにファイル転送します(初期値)。
 *   PUT:ローカルファイルをFTPサーバーに PUT(STORE、SAVE、UPLOAD、などと同意語)します。
 *   DEL:FTPサーバーの指定のファイルを削除します。この場合のみ、-localFile 属性の指定は不要です。
 *
 * -passive=[true/false] は、パッシブモード(ローカルからサーバーへ接続を張る)を利用する場合に宣言します。
 * 初期値は、true:使用する です。
 * 通常のFTPでは、アクティブモードでファイルをやり取りします。これは、クライアント側が、サーバーソケットを
 * オープンし、指定のポートで待ちうけ状態を作り、FTPサーバー側から接続してもらいます。
 * 一般的な企業内では、ファイアウォールの設定等で、外部からの接続を制限しているケースが多く、アクティブモード
 * では、繋がらない場合が、ほとんどです。
 * このクラスでは、初期値をパッシブモードにすることで、企業内のファイアウォールのある環境でも
 * 接続できるようにしています。
 *
 * -mkdirs=[true/false] は、受け側のファイル(GET時:LOCAL、PUT時:FTPサーバー)に取り込むファイルのディレクトリが
 * 存在しない場合に、作成するかどうかを指定します(初期値:true)。
 * 通常、FTPサーバーに、フォルダ階層を作成してPUTする場合、動的にフォルダ階層を作成したいケースで便利です。
 * 逆に、フォルダは確定しており、指定フォルダ以外に PUT するのはバグっていると事が分かっている場合には
 * false に設定して、存在しないフォルダにPUT しようとすると、エラーになるようにします。
 *
 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。
 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に
 * 繋げてください。
 *
 * @og.formSample
 *  FTPConnect -host=FTPサーバー -user=ユーザー -passwd=パスワード -remoteFile=FTP先のファイル名 [-localFile=ローカルのファイル名]
 *                   [-mode=[ASCII/BINARY]  ] [-command=[GET/PUT/DEL] ] [-passive=[true/false] ]
 *
 *    -host=FTPサーバー                 ：接続先のFTPサーバーのアドレスまたは、サーバー名
 *    -user=ユーザー                    ：接続するユーザー名
 *    -passwd=パスワード                ：接続するユーザーのパスワード
 *    -remoteFile=FTP先のファイル名     ：接続先のFTPサーバー側のファイル名。PUT,GET 関係なくFTP側として指定します。
 *   [-localFile=ローカルのファイル名]  ：ローカルのファイル名。PUT,GET 関係なくローカルファイルを指定します。
 *   [-port=ポート ]                    ：接続するサーバーのポートを指定します。
 *   [-mode=[ASCII/BINARY]  ]           ：扱うファイルの種類を指定します(初期値:ASCII)
 *   [-command=[GET/PUT/DEL] ]          ：FTPサーバー側での処理の方法を指定します。
 *                                              GET:FTP⇒LOCAL、PUT:LOCAL⇒FTP への転送です(初期値:GET)
 *                                              DEL:FTPファイルを削除します。
 *   [-passive=[true/false] ]           ：パッシブモード(ローカルからサーバーへ接続を張る)を利用するかどうか(初期値:true)
 *                                              (false:アクティブモード(通常のFTPの初期値)で通信します。)
 *   [-mkdirs=[true/false]  ]           ：受け側ファイル(GET時:LOCAL、PUT時:FTPサーバー)にディレクトリを作成するかどうか(初期値:true)
 *                                              (false:ディレクトリが無ければ、エラーにします。)
 *   [-encode=エンコード名 ]            ：日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)
 *   [-timeout=タイムアウト[秒] ]       ：Dataタイムアウト(初期値:600 [秒])
 *   [-display=[false/true] ]           ：trueは、検索状況を表示します(初期値:false)
 *   [-debug=[false|true]   ]           ：デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
 *
 * @og.rev 5.1.6.0 (2010/05/01) 新規追加
 *
 * @version  5.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class FTPConnect extends AbstractConnect {
	private final FTPClient ftp;

	private static final int DEF_PORT	= 21;	// ポート

	private boolean	isAsciiMode = true;			// 扱うファイルの種類を指定します。		(true:ASCII / false:BINARY)
	private boolean	isPassive	= true;			// パッシブモードを利用するかどうか。	(true:パッシブ / false:アクティブ)
	private String	encode		= "UTF-8";		// 日本語ファイル名などのエンコード名(初期値:UTF-8)

	private boolean	isConnect	;				// コネクト済みかどうか。

	private String	lastRemoteDir = "/";		// FTP先の最後に登録したフォルダ名(mkdir の高速化のため)

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
	 */
	public FTPConnect() {
		super();
		ftp = new FTPClient();
	}

	/**
	 * FTPサーバーへの接続、ログインを行います。
	 *
	 * このメソッドは、初期化メソッドです。
	 * FTPサーバーへの接続、ログインを行いますので、複数ファイルを転送する
	 * ケースでは、最初に１度だけ呼び出すだけです。
	 * 接続先を変更する場合は、もう一度このメソッドをコールする必要があります。
	 * (そのような場合は、通常、オブジェクトを構築しなおす方がよいと思います。)
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
	 */
	@Override
	public void connect() {
		if( isDisplay ) { System.out.println( "CONNECT: HOST=" + host + ",USER=" + user + ",PORT=" + port ); }

		// もし、すでに接続されていた場合は、クロース処理を行います。
		if( isConnect ) { disconnect(); }

		try {
			// connectメソッドの前に呼び出さなければならないらしい。
			// 日本語ファイル名対応(初期値:UTF-8)
			if( encode != null ) {
				ftp.setControlEncoding( encode );
			}

			// サーバーに対して接続を行います。
			ftp.connect( host, getPort( DEF_PORT ) );

			final int reply = ftp.getReplyCode();

			if( !FTPReply.isPositiveCompletion( reply ) ) {
				errAppend( "FTP server refused connection. [PositiveCompletion]" );
				errAppend( "   host    = [" , host	, "]" );
				errAppend( "   user    = [" , user	, "]" );
				errAppend( "   port    = [" , port	, "]" );
				errAppend( ftp.getReplyString() );
				disconnect();
				throw new OgRuntimeException( getErrMsg() );
			}

			// サーバーに対してログインを行います。
			if( !ftp.login( user, passwd ) ) {
				errAppend( "ログインに失敗しました。" );
				errAppend( "   host    = [" , host	, "]" );
				errAppend( "   user    = [" , user	, "]" );
				errAppend( "   port    = [" , port	, "]" );
				errAppend( ftp.getReplyString() );
				disconnect();
				throw new OgRuntimeException( getErrMsg() );
			}

			// 各種パラメータを設定します。
			paramInit();
		}
		catch( final IOException ex ) {
			errAppend( "FTP server refused connection." );
			errAppend( ftp.getReplyString() );
			errAppend( ex );
			if( isDebug ) { System.err.println( ThrowUtil.ogStackTrace( ex ) ); }
			disconnect();
			throw new OgRuntimeException( getErrMsg(),ex );
		}

		isConnect = true;
	}

	/**
	 * 設定されたパラメータの初期化処理を行います。
	 *
	 * このメソッドは、ftp.connectt( host )メソッドの実行後に
	 * 呼び出す必要があります。
	 * パラメータを再セット後に、このメソッドを呼び出すことになります。
	 * timeout , encode , CommandListener(display や debug属性) , isPassive ,
	 * isAsciiMode など、ファイル名以外の属性情報の設定を反映させます。
	 * 複数ファイルの転送時には、localFile , remoteFile 属性を設定の上、action() メソッドを
	 * 呼び出すことで、対応できます。
	 *
	 * @throws IOException 入出力エラーが発生したとき
	 */
	private void paramInit() throws IOException {

		// Dataタイムアウト(初期値:600 [秒])。FTPClient のメソッドには、ミリ秒でセットします。
		ftp.setDataTimeout( timeout*1000 );

		// コマンドリスナーのセット(設定済みのListenerをremoveする実装は、入れてません。)
		if( isDisplay && isDebug ) {
			ftp.addProtocolCommandListener( new PrintCommandListener( new PrintWriter(System.out) ) );
		}

		// パッシブモードを利用するかどうか。 (true:パッシブ / false:アクティブ)
		if( isPassive ) { ftp.enterLocalPassiveMode();	}
		else			{ ftp.enterLocalActiveMode();	}

		// 扱うファイルの種類を指定(true:ASCII / false:BINARY)
		boolean isOK = FLAG_OK ;
		if( isAsciiMode ) { isOK = ftp.setFileType( FTP.ASCII_FILE_TYPE ); }
		else			  { isOK = ftp.setFileType( FTP.BINARY_FILE_TYPE );}

		if( !isOK ) {
			errAppend( "paramInit Error." );
			errAppend( ftp.getReplyString() );
			throw new OgRuntimeException( getErrMsg() );
		}
	}

	/**
	 * FTPサーバーとの接続をクローズします。
	 *
	 * ログインされている場合は、ログアウトも行います。
	 * コネクトされている場合は、ディスコネクトします。
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
	 */
	@Override
	public void disconnect() {
		if( isDisplay ) { System.out.println( "DISCONNECT:" ); }

		if( isConnect ) {
			isConnect = false;
			try {
				ftp.logout();
				if( ftp.isConnected() ) {
					ftp.disconnect();
				}
			}
			catch( final Throwable th ) {
				errAppend( "disconnect Error." );
				errAppend( ftp.getReplyString() );
				errAppend( th );
				if( isDebug ) { System.err.println( ThrowUtil.ogStackTrace( th ) ); }			// 6.4.2.0 (2016/01/29)
				throw new OgRuntimeException( getErrMsg(),th );
			}
		}
	}

	/**
	 * command="GET" が指定されたときの処理を行います。
	 *
	 * 接続先のFTPサーバー側のファイル名をローカルにダウンロードします。
	 *
	 * @param	localFile 	ローカルのファイル名
	 * @param	remoteFile	FTP先のファイル名
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	protected void actionGET( final String localFile, final String remoteFile ) throws IOException {
		if( isDebug ) { System.out.println( "GET: " + remoteFile + " => " + localFile ); }

		// GET(DOWNLOAD)取得時は、ローカルファイルのディレクトリを作成する必要がある。
		if( isMkdirs ) {
			makeLocalDir( localFile );
		}

	//	InputStream  input  = null;
		OutputStream output = null;
		try {
	//		input  = ftp.retrieveFileStream( remoteFile );	// 連続読み込みを行うと、null になった。
			output = new FileOutputStream( localFile );
	//		isOK = FileUtil.copy( input,output );
			if( !ftp.retrieveFile( remoteFile, output ) ) {
				errAppend( "ファイルの取得(GET)に失敗しました。" );
				errAppend( "   localFile  = [" , localFile	, "]" );
				errAppend( "   remoteFile = [" , remoteFile , "]" );
				errAppend( ftp.getReplyString() );
				throw new OgRuntimeException( getErrMsg() );
			}
		}
		finally {
	//		Closer.ioClose( input );
			Closer.ioClose( output );
		}
	}

	/**
	 * command="GETDIR" が指定されたときの処理を行います。
	 *
	 * 接続先のFTPサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。
	 *
	 * @param	localDir 	ローカルのディレクトリ名
	 * @param	remoteDir	FTP先のディレクトリ名
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	protected void actionGETdir( final String localDir, final String remoteDir ) throws IOException {
		final FTPFile[] rmtFiles = ftp.listFiles( remoteDir );
		for( int i=0; i<rmtFiles.length; i++ ) {
			final String rmt = rmtFiles[i].getName();
			if( rmtFiles[i].isDirectory() ) {
				actionGETdir( addFile( localDir,rmt ),addFile( remoteDir,rmt ) );
			}
			else {
				actionGET( addFile( localDir,rmt ),addFile( remoteDir,rmt ) );
			}
		}
	}

	/**
	 * command="PUT" が指定されたときの処理を行います。
	 *
	 * ローカルファイルを、接続先のFTPサーバー側にアップロードします。
	 *
	 * @param	localFile 	ローカルのファイル名
	 * @param	remoteFile	FTP先のファイル名
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	protected void actionPUT( final String localFile, final String remoteFile ) throws IOException {
		if( isDebug ) { System.out.println( "PUT: " + localFile + " => " + remoteFile ); }

		boolean isOK = FLAG_OK;

		// PUT(UPLOAD)登録時は、リモートファイルのディレクトリを作成する必要がある。
		if( isMkdirs ) {
			// 前回のDIRとの比較で、すでに存在していれば、makeDirectory 処理をパスする。
			final int ad = remoteFile.lastIndexOf( '/' ) + 1;			// 区切り文字を＋１する。
			final String tmp = remoteFile.substring( 0,ad );
			if( ad > 0 && !lastRemoteDir.startsWith( tmp ) ) {
				lastRemoteDir = tmp;
				if( StringUtil.startsChar( tmp , '/' ) ) {				// 6.2.0.0 (2015/02/27) １文字 String.startsWith
					final String[] fls = tmp.split( "/" );
					ftp.changeWorkingDirectory( "/" );
					for( int i=1; i<fls.length; i++ ) {
						isOK = ftp.changeWorkingDirectory( fls[i] );	// まず、チェンジしてみる。
						if( ! isOK ) {
							isOK = ftp.makeDirectory( fls[i] );			// チェンジできなければ、作る。
							if( isOK ) {
								isOK = ftp.changeWorkingDirectory( fls[i] );	// 作れれば、チェンジする。
							}
						}
						// チェンジできなければ、エラー
						if( ! isOK ) {
							errAppend( "チェンジディレクトリに失敗しました。" );
							errAppend( "   remoteDir  = [" , lastRemoteDir	, "]" );
							errAppend( ftp.getReplyString() );
							throw new OgRuntimeException( getErrMsg() );
						}
					}
				}
				if( ! isOK ) {
					errAppend( "ディレクトリの作成に失敗しました。" );
					errAppend( "   remoteDir  = [" , lastRemoteDir	, "]" );
					errAppend( ftp.getReplyString() );
					throw new OgRuntimeException( getErrMsg() );
				}
			}
		}

		InputStream  input  = null;
	//	OutputStream output = null;
		try {
			input  = new FileInputStream( localFile );
	//		output = ftp.storeFileStream( remoteFile );
	//		isOK = FileUtil.copy( input,output );
			if( !ftp.storeFile( remoteFile, input ) ) {
				errAppend( "ファイルのストア(PUT)に失敗しました。" );
				errAppend( "   localFile  = [" , localFile	, "]" );
				errAppend( "   remoteFile = [" , remoteFile , "]" );
				errAppend( ftp.getReplyString() );
				throw new OgRuntimeException( getErrMsg() );
			}
		}
		finally {
			Closer.ioClose( input );
	//		Closer.ioClose( output );
		}
	}

	/**
	 * command="DEL" が指定されたときの処理を行います。
	 *
	 * 接続先のFTPサーバー側のファイル名を削除します。
	 *
	 * @param	remoteFile	FTP先のファイル名
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	protected void actionDEL( final String remoteFile ) throws IOException {
		if( isDebug ) { System.out.println( "DEL: " + remoteFile ); }

		if( !ftp.deleteFile( remoteFile ) ) {
			errAppend( "ファイルの削除(DEL)に失敗しました。" );
			errAppend( "   remoteFile = [" , remoteFile , "]" );
			errAppend( ftp.getReplyString() );
			throw new OgRuntimeException( getErrMsg() );
		}
	}

	/**
	 * command="DELDIR" が指定されたときの処理を行います。
	 *
	 * 接続先のFTPサーバー側のディレクトリ名を削除します。
	 *
	 * @param	remoteDir	FTP先のディレクトリ名
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	protected void actionDELdir( final String remoteDir ) throws IOException {

		final FTPFile[] rmtFiles = ftp.listFiles( remoteDir );
		for( int i=0; i<rmtFiles.length; i++ ) {
			final String rmt = addFile( remoteDir,rmtFiles[i].getName() );
			if( rmtFiles[i].isDirectory() ) {
				actionDELdir( rmt );
			}
			else {
				actionDEL( rmt );
			}
		}

		if( !ftp.removeDirectory( remoteDir ) ) {
			errAppend( "Directoryの削除(DEL)に失敗しました。" );
			errAppend( "   remoteDir = [" , remoteDir , "]" );
			errAppend( ftp.getReplyString() );
			throw new OgRuntimeException( getErrMsg() );
		}
	}

	/**
	 * 扱うファイルの種類を指定します (初期値:ASCII)。
	 *
	 * ファイルを FTP でやり取りする場合、ASCII 指定すると、OSの改行コードの違いを
	 * 吸収して、ファイルのやり取りが行われます。(つまり、改行コード変換が行われます。)
	 * BINARY 指定すると、現状のファイルのまま、やり取りされます。
	 * これは、たとえ、アスキーファイルであっても、そのままの姿でやり取りされるということです。
	 * 内部的には、isAsciiMode 属性に、true:ASCII / false:BINARY で管理されます。
	 *
	 * @param	mode	扱うファイルの種類を指定します [ASCII/BINARY]
	 */
	public void setMode( final String mode ) {
		if( mode != null ) {
			if( ! "ASCII".equals( mode ) && ! "BINARY".equals( mode ) ) {
				errAppend( "モード指定は、ASCII または、BINARY と指定ください。" );
				errAppend( "   mode  = [" , mode	, "]" );
				throw new OgRuntimeException( getErrMsg() );
			}

			isAsciiMode = "ASCII".equals( mode ) ;		// "ASCII" 以外は、すべて "BINARY" になる。
		}
	}

	/**
	 * パッシブモードを利用するかどうか(true:パッシブ)を設定します(初期値:true)。
	 *
	 * パッシブモード(ローカルからサーバーへ接続を張る)を利用する場合に宣言します。
	 * 通常のFTPでは、アクティブモードでファイルをやり取りします。これは、クライアント側が、サーバーソケットを
	 * オープンし、指定のポートで待ちうけ状態を作り、FTPサーバー側から接続してもらいます。
	 * 一般的な企業内では、ファイアウォールの設定等で、外部からの接続を制限しているケースが多く、アクティブモード
	 * では、繋がらない場合が、ほとんどです。
	 * このクラスでは、初期値をパッシブモードにすることで、企業内のファイアウォールのある環境でも
	 * 接続できるようにしています。
	 *
	 * @param	isPassive	パッシブモードを利用するかどうか。(true:パッシブ)
	 */
	public void setPassive( final boolean isPassive ) {
		this.isPassive = isPassive ;
	}

	/**
	 * 日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)。
	 *
	 * @param	encode	エンコード名
	 */
	public void setEncode( final String encode ) {
		if( encode != null ) {
			this.encode = encode ;
		}
	}

	/**
	 * このクラスの動作確認用の、main メソッドです。
	 *
	 * @param	args	コマンド引数配列
	 */
	public static void main( final String[] args ) {

		final String[] MODE_LST = new String[] { "ASCII","BINARY" };
		final String[] CMD_LST  = new String[] { "GET","PUT","DEL","GETDIR","PUTDIR","DELDIR" };

		final Map<String,String> mustProparty   ;		// ［プロパティ］必須チェック用 Map
		final Map<String,String> usableProparty ;		// ［プロパティ］整合性チェック Map

		mustProparty = new LinkedHashMap<>();
		mustProparty.put( "host",		"接続先のFTPサーバーのアドレスまたは、サーバー名(必須)" );
		mustProparty.put( "user",		"接続するユーザー名(必須)" );
		mustProparty.put( "passwd",		"接続するユーザーのパスワード(必須)" );
		mustProparty.put( "remoteFile",	"接続先のFTPサーバー側のファイル名(必須)" );

		usableProparty = new LinkedHashMap<>();
		usableProparty.put( "localFile",	"ローカルのファイル名" );
		usableProparty.put( "port",			"接続に利用するポート番号を設定します。" );
		usableProparty.put( "mode",			"扱うファイルの種類(ASCII/BINARY)を指定します(初期値:ASCII)" );
		usableProparty.put( "command",		"FTPサーバー側での処理の方法(GET/PUT/DEL)を指定します(初期値:GET)" );
		usableProparty.put( "passive",		"パッシブモード(ローカルからサーバーへ接続を張る)を利用するかどうか(初期値:true)" );
		usableProparty.put( "mkdirs",		"受け側ファイル(GET時:LOCAL、PUT時:FTPサーバー)にディレクトリを作成するかどうか(初期値:true)" );
		usableProparty.put( "encode",		"日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)" );
		usableProparty.put( "timeout",		"Dataタイムアウト(初期値:600 [秒])" );
		usableProparty.put( "display",		"[false/true]:trueは、検索状況を表示します(初期値:false)" );
		usableProparty.put( "debug",		"デバッグ情報を標準出力に表示する(true)かしない(false)か" +
											CR + "(初期値:false:表示しない)" );

		// ******************************************************************************************************* //
		//       以下、単独で使用する場合の main処理
		// ******************************************************************************************************* //
		final Argument arg = new Argument( "org.opengion.fukurou.util.FTPConnect" );
		arg.setMustProparty( mustProparty );
		arg.setUsableProparty( usableProparty );
		arg.setArgument( args );

		final FTPConnect ftp = new FTPConnect();

		final String host   = arg.getProparty( "host");			// FTPサーバー
		final String user   = arg.getProparty( "user" );			// ユーザー
		final String passwd = arg.getProparty( "passwd" );		// パスワード

		ftp.setHostUserPass( host , user , passwd );

		ftp.setPort(	arg.getProparty( "port"					) );		// 接続に利用するポート番号を設定します。
		ftp.setMode(	arg.getProparty( "mode"		,"ASCII",MODE_LST ) );	// 扱うファイルの種類を指定します。
		ftp.setPassive(	arg.getProparty( "passive"	,true		) );		// パッシブモードを利用するかどうか
		ftp.setMkdirs(	arg.getProparty( "mkdirs"	,true		) );		// 受け側ファイルにディレクトリを作成するかどうか
		ftp.setEncode(	arg.getProparty( "encode"	,"UTF-8"    ) );		// 日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)
		ftp.setTimeout(	arg.getProparty( "timeout"	,TIMEOUT    ) );		// Dataタイムアウト(初期値:600 [秒])
		ftp.setDisplay(	arg.getProparty( "display"	,false		) );		// trueは、検索状況を表示します(初期値:false)
		ftp.setDebug(	arg.getProparty( "debug"	,false		) );		// デバッグ情報を標準出力に表示する(true)かしない(false)か

		try {
			// コネクトします。
			ftp.connect();

			final String command		= arg.getProparty( "command" ,"GET" ,CMD_LST  );	// FTP処理の方法を指定します。
			final String localFile	= arg.getProparty( "localFile"  );					// ローカルのファイル名
			final String remoteFile	= arg.getProparty( "remoteFile" );					// FTP先のファイル名

			// command , localFile , remoteFile を元に、SFTP処理を行います。
			ftp.action( command,localFile,remoteFile );
		}
		catch( final RuntimeException ex ) {
			System.err.println( ftp.getErrMsg() );
		}
		finally {
			// ホストとの接続を終了します。
			ftp.disconnect();
		}
	}
}
