昔、java.lang.Runtime から Runtime.getRuntime().exec で Process を作ってスクリプトを実行するラッパーを書いていた。
Process p = Runtime.getRuntime().exec(this.arrange()); _ProcessStreamReader p_stderr = new _ProcessStreamReader(p.getErrorStream()); _ProcessStreamReader p_stdout = new _ProcessStreamReader(p.getInputStream()); /*プロセス起動後、プロセスが入力を求めるなら、PrintWriter pw = new PrintWriter(p.getOutputStream(); * を用意して、次に PrintWriter で入力内容を print する */ p_stderr.start(); p_stdout.start(); p_stderr.join(); p_stdout.join(); p.waitFor(); int sts = p.exitValue();
このようにするのだが、IOException を InterruptedException 捕捉を書いてどうするかを呼出し側に委ねなくてはならない。
上記をラップしたメソッドの呼び出しも使いづらい。
だったらきちんと例外を捕捉してそれを標準エラー出力結果に出すようにすればいいだけなのですが、
どうせなら、
・実行スクリプトを Supplier<String> で渡す
・標準出力とエラー出力結果を BiConsumer<String, String> = <stdout, stderr>
あるいは、BiConsumer<InputStream, InputStream> = <stdout の InputStream, stderr InputStream>
で処理する
というのを用意します。
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.stream.Collectors; /** * ラムダ・スクリプト実行. */ public final class ScriptExecutor{ private ScriptExecutor(){} /** * ラムダ・スクリプト実行(String → BiConsumer) * @param scriptSupplier 実行するスクリプト * @param consumer BiConsumer<String, String> = <stdout, stderr> * @return java.lang.Process の exitValue() 結果、例外発生時は 1 を返す。 */ public static int run(Supplier<String> scriptSupplier, BiConsumer<String, String> consumer){ int rtn = 0; String stdout; String stderr; try{ Process p = Runtime.getRuntime().exec(scriptSupplier.get()); _processStreamReader p_stderr = new _processStreamReader(p.getErrorStream()); _processStreamReader p_stdout = new _processStreamReader(p.getInputStream()); p_stderr.start(); p_stdout.start(); p_stderr.join(); p_stdout.join(); p.waitFor(); rtn = p.exitValue(); stdout = p_stdout.getString(); stderr = p_stderr.getString(); }catch(Exception ex){ rtn = 1; stdout = ""; StringBuilder sb = new StringBuilder(); sb.append(ex.getMessage()); sb.append("\n"); sb.append(Arrays.stream(ex.getStackTrace()).map(t->t.toString()).collect(Collectors.joining("\n\t"))); Optional.ofNullable(ex.getCause()).ifPresent(x->{ sb.append("\n"); sb.append("Caused by: "); sb.append(x.getMessage()); sb.append("\n"); sb.append(Arrays.stream(x.getStackTrace()).map(t->t.toString()).collect(Collectors.joining("\n\t"))); }); stderr = sb.toString(); } consumer.accept(stdout, stderr); return rtn; } /** * ラムダ・スクリプト実行(InputStream → BiConsumer) * @param scriptSupplier 実行するスクリプト * @param consumer BiConsumer<InputStream, InputStream> = <stdout の InputStream, stderr InputStream> * @return java.lang.Process の exitValue() 結果、例外発生時は 1 を返す。 */ public static int runStream(Supplier<String> scriptSupplier, BiConsumer<InputStream, InputStream> consumer){ int rtn = 0; try{ Process p = Runtime.getRuntime().exec(scriptSupplier.get()); p.waitFor(); consumer.accept(p.getInputStream(), p.getErrorStream()); return p.exitValue(); }catch(Exception ex){ rtn = 1; StringBuilder sb = new StringBuilder(); sb.append(ex.getMessage()); sb.append("\n"); sb.append(Arrays.stream(ex.getStackTrace()).map(t->t.toString()).collect(Collectors.joining("\n\t"))); Optional.ofNullable(ex.getCause()).ifPresent(x->{ sb.append("\n"); sb.append("Caused by: "); sb.append(x.getMessage()); sb.append("\n"); sb.append(Arrays.stream(x.getStackTrace()).map(t->t.toString()).collect(Collectors.joining("\n\t"))); }); try{ consumer.accept(new ByteArrayInputStream("".getBytes()) , new ByteArrayInputStream(sb.toString().getBytes("UTF-8"))); }catch(UnsupportedEncodingException e){ e.printStackTrace(); } } return rtn; } static class _processStreamReader extends Thread{ StringBuffer sb; InputStreamReader inredaer; public _processStreamReader(InputStream in){ super(); this.inredaer = new InputStreamReader(in); this.sb = new StringBuffer(); } @Override public void run(){ try{ int i; int BUFFER_SIZE = 1024; char[] c = new char[BUFFER_SIZE]; while((i = this.inredaer.read(c,0,BUFFER_SIZE - 1)) > 0){ this.sb.append(c,0,i); if (i < BUFFER_SIZE - 1){ break; } } this.inredaer.close(); }catch(IOException e){} } public String getString(){ return this.sb.toString(); } } }
これで呼出しは、とても整理できてきます。
例えば テストとして、Python スクリプト foo.py というのを用意して、
Windows 上で実行なら、”cmd.exe /C ”を付けて実行します
int sts = ScriptExecutor.run(()->"cmd.exe /C python c:/work/hello.py", (t, e)->{ // t = 標準出力 // e = 標準エラー出力 }); // sts = プロセス実行終了コード、Process の exitValue() 結果