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.FileInputStream; 019import java.io.FileOutputStream; 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.io.BufferedInputStream; 023import java.io.BufferedOutputStream; 024import java.io.IOException; 025import java.util.Map; 026import java.util.LinkedHashMap ; 027import java.net.MalformedURLException ; 028 029import jcifs.smb.SmbFile; 030 031/** 032 * SMBConnect.java は、共通的に使用される Smb関連の基本機能を実装した、クラスです。 033 * 034 * これは、jcifs.smb パッケージをベースに開発されています。 035 * このクラスの実行には、jcifs-1.3.14.jar が必要です。 036 * 037 * 接続先のURL schemeは、以下の形式をしています。 038 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]] 039 * このURLは、内部的に自動作成します。/[[share/[dir/]file]] 部分が、remoteFile として指定する部分になります。 * 040 * 041 * -host=Smbサーバー -user=ユーザー -passwd=パスワード -remoteFile=Smb先のファイル名 を必須設定します。 042 * -localFile=ローカルのファイル名は、必須ではありませんが、-command=DEL の場合にのみ不要であり、 043 * それ以外の command の場合は、必要です。 044 * 045 * -command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] は、SFTPサーバーに対しての処理の方法を指定します。 046 * GET:Smbサーバーからローカルにファイル転送します(初期値) 047 * PUT:ローカルファイルをSmbサーバーに PUT(STORE、SAVE、UPLOAD、などと同意語)します。 048 * DEL:Smbサーバーの指定のファイルを削除します。この場合のみ、-localFile 属性の指定は不要です。 049 * GETDIR,PUTDIR,DELDIR:指定のフォルダ以下のファイルを処理します。 050 * 051 * -mkdirs=[true/false] は、受け側のファイル(GET時:LOCAL、PUT時:Smbサーバー)に取り込むファイルのディレクトリが 052 * 存在しない場合に、作成するかどうかを指定します(初期値:true)。 053 * 通常、Smbサーバーに、フォルダ階層を作成してPUTする場合、動的にフォルダ階層を作成したいケースで便利です。 054 * 逆に、フォルダは確定しており、指定フォルダ以外に PUT するのはバグっていると事が分かっている場合には 055 * false に設定して、存在しないフォルダにPUT しようとすると、エラーになるようにします。 056 * 057 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。 058 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に 059 * 繋げてください。 060 * 061 * @og.formSample 062 * SMBConnect -host=Smbサーバー -user=ユーザー -passwd=パスワード -remoteFile=Smb先のファイル名 [-localFile=ローカルのファイル名] 063 * [-command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] ] ] 064 * 065 * -host=Smbサーバー :接続先のSmbサーバーのアドレスまたは、サーバー名 066 * -user=ユーザー :接続するユーザー名 067 * -passwd=パスワード :接続するユーザーのパスワード 068 * -remoteFile=Smb先のファイル名 :接続先のSmbサーバー側のファイル名。PUT,GET 関係なくSmb側として指定します。 069 * [-localFile=ローカルのファイル名] :ローカルのファイル名。PUT,GET 関係なくローカルファイルを指定します。 070 * [-domain=ドメイン ] :接続するサーバーのドメインを指定します。 071 * [-port=ポート ] :接続するサーバーのポートを指定します。 072 * [-command=[GET/PUT/DEL] ] :Smbサーバー側での処理の方法を指定します。 073 * [GETDIR/PUTDIR/DELDIR]] GET:Smb⇒LOCAL、PUT:LOCAL⇒Smb への転送です(初期値:GET) 074 * DEL:Smbファイルを削除します。 075 * GETDIR,PUTDIR,DELDIR 指定のフォルダ以下のファイルを処理します。 076 * [-mkdirs=[true/false] ] :受け側ファイル(GET時:LOCAL、PUT時:Smbサーバー)にディレクトリを作成するかどうか(初期値:true) 077 * (false:ディレクトリが無ければ、エラーにします。) 078 * [-display=[false/true] ] :trueは、検索状況を表示します(初期値:false) 079 * [-debug=[false|true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 080 * 081 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 082 * 083 * @version 5.0 084 * @author Kazuhiko Hasegawa 085 * @since JDK5.0, 086 */ 087public final class SMBConnect extends AbstractConnect { 088 private String domain = null; // ドメイン 089 private String connURI = null; // Smb接続する場合のURI文字列(の先頭) 090 091 /** 092 * Smbサーバーへの接続、ログインを行います。 093 * 094 * このメソッドは、初期化メソッドです。 095 * Smbサーバーへの接続、ログインを行いますので、複数ファイルを転送する 096 * ケースでは、最初に1度だけ呼び出すだけです。 097 * 接続先を変更する場合は、もう一度このメソッドをコールする必要があります。 098 * (そのような場合は、通常、オブジェクトを構築しなおす方がよいと思います。) 099 * 接続時のアドレスは、下記の形式になりますが、ファイル名の箇所は、action 時に指定するため、 100 * ここでは、先頭部分を先に用意しておきます。 101 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]] 102 * 103 */ 104 @Override 105 public void connect() { 106 if( isDisplay ) { System.out.println( "CONNECT: HOST=" + host + ",USER=" + user + ",PORT=" + port ); } 107 108 if( host == null ) { 109 errAppend( "host は、必須です。" ); 110 throw new RuntimeException( getErrMsg() ); 111 } 112 113 // smb://[[[domain;]username[:password]@]server[:port] ここまで作成 114 connURI = "smb://" 115 + ((domain == null) ? "" : domain + ";" ) 116 + ((user == null) ? "" : user ) 117 + ((passwd == null) ? "" : ":" + passwd ) 118 + ((user == null) ? "" : "@" ) 119 + host 120 + ((port == null) ? "" : ":" + port ) ; 121 122 if( isDebug ) { System.out.println( "connURI=" + connURI ); } 123 } 124 125 /** 126 * Smbサーバーとの接続をクローズします。 127 * 128 * ここでは、何も処理を行いません。 129 * 130 */ 131 @Override 132 public void disconnect() { 133 if( isDisplay ) { System.out.println( "DISCONNECT:" ); } 134 } 135 136 /** 137 * command="GET" が指定されたときの処理を行います。 138 * 139 * 接続先のSmbサーバー側のファイル名をローカルにダウンロードします。 140 * 141 * @param localFile ローカルのファイル名 142 * @param remoteFile Smb先のファイル名 143 * @throws IOException 入出力エラーが発生したとき 144 */ 145 @Override 146 protected void actionGET( final String localFile, final String remoteFile ) throws IOException { 147 if( isDebug ) { System.out.println( "GET: " + remoteFile + " => " + localFile ); } 148 149 SmbFile rmtFile = makeSmbURI( remoteFile ); 150 151 // GET(DOWNLOAD)取得時は、ローカルファイルのディレクトリを作成する必要がある。 152 if( isMkdirs ) { 153 makeLocalDir( localFile ); 154 } 155 156 InputStream input = null; 157 OutputStream output = null; 158 try { 159 input = new BufferedInputStream( rmtFile.getInputStream() ); 160 output = new FileOutputStream( localFile ); 161 FileUtil.copy( input,output ); 162 } 163 finally { 164 Closer.ioClose( input ); 165 Closer.ioClose( output ); 166 } 167 } 168 169 /** 170 * command="GETDIR" が指定されたときの処理を行います。 171 * 172 * 接続先のSmbサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。 173 * 174 * @param localDir ローカルのディレクトリ名 175 * @param remoteDir Smb先のディレクトリ名 176 * @throws IOException 入出力エラーが発生したとき 177 */ 178 @Override 179 protected void actionGETdir( final String localDir, final String remoteDir ) throws IOException { 180 SmbFile rmtFile = makeSmbURI( remoteDir ); 181 182 SmbFile[] rmtFiles = rmtFile.listFiles(); 183 for( int i=0; i<rmtFiles.length; i++ ) { 184 String rmt = rmtFiles[i].getName(); 185 if( rmtFiles[i].isDirectory() ) { 186 actionGETdir( addFile( localDir,rmt ),addFile( remoteDir,rmt ) ); 187 } 188 else { 189 actionGET( addFile( localDir,rmt ),addFile( remoteDir,rmt ) ); 190 } 191 } 192 } 193 194 /** 195 * command="PUT" が指定されたときの処理を行います。 196 * 197 * ローカルファイルを、接続先のSmbサーバー側にアップロードします。 198 * 199 * @param localFile ローカルのファイル名 200 * @param remoteFile Smb先のファイル名 201 * @throws IOException 入出力エラーが発生したとき 202 */ 203 @Override 204 protected void actionPUT( final String localFile, final String remoteFile ) throws IOException { 205 if( isDebug ) { System.out.println( "PUT: " + localFile + " => " + remoteFile ); } 206 207 SmbFile rmtFile = makeSmbURI( remoteFile ); 208 209 // 存在チェック:すでに存在している場合は、先に削除します。 210 if( rmtFile.exists() ) { rmtFile.delete() ; } 211 else { 212 // PUT(UPLOAD)登録時は、リモートファイルのディレクトリを作成する必要がある。 213 if( isMkdirs ) { 214 String tmp = rmtFile.getParent(); 215 SmbFile dir = new SmbFile( tmp ); 216 if( !dir.exists() ) { dir.mkdirs() ; } 217 } 218 } 219 220 InputStream input = null; 221 OutputStream output = null; 222 try { 223 input = new FileInputStream( localFile ); 224 output = new BufferedOutputStream( rmtFile.getOutputStream() ); 225 226 FileUtil.copy( input,output ); 227 } 228 finally { 229 Closer.ioClose( input ); 230 Closer.ioClose( output ); 231 } 232 } 233 234 /** 235 * command="DEL" が指定されたときの処理を行います。 236 * 237 * 接続先のSmbサーバー側のファイル名を削除します。 238 * 239 * @param remoteFile Smb先のファイル名 240 * @throws IOException 入出力エラーが発生したとき 241 */ 242 @Override 243 protected void actionDEL( final String remoteFile ) throws IOException { 244 if( isDebug ) { System.out.println( "DEL: " + remoteFile ); } 245 246 SmbFile rmtFile = makeSmbURI( remoteFile ); 247 rmtFile.delete() ; 248 } 249 250 /** 251 * command="DELDIR" が指定されたときの処理を行います。 252 * 253 * 接続先のSmbサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。 254 * 255 * @param remoteDir Smb先のディレクトリ名 256 * @throws IOException 入出力エラーが発生したとき 257 */ 258 @Override 259 protected void actionDELdir( final String remoteDir ) throws IOException { 260 SmbFile rmtFile = makeSmbURI( remoteDir ); 261 SmbFile[] rmtFiles = rmtFile.listFiles(); 262 for( int i=0; i<rmtFiles.length; i++ ) { 263 String rmt = addFile( remoteDir,rmtFiles[i].getName() ); 264 if( rmtFiles[i].isDirectory() ) { 265 actionDELdir( rmt ); 266 } 267 else { 268 actionDEL( rmt ); 269 } 270 } 271 rmtFile.delete(); 272 } 273 274 /** 275 * SMB形式のURLを作成します。 276 * 277 * 処理的には、内部で先に作成済みの connURI と、引数のファイルパスを文字列連結します。 278 * connURI の最後には、"/" を付加していませんので、引数のファイルパスに、 279 * "/" を含まない場合は、"/" を付加します。 280 * 281 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]] 282 * 283 * @param remoteFile Smb先のファイル名 284 * 285 * @return SMB形式のURL 286 */ 287 private SmbFile makeSmbURI( final String remoteFile ) throws MalformedURLException { 288 final String smbFile ; 289 290 if( remoteFile.startsWith( "smb://" ) ) { 291 smbFile = remoteFile; 292 } 293 else if( remoteFile.startsWith( "/" ) ) { 294 smbFile = connURI + remoteFile ; 295 } 296 else { 297 smbFile = connURI + "/" + remoteFile ; 298 } 299 300 return new SmbFile( smbFile ); 301 } 302 303 /** 304 * 接続先にログインするドメインを設定します。 305 * 306 * @param domain 接続先にログインするドメイン 307 */ 308 public void setDomain( final String domain ) { 309 this.domain = domain ; 310 } 311 312 // ******************************************************************************************************* // 313 // 以下、単独で使用する場合の main処理 314 // ******************************************************************************************************* // 315 316 private static final Map<String,String> mustProparty ; // [プロパティ]必須チェック用 Map 317 private static final Map<String,String> usableProparty ; // [プロパティ]整合性チェック Map 318 319 static { 320 mustProparty = new LinkedHashMap<String,String>(); 321 mustProparty.put( "host", "接続先のSmbサーバーのアドレスまたは、サーバー名(必須)" ); 322 mustProparty.put( "user", "接続するユーザー名(必須)" ); 323 mustProparty.put( "passwd", "接続するユーザーのパスワード(必須)" ); 324 mustProparty.put( "command", "Smbサーバー側での処理の方法(GET/PUT/DEL/GETDIR/PUTDIR/DELDIR)を指定します。(必須)" ); 325 mustProparty.put( "remoteFile", "接続先のSmbサーバー側のファイル名(必須)" ); 326 327 usableProparty = new LinkedHashMap<String,String>(); 328 usableProparty.put( "localFile", "ローカルのファイル名" ); 329 usableProparty.put( "domain", "接続先にログインするドメインを指定します。" ); 330 usableProparty.put( "port", "接続に利用するポート番号を設定します。" ); 331 usableProparty.put( "mkdirs", "受け側ファイル(GET時:LOCAL、PUT時:Smbサーバー)にディレクトリを作成するかどうか(初期値:true)" ); 332 usableProparty.put( "display", "[false/true]:trueは、検索状況を表示します(初期値:false)" ); 333 usableProparty.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 334 CR + "(初期値:false:表示しない)" ); 335 } 336 337 private static final String[] CMD_LST = new String[] { "GET","PUT","DEL","GETDIR","PUTDIR","DELDIR" }; 338 339 /** 340 * このクラスの動作確認用の、main メソッドです。 341 * 342 * @param args コマンド引数配列 343 */ 344 public static void main( final String[] args ) { 345 Argument arg = new Argument( "org.opengion.fukurou.util.SMBConnect" ); 346 arg.setMustProparty( mustProparty ); 347 arg.setUsableProparty( usableProparty ); 348 arg.setArgument( args ); 349 350 SMBConnect smb = new SMBConnect(); 351 352 String host = arg.getProparty( "host"); // Smbサーバー 353 String user = arg.getProparty( "user" ); // ユーザー 354 String passwd = arg.getProparty( "passwd" ); // パスワード 355 356 smb.setHostUserPass( host,user,passwd ); 357 358 smb.setDomain( arg.getProparty( "domain" ) ); // 接続先にログインするドメインを指定します。 359 smb.setPort( arg.getProparty( "port" ) ); // 接続に利用するポート番号を設定します。 360 smb.setMkdirs( arg.getProparty( "mkdirs" ,true ) ); // 受け側ファイルにディレクトリを作成するかどうか 361 smb.setDisplay( arg.getProparty( "display" ,false ) ); // trueは、検索状況を表示します(初期値:false) 362 smb.setDebug( arg.getProparty( "debug" ,false ) ); // デバッグ情報を標準出力に表示する(true)かしない(false)か 363 364 try { 365 // コネクトします。 366 smb.connect(); 367 368 String command = arg.getProparty( "command" ,"GET" ,CMD_LST ); // Smb処理の方法を指定します。 369 String localFile = arg.getProparty( "localFile" ); // ローカルのファイル名 370 String remoteFile = arg.getProparty( "remoteFile" ); // Smb先のファイル名 371 372 // command , localFile , remoteFile を元に、SFTP処理を行います。 373 smb.action( command,localFile,remoteFile ); 374 } 375 catch( RuntimeException ex ) { 376 System.err.println( smb.getErrMsg() ); 377 } 378 finally { 379 // ホストとの接続を終了します。 380 smb.disconnect(); 381 } 382 } 383}