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.util.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                     = null;
095//      private File            tempFile        = null;
096
097        private String  host            = null;                 // FTPサーバー
098        private String  user            = null;                 // ユーザー
099//      private String  pass            = null;                 // パスワード
100        private String  command         = "PUT";                // FTPサーバー側での処理の方法(GET/PUT/DEL)を指定します。(PUTのみ固定)
101        private String  localPath       = null;                 // 上流で検索されたファイルパスのローカル側共通部分
102        private String  remotePath      = null;                 // 上流で検索されたファイルパスのFTP側共通部分
103
104        private boolean display         = false;                // trueは、検索状況を表示します(初期値:false)
105        private boolean debug           = false;                // デバッグ情報を標準出力に表示する(true)かしない(false)か
106
107        private int     localPathLen    = 0;
108        private int     inCount                 = 0;
109
110        private static final Map<String,String> mustProparty   ;          // [プロパティ]必須チェック用 Map
111        private static final Map<String,String> usableProparty ;          // [プロパティ]整合性チェック Map
112
113        private static final String[] MODE_LST = new String[] { "ASCII","BINARY" };
114//      private static final String[] CMD_LST  = new String[] { "GET","PUT","DEL" };
115
116        static {
117                mustProparty = new LinkedHashMap<String,String>();
118                mustProparty.put( "host",               "接続先のFTPサーバーのアドレスまたは、サーバー名(必須)" );
119                mustProparty.put( "user",               "接続するユーザー名(必須)" );
120                mustProparty.put( "pass",               "接続するユーザーのパスワード(必須)" );
121                mustProparty.put( "localPath",  "上流で検索されたファイルパスのローカル側共通部分(必須)" );
122                mustProparty.put( "remotePath", "上流で検索されたファイルパスのFTP側共通部分(必須)" );
123
124                usableProparty = new LinkedHashMap<String,String>();
125                usableProparty.put( "mode",                     "扱うファイルの種類(ASCII/BINARY)を指定します(初期値:ASCII)" );
126        //      usableProparty.put( "command",          "FTPサーバー側での処理の方法(GET/PUT/DEL)を指定します(初期値:PUT)" );
127                usableProparty.put( "passive",          "パッシブモード(ローカルからサーバーへ接続を張る)を利用するかどうか(初期値:true)" );
128                usableProparty.put( "mkdirs",           "受け側ファイル(GET時:LOCAL、PUT時:FTPサーバー)にディレクトリを作成するかどうか(初期値:true)" );
129                usableProparty.put( "encode",           "日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)" );
130                usableProparty.put( "timeout",          "Dataタイムアウト(初期値:600 [秒])" );
131                usableProparty.put( "display",          "[false/true]:trueは、検索状況を表示します(初期値:false)" );
132                usableProparty.put( "debug",            "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
133                                                                                        CR + "(初期値:false:表示しない)" );
134        }
135
136        /**
137         * デフォルトコンストラクター。
138         * このクラスは、動的作成されます。デフォルトコンストラクターで、
139         * super クラスに対して、必要な初期化を行っておきます。
140         *
141         */
142        public Process_FileFtp() {
143                super( "org.opengion.fukurou.process.Process_FileFtp",mustProparty,usableProparty );
144        }
145
146        /**
147         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
148         * 初期処理(ファイルオープン、DBオープン等)に使用します。
149         *
150         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
151         */
152        public void init( final ParamProcess paramProcess ) {
153                Argument arg = getArgument();
154
155//              String host = arg.getProparty( "host");                         // FTPサーバー
156//              String user = arg.getProparty( "user" );                        // ユーザー
157                String pass = arg.getProparty( "pass" );                        // パスワード
158                host = arg.getProparty( "host");                        // FTPサーバー
159                user = arg.getProparty( "user" );                       // ユーザー
160//              pass = arg.getProparty( "pass" );                       // パスワード
161
162                ftp = new FTPConnect();
163
164                ftp.setHostUserPass( host , user , pass );
165
166                // localPath と、remotePath をセットします。
167                localPath       = arg.getProparty("localPath" );
168                remotePath      = arg.getProparty("remotePath" );
169
170                localPathLen  = localPath.length();
171
172                // FTP処理に必要な各種パラメータをセットします。
173                ftp.setMode(    arg.getProparty( "mode"         ,"ASCII",MODE_LST       ));             // 扱うファイルの種類を指定します。
174//              ftp.setCommand( "PUT"                                                                                    );             // FTP処理の方法を指定します。
175                ftp.setPassive( arg.getProparty( "passive"      ,true                           ));             // パッシブモードを利用するかどうか
176                ftp.setMkdirs(  arg.getProparty( "mkdirs"       ,true                           ));             // 受け側ファイルにディレクトリを作成するかどうか
177                ftp.setEncode(  arg.getProparty( "encode"       ,"UTF-8"                        ));             // 日本語ファイル名などのエンコード名を指定します(初期値:UTF-8)
178                ftp.setTimeout( arg.getProparty( "timeout"      ,TIMEOUT                        ));             // Dataタイムアウト(初期値:600 [秒])
179
180                display = arg.getProparty("display",display);
181                debug   = arg.getProparty("display",debug);
182//              if( debug ) { println( arg.toString() ); }                      // 5.7.3.0 (2014/02/07) デバッグ情報
183
184                ftp.setDisplay( display );              // trueは、検索状況を表示します。(初期値:false)
185                ftp.setDebug(   debug   );              // デバッグ情報を標準出力に表示する(true)かしない(false)か
186
187                // FTPConnect を初期化します。
188                ftp.connect();
189        }
190
191        /**
192         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
193         * 終了処理(ファイルクローズ、DBクローズ等)に使用します。
194         *
195         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
196         */
197        public void end( final boolean isOK ) {
198                if( ftp != null ) {
199                        ftp.disconnect();
200                }
201        }
202
203        /**
204         * 引数の LineModel を処理するメソッドです。
205         * 変換処理後の LineModel を返します。
206         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
207         * null データを返します。つまり、null データは、後続処理を行わない
208         * フラグの代わりにも使用しています。
209         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
210         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
211         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
212         * 各処理ごとに自分でコピー(クローン)して下さい。
213         *
214         * @param       data    オリジナルのLineModel
215         *
216         * @return      処理変換後のLineModel
217         */
218        public LineModel action( final LineModel data ) {
219                inCount++ ;
220                final FileLineModel fileData ;
221                if( data instanceof FileLineModel ) {
222                        fileData = (FileLineModel)data ;
223                }
224                else {
225                        // LineModel が FileLineModel でない場合、オブジェクトを作成します。
226                        fileData = new FileLineModel( data );
227                }
228
229                File localFile = fileData.getFile() ;   // LineModel から取得したファイル。
230                if( ! localFile.isFile() ) {
231                        if( display ) { println( data.dataLine() ); }
232                        return data;
233                }
234
235                String lclFileName = localFile.getAbsolutePath();
236
237                // ファイル名は、引数ファイル名 から、 localPathを引き、remotePath を加えます。
238                String rmtFileName = remotePath + lclFileName.substring( localPathLen );
239
240//              ftp.setLocalFile(       lclFileName );                                  // ローカルのファイル名
241//              ftp.setRemoteFile(      rmtFileName );                                  // FTP先のファイル名
242
243                ftp.action( command,lclFileName,rmtFileName );
244
245                if( display ) { println( data.dataLine() ); }
246                return data ;
247        }
248
249        /**
250         * プロセスの処理結果のレポート表現を返します。
251         * 処理プログラム名、入力件数、出力件数などの情報です。
252         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
253         * 形式で出してください。
254         *
255         * @return   処理結果のレポート
256         */
257        public String report() {
258                String report = "[" + getClass().getName() + "]" + CR
259                                + TAB + "Copy Count : " + inCount   + CR
260                                + TAB + "host       : " + host      + CR
261                                + TAB + "user       : " + user      + CR
262                                + TAB + "localPath  : " + localPath + CR
263                                + TAB + "remotePath : " + remotePath ;
264
265                return report ;
266        }
267
268        /**
269         * このクラスの使用方法を返します。
270         *
271         * @return      このクラスの使用方法
272         */
273        public String usage() {
274                StringBuilder buf = new StringBuilder();
275
276                buf.append( "Process_FileFtp は、上流から受け取った FileLineModel を処理する"                                   ).append( CR );
277                buf.append( "ChainProcess インターフェースの実装クラスです。"                                                                    ).append( CR );
278                buf.append( CR );
279                buf.append( "上流から受け取った FileLineModel の ファイルから、localPath のローカル共通パスを"     ).append( CR );
280                buf.append( "remotePath のFTP共通パスに、PFT伝送します。(-command=PUT 処理のみ) "                                ).append( CR );
281                buf.append( "ファイルそのものの階層構造は、維持されるため、ローカルからFTPサーバー"                              ).append( CR );
282                buf.append( "へのフォルダコピーに近いイメージになります。"                                                                            ).append( CR );
283                buf.append( CR );
284                buf.append( "Process_FileCopy との違いは、ファイルのエンコード変換は行いません。ただし、"            ).append( CR );
285                buf.append( "FTP伝送での改行コードの変換は、-mode=ASCII で指定できます。"                                             ).append( CR );
286                buf.append( "もうひとつ、Process_FileCopy では、inPath と outPath でのCOPY処理でしたが、"          ).append( CR );
287                buf.append( "このクラスでは、localPath と、remotePath でそれぞれの共通パスを指定します。"          ).append( CR );
288                buf.append( CR );
289                buf.append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト"                       ).append( CR );
290                buf.append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを"                     ).append( CR );
291                buf.append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し"           ).append( CR );
292                buf.append( "できれば、使用可能です。"                                                                                                                      ).append( CR );
293                buf.append( CR ).append( CR );
294
295                buf.append( getArgument().usage() ).append( CR );
296
297                return buf.toString();
298        }
299
300        /**
301         * このクラスは、main メソッドから実行できません。
302         *
303         * @param       args    コマンド引数配列
304         */
305        public static void main( final String[] args ) {
306                LogWriter.log( new Process_FileFtp().usage() );
307        }
308}