🦿
JavaのProcessBuilderで外部アプリ(e.g. Python)と「継続的」に連携する方法
JavaのProcessBuilder
を使用すると外部アプリを起動してその出力を受け取れます。更にそのアプリに入力してデータ処理結果を受け取る事も出来ます。
つまりPythonにしか存在しないライブラリをJavaから使用するといった使い方ができます。
しかし(英語含めて)サンプルコードがあまり無いようなので具体的なコードを紹介します。
ここではPythonとの連携を考えます(実際には他のものにも使えます)。
以下ではコードを単純化するため「文字列を2倍にするメソッド」を実装しています。
# エンコードを明示的にUTF-8にする必要がある
sys.stdout.reconfigure(encoding='utf-8')
sys.stdin.reconfigure(encoding='utf-8')
def python_func(s):
return s * 3
a = input()
print(python_func(a))
b = input()
print(python_func(b))
Java:
String PYTHON_FILE = "test.py";
//実際に使用する仮想環境のpythonファイル
String PYTHON_EVN = "C:\\SDK\\miniforge-pypy3\\envs\\stanza_interface\\python.exe";
ProcessBuilder pb = new ProcessBuilder(PYTHON_EVN, "-u", PYTHON_FILE);
try {
Process pythonProcess = pb.start();
try (var pythonReader = pythonProcess.inputReader()) {
try (var pythonWriter = pythonProcess.outputWriter()) {
//外部アプリに「データ」を入力
pythonWriter.write('d');
//「入力終了」を知らせるために「改行」という形で区切り文字を入れる
pythonWriter.newLine();
//flush()で入力を「反映」
pythonWriter.flush();
//外部アプリからの入力を受け取る
String output1 = pythonReader.readLine();
System.out.println(output1);//ddd
//上記と同様
pythonWriter.write('f');
pythonWriter.newLine();
pythonWriter.flush();
String output2 = pythonReader.readLine();
System.out.println(output2);//fff
}
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
なお、外部アプリからデータを受け取る場合、「相手のアプリが今後も出力を続けるつもりか」はJava側では判断できないので、例えば「空行を受け取ったらそこで終了」と事前に決めておいて外部アプリにその処理を加えると同時に、Java側で処理する必要があります。
while (true) {
line = pythonReader.readLine();
//空行(Pythonがprint()を実行)が出力されたら終了と判断してループを抜ける
if (StringUtils.isEmpty(line)) break;
System.out.println(line);
}
追記:
エラー出力がある場合
Thread thread = new Thread(() -> {
String err;
try (var errorReader = pythonProcess.errorReader()) {
while ((err = errorReader.readLine()) != null) {
System.out.println(err);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
thread.start();
Discussion