Java 使用Processbuilder运行jar不会';我不能正常工作

Java 使用Processbuilder运行jar不会';我不能正常工作,java,jar,processbuilder,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.

我有以下代码:

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-jartest.jar135 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终端,执行jar文件


所以我想知道,这是否与eclipse中未显示的输出有关?我真的不明白。这是一个很好的解决办法。但是我真的很想在每次执行jar时都不让终端打开的情况下让它工作。

你能试试这个吗

更新 代码:

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

首先,我无法重现您的问题,因此此答案将仅基于文档

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

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

让我们看看你的代码;您正在调用
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 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”线程(用于 每个进程)从stdout和stderr管道中提取数据 将数据输入stdin?如果您的代码启动了许多进程,您可以 有几十个或几百个线程除了泵送数据什么都不做

    NuProcess使用JNA库使用特定于平台的本机API 在Java进程和 衍生过程

    从运行外部进程时,有许多方法可供选择 JAVA有一些JRE选项,例如Runtime.exec()和 ProcessBuilder。还有ApacheCommonsExec。然而我们 创建了另一个进程库(YAPL)

    这一疯狂努力的一些原因

    改进了流读取/写入流重定向的处理 从stderr到stdout改进了超时处理改进了检查 出口代码改进了API一行程序,用于非常复杂的用例一 将进程输出转换为访问进程的字符串的行程序 改进了对异步进程的对象可用支持(未来) 支持多进程的SLF4J API日志记录


  • Java的进程API中还有其他缺陷,请参阅这篇JavaWorld文章以了解更多信息。

    进程中是否存在任何错误/异常?请尝试给出
    Java
    @MikaëlB:不,很遗憾没有。@Petermm:我也尝试过,但结果相同。或者等等……你说java是什么意思。你是说jar文件的完整路径还是java jre?@petermm:我现在尝试了这个:/usr/lib64/jvm/java-7-openjdk/bin/java-jar。。。。但这也不起作用。不幸的是,情况也一样。执行该过程。但是没有输出,数据库中也没有存储任何内容。就像这个过程不是真正的加工。这太奇怪了。这个过程的
    exitValue
    是什么?()过程不会停止(这可能是一个优先事项或类似的事情吗?我不这么认为。你还有这个
    waitFor()
    ?如果有,你能在
    System.out.println(新字符串(错误));
    ?哇,谢谢这个伟大的答案。我目前正在测试方法1。我需要做新的BufferedReader()吗?)而不是InputStreamConsumerThread的run方法中的new BufferedInputStream(),因为它说,我不能从B转换