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 */ 016 package org.opengion.fukurou.util; 017 018 import java.io.BufferedReader; 019 import java.io.InputStream; 020 import java.io.InputStreamReader; 021 import java.io.File; 022 import java.io.IOException; 023 // import java.text.DateFormat; 024 // import java.text.SimpleDateFormat; 025 // import java.util.Date; 026 // import java.util.Locale; 027 028 /** 029 * Shell は、Runtime.exec の簡易的に実行するクラスです? 030 * ?な処??通常の Runtime.exec を使用する?がありますが?ほとんどの 031 * プロセス実行につ?は、このクラスで十?であると?て?す? 032 * 033 * こ?クラスでは、OS(特にWindows)でのバッチファイルの実行において? 034 * OS自動認識を行い、簡易的なコマンドをセ?する?で実行できるように 035 * して?す? 036 * 037 * @version 4.0 038 * @author Kazuhiko Hasegawa 039 * @since JDK5.0, 040 */ 041 public class Shell { 042 /** Shell オブジェクト?状態を表します?正常 {@value} */ 043 public static final int OK = 0; // 0:正常 044 /** Shell オブジェクト?状態を表します?実行中 {@value} */ 045 public static final int RUNNING = 1; // 1:実行中 046 /** Shell オブジェクト?状態を表します?取? {@value} */ 047 public static final int CANCEL = 9; // 9:取? 048 /** Shell オブジェクト?状態を表します?異常終?? {@value} */ 049 public static final int ERROR = -1; // -1:異常終?? 050 051 // private static final String CMD_95 = "C:\\windows\\command.com /c "; 052 private static final String CMD_NT = "C:\\WINNT\\system32\\cmd.exe /c "; 053 private static final String CMD_XP = "C:\\WINDOWS\\system32\\cmd.exe /c "; 054 private static final String OS_NAME = System.getProperty("os.name"); 055 private static final String CR = System.getProperty("line.separator"); 056 private String command = null; 057 private File workDir = null; 058 private String[] envp = null; 059 private boolean isWait = true; // プロセスの終??かど? (?ォル??) 060 private Process prcs = null; 061 private ProcessReader pr1 = null; 062 private ProcessReader pr2 = null; 063 private int rtnCode = ERROR; // 0:正常 1:実行中 9:取? -1:異常終?? 064 // private final DateFormat formatter = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss",Locale.JAPAN ); 065 066 // 3.6.1.0 (2005/01/05) タイ?ウト時間を設? 067 private long timeout = 0 ; // 初期値は、タイ?ウトな? 068 069 // 3.8.9.2 (2007/07/13) Windows Vista対? 070 // 5.6.7.1 (2013/07/09) NTでもunknown時?CMD_XPとする 071 private static final String CMD_COM ; 072 static { 073 if( (OS_NAME.indexOf( "NT" ) >= 0 || 074 OS_NAME.indexOf( "2000" ) >= 0) 075 && OS_NAME.indexOf( "unknown" ) < 0 ) { 076 CMD_COM = CMD_NT ; 077 } 078 // else if( OS_NAME.indexOf( "XP" ) >= 0 || 079 // OS_NAME.indexOf( "2003" ) >= 0 080 // OS_NAME.indexOf( "Vista" ) >= 0 ) { 081 // CMD_COM = CMD_XP ; 082 // } 083 else { 084 CMD_COM = CMD_XP ; 085 } 086 } 087 088 /** 089 * プロセスを実行する時に引き渡すコマン? 090 * 第?引数には、コマンドがBATかEXEかを?できます? 091 * true の場合??バ?コマンドとして処?れます? 092 * 093 * @og.rev 3.3.3.0 (2003/07/09) Windows XP 対? 094 * @og.rev 3.7.0.1 (2005/01/31) Windows 2003 対? Windows 95 除? 095 * @og.rev 3.8.9.2 (2007/07/13) Windows Vista 対? 096 * 097 * @param cmd コマン? 098 * @param batch true:バッチファイル/false:EXEファイル 099 */ 100 public void setCommand( final String cmd,final boolean batch ) { 101 if( batch ) { 102 command = CMD_COM + cmd; 103 } 104 else { 105 command = cmd ; 106 } 107 } 108 109 /** 110 * プロセスを実行する時に引き渡すコマン? 111 * 112 * @param cmd EXEコマン? 113 */ 114 public void setCommand( final String cmd ) { 115 setCommand( cmd,false ); 116 } 117 118 /** 119 * プロセスの実行???終??かど? 120 * 121 * @param flag true:?(?ォル?/ false:?な? 122 */ 123 public void setWait( final boolean flag ) { 124 isWait = flag; 125 } 126 127 /** 128 * プロセスの実行???タイ?ウトを設定します? 129 * ゼロ(0) の場合?、割り込みが?るまで?つづけます? 130 * 131 * @param tout タイ?ウト時?? ゼロは、無制? 132 * 133 */ 134 public void setTimeout( final int tout ) { 135 timeout = (long)tout * 1000; 136 } 137 138 /** 139 * 作業?レクトリを指定します? 140 * 141 * シェルを実行する?作業?レクトリを指定します? 142 * ?しな??合?、このJava仮想マシンの作業?レクトリで実行されます? 143 * 144 * @param dir 作業?レクトリ 145 */ 146 public void setWorkDir( final File dir ) { 147 workDir = dir; 148 } 149 150 /** 151 * 環?数設定?配??します? 152 * 153 * 環?数を?name=value と?形式で、文字?配?で?します? 154 * null の場合?、現在のプロセスの環?定を継承します? 155 * 156 * @param env ??の配?。?列????、name=value と?形式で環?数設定を保持する? 157 */ 158 public void setEnvP( final String[] env ) { 159 if( env != null && env.length > 0 ) { 160 int size = env.length; 161 envp = new String[size]; 162 System.arraycopy( env,0,envp,0,size ); 163 } 164 else { 165 envp = null; 166 } 167 } 168 169 /** 170 * プロセスの実行?? 171 * 172 * @return サブ?ロセスの終?ードを返します?0 は正常終?示? 173 */ 174 public int exec() { 175 Runtime rt = Runtime.getRuntime(); 176 Thread wait = null; 177 try { 178 prcs = rt.exec( command,envp,workDir ); // 3.3.3.0 (2003/07/09) 179 pr1 = new ProcessReader( prcs.getInputStream() ); 180 pr1.start(); 181 pr2 = new ProcessReader( prcs.getErrorStream() ); 182 pr2.start(); 183 184 if( isWait ) { 185 // 3.6.1.0 (2005/01/05) 186 wait = new WaitJoin( timeout,prcs ); 187 wait.start(); 188 rtnCode = prcs.waitFor(); 189 if( rtnCode > OK ) { rtnCode = -rtnCode; } 190 } 191 else { 192 rtnCode = RUNNING; // プロセスの終??な??で?:処? を返します? 193 } 194 } 195 catch(IOException ex) { 196 String errMsg = "入出力エラーが発生しました?; 197 LogWriter.log( errMsg ); 198 LogWriter.log( ex ); 199 } 200 catch(InterruptedException ex) { 201 String errMsg = "現在のスレ?が?中にほか?スレ?によって強制終?れました?; 202 LogWriter.log( errMsg ); 203 LogWriter.log( ex ); 204 } 205 finally { 206 if( wait != null ) { wait.interrupt(); } 207 } 208 209 return rtnCode; 210 } 211 212 /** 213 * プロセスの実行時の標準?力を取得します? 214 * 215 * @return 実行時の標準?力文字? 216 */ 217 public String getStdoutData() { 218 final String rtn ; 219 if( pr1 == null ) { 220 rtn = "\n.......... Process is not Running. ...."; 221 } 222 else if( pr1.isEnd() ) { 223 rtn = pr1.getString(); 224 } 225 else { 226 rtn = pr1.getString() + "\n......... stdout Process is under execution. ..."; 227 } 228 return rtn ; 229 } 230 231 /** 232 * プロセスの実行時のエラー出力を取得します? 233 * 234 * @return 実行時の標準?力文字? 235 */ 236 public String getStderrData() { 237 final String rtn ; 238 if( pr2 == null ) { 239 rtn = "\n.......... Process is not Running. ...."; 240 } 241 else if( pr2.isEnd() ) { 242 rtn = pr2.getString(); 243 } 244 else { 245 rtn = pr2.getString() + "\n......... stderr Process is under execution. ..."; 246 } 247 return rtn ; 248 } 249 250 /** 251 * プロセスが実際に実行するコマンドを取得します? 252 * バッチコマンドかど?で、実行されるコマンドが異なります?で? 253 * ここで取得して確認することができます? 254 * 主に??用途です? 255 * 256 * @return 実行時の標準?力文字? 257 */ 258 public String getCommand() { 259 return command; 260 } 261 262 /** 263 * サブ?ロセスを終?ます? 264 * こ? Process オブジェクトが表すサブ?ロセスは強制終?れます? 265 * 266 */ 267 public void destroy() { 268 if( prcs != null ) { prcs.destroy() ; } 269 rtnCode = CANCEL; 270 } 271 272 /** 273 * プロセスが終?て?かど?[true/false]を確認します? 274 * こ? Process オブジェクトが表すサブ?ロセスは強制終?れます? 275 * 276 * @return プロセスが終?て?かど?[true/false] 277 */ 278 public boolean isEnd() { 279 boolean flag = true; 280 if( rtnCode == RUNNING ) { 281 flag = pr1.isEnd() && pr2.isEnd() ; 282 if( flag ) { rtnCode = OK; } 283 } 284 return flag ; 285 } 286 287 /** 288 * サブ?ロセスの終?ードを返します? 289 * 290 * @return こ? Process オブジェクトが表すサブ?ロセスの終?ード?0 は正常終?示? 291 * @throws IllegalThreadStateException こ? Process オブジェクトが表すサブ?ロセスがま??て???? 292 */ 293 public int exitValue() { 294 if( rtnCode == RUNNING && isEnd() ) { 295 rtnCode = prcs.exitValue(); 296 if( rtnCode > OK ) { rtnCode = -rtnCode ; } 297 } 298 return rtnCode; 299 } 300 301 /** 302 * こ? Shell のインフォメーション(??)を?力します? 303 * コマンド?開始時刻、終?刻、状?実行中、終?などの??を? 304 * 出力します? 305 * 306 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します? 307 * 308 * @return インフォメーション(??) 309 */ 310 @Override 311 public String toString() { 312 boolean isEnd = isEnd() ; 313 // String st = formatter.format( new Date( pr1.getStartTime() ) ) ; 314 // String ed = ( isEnd ) ? formatter.format( new Date( pr1.getEndTime() ) ) : "----/--/-- --:--:--" ; 315 String st = HybsDateUtil.getDate( pr1.getStartTime() , "yyyy/MM/dd HH:mm:ss" ) ; 316 String ed = ( isEnd ) ? HybsDateUtil.getDate( pr1.getEndTime() , "yyyy/MM/dd HH:mm:ss" ) : "----/--/-- --:--:--" ; 317 318 StringBuilder buf = new StringBuilder(); 319 buf.append( "command = [" ).append( getCommand() ).append( "]\n" ); 320 buf.append( " isEnd = [" ).append( isEnd ).append( "]\n" ); 321 buf.append( " rtnCode = [" ).append( exitValue() ).append( "]\n" ); 322 buf.append( " startTime = [" ).append( st ).append( "]\n" ); 323 buf.append( " endTime = [" ).append( ed ).append( "]\n" ); 324 325 return buf.toString(); 326 } 327 328 /** 329 * stdout と stderr の取得をスレ?化する為のインナ?クラスです? 330 * これ自身が?Thread の サブクラスになって?す? 331 * 332 * @version 4.0 333 * @author Kazuhiko Hasegawa 334 * @since JDK5.0, 335 */ 336 static class ProcessReader extends Thread { 337 private final BufferedReader in ; 338 private final StringBuilder inStream = new StringBuilder(); 339 private boolean endFlag = false; 340 private long startTime = -1; 341 private long endTime = -1; 342 343 /** 344 * コンストラクター? 345 * 346 * ここで、スレ?化したい入力ストリー?引数に、オブジェクトを生?します? 347 * 348 * @param ins InputStream 入力ストリー? 349 * 350 */ 351 ProcessReader( InputStream ins ) { 352 // in = new BufferedReader( new InputStreamReader(ins) ); 353 in = new BufferedReader( new InputStreamReader(ins,StringUtil.DEFAULT_CHARSET) ); // 5.5.2.6 (2012/05/25) findbugs対? 354 setDaemon( true ); // 3.5.4.6 (2004/01/30) 355 } 356 357 /** 358 * Thread が実行された場合に呼び出される?run メソ?です? 359 * 360 * Thread のサブクラスは、このメソ?をオーバ?ライドしなければなりません? 361 * 362 */ 363 public void run() { 364 startTime = System.currentTimeMillis() ; 365 String outline; 366 try { 367 while ((outline = in.readLine()) != null) { 368 inStream.append( outline ); 369 inStream.append( CR ); 370 } 371 } 372 catch(IOException ex) { 373 String errMsg = "入出力エラーが発生しました?; 374 LogWriter.log( errMsg ); 375 LogWriter.log( ex ); 376 } 377 finally { 378 Closer.ioClose( in ); 379 } 380 endTime = System.currentTimeMillis() ; 381 endFlag = true; 382 } 383 384 /** 385 * 現在書き込みが行われて?ストリー???にして返します? 386 * 387 * @return ストリー???? 388 * 389 */ 390 public String getString() { 391 return inStream.toString(); 392 } 393 394 /** 395 * ストリー?ら?読取が終?て?か確認します? 396 * 397 * @return 読取終?true) / 読み取り中(false) 398 * 399 */ 400 public boolean isEnd() { 401 return endFlag; 402 } 403 404 /** 405 * ストリー????開始時刻を返します? 406 * 開始して??態??1 を返します? 407 * 408 * @return 開始時刻 409 * 410 */ 411 public long getStartTime() { 412 return startTime; 413 } 414 415 /** 416 * ストリー????終?刻を返します? 417 * 終?て??態??1 を返します? 418 * 419 * @return 終?刻 420 * 421 */ 422 public long getEndTime() { 423 return endTime; 424 } 425 } 426 427 /** 428 * スレ?のウェイト??ラス 429 * ??タイ?ウト時間が来ると、設定されたプロセスを?強制終?destroy)します? 430 * ??プロセス側は、??終?た?合?、このThreadに、割り込み(interrupt) 431 * をかけて、この処?のも?を終?せてください? 432 * 433 * @version 4.0 434 * @author Kazuhiko Hasegawa 435 * @since JDK5.0, 436 */ 437 static class WaitJoin extends Thread { 438 private static final long MAX_WAIT = 3600 * 1000 ; // ?時間に設? 439 440 private final long wait ; 441 private final Process prcs; 442 443 /** 444 * コンストラクター 445 * 446 * @param wait long ウェイトする時?ミリ? 447 * @param prcs Process 強制終?destroy) させる?ロセス 448 */ 449 WaitJoin( final long wait,Process prcs ) { 450 this.wait = ( wait > 0L ) ? wait : MAX_WAIT ; 451 this.prcs = prcs; 452 } 453 454 /** 455 * Thread の run() メソ? 456 * コンストラクタで??ミリ秒だけウェイトし、それが経過すると? 457 * ??プロセスを強制終?destroy)させます? 458 * 外部より割り込み(interrupt)があると、ウェイト状態から復帰します? 459 * 先に割り込みが?って?場合?、wait せずに抜けます? 460 * 461 * @og.rev 5.4.2.2 (2011/12/14) Threadでwaitをかける場合?synchronized しな?エラーにな?対? 462 */ 463 public void run() { 464 try { 465 long startTime = System.currentTimeMillis() ; 466 boolean waitFlag = true; 467 synchronized( this ) { 468 while( ! isInterrupted() && waitFlag ) { 469 wait( wait ); 470 waitFlag = ( startTime + wait ) > System.currentTimeMillis() ; 471 } 472 } 473 prcs.destroy() ; 474 System.out.println( "タイ?ウトにより強制終?ました? ); 475 } 476 catch( InterruptedException ex ) { 477 LogWriter.log( "終?ました? ); 478 } 479 } 480 } 481 }