개요
오픈 소스 기반 프로그램 수행을 위해 curl 커맨드를 외부 데이터베이스와 연결하여 결과를 받는데 windows 환경에서는 잘 되었지만 linux 기반에 동일한 tomcat과 java 환경에서 수행 시 오류가 발생하였습니다. 오류 메시지는 종류도 다양하지만 주로 아래와 같은 메시지가 나왔습니다.
보내는 메시지는 다음과 같았습니다.
String queryTable = "curl -X POST -H \"Content-Type:application/json\" " + " -d @" + filePath + " " + serverUrl;
형태의 비교적 단순한 문장이었고, 쿼리를 담은 파일을 포함하여 발송합니다.
여기서 filePath 는 데이터를 전송하기 위해 생성한 데이터 파일의 절대경로입니다. serverUrl은 전송 대상 서버의 전체 url 입니다.
옵션 중 -d 뒤의 @ 옵션을 제거하면 파일이 아닌 문자 데이터 형태로 전송할 수 있습니다.
에러 메시지는 대략 모두 비슷한 유형으로 <h1>이 H4로 바뀌는 정도의 오류였습니다.
illegal character varchar='"
<h1>bad message 400
windows 기반의 tomcat9에서 수도 없이 많은 테스트를 거친 프로그램이기에 믿고 리눅스에 맡겼더니 배신도 이런 배신이 없습니다.
문제코드
여러 검색 결과 문제는 java 자체의 Process 클래스에 의한 처리였습니다. 외부 명령을 실행하기 위해 다음과 같은 코드를 작성하여 사용해왔습니다.
StringBuffer output = new StringBuffer();
Process p;
String outString = "";
try {
p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
p.waitFor();
outString= output.toString();
if (p.exitValue() != 0) {
// shell 실행이 비정상 종료되었을 경우
BufferedReader errorBufferReader = null;
StringBuffer errorOutput = new StringBuffer();
errorBufferReader = new BufferedReader(new InputStreamReader(p.getErrorStream(), "EUC-KR"));
String msg = "";
while ((msg = errorBufferReader.readLine()) != null) {
errorOutput .append(msg + System.getProperty("line.separator"));
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return output.toString();
}
상위 스크립트 처럼 java 자체의 process runtime exec를 사용 할 경우 linux나 macos 환경에서 알 수 없는 오류로 고생하게 됩니다. 또한 java 자체의 processbuilder를 활용하더라도 동일한 현상이 나타나는 것을 확인하였습니다.
여러 문서들을 검토 한 결과 Apache commons common-exec 를 사용할 경우 해당 문제가 해결된다고 합니다.
https://commons.apache.org/proper/commons-exec/
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
해결 코드
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class apCmdLine {
private static final Logger logger = LoggerFactory.getLogger(apCmdLine.class);
/**
* linux 에서 shell script가 비정상 실행되는 오류에 대한 fix
* @param command
* @return
* @throws Exception
*/
public String execAndRtnResult(CommandLine command) throws Exception {
String rtnStr = "";
DefaultExecutor executor = new DefaultExecutor();
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(bs);
executor.setStreamHandler(streamHandler);
int exitCode = executor.execute(new CommandLine(command));
rtnStr = bs .toString();
logger.info("exitCode : " + exitCode);
logger.debug("outputStr : " + rtnStr );
return rtnStr;
}
}