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 }