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 * 初期処理(ファイルオープン、DBオープン等)に使用します。 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 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 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}