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