【実装】javaのProcessクラス注意事項

# バージョン
java 8
# API
先ずJava API Docsから[`Process`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Process.html)クラスを一緒に見ましょう
> **クラスProcess**
java.lang.Object
　　java.lang.Process<br/>
> ProcessBuilder.start()メソッドやRuntime.execメソッドはネイティブのプロセスを作成し、Processのサブクラスのインスタンスを返しますが、これを使えば、そのプロセスを制御したり情報を取得したりできます。Processクラスは、プロセスからの入力、プロセスへの出力、プロセス完了の待機、プロセス終了状態の確認、およびプロセスの破棄(終了)を実行するための各メソッドを提供します。
> たとえば、ネイティブなウィンドウ処理プロセス、デーモン・プロセス、Microsoft Windows環境でのWin16/DOSプロセス、あるいはシェル・スクリプトといったプロセスです。

> デフォルトでは、作成されたサブプロセスは、自身の端末またはコンソールを持ちません。その標準入出力(つまり標準入力、標準出力、標準エラー)の処理はすべて親プロセスにリダイレクトされますが、それらの情報にアクセスするには、メソッドgetOutputStream()、getInputStream()、およびgetErrorStream()を使って取得されるストリームを使用します。親プロセスはこれらのストリームを使って、サブプロセスに入力を送ったり、サブプロセスからの出力を取得したりします。ネイティブなプラットフォームには標準入出力ストリームに使うバッファのサイズが限られるものもあるので、サブプロセスの入力ストリームの書き込みあるいはストリーム出力の読込みが失敗した場合、サブプロセスはブロックされるか、デッドロック状態になる可能性があります。

> 必要に応じて、ProcessBuilderクラスのメソッドを使ってサブプロセスの入出力をリダイレクトすることもできます。

> Processオブジェクトへの参照がなくなった場合でも、サブプロセスは終了されず、非同期的に実行を続けます。

> Processオブジェクトが表すプロセスの実行については、Processオブジェクトを所有するJavaプロセスと非同期でなかったり、並行でなかったりしてもかまいません。

# 注意事項
### デッドロック
> ネイティブなプラットフォームには標準入出力ストリームに使うバッファのサイズが限られるものもあるので、サブプロセスの入力ストリームの書き込みあるいはストリーム出力の読込みが失敗した場合、サブプロセスはブロックされるか、デッドロック状態になる可能性があります。

一番注意しないといけないのは、デッドロック状態になる可能性がありますね。

```bad.java
Process p = Runtime.getRuntime().exec("cmd /c dir");
// プロセスが終了するまで現在のスレッドを待機させます。
// ★★★標準出力ストリームに出力量が多い場合は、プロセスはブロックされる★★★
p.waitFor(); 
```

### ストリーム
> ProcessBuilder.start()メソッドやRuntime.execメソッドはネイティブのプロセスを作成し、Processのサブクラスのインスタンスを返しますが、これを使えば、そのプロセスを制御したり情報を取得したりできます。

プロセスを制御したり情報を取得したり方法は提供しています。
上記デッドロックを回避するため、標準出力ストリーム、標準エラーストリームをJVMから読み込ん
[`getInputStream`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Process.html#getInputStream--)と[`getErrorStream`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Process.html#getErrorStream--)メソッドを利用できます

```demo.java
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
	String line = null;
	while ((line = br.readLine()) != null) {
		// 出力しなくても構わない
	}
} catch (IOException e) {
	e.printStackTrace();
}
```

### プロセス終了
> Processオブジェクトへの参照がなくなった場合でも、サブプロセスは終了されず、非同期的に実行を続けます。

API Docs中の説明を考えると、Processオブジェクトを使う後で、javaのGCより、Processオブジェクトを解放されるはずですが、ただ違う、引き続き実行しています。それでdestroy()メソッドを呼出するほうがいいです。

```bad.java
Process p = Runtime.getRuntime().exec("cmd /c dir");
// p.destroy();　★★★終了しません★★★
```

# サンプルコード
### Processサンプル

```MainTest.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

//サンプル
public class MainTest {

	public static void main(String[] args) throws IOException, InterruptedException {
		Process p = Runtime.getRuntime().exec("cmd /c cd bin && java ProcessTest");

		// 出力ストリーム
		new StreamThread(p.getInputStream(), "OUTPUT").start();
		// エラーストリーム
		new StreamThread(p.getErrorStream(), "ERROR").start();

		// プロセス実行結果を取得
		int result = p.waitFor();
		p.destroy();

		// データを出力
		System.out.println("■実行結果コード：" + result);
	}
}

// ストリームスレッド
class StreamThread extends Thread {
	private InputStream in;
	private String type;

	public StreamThread(InputStream in, String type) {
		this.in = in;
		this.type = type;
	}

	@Override
	public void run() {
		try (BufferedReader br = new BufferedReader(new InputStreamReader(in, "MS932"))) {
			String line = null;
			while ((line = br.readLine()) != null) {
				// ログなど出力する
				System.out.println(type + ">" + line);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
```

### 呼び出す子プロセス（テスト用）
```ProcessTest.java
public class ProcessTest {

	public static void main(String[] args) {
		for (int i = 1; i <= 1000; i++) {
			// 標準出力
			System.out.println("これは標準出力：" + i + "番。");
			// 標準エラー
			System.err.println("これは標準エラー：" + i + "番。");
		}
	}
}
```