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

时间:2016-06-23 14:03:05

标签: 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();

使用此命令从终端运行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时都没有打开终端工作。

2 个答案:

答案 0 :(得分:5)

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

  

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

java.lang.Process Documentation

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

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

如何克服这个问题:

  1. 方法正在使用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
    }
    
  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" );
    
  3. 方法正在使用第三方库。如果您不想与ProcessBuilder和消费者自己讨厌,我知道有两个库可以很好地处理创建子流程。

      

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

         

    你有没有因为每当你产生一个东西而烦恼   在Java中你必须创建两个或三个“pumper”线程(for   每个进程)从stdout和stderr管道中提取数据   将数据泵入stdin?如果您的代码可以启动很多流程   除了抽取数据之外,还有几十个或几百个线程无所事事。

         

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

    NuProcess

      

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

         

    这种疯狂努力的一些原因

         

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

    ZT Process Executor

  4. 此外,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"));