    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.process;
    017
    018import org.opengion.fukurou.util.Argument;
    019import org.opengion.fukurou.util.FTPConnect;
    020import org.opengion.fukurou.system.LogWriter;
    021
    022import java.util.Map ;
    023import java.util.LinkedHashMap ;
    024
    025import java.io.File;
    026
    027/**
    028 * Process_FileFtp は、上流から受け取った FileLineModel を処理する、
    029 * ChainProcess インターフェースの実装クラスです。
    030 *
    031 * 上流から受け取った FileLineModel の ファイルから、localPath のローカル共通パスを
    032 * remotePath のFTP共通パスに、PFT伝送します。(-command=PUT 処理のみ)
    033 * ファイルそのものの階層構造は、維持されるため、ローカルからFTPサーバー
    034 * へのフォルダコピーに近いイメージになります。
    035 *
    036 * Process_FileCopy との違いは、ファイルのエンコード変換は行いません。ただし、
    037 * FTP伝送での改行コードの変換は、-mode=ASCII で指定できます。
    038 * もうひとつ、Process_FileCopy では、inPath と outPath でのCOPY処理でしたが、
    039 * このクラスでは、localPath と、remotePath でそれぞれの共通パスを指定します。
    040 *
    041 * 上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト
    042 * である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを
    043 * 使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し
    044 * できれば、使用可能です。
    045 *
    046 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。
    047 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に
    048 * 繋げてください。
    049 *
    050 * @og.formSample
    051 *  Process_FileFtp -host=FTPサーバー -user=ユーザー -pass=パスワード -localPath=ローカル共通パス -remotePath=FTP共通パス
    052 *                   [-mode=[ASCII/BINARY]  ] [-passive=[true/false] ]
    053 *
    054 *    -host=FTPサーバー             ：FTPサーバー
    055 *    -user=ユーザー                ：ユーザー
    056 *    -pass=パスワード              ：パスワード
    057 *    -localPath=ローカル共通パス   ：上流で検索されたファイルパスのローカル側共通部分
    058 *    -remotePath=FTP共通パス       ：上流で検索されたファイルパスのFTP側共通部分
    059 *   [-mode=[ASCII/BINARY]  ]       ：扱うファイルの種類を指定します(初期値:ASCII)
    060 *   [-passive=[true/false] ]       ：パッシブモード(ローカルからサーバーへ接続を張る)を利用するかどうか(初期値:true)
    061 *                                      (false:アクティブモード(通常のFTPの初期値)で通信します。)
    062 *   [-mkdirs=[true/false]  ]       ：受け側ファイル(GET時:LOCAL、PUT時:FTPサーバー)にディレクトリを作成するかどうか(初期値:true)
    063 *                                      (false:ディレクトリが無ければ、エラーにします。)
    064 *   [-encode=エンコード名 ]        ：日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)
    065 *   [-timeout=タイムアウト[秒] ]   ：Dataタイムアウト(初期値:600 [秒])
    066 *   [-display=[false/true] ]       ：trueは、検索状況を表示します(初期値:false)
    067 *   [-debug=[false/true]   ]       ：デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    068 *
    069 * @og.rev 5.1.5.0 (2010/04/01) 新規追加
    070 *
    071 * ※ 注意
    072 *    Windwosにおいて、大量ファイルのFTP伝送を行う場合は、注意が必要です。
    073 *    Windowsにおけるソケットの最大値は、5000がデフォルト値です。
    074 *    また、TIME_WAITのデフォルト値は、4分(=240秒)です。
    075 *    FTPの様にデータ伝送時に毎回、ソケットを作成すると、ポートが枯渇します。
    076 *
    077 *    この値を変更するには、レジストリに以下のキーを設定する必要があります。
    078 *
    079 *    ■ソケットの最大数(5,000～65,534の間で設定)：
    080 *    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort (DWORD)
    081 *
    082 *    ■TIME_WAITの時間(30～300秒の間で設定)：
    083 *    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay (DWORD)
    084 *
    085 *    ※ 設定後は再起動しないと設定が反映されません。
    086 *
    087 * @version  4.0
    088 * @author   Kazuhiko Hasegawa
    089 * @since    JDK5.0,
    090 */
    091public class Process_FileFtp extends AbstractProcess implements ChainProcess {
    092        private static final int     TIMEOUT = 600 ;    // Dataタイムアウト(初期値:600 [秒])
    093
    094        private FTPConnect      ftp             ;
    095
    096        private String  host            ;                       // FTPサーバー
    097        private String  user            ;                       // ユーザー
    098        private static final String     command         = "PUT";        // FTPサーバー側での処理の方法(GET/PUT/DEL)を指定します。(PUTのみ固定)
    099        private String  localPath       ;                       // 上流で検索されたファイルパスのローカル側共通部分
    100        private String  remotePath      ;                       // 上流で検索されたファイルパスのFTP側共通部分
    101
    102        private boolean display         ;                       // trueは、検索状況を表示します(初期値:false)
    103        private boolean debug           ;                       // デバッグ情報を標準出力に表示する(true)かしない(false)か
    104
    105        private int     localPathLen    ;
    106        private int     inCount                 ;
    107
    108        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    109        private static final Map<String,String> MUST_PROPARTY   ;               // ［プロパティ］必須チェック用 Map
    110        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    111        private static final Map<String,String> USABLE_PROPARTY ;               // ［プロパティ］整合性チェック Map
    112
    113        private static final String[] MODE_LST = { "ASCII","BINARY" };
    114
    115        static {
    116                MUST_PROPARTY = new LinkedHashMap<>();
    117                MUST_PROPARTY.put( "host",              "接続先のFTPサーバーのアドレスまたは、サーバー名(必須)" );
    118                MUST_PROPARTY.put( "user",              "接続するユーザー名(必須)" );
    119                MUST_PROPARTY.put( "pass",              "接続するユーザーのパスワード(必須)" );
    120                MUST_PROPARTY.put( "localPath", "上流で検索されたファイルパスのローカル側共通部分(必須)" );
    121                MUST_PROPARTY.put( "remotePath",        "上流で検索されたファイルパスのFTP側共通部分(必須)" );
    122
    123                USABLE_PROPARTY = new LinkedHashMap<>();
    124                USABLE_PROPARTY.put( "mode",                    "扱うファイルの種類(ASCII/BINARY)を指定します(初期値:ASCII)" );
    125        //      USABLE_PROPARTY.put( "command",         "FTPサーバー側での処理の方法(GET/PUT/DEL)を指定します(初期値:PUT)" );
    126                USABLE_PROPARTY.put( "passive",         "パッシブモード(ローカルからサーバーへ接続を張る)を利用するかどうか(初期値:true)" );
    127                USABLE_PROPARTY.put( "mkdirs",          "受け側ファイル(GET時:LOCAL、PUT時:FTPサーバー)にディレクトリを作成するかどうか(初期値:true)" );
    128                USABLE_PROPARTY.put( "encode",          "日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)" );
    129                USABLE_PROPARTY.put( "timeout",         "Dataタイムアウト(初期値:600 [秒])" );
    130                USABLE_PROPARTY.put( "display",         "[false/true]:trueは、検索状況を表示します(初期値:false)" );
    131                USABLE_PROPARTY.put( "debug",           "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
    132                                                                                        CR + "(初期値:false:表示しない)" );
    133        }
    134
    135        /**
    136         * デフォルトコンストラクター。
    137         * このクラスは、動的作成されます。デフォルトコンストラクターで、
    138         * super クラスに対して、必要な初期化を行っておきます。
    139         *
    140         */
    141        public Process_FileFtp() {
    142                super( "org.opengion.fukurou.process.Process_FileFtp",MUST_PROPARTY,USABLE_PROPARTY );
    143        }
    144
    145        /**
    146         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
    147         * 初期処理(ファイルオープン、ＤＢオープン等)に使用します。
    148         *
    149         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
    150         */
    151        public void init( final ParamProcess paramProcess ) {
    152                final Argument arg = getArgument();
    153
    154                final String pass = arg.getProparty( "pass" );                  // パスワード
    155                host = arg.getProparty( "host");                        // FTPサーバー
    156                user = arg.getProparty( "user" );                       // ユーザー
    157
    158                ftp = new FTPConnect();
    159
    160                ftp.setHostUserPass( host , user , pass );
    161
    162                // localPath と、remotePath をセットします。
    163                localPath       = arg.getProparty("localPath" );
    164                remotePath      = arg.getProparty("remotePath" );
    165
    166                localPathLen  = localPath.length();
    167
    168                // FTP処理に必要な各種パラメータをセットします。
    169                ftp.setMode(    arg.getProparty( "mode"         ,"ASCII",MODE_LST       ));             // 扱うファイルの種類を指定します。
    170                ftp.setPassive( arg.getProparty( "passive"      ,true                           ));             // パッシブモードを利用するかどうか
    171                ftp.setMkdirs(  arg.getProparty( "mkdirs"       ,true                           ));             // 受け側ファイルにディレクトリを作成するかどうか
    172                ftp.setEncode(  arg.getProparty( "encode"       ,"UTF-8"                        ));             // 日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)
    173                ftp.setTimeout( arg.getProparty( "timeout"      ,TIMEOUT                        ));             // Dataタイムアウト(初期値:600 [秒])
    174
    175                display = arg.getProparty("display",display);
    176                debug   = arg.getProparty("display",debug);
    177
    178                ftp.setDisplay( display );              // trueは、検索状況を表示します。(初期値:false)
    179                ftp.setDebug(   debug   );              // デバッグ情報を標準出力に表示する(true)かしない(false)か
    180
    181                // FTPConnect を初期化します。
    182                ftp.connect();
    183        }
    184
    185        /**
    186         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
    187         * 終了処理(ファイルクローズ、ＤＢクローズ等)に使用します。
    188         *
    189         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
    190         */
    191        public void end( final boolean isOK ) {
    192                if( ftp != null ) {
    193                        ftp.disconnect();
    194                }
    195        }
    196
    197        /**
    198         * 引数の LineModel を処理するメソッドです。
    199         * 変換処理後の LineModel を返します。
    200         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
    201         * null データを返します。つまり、null データは、後続処理を行わない
    202         * フラグの代わりにも使用しています。
    203         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
    204         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
    205         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
    206         * 各処理ごとに自分でコピー(クローン)して下さい。
    207         *
    208         * @param       data    オリジナルのLineModel
    209         *
    210         * @return      処理変換後のLineModel
    211         */
    212        @Override       // ChainProcess
    213        public LineModel action( final LineModel data ) {
    214                inCount++ ;
    215                final FileLineModel fileData ;
    216                if( data instanceof FileLineModel ) {
    217                        fileData = (FileLineModel)data ;
    218                }
    219                else {
    220                        // LineModel が FileLineModel でない場合、オブジェクトを作成します。
    221                        fileData = new FileLineModel( data );
    222                }
    223
    224                final File localFile = fileData.getFile() ;     // LineModel から取得したファイル。
    225                if( ! localFile.isFile() ) {
    226                        if( display ) { println( data.dataLine() ); }
    227                        return data;
    228                }
    229
    230                final String lclFileName = localFile.getAbsolutePath();
    231
    232                // ファイル名は、引数ファイル名 から、 localPathを引き、remotePath を加えます。
    233                final String rmtFileName = remotePath + lclFileName.substring( localPathLen );
    234
    235                ftp.action( command,lclFileName,rmtFileName );
    236
    237                if( display ) { println( data.dataLine() ); }
    238                return data ;
    239        }
    240
    241        /**
    242         * プロセスの処理結果のレポート表現を返します。
    243         * 処理プログラム名、入力件数、出力件数などの情報です。
    244         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
    245         * 形式で出してください。
    246         *
    247         * @return   処理結果のレポート
    248         */
    249        public String report() {
    250                // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX'
    251                return "[" + getClass().getName() + "]" + CR
    252//              final String report = "[" + getClass().getName() + "]" + CR
    253                                + TAB + "Copy Count : " + inCount   + CR
    254                                + TAB + "host       : " + host      + CR
    255                                + TAB + "user       : " + user      + CR
    256                                + TAB + "localPath  : " + localPath + CR
    257                                + TAB + "remotePath : " + remotePath ;
    258
    259//              return report ;
    260        }
    261
    262        /**
    263         * このクラスの使用方法を返します。
    264         *
    265         * @return      このクラスの使用方法
    266         * @og.rtnNotNull
    267         */
    268        public String usage() {
    269                final StringBuilder buf = new StringBuilder( BUFFER_LARGE )
    270                        .append( "Process_FileFtp は、上流から受け取った FileLineModel を処理する"                                      ).append( CR )
    271                        .append( "ChainProcess インターフェースの実装クラスです。"                                                                       ).append( CR )
    272                        .append( CR )
    273                        .append( "上流から受け取った FileLineModel の ファイルから、localPath のローカル共通パスを"        ).append( CR )
    274                        .append( "remotePath のFTP共通パスに、PFT伝送します。(-command=PUT 処理のみ) "                           ).append( CR )
    275                        .append( "ファイルそのものの階層構造は、維持されるため、ローカルからFTPサーバー"                 ).append( CR )
    276                        .append( "へのフォルダコピーに近いイメージになります。"                                                                               ).append( CR )
    277                        .append( CR )
    278                        .append( "Process_FileCopy との違いは、ファイルのエンコード変換は行いません。ただし、"               ).append( CR )
    279                        .append( "FTP伝送での改行コードの変換は、-mode=ASCII で指定できます。"                                                ).append( CR )
    280                        .append( "もうひとつ、Process_FileCopy では、inPath と outPath でのCOPY処理でしたが、"             ).append( CR )
    281                        .append( "このクラスでは、localPath と、remotePath でそれぞれの共通パスを指定します。"             ).append( CR )
    282                        .append( CR )
    283                        .append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト"          ).append( CR )
    284                        .append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを"                        ).append( CR )
    285                        .append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し"              ).append( CR )
    286                        .append( "できれば、使用可能です。"                                                                                                                 ).append( CR )
    287                        .append( CR ).append( CR )
    288                        .append( getArgument().usage() ).append( CR );
    289
    290                return buf.toString();
    291        }
    292
    293        /**
    294         * このクラスは、main メソッドから実行できません。
    295         *
    296         * @param       args    コマンド引数配列
    297         */
    298        public static void main( final String[] args ) {
    299                LogWriter.log( new Process_FileFtp().usage() );
    300        }
    301}