我在使用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
但是,服务器需要从“进程”中获取正在进行的输出.getErrorStream()&#39;在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失败的原因是因为用户&#39; tomcat8&#39;没有权限运行&#39; w_scan&#39;。 &#39; w_scan&#39;从终端工作,但不从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前端属于&#39;视频&#39;组。所以我需要将用户tomcat8添加到该组。
usermod -a -G video tomcat8
现在一切正常......
答案 0 :(得分:4)
你对Runtime.exe所做的ProcessBuilder没有做同样的事情,所以我不知道为什么你认为ProcessBuilder就是问题。
关于如何将命令的输出写入文件,您遇到了一些问题。
首先,ProcessBuilder命令中">", satellite_scan_temp_file
的存在是不正确的。输出重定向不是任何命令的一部分;它由shell处理。但是当您使用Runtime.exec或ProcessBuilder运行时,您没有在shell中运行,而是直接执行该过程。 w_scan和任何其他命令都不会将>
视为特殊字符。
重定向到文件的正确方法是使用redirectOutput方法:
ProcessBuilder pb = new ProcessBuilder(
"/usr/bin/w_scan", "-fs", "-s" + satellite_code, "-c" + country_code);
pb.redirectOutput(new File(satellite_scan_temp_file));
其次,您的ScanThread代码忽略了(当前不正确的)重定向,并且正在尝试读取命令的输出。但是没有输出,因为你将它全部重定向到一个文件。
将输出正确地重定向到文件后,可以完全删除BufferedReader和BufferedWriter循环。
最后,值得注意的是,您捕获的错误输出可能告诉您>
不是w_scan进程的有效参数。