RunProcess.java

package jasper.component.vm;

import jasper.errors.ScriptException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

public class RunProcess {
	private static final Logger logger = LoggerFactory.getLogger(RunProcess.class);

	public static String runProcess(Process process, int timeoutMs) throws ScriptException {
		final var output = new StringBuilder();
		final var errors = new StringBuilder();
		var outputThread = Thread.ofVirtual().start(() -> {
			try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
				String line;
				while ((line = reader.readLine()) != null) {
					output.append(line).append("\n");
				}
			} catch (IOException e) {
				logger.error("Error reading output stream: {}", e.getMessage());
			}
		});
		var errorThread = Thread.ofVirtual().start(() -> {
			try (var reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
				String line;
				while ((line = reader.readLine()) != null) {
					errors.append(line).append("\n");
				}
			} catch (IOException e) {
				logger.error("Error reading error stream: {}", e.getMessage());
			}
		});

		boolean finished;
		try {
			finished = process.waitFor(timeoutMs, TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
			process.destroy();
			Thread.currentThread().interrupt();
			throw new ScriptException("Script execution interrupted", ""+errors + output);
		}
		process.destroy();
		try {
			outputThread.join(100);
			errorThread.join(100);
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}
		if (!finished) {
			throw new ScriptException("Script execution timed out", ""+errors + output);
		}
		var exitCode = process.exitValue();
		if (exitCode != 0) {
			throw new ScriptException("Script execution failed with exit code: " + exitCode, ""+errors + output);
		}
		return output.toString();
	}
}