Java

Java에서 외부 프로그램 실행하기 (zt-exec)

jaamong 2024. 7. 6. 15:51

프로젝트를 진행하면서 Java 환경에서 Python 프로그램을 실행해야 할 일이 생겼다. 해본 적이 없어서 할 수 있는지가 걱정이었는데 역시 안될 건 없다.

 

종류

일단 외부 프로그램을 실행할 때 쓰이는 방법들을 찾아보았다.

  • JSR-233 Scripting Engine
  • Jython
  • ProcessBuilder
  • Apache Common Exec(thrid-party lib)
  • ZT Process Executor
  • HTTP (python server)

 

JSR-233 Scripting Engine

  • Java 6 부터 지원
  • set of scripting APIs
  • `CLASSPATH`에 `Jython`이 있어야 함
    • Jython의 경우 파이썬 라이브러리를 한정적으로 사용하게 됨

 

Jython

  • 자바 코드에 파이썬 코드를 직접적으로 임베딩할 수 있음
    • 자바 코드에 파이썬 코드가 섞여서 유지보수가 어려워짐
  • Jython의 경우 파이썬 라이브러리를 한정적으로 사용하게 됨

 

ProcessBuilder

  • 자바에서 외부 프로그램을 실행하고 관리하기 위한 도구
  • 작성된 파이썬 파일을 실행하는 방식
  • `assumption` PATH 변수를 통해 파이썬을 사용할 수 있어야 함
  • 안전하게 스트림을 처리하려면 3~4개 이상의 전용 클래스를 구현해야 함
    • "Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock."

 

Apache Common Exec

  • ProcessBuilder 코드와 비슷함
  • 작성된 파이썬 파일을 실행하는 방식
  • 일관된 API로 폭넓은 OS를 지원하는 것이 주요 철학

 

ZT Process Executor

  • `ProcessBuilder`, `common-exec` 기능 사용 가능
    • Improved handling of stream
    • One liners to get process output into a String
    • Improved logging with SLF4J API
  • only 3rd-party dependency is SLF4J
  • 🔗깃허브 주소

 

HTTP

python -m http.server 9000

또는 Flask나 Django를 이용하여 HTTP 통신을 하는 방법

 

 

나는 이 중에서 `ZT Process Executor`를 선택했다. 제공하는 기능이 폭넓고 예제 코드도 나와있어서 좋았다. 또한 사용하기 위해 어떤 추가적인 라이브러리 등이 필요하지 않다는 점이 좋았다.

 

ZT Process Executor

실행될 파이썬 프로그램은 파일을 인자로 받으며, 자바는 파이썬을 호출하고 나서 해당 결과를 파싱 하여 저장한다.

...
public class FileHandler {

    private final FileService fileService;
	...

    public returnType pythonExecutor(MultipartFile file) {

        File saveFile = fileService.saveFile(file);
        String savedPath = saveFile.getSavedPath();
        final String pyPath = "...";

        String[] commands = {"python", pyPath, savedPath};
        String output = null;

        //ZT Process Executor를 이용한 python 파일 호출
        try {
            output = new ProcessExecutor().command(commands)
                    .readOutput(true).execute()
                    .outputUTF8();
        } catch (IOException | InterruptedException | TimeoutException e) {
            log.error("Process Executor Error occur: ", e);
            throw new RuntimeException(e);
        }
        // 결과 파싱
        JSONObject result = parseJson(output);
        String field = result.get("원하는 필드"); //이런식으로 파싱한 결과의 필드를 꺼낼 수 있음
        
		...
    }

    private JSONObject parseJson(String output) {
        final String jsonOuterKey = "field";

        JSONParser parser = new JSONParser();
        JSONObject object;

        try {
            object = (JSONObject) parser.parse(output);
        } catch (ParseException e) {
            log.error("JSON Parse Error occur: ", e);
            throw new RuntimeException(e);
        }

        return (JSONObject) object.get(jsonOuterKey);
    }
}

 

`parseJson` 메서드에서 `jsonOuterKey`는 이런 경우를 위함이다. 겉에 아무것도 없다면 바로 꺼내면 된다.

"field" : {
	"aaa": ...,
    ...
}

 

 

자세한 ZT Process Executor 사용법은 깃허브 링크를 참고!