简体   繁体   English

使用Processbuilder运行jar无法正常工作

[英]Running jar with Processbuilder doesn't work properly

I have the following code: 我有以下代码:

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();

When running the jar file from my terminal with this command: 使用此命令从终端运行jar文件时:

java -jar test.jar 135 3 3 appName java -jar test.jar 135 3 3 appName

Then it works like a charm. 然后它就像一个魅力。 The jar pushes some stuff in my database so I see that it is working. jar会在我的数据库中推送一些内容,所以我发现它正在运行。 But when doing it from my JavaServlet with the processBuilder-code mentioned above, I don't get any data in my database and I don't get any errors either. 但是当我使用上面提到的processBuilder代码从我的JavaServlet中执行它时,我没有在我的数据库中获得任何数据,也没有任何错误。

However the process itself is running, I checked it with "ps ax" in my terminal. 但是进程本身正在运行,我在终端中用“ps ax”检查了它。 So I wonder where is here the difference? 所以我想知道这里的区别在哪里? What am I doing wrong? 我究竟做错了什么?

Has someone an idea? 有人有想法吗?

Edit: more Code: 编辑:更多代码:

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 ) );

UPDATE: 更新:

I tried to call a shell-script instead of my jar. 我试着调用shell脚本而不是jar。 So I called a shell script with the processbuilder from my java-file. 所以我用java文件中的processbuilder调用了一个shell脚本。

My shell script does this: 我的shell脚本执行此操作:

java -jar test.jar "$1" "$2" "$3" "$4"

Well it still didn't work. 好吧它仍然无法正常工作。 So I tried this: 所以我尝试了这个:

gnome-terminal -x java -jar test.jar "$1" "$2" "$3" "$4"

And suddenly it works!! 突然它有效!! BUT it opens the gnome-terminal, which executes the jar-file. 但是它打开了gnome-terminal,它执行jar文件。

So I wonder, could this has anything to do with the output which isn't shown in eclipse? 所以我想知道,这可能与eclipse中没有显示的输出有关吗? I really don't get it. 我真的不明白。 This is now a nice workaround. 这是一个很好的解决方法。 But I really would like to get this working without having my terminal opening each time the jar gets executed. 但是我真的希望每次执行jar时都不会打开终端。

First of all, I couldn't reproduce your problem so this answer will be based solely on documentation. 首先,我无法重现您的问题所以这个答案将完全基于文档。

By default, the created subprocess does not have its own terminal or console. 默认情况下,创建的子进程没有自己的终端或控制台。 All its standard I/O (ie stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). 它的所有标准I / O(即stdin,stdout,stderr)操作将被重定向到父进程,在那里可以通过使用方法getOutputStream(),getInputStream()和getErrorStream()获得的流来访问它们。 The parent process uses these streams to feed input to and get output from the subprocess. 父进程使用这些流向子进程提供输入并从子进程获取输出。 Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock. 由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞甚至死锁。

java.lang.Process Documentation java.lang.Process文档

Basically this tells you that you need to handle the streams of your external process properly otherwise it may cause deadlocks in some platforms. 基本上这告诉你,你需要处理的外部进程的流正常 ,否则可能会导致一些平台死锁 Which means if the command we run produces some output you have to read that output. 这意味着如果我们运行的命令产生一些输出,你必须读取该输出。

So lets take a look at your code; 那么让我们来看看你的代码; you are calling process.waitFor() to wait until process is finished but the thing is your process can't finish without you read/consume it's output hence you are creating a deadlock. 你正在调用process.waitFor()来等待进程完成但事情是你的进程无法完成而没有你读取/消耗它的输出因此你正在创建一个死锁。

How to overcome this: 如何克服这个问题:

  1. Method is using an InputStreamConsumerThread to handle input/error stream properly; 方法是使用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(); } } 

    your code will be; 你的代码将是;

     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 } 
  2. Method is using ProcessBuilder to redirect output. 方法是使用ProcessBuilder重定向输出。 If you just want your sub-process use the same input/output stream with your parent process, you can use ProcessBuilder.Redirect.INHERIT like this; 如果您只是希望子进程与父进程使用相同的输入/输出流,则可以像这样使用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" ); 
  3. Method is using a 3rd party library. 方法是使用第三方库。 If you don't wanna hassle with ProcessBuilder and the consumer threads yourself, there are 2 libraries that I know of that handles creating sub-process very nicely. 如果你不想使用ProcessBuilder和消费者自己解决问题,我知道有两个库可以非常好地处理创建子流程。

    A low-overhead, non-blocking I/O, external Process execution implementation for Java. Java的低开销,非阻塞I / O外部流程执行实现。 It is a replacement for java.lang.ProcessBuilder and java.lang.Process. 它是java.lang.ProcessBuilder和java.lang.Process的替代品。

    Have you ever been annoyed by the fact that whenever you spawn a process in Java you have to create two or three "pumper" threads (for every process) to pull data out of the stdout and stderr pipes and pump data into stdin? 您是否曾经因为每当您使用Java生成进程时必须创建两个或三个“pumper”线程(对于每个进程)来从stdout和stderr管道中提取数据并将数据泵入stdin这一事实,您是否曾为此烦恼? If your code starts a lot of processes you can have dozens or hundreds of threads doing nothing but pumping data. 如果您的代码启动了很多进程,那么除了抽取数据之外,您可以拥有数十个或数百个线程。

    NuProcess uses the JNA library to use platform-specific native APIs to achive non-blocking I/O on the pipes between your Java process and the spawned processes. NuProcess使用JNA库来使用特定于平台的本机API,以在Java进程和生成的进程之间的管道上实现非阻塞I / O.

    NuProcess NuProcess

    There are many approaches to take when running external processes from Java. 从Java运行外部进程时需要采取许多方法。 There are the JRE options such as the Runtime.exec() and ProcessBuilder. 有一些JRE选项,例如Runtime.exec()和ProcessBuilder。 Also there is the Apache Commons Exec. 还有Apache Commons Exec。 Nevertheless we created yet another process library (YAPL). 然而,我们创建了另一个流程库(YAPL)。

    Some of the reasons for this crazy endeavour 这种疯狂努力的一些原因

    Improved handling of streams Reading/writing to streams Redirecting stderr to stdout Improved handling of timeouts Improved checking of exit codes Improved API One liners for quite complex usecases One liners to get process output into a String Access to the Process object available Support for async processes ( Future ) Improved logging with SLF4J API Support for multiple processes 改进了流的处理读取/写入流将stderr重定向到stdout改进了超时的处理改进了退出代码的检查改进的API一个衬里用于非常复杂的用例一个衬里将进程输出转换为String访问可用的Process对象支持异步进程(未来)使用SLF4J API改进日志记录支持多个进程

    ZT Process Executor ZT Process Executor

Also there are other pitfalls in Java's Process API, take a look at this JavaWorld article for more info When Runtime.exec() won't . Java的Process API中还有其他陷阱,请查看此JavaWorld文章了解更多信息Runtime.exec()不会

Can you try this instead ? 你能尝试一下吗?

UPDATE UPDATE

Code : 代码:

// 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"));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM