Запуск 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.jar 135 3 3 appName




Тогда это работает как заклинание. Баночка засовывает кое-что в мою базу данных, так что я вижу, что она работает. Но когда я делаю это из моего JavaServlet с processBuilder-кодом, упомянутым выше, я не получаю никаких данных в своей базе данных и не получаю никаких ошибок.



Однако сам процесс запущен, я проверил его с помощью "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 ) );




Обновление:



Я попытался вызвать сценарий оболочки вместо моего jar. Поэтому я вызвал сценарий оболочки с processbuilder из моего java-файла.



Мой сценарий оболочки делает это:



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


Ну, это все равно не сработало. Поэтому я попробовал это:



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


И вдруг это срабатывает!! но он открывает gnome-терминал, который выполняет jar-файл.



Итак, я задаюсь вопросом, Может ли это иметь какое-то отношение к выходу, который не показан в eclipse? Я действительно не понимаю. Теперь это хороший обходной путь. Но я действительно хотел бы, чтобы это работало без того, чтобы мой терминал открывался каждый раз, когда выполняется jar.

679   2  

2 ответов:

Во-первых, я не смог воспроизвести вашу проблему, поэтому этот ответ будет основан исключительно на документации.

По умолчанию созданный подпроцесс не имеет собственного терминала или приставка. Все его стандартного ввода-вывода (т. е. стандартный ввод, стандартный вывод, стандартный вывод) операции будут перенаправлены в родительский процесс, где они могут быть доступны через потоки, полученные с помощью методов getOutputStream(), getInputStream () и getErrorStream (). Родительский процесс использует их потоки для питания вход в подпроцесс и получение выходных данных из него. потому что некоторые нативные платформы предоставляют только ограниченный размер буфера для стандарта входные и выходные потоки, неспособность быстро записать входной поток или считывание выходного потока подпроцесса может вызвать подпроцесс блокировать, а то и вовсе загнать в тупик.

Ява.яз..Документация Процесса

В основном это говорит вам, что вам нужно правильно обрабатывать потоки вашего внешнего процесса в противном случае это может вызвать тупики в некоторых платформах. Это означает, что если команда, которую мы запускаем, производит какой-то вывод, вы должны прочитать этот вывод.

Итак, давайте взглянем на ваш код; вы вызываете 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 и потоками-потребителями самостоятельно, есть 2 библиотеки, которые я знаю, которые очень хорошо справляются с созданием подпроцесса.

    Низко-накладные расходы, неблокирующий ввод-вывод, выполнение внешнего процесса реализация для Java. Это замена для Ява.яз..ProcessBuilder и java.яз..Процесс.

    Вы когда-нибудь были раздражены тем фактом, что всякий раз, когда вы порождаете процесс в Java вы должны создать два или три потока "pumper" (для каждый процесс) для извлечения данных из каналов stdout и stderr и перекачивать данные в stdin? Если ваш код запускает много процессов, вы можете есть десятки или сотни из потоков, не делающих ничего, кроме перекачки данных.

    NuProcess использует библиотеку JNA для использования специфичных для платформы собственных API, чтобы достигните неблокирующего ввода-вывода на каналах между вашим процессом Java и порожденные процессы.

    NuProcess

    Существует много подходов, которые можно использовать при запуске внешних процессов из Ява. Существуют параметры JRE, такие как среда выполнения.exec() и ProcessBuilder. Также есть Apache Commons Exec. Тем не менее мы создал еще одну библиотеку процессов (YAPL).

    Некоторые причины этого безумного начинания

    Улучшена обработка потоков чтения / записи для перенаправления потоков stderr to stdout улучшена обработка тайм-аутов улучшена проверка коды выхода улучшенный API один вкладыши для достаточно сложной навигации одного лайнеры для получения вывода процесса в строку доступ к процессу объект доступна поддержка асинхронных процессов (будущее ) улучшено лесозаготовительный с поддержкой API SLF4J для нескольких процессов

    исполнитель процесса ZT

Также есть и другие подводные камни в API процессов Java, взгляните на эту статью JavaWorld для получения дополнительной информации во время выполнения.exec () не будет .

Можете ли вы попробовать это вместо этого ?

Обновление

Код:

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

Comments

    Ничего не найдено.