java调用工具包_Java 调用外部程序工具包Apache Commons Exec

秋向阳
2023-12-01

Java管理进程,API级别是使用:Runtime.getRuntime().exec(“shell”);这个方法。

Java在执行命令时输出到某个Buffer里,这个Buffer是有容量限制的,如果满了一直没读取,就会一直等待,造成进程锁死的现象。

使用Apache Commons Exec,应该可以避免很多类似的坑。

它提供一些常用的方法用来执行外部进程,另外,它提供了监视狗Watchdog来设监视进程的执行超时,同时也还实现了同步和异步功能,

Apache Commons Exec涉及到多线程,比如新启动一个进程,Java中需要再开三个线程来处理进程的三个数据流,分别是标准输入,标准输出和错误输出。

maven依赖

org.apache.commons

commons-exec

1.3

工具类ScriptUtil

import org.apache.commons.exec.CommandLine;

import org.apache.commons.exec.DefaultExecutor;

import org.apache.commons.exec.PumpStreamHandler;

import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;

import java.io.FileOutputStream;

import java.io.IOException;

/**

* 1、内嵌编译器如"PythonInterpreter"无法引用扩展包,因此推荐使用java调用控制台进程方式"Runtime.getRuntime().execCmd()"来运行脚本(shell或python);

* 2、因为通过java调用控制台进程方式实现,需要保证目标机器PATH路径正确配置对应编译器;

* 3、暂时脚本执行日志只能在脚本执行结束后一次性获取,无法保证实时性;因此为确保日志实时性,可改为将脚本打印的日志存储在指定的日志文件上;

* 4、python 异常输出优先级高于标准输出,体现在Log文件中,因此推荐通过logging方式打日志保持和异常信息一致;否则用prinf日志顺序会错乱

*

*

* @author lism

* @date 2018年11月8日10:02:30

*/

public class ScriptUtil {

/**

* make script file

*

* @param scriptFileName

* @param content

* @throws IOException

*/

public static void markScriptFile(String scriptFileName, String content) throws Exception {

// make file, filePath/gluesource/666-123456789.py

FileOutputStream fileOutputStream = null;

try {

fileOutputStream = new FileOutputStream(scriptFileName);

fileOutputStream.write(content.getBytes("UTF-8"));

fileOutputStream.close();

} catch (Exception e) {

throw e;

} finally {

if (fileOutputStream != null) {

fileOutputStream.close();

}

}

}

/**

* 日志文件输出方式

*

* 优点:支持将目标数据实时输出到指定日志文件中去

* 缺点:

* 标准输出和错误输出优先级固定,可能和脚本中顺序不一致

* Java无法实时获取

*

* @param command

* @param scriptFile

* @param logFile

* @param params

* @return

* @throws IOException

*/

public static int execToFile(String command, String scriptFile, String logFile, String... params) throws IOException {

// 标准输出:print (null if watchdog timeout)

// 错误输出:logging + 异常 (still exists if watchdog timeout)

// 标准输入

FileOutputStream fileOutputStream = null;

try {

fileOutputStream = new FileOutputStream(logFile, true);

PumpStreamHandler streamHandler = new PumpStreamHandler(fileOutputStream, fileOutputStream, null);

int exitValue = execCmd(command, scriptFile, params, streamHandler);

return exitValue;

} catch (Exception e) {

return -1;

} finally {

if (fileOutputStream != null) {

try {

fileOutputStream.close();

} catch (IOException e) {

}

}

}

}

public static int execCmd(String command, String scriptFile, String... params) throws IOException {

PumpStreamHandler streamHandler = new PumpStreamHandler(new CollectingLogOutputStream());

// command

return execCmd(command, scriptFile, params, streamHandler);

}

public static int execCmd(String command, String scriptFile, String[] params, PumpStreamHandler streamHandler) throws IOException {

CommandLine commandline = new CommandLine(command);

if(!StringUtils.isEmpty(scriptFile)){

commandline.addArgument(scriptFile);

}

if (params != null && params.length > 0) {

commandline.addArguments(params);

}

// execCmd

DefaultExecutor exec = new DefaultExecutor();

exec.setExitValues(null);

exec.setStreamHandler(streamHandler);

int exitValue = exec.execute(commandline);// exit code: 0=success, 1=error

return exitValue;

}

public static String execToString(String command, String scriptFile, String logFile, String... params) throws IOException {

// 标准输出:print (null if watchdog timeout)

// 错误输出:logging + 异常 (still exists if watchdog timeout)

// 标准输入

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

try {

PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, outputStream);

execCmd(command, scriptFile, params, streamHandler);

} catch (Exception e) {

return null;

} finally {

if (outputStream != null) {

try {

outputStream.close();

} catch (IOException e) {

}

}

}

return outputStream.toString("gbk");

}

}

如果不是要把脚本的日志输出到文件或者流中,而是实时处理每一行输出,需要实现LogOutputStream,类似下面的实现类CollectingLogOutputStream

import org.apache.commons.exec.LogOutputStream;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.util.LinkedList;

import java.util.List;

/**

* @author lism

*/

public class CollectingLogOutputStream extends LogOutputStream {

private static Logger logger = LoggerFactory.getLogger(CollectingLogOutputStream.class);

private final List lines = new LinkedList();

@Override protected void processLine(String line, int level) {

lines.add(line);

logger.info("日志级别{}:{}",level,line);

}

public List getLines() {

return lines;

}

}

使用方式是将CollectingLogOutputStream实现类传给PumpStreamHandler当参数,这样就能实时处理脚本输出了

public static int execCmd(String command, String scriptFile, String... params) throws IOException {

PumpStreamHandler streamHandler = new PumpStreamHandler(new CollectingLogOutputStream());

// command

return execCmd(command, scriptFile, params, streamHandler);

}

 类似资料: