    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.util;
    017
    018import java.io.File;
    019import java.io.IOException;
    020
    021/**
    022 * AbstractConnect.java は、共通的に使用される ファイル伝送関連の基本機能を実装した、Abstractクラスです。
    023 *
    024 * -host=サーバー -user=ユーザー -passwd=パスワード -remoteFile=接続先のファイル名 を必須設定します。
    025 * -localFile=ローカルのファイル名は、必須ではありませんが、-command=DEL の場合にのみ不要であり、
    026 * それ以外の command の場合は、必要です。
    027 *
    028 * -command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] は、サーバーに対しての処理の方法を指定します。
    029 *   GET:サーバーからローカルにファイル転送します(初期値)
    030 *   PUT:ローカルファイルをサーバーに PUT(STORE、SAVE、UPLOAD、などと同意語)します。
    031 *   DEL:サーバーの指定のファイルを削除します。この場合のみ、-localFile 属性の指定は不要です。
    032 *   GETDIR,PUTDIR,DELDIR:指定のフォルダ以下のファイルを処理します。
    033 *
    034 * -mkdirs=[true/false] は、受け側のファイル(GET時:LOCAL、PUT時:サーバー)に取り込むファイルのディレクトリが
    035 * 存在しない場合に、作成するかどうかを指定します(初期値:true)
    036 * 通常、サーバーに、フォルダ階層を作成してPUTする場合、動的にフォルダ階層を作成したいケースで便利です。
    037 * 逆に、フォルダは確定しており、指定フォルダ以外に PUT するのはバグっていると事が分かっている場合には
    038 * false に設定して、存在しないフォルダにPUT しようとすると、エラーになるようにします。
    039 *
    040 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。
    041 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に
    042 * 繋げてください。
    043 *
    044 * @og.formSample
    045 *  XXXConnect -host=サーバー -user=ユーザー -passwd=パスワード -remoteFile=接続先のファイル名 [-localFile=ローカルのファイル名]
    046 *                   [-command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] ] [-display=[true/false] ] ・・・・
    047 *
    048 *    -host=サーバー                    ：接続先のサーバーのアドレスまたは、サーバー名
    049 *    -user=ユーザー                    ：接続するユーザー名
    050 *    -passwd=パスワード                ：接続するユーザーのパスワード
    051 *    -remoteFile=接続先のファイル名    ：接続先のサーバー側のファイル名。PUT,GET 関係なくFTP側として指定します。
    052 *   [-localFile=ローカルのファイル名]  ：ローカルのファイル名。PUT,GET 関係なくローカルファイルを指定します。
    053 *   [-port=ポート ]                    ：接続するサーバーのポートを指定します。
    054 *   [-command=[GET/PUT/DEL] ]          ：サーバー側での処理の方法を指定します。
    055 *             [GETDIR/PUTDIR/DELDIR]]          GET:FTP⇒LOCAL、PUT:LOCAL⇒FTP への転送です(初期値:GET)
    056 *                                              DEL:FTPファイルを削除します。
    057 *                                              GETDIR,PUTDIR,DELDIR 指定のフォルダ以下のファイルを処理します。
    058 *   [-mkdirs=[true/false]  ]           ：受け側ファイル(GET時:LOCAL、PUT時:サーバー)にディレクトリを作成するかどうか(初期値:true)
    059 *                                              (false:ディレクトリが無ければ、エラーにします。)
    060 *   [-encode=エンコード名 ]            ：日本語ファイル名などのエンコード名を指定します(初期値:Windows-31J)
    061 *   [-timeout=タイムアウト[秒] ]       ：Dataタイムアウト(初期値:600 [秒])
    062 *   [-display=[false/true] ]           ：trueは、検索状況を表示します(初期値:false)
    063 *   [-debug=[false|true]   ]           ：デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    064 *
    065 * @og.rev 5.1.6.0 (2010/05/01) 新規追加
    066 *
    067 * @version  5.0
    068 * @author       Kazuhiko Hasegawa
    069 * @since    JDK5.0,
    070 */
    071public abstract class AbstractConnect implements ConnectIF {
    072        public static final String CR = System.getProperty("line.separator");
    073        private final StringBuilder errMsg = new StringBuilder( 200 );
    074
    075        /** 正常フラグ  {@value} */
    076        public static final boolean FLAG_OK = true;
    077        /** 異常フラグ  {@value} */
    078        public static final boolean FLAG_NG = false;
    079        /** Dataタイムアウト(初期値:{@value} [秒]) */
    080        public static final int         TIMEOUT = 600 ;
    081
    082        /** サーバー */
    083        protected String        host            = null;                 // サーバー
    084        /** ユーザー */
    085        protected String        user            = null;                 // ユーザー
    086        /** パスワード */
    087        protected String        passwd          = null;                 // パスワード
    088        /** ポート */
    089        protected String        port            = null;                 // ポート
    090
    091        /** ディレクトリを作成するかどうか */
    092        protected boolean       isMkdirs        = true;                 // 受け側ファイルにディレクトリを作成するかどうか。true:作成する / false:無ければエラー
    093        /** Dataタイムアウト  */
    094        protected int           timeout         = TIMEOUT;              // Dataタイムアウト(初期値:600 [秒])
    095        /** 検索状況を表示するかどうか  */
    096        protected boolean       isDisplay       = false;                // trueは、検索状況を表示します(初期値:false)
    097        /** デバッグ情報を表示するかどうか  */
    098        protected boolean       isDebug         = false;                // デバッグ情報を標準出力に表示する(true)かしない(false)か
    099
    100        /**
    101         * サーバーへの接続、ログインを行います。
    102         */
    103        @Override
    104        public abstract void connect() ;
    105
    106        /**
    107         * command , localFile , remoteFile を元に、FTP処理を行います。
    108         *
    109         * このメソッドは、connect( String , String , String )メソッド、および、
    110         * paramInit() 実行後に、呼び出す必要があります。
    111         *
    112         * ※ 内部で、command に指定できない値をセットしたか、何らかのエラーが発生した場合。
    113         *
    114         * @param       command GET:HOST⇒LOCAL 、PUT:LOCAL⇒HOST 、DEL:HOSTファイルを削除
    115         * @param       localFile       ローカルのファイル名
    116         * @param       remoteFile      HOST接続先のファイル名
    117         */
    118        @Override
    119        public void action( final String command, final String localFile, final String remoteFile ) {
    120                String rmtFile = remoteFile.replace( '\\','/' );
    121                if( isDisplay ) {
    122                        System.out.println( "ACTION: command=" + command + ",localFile=" + localFile + ",remoteFile=" + rmtFile );
    123                }
    124
    125                try {
    126                        // 実際の処理を行います。(GET/PUT/DEL)
    127                        if( "GET".equals( command ) ) {
    128                                actionGET( localFile, rmtFile );
    129                        }
    130                        else if( "PUT".equals( command ) ) {
    131                                actionPUT( localFile, rmtFile );
    132                        }
    133                        else if( "DEL".equals( command ) ) {
    134                                actionDEL( rmtFile );
    135                        }
    136                        else if( "GETDIR".equals( command ) ) {
    137                                actionGETdir( localFile, rmtFile );
    138                        }
    139                        else if( "PUTDIR".equals( command ) ) {
    140                                actionPUTdir( localFile, rmtFile );
    141                        }
    142                        else if( "DELDIR".equals( command ) ) {
    143                                actionDELdir( rmtFile );
    144                        }
    145                        else {
    146                                errAppend( "commandは、GET/PUT/DEL/GETDIR/PUTDIR/DELDIR から指定ください。" );
    147                                errAppend( "   command    = [" , command , "]" );
    148                                throw new RuntimeException(  getErrMsg() );
    149                        }
    150                }
    151                catch( Exception ex ) {
    152                        errAppend( "Server action Error." );
    153                        errAppend( "   command    = [" , command        , "]" );
    154                        errAppend( "   localFile  = [" , localFile      , "]" );
    155                        errAppend( "   remoteFile = [" , remoteFile , "]" );
    156                        errAppend( ex.getMessage() );
    157                        if( isDebug ) { ex.printStackTrace(); }
    158                        throw new RuntimeException( getErrMsg(),ex );
    159                }
    160        }
    161
    162        /**
    163         * サーバーとの接続をクローズします。
    164         *
    165         * ログインされている場合は、ログアウトも行います。
    166         * コネクトされている場合は、ディスコネクトします。
    167         */
    168        @Override
    169        public abstract void disconnect();
    170
    171        /**
    172         * command="GET" が指定されたときの処理を行います。
    173         *
    174         * 接続先のサーバー側のファイル名をローカルにダウンロードします。
    175         *
    176         * @param       localFile       ローカルのファイル名
    177         * @param       remoteFile      接続先のファイル名
    178         * @throws Exception 何らかのエラーが発生した場合。
    179         */
    180        protected abstract void actionGET( final String localFile, final String remoteFile ) throws Exception ;
    181
    182        /**
    183         * command="GETDIR" が指定されたときの処理を行います。
    184         *
    185         * 接続先のサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。
    186         *
    187         * @param       localDir        ローカルのディレクトリ名
    188         * @param       remoteDir       接続先のディレクトリ名
    189         * @throws Exception 何らかのエラーが発生した場合。
    190         */
    191        protected abstract void actionGETdir( final String localDir, final String remoteDir ) throws Exception ;
    192
    193        /**
    194         * command="PUT" が指定されたときの処理を行います。
    195         *
    196         * ローカルファイルを、接続先のサーバー側にアップロードします。
    197         *
    198         * @param       localFile       ローカルのファイル名
    199         * @param       remoteFile      接続先のファイル名
    200         * @throws Exception 何らかのエラーが発生した場合。
    201         */
    202        protected abstract void actionPUT( final String localFile, final String remoteFile ) throws Exception ;
    203
    204        /**
    205         * command="PUTDIR" が指定されたときの処理を行います。
    206         *
    207         * ローカルファイルのディレクトリ以下を、接続先のサーバー側のディレクトリに階層構造のままアップロードします。
    208         *
    209         * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
    210         *
    211         * @param       localDir        ローカルのディレクトリ名
    212         * @param       remoteDir       接続先のディレクトリ名
    213         * @throws Exception  何らかのエラーが発生した場合。
    214         */
    215        protected void actionPUTdir( final String localDir, final String remoteDir ) throws Exception {
    216                File[] lclFiles = new File( localDir ).listFiles();
    217
    218                // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
    219                if( lclFiles == null ) {
    220                        errAppend( "指定のディレクトリは、アクセスできません。" );
    221                        errAppend( "   localDir   = [" , localDir       , "]" );
    222                        throw new RuntimeException( getErrMsg() );
    223                }
    224
    225                for( int i=0; i<lclFiles.length; i++ ) {
    226                        String lcl = lclFiles[i].getName();
    227                        if( lclFiles[i].isDirectory() ) {
    228                                actionPUTdir( addFile( localDir,lcl ),addFile( remoteDir,lcl ) );
    229                        }
    230                        else {
    231                                actionPUT( addFile( localDir,lcl ),addFile( remoteDir,lcl ) );
    232                        }
    233                }
    234        }
    235
    236        /**
    237         * command="DEL" が指定されたときの処理を行います。
    238         *
    239         * 接続先のサーバー側のファイル名を削除します。
    240         *
    241         * @param       remoteFile      接続先のファイル名
    242         * @throws Exception 何らかのエラーが発生した場合。
    243         */
    244        protected abstract void actionDEL( final String remoteFile ) throws Exception ;
    245
    246        /**
    247         * command="DELDIR" が指定されたときの処理を行います。
    248         *
    249         * 接続先のサーバー側のディレクトリ名を削除します。
    250         *
    251         * @param       remoteDir       接続先のディレクトリ名
    252         * @throws Exception 何らかのエラーが発生した場合。
    253         */
    254        protected abstract void actionDELdir( final String remoteDir ) throws Exception ;
    255
    256        /**
    257         * ローカルファイルのディレクトリを作成します。
    258         *
    259         * 引数のファイル名は、ファイル名です。作成するディレクトリは、そのファイルオブジェクトの
    260         * getParentFile() で取得されるディレクトリまでを作成します。
    261         *
    262         * ※ ローカルファイルのディレクトリの作成に失敗した場合は、RuntimeException が throw されます。
    263         *
    264         * @param       localFile       ローカルのファイル名
    265         * @throws IOException File#getCanonicalFile() で発生する入出力エラー
    266         */
    267        protected void makeLocalDir( final String localFile ) throws IOException {
    268                File fileDir = new File( localFile ).getCanonicalFile().getParentFile();
    269                if( !fileDir.exists() ) {
    270                        if( ! fileDir.mkdirs() ) {
    271                                errAppend( "ローカルファイルのディレクトリの作成に失敗しました。" );
    272                                errAppend( "   localFile   = [" , localFile     , "]" );
    273                                throw new RuntimeException( getErrMsg() );
    274                        }
    275                }
    276        }
    277
    278        /**
    279         * ディレクトリとファイル名を合成します。
    280         *
    281         * 単純に、ディレクトリの最後と、ファイルの最初の、"/" をチェックし、
    282         * 存在すれば、そのまま、結合し、存在しなければ、"/" を追加します。
    283         * 両方に存在する場合は、片方をはずします。
    284         *
    285         * @param       dir     ディレクトリ名
    286         * @param       file    ファイル名
    287         *
    288         * @return      合成されたファイル名
    289         */
    290        protected String addFile( final String dir,final String file ) {
    291                final String filepath ;
    292
    293                char ch1 = dir.charAt( dir.length()-1 ) ;       // ディレクトリの最後の文字
    294                char ch2 = file.charAt( 0 ) ;                           // ファイルの最初の文字
    295
    296                if( ch1 == '/' || ch1 == '\\' ) {
    297                        if( ch2 == '/' || ch2 == '\\' ) {       // 両方とも存在する。
    298                                filepath = dir + file.substring(1);
    299                        }
    300                        else {
    301                                filepath = dir + file;
    302                        }
    303                }
    304                else {
    305                        if( ch2 == '/' || ch2 == '\\' ) {       // 片方のみ存在する。
    306                                filepath = dir + file;
    307                        }
    308                        else {
    309                                filepath = dir + "/" + file;
    310                        }
    311                }
    312
    313                return filepath ;
    314        }
    315
    316        /**
    317         * サーバーの、ホスト、ユーザー、パスワードを設定します。
    318         *
    319         * @param       host    サーバー
    320         * @param       user    ユーザー
    321         * @param       passwd  パスワード
    322         */
    323        @Override
    324        public void setHostUserPass( final String host , final String user , final String passwd ) {
    325                this.host = host;
    326                this.user = user;
    327                this.passwd = passwd;
    328        }
    329
    330        /**
    331         * サーバー名を取得します。
    332         *
    333         * @return      サーバー
    334         */
    335//      protected String getHost() { return host; }
    336
    337        /**
    338         * ユーザー名を取得します。
    339         *
    340         * @return      ユーザー
    341         */
    342//      protected String getUser() { return user; }
    343
    344        /**
    345         * パスワードを取得します。
    346         *
    347         * @return      パスワード
    348         */
    349//      protected String getPassword() { return passwd; }
    350
    351        /**
    352         * 接続に利用するポート番号を設定します。
    353         *
    354         * @param       port    接続に利用するポート番号
    355         */
    356        @Override
    357        public void setPort( final String port ) {
    358                if( port != null ) {
    359                        this.port = port ;
    360                }
    361        }
    362
    363        /**
    364         * ポートを取得します。
    365         * 設定されている生のport属性(nullもありうる)を返します。
    366         *
    367         * @return      ポート
    368         */
    369        protected String getPort() { return port; }
    370
    371        /**
    372         * ポートを取得します。
    373         * 設定されているport属性が、nullの場合は、defPortを返します。
    374         *
    375         * @param       defPort port が null の場合の初期値
    376         *
    377         * @return      ポート
    378         */
    379        protected int getPort( final int defPort) {
    380                return ( port == null ) ? defPort : Integer.parseInt( port );
    381        }
    382
    383        /**
    384         * それぞれの受け側ファイルにディレクトリを作成するかどうか(初期値:true:作成する)。
    385         *
    386         * -mkdirs=[true/false] は、受け側のファイル(GET時:LOCAL、PUT時:サーバー)に取り込むファイルのディレクトリが
    387         * 存在しない場合に、作成するかどうかを指定します(初期値:true)
    388         * 通常、サーバーに、フォルダ階層を作成してPUTする場合、動的にフォルダ階層を作成したいケースで便利です。
    389         * 逆に、フォルダは確定しており、指定フォルダ以外に PUT するのはバグっていると事が分かっている場合には
    390         * false に設定して、存在しないフォルダにPUT しようとすると、エラーになるようにします。
    391         *
    392         * @param       isMkdirs        受け側ファイルにディレクトリを作成するかどうか。true:作成する
    393         */
    394        @Override
    395        public void setMkdirs( final boolean isMkdirs ) {
    396                this.isMkdirs = isMkdirs ;
    397        }
    398
    399        /**
    400         * 受け側ファイルにディレクトリを作成するかどうかを取得します。
    401         *
    402         * @return      受け側ファイルにディレクトリを作成するかどうか。true:作成する
    403         */
    404//      protected boolean isMkdirs() { return isMkdirs; }
    405
    406        /**
    407         * タイムアウトを秒で指定します(初期値:600 [秒])。
    408         *
    409         * オリジナルの FTPClient#setDataTimeout( int ) は、ミリ秒でセット
    410         * しますが、ここのメソッドでは、秒でセットします。
    411         *
    412         * @param       timeout タイムアウト[秒]
    413         * @throws RuntimeException タイムアウトの指定が大きすぎた場合
    414         */
    415        @Override
    416        public void setTimeout( final int timeout ) {
    417                if( Integer.MAX_VALUE / 1000 < timeout ) {
    418                        errAppend( "タイムアウトの指定が大きすぎます。" );
    419                        errAppend( "   timeout   = [" , timeout , "]" );
    420                        throw new RuntimeException( getErrMsg() );
    421                }
    422
    423                this.timeout = timeout ;
    424        }
    425
    426        /**
    427         *指定されたタイムアウトを秒で取得します(初期値:600 [秒])。
    428         *
    429         * @return      タイムアウト[秒]
    430         */
    431//      protected int getTimeout() { return timeout; }
    432
    433        /**
    434         * 実行状況の表示可否 を設定します(初期値:false:表示しない)。
    435         *
    436         * @param       isDisplay       実行状況の表示可否
    437         */
    438        @Override
    439        public void setDisplay( final boolean isDisplay ) {
    440                this.isDisplay = isDisplay ;
    441        }
    442
    443        /**
    444         * 実行状況の表示可否を取得します。
    445         *
    446         * @return      実行状況の表示可否
    447         */
    448//      protected boolean isDisplay() { return isDisplay; }
    449
    450        /**
    451         * デバッグ情報の表示可否 を設定します(初期値:false:表示しない)。
    452         *
    453         * @param       isDebug デバッグ情報の表示可否
    454         */
    455        @Override
    456        public void setDebug( final boolean isDebug ) {
    457                this.isDebug = isDebug ;
    458        }
    459
    460        /**
    461         * デバッグ情報の表示可否 を取得します。
    462         *
    463         * @return      デバッグ情報の表示可否
    464         */
    465//      protected boolean isDebug() { return isDebug; }
    466
    467        /**
    468         * 処理中に発生したエラーメッセージをセットします。
    469         *
    470         * @param       msg  メッセージ化したいオブジェクト
    471         */
    472        protected void errAppend( final Object msg ) {
    473                errMsg.append( String.valueOf(msg) ).append( CR );
    474        }
    475
    476        /**
    477         * 処理中に発生したエラーメッセージをセットします。
    478         *
    479         * @param       msgs  Object...
    480         */
    481        protected void errAppend( final Object... msgs ) {
    482                for( int i=0; i<msgs.length; i++ ) {
    483                        errMsg.append( String.valueOf(msgs[i]) );
    484                }
    485
    486                errMsg.append( CR );
    487        }
    488
    489        /**
    490         * 処理中に発生したエラーメッセージを取り出します。
    491         *
    492         * @return      エラーメッセージ
    493         */
    494        @Override
    495        public String getErrMsg() {
    496                return errMsg.toString();
    497        }
    498}