我有以下代码:
ProcessBuilder pb = new ProcessBuilder( "java", "-jar", "test.jar", Integer.toString( jobId ), Integer.toString( software ), Integer.toString( entryPoint ), application );
pb.directory( new File("/home/userName/TestBSC") );
Process proc = pb.start();
使用此命令从终端运行jar文件时:
java -jar test.jar 135 3 3 appName
然后它就像一个魅力。 jar会在我的数据库中推送一些内容,所以我发现它正在运行。但是当我使用上面提到的processBuilder代码从我的JavaServlet执行此操作时,我在数据库中没有获得任何数据,也没有出现任何错误。
然而,进程本身正在运行,我在终端中用“ps ax”检查了它。所以我想知道这里的区别在哪里?我做错了什么?
有人有想法吗?
修改:更多代码:
ProcessBuilder pb = new ProcessBuilder( "java", "-jar", "test.jar", Integer.toString( jobId ), Integer.toString( software ), Integer.toString( entryPoint ), application );
pb.directory( new File("/home/userName/TestBSC") );
Process proc = pb.start();
System.out.println( "Job running" );
proc.waitFor(); // wait until jar is finished
System.out.println( "Job finished" );
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();
byte result[] = new byte[ in.available() ];
in.read( result, 0, result.length );
System.out.println( new String( result ) );
byte error[] = new byte[ err.available() ];
err.read( error, 0, error.length );
System.out.println( new String( error ) );
我试图调用shell脚本而不是jar。所以我用java文件中的processbuilder调用了一个shell脚本。
我的shell脚本执行此操作:
java -jar test.jar "$1" "$2" "$3" "$4"
嗯,它仍然无法正常工作。所以我尝试了这个:
gnome-terminal -x java -jar test.jar "$1" "$2" "$3" "$4"
突然它起作用!! 但是会打开gnome-terminal,执行jar文件。
所以我想知道,这是否与eclipse中未显示的输出有关?我真的不明白。这是一个很好的解决方法。但我真的很想在每次执行jar时都没有打开终端工作。
答案 0 :(得分:5)
首先,我无法重现您的问题所以这个答案将完全基于文档。
默认情况下,创建的子进程没有自己的终端或 安慰。所有标准I / O(即stdin,stdout,stderr)操作 将被重定向到父进程,在那里可以访问它们 通过使用方法getOutputStream()获得的流, getInputStream()和getErrorStream()。父进程使用这些 stream将输入提供给子进程并从子进程获取输出。的因为 某些本机平台仅为标准提供有限的缓冲区大小 输入和输出流,无法及时写入输入流 或者读取子进程的输出流可能会导致子进程 阻止,甚至死锁。
java.lang.Process Documentation
基本上,这告诉您需要处理外部进程的流正确,否则可能会在某些平台中导致死锁。这意味着如果我们运行的命令产生一些输出,你必须读取该输出。
让我们来看看你的代码;你正在调用process.waitFor()
等待进程完成但事情是你的进程无法完成而没有你读取/消耗它的输出因此你正在创建一个死锁。
如何克服这个问题:
方法正在使用InputStreamConsumerThread
来正确处理输入/错误流;
public class InputStreamConsumerThread extends Thread
{
private InputStream is;
private boolean sysout;
private StringBuilder output = new StringBuilder();
public InputStreamConsumerThread (InputStream is, boolean sysout)
{
this.is=is;
this.sysout=sysout;
}
public void run()
{
try(BufferedReader br = new BufferedReader(new InputStreamReader(is)))
{
for (String line = br.readLine(); line != null; line = br.readLine())
{
if (sysout)
System.out.println(line);
output.append(line).append("\n");
}
}
}
public String getOutput(){
return output.toString();
}
}
你的代码将是;
String systemProperties = "-Dkey=value";
ProcessBuilder pb = new ProcessBuilder( "java", systemProperties, "-jar", "test.jar", Integer.toString( jobId ), Integer.toString( software ), Integer.toString( entryPoint ), application );
pb.directory( new File("/home/userName/TestBSC") );
Process proc = pb.start();
InputStreamConsumerThread inputConsumer =
new InputStreamConsumerThread(proc.getInputStream(), true);
InputStreamConsumerThread errorConsumer =
new InputStreamConsumerThread(proc.getErrorStream(), true);
inputConsumer.start();
errorConsumer.start();
System.out.println( "Job running" );
proc.waitFor(); // wait until jar is finished
System.out.println( "Job finished" );
String processOutput = inputConsumer.getOutput();
String processError = errorConsumer.getOutput();
if(!processOutput.isEmpty()){
//there were some output
}
if(!processError.isEmpty()){
//there were some error
}
方法正在使用ProcessBuilder
重定向输出。如果您只是希望子流程与父流程使用相同的输入/输出流,则可以像这样使用ProcessBuilder.Redirect.INHERIT
;
ProcessBuilder pb = new ProcessBuilder( "java", "-jar", "test.jar", Integer.toString( jobId ), Integer.toString( software ), Integer.toString( entryPoint ), application );
pb.directory( new File("/home/userName/TestBSC") );
pb.redirectErrorStream(true); // redirect error stream to output stream
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process proc = pb.start();
System.out.println( "Job running" );
//since process builder will handle the streams for us
//we can call waitFor() safely
proc.waitFor();
System.out.println( "Job finished" );
方法正在使用第三方库。如果您不想与ProcessBuilder
和消费者自己讨厌,我知道有两个库可以很好地处理创建子流程。
低开销,非阻塞I / O,外部流程执行 Java的实现。它是一个替代品 java.lang.ProcessBuilder和java.lang.Process。
你有没有因为每当你产生一个东西而烦恼 在Java中你必须创建两个或三个“pumper”线程(for 每个进程)从stdout和stderr管道中提取数据 将数据泵入stdin?如果您的代码可以启动很多流程 除了抽取数据之外,还有几十个或几百个线程无所事事。
NuProcess使用JNA库来使用特定于平台的本机API 在Java进程和Java进程之间的管道上实现非阻塞I / O. 产生过程。
从中运行外部进程时需要采取许多方法 Java的。有一些JRE选项,例如Runtime.exec()和 的ProcessBuilder。还有Apache Commons Exec。不过我们 创建了另一个流程库(YAPL)。
这种疯狂努力的一些原因
改进了对流的处理读取/写入流重定向 stderr到标准输出改进了超时处理改进了检查 退出代码改进的API一个衬垫用于相当复杂的用例一 用于将进程输出转换为进程的String Access的内衬 对象可用支持异步进程(未来)改进 使用SLF4J API进行日志记录支持多个进程
此外,Java的Process API还有其他陷阱,请查看此JavaWorld文章了解更多信息When Runtime.exec() won't。
答案 1 :(得分:0)
你能试试吗?
代码:
// Java runtime
Runtime runtime = Runtime.getRuntime();
// Command
String[] command = {"java", "-jar", "test.jar", Integer.toString( jobId ), Integer.toString( software ), Integer.toString( entryPoint ), application};
// Process
Process process = runtime.exec(command, null, new File("/home/userName/TestBSC"));