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

pro*_*bie 4 java jar processbuilder

我有以下代码:

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();
Run Code Online (Sandbox Code Playgroud)

使用此命令从终端运行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 ) );
Run Code Online (Sandbox Code Playgroud)

更新:

我试着调用shell脚本而不是jar.所以我用java文件中的processbuilder调用了一个shell脚本.

我的shell脚本执行此操作:

java -jar test.jar "$1" "$2" "$3" "$4"
Run Code Online (Sandbox Code Playgroud)

好吧它仍然无法正常工作.所以我尝试了这个:

gnome-terminal -x java -jar test.jar "$1" "$2" "$3" "$4"
Run Code Online (Sandbox Code Playgroud)

突然它有效!! 但是它打开了gnome-terminal,它执行jar文件.

所以我想知道,这可能与eclipse中没有显示的输出有关吗?我真的不明白.这是一个很好的解决方法.但是我真的希望每次执行jar时都不会打开终端.

Onu*_*nur 5

首先,我无法重现您的问题所以这个答案将完全基于文档.

默认情况下,创建的子进程没有自己的终端或控制台.它的所有标准I/O(即stdin,stdout,stderr)操作将被重定向到父进程,在那里可以通过使用方法getOutputStream(),getInputStream()和getErrorStream()获得的流来访问它们.父进程使用这些流向子进程提供输入并从子进程获取输出.由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞甚至死锁.

java.lang.Process文档

基本上这告诉你,你需要处理的外部进程的流正常,否则可能会导致死锁在某些平台上.这意味着如果我们运行的命令产生一些输出,你必须读取该输出.

那么让我们来看看你的代码; 你正在调用process.waitFor()等待进程完成但事情是你的进程无法完成而没有你读取/消耗它的输出因此你正在创建一个死锁.

如何克服这个问题:

  1. 方法是使用a 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();
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    你的代码将是;

    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
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 方法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" );
    
    Run Code Online (Sandbox Code Playgroud)
  3. 方法是使用第三方库.如果你不想麻烦ProcessBuilder和消费者自己解决,我知道有两个库可以很好地处理创建子流程.

    Java的低开销,非阻塞I/O外部流程执行实现.它是java.lang.ProcessBuilder和java.lang.Process的替代品.

    您是否曾经因为每当您使用Java生成进程时必须创建两个或三个"pumper"线程(对于每个进程)来从stdout和stderr管道中提取数据并将数据泵入stdin这一事实,您是否曾为此烦恼?如果您的代码启动了很多进程,那么除了抽取数据之外,您可以拥有数十个或数百个线程.

    NuProcess使用JNA库来使用特定于平台的本机API,以在Java进程和生成的进程之间的管道上实现非阻塞I/O.

    NuProcess

    从Java运行外部进程时需要采取许多方法.有一些JRE选项,例如Runtime.exec()和ProcessBuilder.还有Apache Commons Exec.然而,我们创建了另一个流程库(YAPL).

    这种疯狂努力的一些原因

    改进了流的处理读取/写入流将stderr重定向到stdout改进了超时的处理改进了退出代码的检查改进的API一个衬里用于相当复杂的用例一个衬里将进程输出转换为String访问可用的Process对象支持异步进程(未来)使用SLF4J API改进日志记录支持多个进程

    ZT Process Executor

Java的Process API中还有其他陷阱,请查看此JavaWorld文章了解更多信息Runtime.exec()不会.