Library

Java ProcessBuilder와 Process API

Andrew's Akashic Records 2024. 4. 26. 10:02
728x90

 

ProcessBuilderProcess 클래스는 자바에서 외부 프로세스를 실행하고 관리하기 위해 사용됩니다. 이 클래스들을 사용하면 자바 코드 내에서 다른 애플리케이션을 실행하고, 그 입력과 출력을 관리할 수 있습니다. 아래에서는 ProcessBuilderProcess 클래스의 주요 기능과 메소드들을 상세히 설명하겠습니다.

 

ProcessBuilder 클래스

ProcessBuilder 클래스는 프로세스의 실행 환경을 설정하는 데 사용됩니다. 이 클래스의 인스턴스는 실행할 프로그램과 그 인자들을 설정하고, 프로세스의 환경 변수와 작업 디렉토리를 구성할 수 있습니다.

 

주요 생성자와 메소드

  • 생성자
    • ProcessBuilder(String... command): 실행할 명령과 그 인자들을 받습니다.
    • ProcessBuilder(List<String> command): 실행할 명령과 그 인자들을 리스트 형태로 받습니다.
  • 메소드
    • start(): 설정된 명령어로 새 프로세스를 시작합니다.
    • command(String... command): 실행할 명령을 설정합니다.
    • command(List<String> command): 실행할 명령을 리스트로 설정합니다.
    • environment(): 프로세스의 환경 변수를 반환합니다. 이를 수정하면 프로세스의 환경 변수를 변경할 수 있습니다.
    • directory(File dir): 프로세스가 실행될 디렉토리를 설정합니다.
    • redirectInput(ProcessBuilder.Redirect source): 프로세스의 표준 입력을 리다이렉션합니다.
    • redirectOutput(ProcessBuilder.Redirect destination): 프로세스의 표준 출력을 리다이렉션합니다.
    • redirectError(ProcessBuilder.Redirect destination): 프로세스의 표준 에러를 리다이렉션합니다.
    • redirectErrorStream(boolean redirectErrorStream): 표준 에러 출력을 표준 출력과 병합할지 여부를 설정합니다.

Process 클래스

Process 클래스는 실행 중인 프로세스를 대표하며, 프로세스의 입력, 출력 및 종료를 관리합니다.

 

주요 메소드

  • getOutputStream(): 프로세스의 표준 입력 스트림에 데이터를 쓰기 위한 OutputStream을 반환합니다.
  • getInputStream(): 프로세스의 표준 출력 스트림에서 데이터를 읽기 위한 InputStream을 반환합니다.
  • getErrorStream(): 프로세스의 표준 에러 스트림에서 데이터를 읽기 위한 InputStream을 반환합니다.
  • waitFor(): 프로세스가 종료될 때까지 현재 스레드를 대기시킵니다. 종료 시 프로세스의 종료 코드를 반환합니다.
  • exitValue(): 프로세스의 종료 코드를 반환합니다. 프로세스가 아직 종료되지 않았을 경우 예외를 발생시킵니다.
  • destroy(): 프로세스를 강제로 종료합니다.

사용 예제

