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 }