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 }