在我们需要启动一个进程的时候可以使用 java 原始的 ProccessBuilder 或使用apache-commons-exec执行命令的时候,其实commons-exec是对ProcessBuidler的一个封装
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private static class ShellExecutor{ private DefaultExecutor executor ;
public ShellExecutor() { this.executor = new DefaultExecutor(); ExecuteWatchdog watchdog = new ExecuteWatchdog(COMMAND_EXEC_TIME_OUT); this.executor.setWatchdog(watchdog);
}
public BufferedReader exec(CommandLine commandLine) throws IOException { PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream(pis); PumpStreamHandler ps = new PumpStreamHandler(pos,pos); DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler(); executor.setStreamHandler(ps); executor.execute(commandLine, handler); return new BufferedReader(new InputStreamReader(pis, Charset.defaultCharset())); }
}
|
这主要发现的错误是在 CommandLine 的构建上
在 JAVA的 ProcessBudiler中 命令参数是分别放在不同的数组中的,在构建的CommandLine 应该是命令和参数分开放置,不然会报一些奇怪的错误,而且commons-exec没有log,很难排查错误
1 2 3 4 5 6 7 8 9 10 11
| private CommandLine createBackupCommand(CommandConfig.BackupRestoreParam param){ Objects.requireNonNull(param.host,"数据库地址不能为空!"); String command = getShellDir()+"/pg_dump"; String commandParam = "--dbname=postgres://%s:%s@%s:%s/%s -v -f %s "; commandParam = String.format(commandParam, param.userName, param.password, param.host, param.port, param.dbName, param.backFilePath); CommandLine commandLine = new CommandLine(command); commandLine.addArguments(commandParam); return commandLine; }
|
commons-exec 执行命令源码如下
org.apache.commons.exec.launcher.Java13CommandLauncher 源码
1 2 3 4 5 6 7 8 9
| @Override public Process exec(final CommandLine cmd, final Map<String, String> env, final File workingDir) throws IOException {
final String[] envVars = EnvironmentUtils.toStrings(env); return Runtime.getRuntime().exec(cmd.toStrings(), envVars, workingDir); }
|
在看看 org.apache.commons.exec.CommandLine toStrings方法
1 2 3 4 5 6
| public String[] toStrings() { final String[] result = new String[arguments.size() + 1]; result[0] = this.getExecutable(); System.arraycopy(getArguments(), 0, result, 1, result.length-1); return result; }
|
这里返回的是数组
还有一个奇怪的问题,我使用单点的Java进程执行,其实是可以解析命令和参数一起放置的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package com.trs.exec;
import org.apache.commons.exec.*;
import java.io.*; import java.nio.charset.Charset; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger;
public class TrsExce {
public static void main(String[] args) throws IOException {
BufferedReader reader = exeCommand(null); if (args.length > 1){ reader = exeCommand(args[0]); }
String line = null; while ((line = reader.readLine()) != null){ Logger.getGlobal().info(line); }
}
public static BufferedReader exeCommand(String cmd) throws IOException { if(cmd == null){ cmd = "/opt/apache-tomcat-7.0.81-8085-kg3/webapps/tckg/WEB-INF/classes/shell/pg_dump --dbname=postgres://postgres:postgres@192.168.1.53:5432/trskg3_tc -v -f /home/postgres/trskg3_tc_20210518092525.dump ";
} CommandLine cmdLine = CommandLine.parse(cmd);
DefaultExecutor executor = new DefaultExecutor(); PipedOutputStream outputStream = new PipedOutputStream(); PipedInputStream pis = new PipedInputStream(outputStream); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(outputStream,outputStream); executor.setStreamHandler(pumpStreamHandler); ExecuteWatchdog watchdog = new ExecuteWatchdog(1000 * 60 * 30); executor.setWatchdog(watchdog); executor.execute(cmdLine,new DefaultExecuteResultHandler()); BufferedReader reader = new BufferedReader(new InputStreamReader(pis, Charset.forName("GBK"))); return reader; }
}
|
在单独的java进程中以上是可以执行的,但是在tomcat7中就不行,应该是类加载器加载了不同版本的commons-exec.还没找到具体原因,这里记录一下。