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 if( isWait ) { 184 // 3.6.1.0 (2005/01/05) 185 wait = new WaitJoin( timeout,prcs ); 186 wait.start(); 187 rtnCode = prcs.waitFor(); 188 if( rtnCode > OK ) { rtnCode = -rtnCode; } 189 } 190 else { 191 rtnCode = RUNNING; // プロセスの終??な??で?:処? を返します? 192 } 193 } 194 catch(IOException ex) { 195 String errMsg = "入出力エラーが発生しました?; 196 LogWriter.log( errMsg ); 197 LogWriter.log( ex ); 198 } 199 catch(InterruptedException ex) { 200 String errMsg = "現在のスレ?が?中にほか?スレ?によって強制終?れました?; 201 LogWriter.log( errMsg ); 202 LogWriter.log( ex ); 203 } 204 finally { 205 if( wait != null ) { wait.interrupt(); } 206 } 207 208 return rtnCode; 209 } 210 211 /** 212 * プロセスの実行時の標準?力を取得します? 213 * 214 * @return 実行時の標準?力文字? 215 */ 216 public String getStdoutData() { 217 final String rtn ; 218 if( pr1 == null ) { 219 rtn = "\n.......... Process is not Running. ...."; 220 } 221 else if( pr1.isEnd() ) { 222 rtn = pr1.getString(); 223 } 224 else { 225 rtn = pr1.getString() + "\n......... stdout Process is under execution. ..."; 226 } 227 return rtn ; 228 } 229 230 /** 231 * プロセスの実行時のエラー出力を取得します? 232 * 233 * @return 実行時の標準?力文字? 234 */ 235 public String getStderrData() { 236 final String rtn ; 237 if( pr2 == null ) { 238 rtn = "\n.......... Process is not Running. ...."; 239 } 240 else if( pr2.isEnd() ) { 241 rtn = pr2.getString(); 242 } 243 else { 244 rtn = pr2.getString() + "\n......... stderr Process is under execution. ..."; 245 } 246 return rtn ; 247 } 248 249 /** 250 * プロセスが実際に実行するコマンドを取得します? 251 * バッチコマンドかど?で、実行されるコマンドが異なります?で? 252 * ここで取得して確認することができます? 253 * 主に??用途です? 254 * 255 * @return 実行時の標準?力文字? 256 */ 257 public String getCommand() { 258 return command; 259 } 260 261 /** 262 * サブ?ロセスを終?ます? 263 * こ? Process オブジェクトが表すサブ?ロセスは強制終?れます? 264 * 265 */ 266 public void destroy() { 267 if( prcs != null ) { prcs.destroy() ; } 268 rtnCode = CANCEL; 269 } 270 271 /** 272 * プロセスが終?て?かど?[true/false]を確認します? 273 * こ? Process オブジェクトが表すサブ?ロセスは強制終?れます? 274 * 275 * @return プロセスが終?て?かど?[true/false] 276 */ 277 public boolean isEnd() { 278 boolean flag = true; 279 if( rtnCode == RUNNING ) { 280 flag = pr1.isEnd() && pr2.isEnd() ; 281 if( flag ) { rtnCode = OK; } 282 } 283 return flag ; 284 } 285 286 /** 287 * サブ?ロセスの終?ードを返します? 288 * 289 * @return こ? Process オブジェクトが表すサブ?ロセスの終?ード?0 は正常終?示? 290 * @throws IllegalThreadStateException こ? Process オブジェクトが表すサブ?ロセスがま??て???? 291 */ 292 public int exitValue() { 293 if( rtnCode == RUNNING && isEnd() ) { 294 rtnCode = prcs.exitValue(); 295 if( rtnCode > OK ) { rtnCode = -rtnCode ; } 296 } 297 return rtnCode; 298 } 299 300 /** 301 * こ? Shell のインフォメーション(??)を?力します? 302 * コマンド?開始時刻、終?刻、状?実行中、終?などの??を? 303 * 出力します? 304 * 305 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します? 306 * 307 * @return インフォメーション(??) 308 */ 309 @Override 310 public String toString() { 311 boolean isEnd = isEnd() ; 312 // String st = formatter.format( new Date( pr1.getStartTime() ) ) ; 313 // String ed = ( isEnd ) ? formatter.format( new Date( pr1.getEndTime() ) ) : "----/--/-- --:--:--" ; 314 String st = HybsDateUtil.getDate( pr1.getStartTime() , "yyyy/MM/dd HH:mm:ss" ) ; 315 String ed = ( isEnd ) ? HybsDateUtil.getDate( pr1.getEndTime() , "yyyy/MM/dd HH:mm:ss" ) : "----/--/-- --:--:--" ; 316 317 StringBuilder buf = new StringBuilder(); 318 buf.append( "command = [" ).append( getCommand() ).append( "]\n" ); 319 buf.append( " isEnd = [" ).append( isEnd ).append( "]\n" ); 320 buf.append( " rtnCode = [" ).append( exitValue() ).append( "]\n" ); 321 buf.append( " startTime = [" ).append( st ).append( "]\n" ); 322 buf.append( " endTime = [" ).append( ed ).append( "]\n" ); 323 324 return buf.toString(); 325 } 326 327 /** 328 * stdout と stderr の取得をスレ?化する為のインナ?クラスです? 329 * これ自身が?Thread の サブクラスになって?す? 330 * 331 * @version 4.0 332 * @author Kazuhiko Hasegawa 333 * @since JDK5.0, 334 */ 335 static class ProcessReader extends Thread { 336 private final BufferedReader in ; 337 private final StringBuilder inStream = new StringBuilder(); 338 private boolean endFlag = false; 339 private long startTime = -1; 340 private long endTime = -1; 341 342 /** 343 * コンストラクター? 344 * 345 * ここで、スレ?化したい入力ストリー?引数に、オブジェクトを生?します? 346 * 347 * @param ins InputStream 入力ストリー? 348 * 349 */ 350 ProcessReader( InputStream ins ) { 351 // in = new BufferedReader( new InputStreamReader(ins) ); 352 in = new BufferedReader( new InputStreamReader(ins,StringUtil.DEFAULT_CHARSET) ); // 5.5.2.6 (2012/05/25) findbugs対? 353 setDaemon( true ); // 3.5.4.6 (2004/01/30) 354 } 355 356 /** 357 * Thread が実行された場合に呼び出される?run メソ?です? 358 * 359 * Thread のサブクラスは、このメソ?をオーバ?ライドしなければなりません? 360 * 361 */ 362 public void run() { 363 startTime = System.currentTimeMillis() ; 364 String outline; 365 try { 366 while ((outline = in.readLine()) != null) { 367 inStream.append( outline ); 368 inStream.append( CR ); 369 } 370 } 371 catch(IOException ex) { 372 String errMsg = "入出力エラーが発生しました?; 373 LogWriter.log( errMsg ); 374 LogWriter.log( ex ); 375 } 376 finally { 377 Closer.ioClose( in ); 378 } 379 endTime = System.currentTimeMillis() ; 380 endFlag = true; 381 } 382 383 /** 384 * 現在書き込みが行われて?ストリー???にして返します? 385 * 386 * @return ストリー???? 387 * 388 */ 389 public String getString() { 390 return inStream.toString(); 391 } 392 393 /** 394 * ストリー?ら?読取が終?て?か確認します? 395 * 396 * @return 読取終?true) / 読み取り中(false) 397 * 398 */ 399 public boolean isEnd() { 400 return endFlag; 401 } 402 403 /** 404 * ストリー????開始時刻を返します? 405 * 開始して??態??1 を返します? 406 * 407 * @return 開始時刻 408 * 409 */ 410 public long getStartTime() { 411 return startTime; 412 } 413 414 /** 415 * ストリー????終?刻を返します? 416 * 終?て??態??1 を返します? 417 * 418 * @return 終?刻 419 * 420 */ 421 public long getEndTime() { 422 return endTime; 423 } 424 } 425 426 /** 427 * スレ?のウェイト??ラス 428 * ??タイ?ウト時間が来ると、設定されたプロセスを?強制終?destroy)します? 429 * ??プロセス側は、??終?た?合?、このThreadに、割り込み(interrupt) 430 * をかけて、この処?のも?を終?せてください? 431 * 432 * @version 4.0 433 * @author Kazuhiko Hasegawa 434 * @since JDK5.0, 435 */ 436 static class WaitJoin extends Thread { 437 private static final long MAX_WAIT = 3600 * 1000 ; // ?時間に設? 438 439 private final long wait ; 440 private final Process prcs; 441 442 /** 443 * コンストラクター 444 * 445 * @param wait long ウェイトする時?ミリ? 446 * @param prcs Process 強制終?destroy) させる?ロセス 447 */ 448 WaitJoin( final long wait,Process prcs ) { 449 this.wait = ( wait > 0L ) ? wait : MAX_WAIT ; 450 this.prcs = prcs; 451 } 452 453 /** 454 * Thread の run() メソ? 455 * コンストラクタで??ミリ秒だけウェイトし、それが経過すると? 456 * ??プロセスを強制終?destroy)させます? 457 * 外部より割り込み(interrupt)があると、ウェイト状態から復帰します? 458 * 先に割り込みが?って?場合?、wait せずに抜けます? 459 * 460 * @og.rev 5.4.2.2 (2011/12/14) Threadでwaitをかける場合?synchronized しな?エラーにな?対? 461 */ 462 public void run() { 463 try { 464 long startTime = System.currentTimeMillis() ; 465 boolean waitFlag = true; 466 synchronized( this ) { 467 while( ! isInterrupted() && waitFlag ) { 468 wait( wait ); 469 waitFlag = ( startTime + wait ) > System.currentTimeMillis() ; 470 } 471 } 472 prcs.destroy() ; 473 System.out.println( "タイ?ウトにより強制終?ました? ); 474 } 475 catch( InterruptedException ex ) { 476 LogWriter.log( "終?ました? ); 477 } 478 } 479 } 480 }