【发布时间】:2017-08-24 12:09:46
【问题描述】:
我遇到了 ProcessBuilder 无法在服务器上运行命令的问题。
在我的项目早期,我使用 Runtime.exec() 只是为了从运行良好的程序中检索输出:
private List<SatelliteCode> getSatelliteCodes() {
List<SatelliteCode> codes = new ArrayList<>();
Runtime runtime = Runtime.getRuntime();
String[] commands = { "w_scan", "-s?" };
Process process;
try {
process = runtime.exec(commands);
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s = error.readLine(); // discard first line
while ((s = error.readLine()) != null) {
s = s.trim();
int i = s.indexOf('\t'); // separated by a tab!?!?
codes.add(new SatelliteCode(s.substring(0, i), s.substring(i)));
}
} catch (IOException e) {
e.printStackTrace();
}
return codes;
}
在终端中运行它工作正常,我得到了我需要的所有输出:
w_scan -fs -cGB -sS19E2 > channels.conf
但是,服务器需要从“process.getErrorStream()”中获取正在进行的输出以显示在 Web 界面中。实际发生的是 ProcessBuilder 失败并返回退出代码 1。
初始化 ProcessBuilder 并开始运行扫描的函数是 [EDIT 1]:
private static StringBuilder scan_error_output = null;
@Override
public boolean startSatelliteScan(String user, String country_code, String satellite_code) {
UserAccountPermissions perm = validateUserEdit(user);
if (perm == null) return false;
Shared.writeUserLog(user, Shared.getTimeStamp() +
": DVB satellite scan started " +
country_code + " - " + satellite_code +
System.lineSeparator() + System.lineSeparator());
scan_error_output = new StringBuilder();
new ScanThread(country_code, satellite_code).start();
// write out country code and satellite code to prefs file
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(satellite_last_scan_codes));
bw.write(country_code); bw.newLine();
bw.write(satellite_code); bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
这将在服务器上运行另外两个线程,一个将自行运行扫描并等待它完成,以便它可以获取最终的扫描数据。另一个不断更新 std 错误流的输出,然后从客户端浏览器每隔一段时间对其进行轮询。这很像显示终端的持续输出。
扫描线程(无法启动进程)[编辑 1]:
private static class ScanThread extends Thread {
private String cc, sc;
public ScanThread(String country_code, String satellite_code) {
cc = country_code;
sc = satellite_code;
}
public void run() {
ProcessBuilder pb = new ProcessBuilder("/usr/bin/w_scan",
"-fs", "-c" + cc, "-s" + sc);
pb.redirectOutput(new File(satellite_scan_file));
Process process;
try {
System.out.println("Scan thread started");
process = pb.start();
IOScanErrorOutputHandler error_output_handler = new IOScanErrorOutputHandler(process.getErrorStream());
error_output_handler.start();
int result = process.waitFor();
System.out.println(cc + " - " + sc + " - " +
"Process.waitFor() result " + result);
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("Scan thread finished");
}
}
捕获由于扫描线程失败而明显无法启动的输出的错误输出流线程:
private static class IOScanErrorOutputHandler extends Thread {
private InputStream inputStream;
IOScanErrorOutputHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
Scanner br = null;
try {
System.out.println("Scan thread Error IO capture running");
br = new Scanner(new InputStreamReader(inputStream));
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
scan_error_output.append(line + System.getProperty("line.separator"));
}
} finally {
br.close();
}
System.out.println("Scan thread Error IO capture finished");
scan_error_output = null;
}
}
以及返回std错误输出进度的服务器函数:
@Override
public String pollScanResult(String user) {
if (validateUserEdit(user) == null) return null;
StringBuilder sb = scan_error_output; // grab instance
if (sb == null) return null;
return sb.toString();
}
如上所述,Runtime.exec() 工作正常,但 ProcessBuilder 失败。
注意:我在 Linux Mint 18.1 上,使用 Apache Tomcat 8 作为服务器,在 Eclipse Neon 中使用 linux 默认 JDK 8 和 GWT 2.7 [从 2.8 更正]。
谁能看出我做错了什么?
提前非常感谢...
[编辑 1]
在另一台机器上为 DVB-T 开发此方法时,Linux Mint 17.2、JDK 8 和 Apache Tomcat 7,此方法运行良好,并且轮询扫描输出显示在客户端的浏览器中。
ProcessBuilder.start 仍然返回 1 并为输出扫描文件创建一个空文件。
[编辑 2]
看来 ProcessBuilder 失败的原因是因为用户“tomcat8”没有运行“w_scan”的权限。 'w_scan' 在终端上工作,但不能在 tomcat 服务器上工作。不知何故,我现在必须解决这个问题。
[解决方案]
在 VGR 为从 ProcessBuilder 获取错误流而设定正确方向后,我开始进一步挖掘,发现我得到了:
main:3909: FATAL: failed to open '/dev/dvb/adapter0/frontend0': 13 Permission denied
Apache tomcat 8 无权访问 DVB-S 前端以运行扫描。这已通过两种方式解决:
1 - 03catalina.policy 我添加了额外的权限(我不知道他们是否有所作为)。
grant codeBase "file:/dev/dvb/-" {
permission java.io.FilePermission "file:/dev/dvb/-", "read, write";
permission java.security.AllPermission;
};
2 - dvb 前端属于“视频”组。所以我需要将用户 tomcat8 添加到该组。
usermod -a -G video tomcat8
目前一切正常...
【问题讨论】:
标签: java gwt tomcat8 processbuilder