아래는 ProcessBuilder를 사용하여 외부 프로그램을 실행하고 그 결과를 출력하는 간단한 예제입니다.

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ProcessExample {
    public static void main(String[] args) {
        ProcessBuilder builder = new ProcessBuilder("yourCommand", "arg1", "arg2");
        builder.redirectErrorStream(true); // 에러 출력을 표준 출력에 병합
        try {
            Process process = builder.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            int exitCode = process.waitFor();
            System.out.println("Process exited with code: " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

이 예제는 외부 명령을 실행하고, 그 출력을 읽으며, 프로세스가 종료될 때까지 기다립니다. 표준 에러 스트림을 표준 출력에 병합하여 모든 출력을 한 곳에서 처리할 수 있게 합니다. 이러한 기능을 활용하여 다양한 외부 프로세스를 효과적으로 관리할 수 있습니다.

ProcessBuilder를 사용하여 프로세스의 표준 출력(getInputStream())과 표준 에러 스트림(getErrorStream())을 동시에 읽으려면 몇 가지 접근 방법이 있습니다. 이러한 방법 중 하나는 프로세스를 시작하기 전에 표준 에러 스트림을 표준 출력 스트림으로 리다이렉션하는 것입니다. 이를 통해 두 스트림을 하나로 통합하여 한 번에 읽을 수 있습니다.

 

표준 에러를 표준 출력으로 리다이렉션하기

ProcessBuilder.redirectErrorStream(true) 메소드를 사용하면 표준 에러 스트림을 표준 출력 스트림으로 리다이렉션할 수 있습니다. 이 설정을 활성화하면, Process.getInputStream()을 통해 두 스트림의 데이터를 모두 읽을 수 있습니다.

 

예제 코드

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class CombinedStreamExample {
    public static void main(String[] args) {
        ProcessBuilder builder = new ProcessBuilder("yourCommand", "arg1", "arg2");
        builder.redirectErrorStream(true);  // 에러 스트림을 출력 스트림으로 리다이렉션

        try {
            Process process = builder.start();

            // 통합된 출력 및 에러 스트림 읽기
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 프로세스 종료 대기
            int exitCode = process.waitFor();
            System.out.println("Exited with code " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

별도의 스레드에서 스트림 읽기

만약 표준 출력과 표준 에러를 분리해서 로깅하거나 다르게 처리해야 하는 경우, 각 스트림을 별도의 스레드에서 읽는 방법이 있습니다. 이 방식은 스트림을 분리하여 동시에 읽을 수 있게 하며, 한 스트림의 버퍼가 가득 차서 프로세스가 정지되는 것을 방지합니다.

 

예제 코드

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class StreamGobbler implements Runnable {
    private InputStream inputStream;
    private String type;

    public StreamGobbler(InputStream inputStream, String type) {
        this.inputStream = inputStream;
        this.type = type;
    }

    @Override
    public void run() {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(type + "> " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class SeparateStreamExample {
    public static void main(String[] args) {
        ProcessBuilder builder = new ProcessBuilder("yourCommand", "arg1", "arg2");
        try {
            Process process = builder.start();

            // 표준 출력과 에러를 별도의 스레드에서 처리
            Thread outputGobbler = new Thread(new StreamGobbler(process.getInputStream(), "OUTPUT"));
            Thread errorGobbler = new Thread(new StreamGobbler(process.getErrorStream(), "ERROR"));

            outputGobbler.start();
            errorGobbler.start();

            // 스레드가 종료되기를 기다림
            outputGobbler.join();
            errorGobbler.join();

            // 프로세스 종료 대기
            int exitCode = process.waitFor();
            System.out.println("Exited with code " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

표준 에러처리 

ProcessBuilder를 사용하여 외부 프로세스를 실행할 때, 오류 처리는 매우 중요합니다. 프로세스가 실패하거나 예상치 못한 출력을 생성할 경우, 이를 적절히 처리하여 애플리케이션의 안정성을 유지해야 합니다. 다음은 프로세스 에러 처리를 위한 몇 가지 주요 방법입니다:

 

1. 표준 에러 스트림 읽기

프로세스가 생성하는 에러 메시지를 읽기 위해 표준 에러 스트림(getErrorStream())을 활용합니다. 이를 통해 발생한 문제를 파악하고 로그에 기록할 수 있습니다.

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ProcessErrorExample {
    public static void main(String[] args) {
        ProcessBuilder builder = new ProcessBuilder("yourCommand");
        try {
            Process process = builder.start();

            // 표준 에러 스트림에서 에러 메시지 읽기
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line;
            while ((line = errorReader.readLine()) != null) {
                System.err.println("Error: " + line);
            }

            // 프로세스 종료 코드 확인
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                System.err.println("Process failed with exit code " + exitCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

2. 종료 코드 검사

프로세스의 종료 코드를 검사하여 프로세스가 성공적으로 완료되었는지 확인합니다. 0이 아닌 종료 코드는 일반적으로 오류를 나타냅니다.

int exitCode = process.waitFor();
if (exitCode != 0) {
    throw new RuntimeException("Process failed with exit code " + exitCode);
}

 

3. 예외 처리

ProcessBuilder.start() 메소드는 IOException을 발생시킬 수 있으며, waitFor() 메소드는 InterruptedException을 발생시킬 수 있습니다. 이러한 예외들을 적절히 처리해야 합니다.

try {
    Process process = builder.start();
    int exitCode = process.waitFor();
    // Handle exit code or other conditions here
} catch (IOException e) {
    e.printStackTrace();  // Handle IO problems, such as command not found
} catch (InterruptedException e) {
    e.printStackTrace();  // Handle the interruption during the waitFor
}

 

4. 리소스 관리

프로세스의 입출력 스트림은 적절히 닫아 주어야 합니다. Java 7 이상에서는 try-with-resources 문을 사용하여 자동으로 리소스를 관리할 수 있습니다.

try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

이러한 방법을 통해 외부 프로세스 실행 시 발생할 수 있는 다양한 오류 상황을 효과적으로 관리할 수 있습니다. 이를 통해 애플리케이션의 안정성과 오류 대응 능력을 높일 수 있습니다.

728x90