it-swarm.com.de

Wie füge ich einen Timeout-Wert hinzu, wenn Sie Java Runtime.exec () verwenden?

Ich habe eine Methode, die ich zum Ausführen eines Befehls auf dem lokalen Host verwende. Ich möchte der Methode einen Timeout-Parameter hinzufügen, damit die Methode mit einem Fehlercode zurückgegeben wird, wenn der aufgerufene Befehl nicht in angemessener Zeit beendet wird. So sieht es bisher aus, ohne Timeout:

public static int executeCommandLine(final String commandLine,
                                     final boolean printOutput,
                                     final boolean printError)
    throws IOException, InterruptedException
{
    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec(commandLine);

    if (printOutput)
    {
        BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        System.out.println("Output:  " + outputReader.readLine());
    }

    if (printError)
    {
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        System.out.println("Error:  " + errorReader.readLine());
    }

    return process.waitFor();
}

Kann jemand einen guten Weg vorschlagen, einen Timeout-Parameter zu implementieren?

68
James Adams
public static int executeCommandLine(final String commandLine,
                                     final boolean printOutput,
                                     final boolean printError,
                                     final long timeout)
      throws IOException, InterruptedException, TimeoutException {
  Runtime runtime = Runtime.getRuntime();
  Process process = runtime.exec(commandLine);
  /* Set up process I/O. */
  ... 
  Worker worker = new Worker(process);
  worker.start();
  try {
    worker.join(timeout);
    if (worker.exit != null)
      return worker.exit;
    else
      throw new TimeoutException();
  } catch(InterruptedException ex) {
    worker.interrupt();
    Thread.currentThread().interrupt();
    throw ex;
  } finally {
    process.destroyForcibly();
  }
}

private static class Worker extends Thread {
  private final Process process;
  private Integer exit;
  private Worker(Process process) {
    this.process = process;
  }
  public void run() {
    try { 
      exit = process.waitFor();
    } catch (InterruptedException ignore) {
      return;
    }
  }  
}
52
erickson

Wenn Sie Java 8 oder höher verwenden, können Sie einfach das neue waitFor mit timeout verwenden:

Process p = ...
if(!p.waitFor(1, TimeUnit.MINUTES)) {
    //timeout - kill the process. 
    p.destroy(); // consider using destroyForcibly instead
}
65

Nach dem answer by erickson habe ich einen allgemeineren Weg geschaffen, um das gleiche zu tun.

public class ProcessWithTimeout extends Thread
{
    private Process m_process;
    private int m_exitCode = Integer.MIN_VALUE;

    public ProcessWithTimeout(Process p_process)
    {
        m_process = p_process;
    }

    public int waitForProcess(int p_timeoutMilliseconds)
    {
        this.start();

        try
        {
            this.join(p_timeoutMilliseconds);
        }
        catch (InterruptedException e)
        {
            this.interrupt();
        }

        return m_exitCode;
    }

    @Override
    public void run()
    {
        try
        { 
            m_exitCode = m_process.waitFor();
        }
        catch (InterruptedException ignore)
        {
            // Do nothing
        }
        catch (Exception ex)
        {
            // Unexpected exception
        }
    }
}

Jetzt müssen Sie nur noch Folgendes tun:

Process process = Runtime.getRuntime().exec("<your command goes here>");
ProcessWithTimeout processWithTimeout = new ProcessWithTimeout(process);
int exitCode = processWithTimeout.waitForProcess(5000);

if (exitCode == Integer.MIN_VALUE)
{
    // Timeout
}
else
{
    // No timeout !
}
15
Muzikant

Ich habe dies mit den drei vorgeschlagenen Ansätzen umgesetzt, die mit detaillierten Code-Beispielen geliefert wurden (ich bin ein Neuling mit Thread-Programmierung und diese Beispielcodes waren von unschätzbarem Wert. Ich würde mir immer noch den Kopf zerkratzen, wenn ich es nur auf Englisch erklären würde ohne Code).

Ich habe die Utility-Klasse, die ich dafür verwende, mit den drei Methoden implementiert, um einen Befehl mit einem Timeout auszuführen:

package com.abc.network.lifecycle.util;

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.concurrent.TimeUnit;
import Java.util.concurrent.TimeoutException;

import org.Apache.commons.logging.Log;
import org.Apache.commons.logging.LogFactory;

/**
 * Utility class for performing process related functions such as command line processing.
 */
public class ProcessUtility
{

    static Log log = LogFactory.getLog(ProcessUtility.class);

    /**
     * Thread class to be used as a worker
     */
    private static class Worker
        extends Thread
    {
        private final Process process;
        private Integer exitValue;

        Worker(final Process process)
        {
            this.process = process;
        }

        public Integer getExitValue()
        {
            return exitValue;
        }

        @Override
        public void run()
        {
            try
            {
                exitValue = process.waitFor();
            }
            catch (InterruptedException ignore)
            {
                return;
            }
        }
    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws Java.io.IOException
     * @throws Java.lang.InterruptedException
     */
    public static int executeCommandWithExecutors(final String command,
                                                  final boolean printOutput,
                                                  final boolean printError,
                                                  final long timeOut)
    {
        // validate the system and command line and get a system-appropriate command line 
        String massagedCommand = validateSystemAndMassageCommand(command);

        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            final Process process = runtime.exec(massagedCommand);

            // consume and display the error and output streams
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
            outputGobbler.start();
            errorGobbler.start();

            // create a Callable for the command's Process which can be called by an Executor 
            Callable<Integer> call = new Callable<Integer>()
            {
                public Integer call()
                    throws Exception
                {
                    process.waitFor();
                    return process.exitValue();
                }
            };

            // submit the command's call and get the result from a 
            Future<Integer> futureResultOfCall = Executors.newSingleThreadExecutor().submit(call);
            try
            {
                int exitValue = futureResultOfCall.get(timeOut, TimeUnit.MILLISECONDS);
                return exitValue;
            }
            catch (TimeoutException ex)
            {
                String errorMessage = "The command [" + command + "] timed out.";
                log.error(errorMessage, ex);
                throw new RuntimeException(errorMessage, ex);
            }
            catch (ExecutionException ex)
            {
                String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
                log.error(errorMessage, ex);
                throw new RuntimeException(errorMessage, ex);
            }
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws Java.io.IOException
     * @throws Java.lang.InterruptedException
     */
    public static int executeCommandWithSleep(final String command,
                                              final boolean printOutput,
                                              final boolean printError,
                                              final long timeOut)
    {
        // validate the system and command line and get a system-appropriate command line 
        String massagedCommand = validateSystemAndMassageCommand(command);

        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(massagedCommand);

            // consume and display the error and output streams
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
            outputGobbler.start();
            errorGobbler.start();

            // run a thread which will set a flag once it has slept for the timeout period
            final boolean[] flags = { true };
            new Thread()
            {
                @Override
                public void run()
                {
                    try
                    {
                        Thread.sleep(timeOut);
                    }
                    catch (InterruptedException ex)
                    {
                        String errorMessage = "Timeout loop thread unexpectedly interrupted.";
                        log.error(errorMessage, ex);
                        throw new RuntimeException(errorMessage, ex);
                    }
                    flags[0] = false;
                }
            }.start();

            // execute the command and wait 
            int returnValue = -1;
            while (flags[0] && (returnValue < 0))
            {
                returnValue = process.waitFor();
            }

            // if the command timed out then log it
            if (returnValue < 0)
            {
                log.warn("The command [" + command + "] did not complete before the timeout period expired (timeout: " +
                         timeOut + " ms)");
            }

            return returnValue;
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws Java.io.IOException
     * @throws Java.lang.InterruptedException
     */
    public static int executeCommandWithWorker(final String command,
                                               final boolean printOutput,
                                               final boolean printError,
                                               final long timeOut)
    {
        // validate the system and command line and get a system-appropriate command line 
        String massagedCommand = validateSystemAndMassageCommand(command);

        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(massagedCommand);

            // consume and display the error and output streams
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
            outputGobbler.start();
            errorGobbler.start();

            // create and start a Worker thread which this thread will join for the timeout period 
            Worker worker = new Worker(process);
            worker.start();
            try
            {
                worker.join(timeOut);
                Integer exitValue = worker.getExitValue();
                if (exitValue != null)
                {
                    // the worker thread completed within the timeout period
                    return exitValue;
                }

                // if we get this far then we never got an exit value from the worker thread as a result of a timeout 
                String errorMessage = "The command [" + command + "] timed out.";
                log.error(errorMessage);
                throw new RuntimeException(errorMessage);
            }
            catch (InterruptedException ex)
            {
                worker.interrupt();
                Thread.currentThread().interrupt();
                throw ex;
            }
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Validates that the system is running a supported OS and returns a system-appropriate command line.
     * 
     * @param originalCommand
     * @return
     */
    private static String validateSystemAndMassageCommand(final String originalCommand)
    {
        // make sure that we have a command
        if (originalCommand.isEmpty() || (originalCommand.length() < 1))
        {
            String errorMessage = "Missing or empty command line parameter.";
            log.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }

        // make sure that we are running on a supported system, and if so set the command line appropriately
        String massagedCommand;
        String osName = System.getProperty("os.name");
        if (osName.equals("Windows XP"))
        {
            massagedCommand = "cmd.exe /C " + originalCommand;
        }
        else if (osName.equals("Solaris") || osName.equals("SunOS") || osName.equals("Linux"))
        {
            massagedCommand = originalCommand;
        }
        else
        {
            String errorMessage = "Unable to run on this system which is not Solaris, Linux, or Windows XP (actual OS type: \'" +
                                  osName + "\').";
            log.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }

        return massagedCommand;
    }
}

Ich habe eine Klasse erstellt, um die Ausgabe- und Fehlerströme eines Befehls zu verbrauchen und anzuzeigen (entnommen aus http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 ):

package com.abc.network.lifecycle.util;

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.InputStreamReader;

import org.Apache.commons.logging.Log;
import org.Apache.commons.logging.LogFactory;

/**
 * Utility thread class which consumes and displays stream input.
 * 
 * Original code taken from http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
 */
class StreamGobbler
    extends Thread
{
    static private Log log = LogFactory.getLog(StreamGobbler.class);
    private InputStream inputStream;
    private String streamType;
    private boolean displayStreamOutput;

    /**
     * Constructor.
     * 
     * @param inputStream the InputStream to be consumed
     * @param streamType the stream type (should be OUTPUT or ERROR)
     * @param displayStreamOutput whether or not to display the output of the stream being consumed
     */
    StreamGobbler(final InputStream inputStream,
                  final String streamType,
                  final boolean displayStreamOutput)
    {
        this.inputStream = inputStream;
        this.streamType = streamType;
        this.displayStreamOutput = displayStreamOutput;
    }

    /**
     * Consumes the output from the input stream and displays the lines consumed if configured to do so.
     */
    @Override
    public void run()
    {
        try
        {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line = null;
            while ((line = bufferedReader.readLine()) != null)
            {
                if (displayStreamOutput)
                {
                    System.out.println(streamType + ">" + line);
                }
            }
        }
        catch (IOException ex)
        {
            log.error("Failed to successfully consume and display the input stream of type " + streamType + ".", ex);
            ex.printStackTrace();
        }
    }
}

Ich habe einen Testbefehl erstellt, der ungefähr 10 Sekunden dauert:

#!/bin/bash
sleep 10
echo 'TEST COMMAND RAN OK'

Dann habe ich ein Testprogramm erstellt, um die drei verschiedenen Methoden zu testen, wobei jede mit einem Timeout-Wert von 5 Sekunden aufgerufen wurde (Befehl sollte fehlschlagen) und mit einem Timeout-Wert von 15 Sekunden (Befehl sollte erfolgreich sein):

package com.abc.network.lifecycle.util;

public class ProcessUtilityTester
{

    /**
     * @param args
     */
    public static void main(final String[] args)
    {
        try
        {
            String command = args[0];
            int exitValue = -1;
            System.out.println("\n\n5000ms timeout With Executors:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 5000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
            System.out.println("\n\n5000ms timeout With Sleep:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 5000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
            System.out.println("\n\n5000ms timeout With Worker:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 5000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
            System.out.println("\n\n15000ms timeout With Executors:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 15000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
            System.out.println("\n\n15000ms timeout With Sleep:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 15000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
            System.out.println("\n\n15000ms timeout With Worker:");
            try
            {
                exitValue = -1;
                exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 15000);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            finally
            {
                System.out.println("\nExit value:" + exitValue);
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            System.exit(0);
        }
    }

}

Folgendes sehe ich, wenn ich das Testprogramm starte:

5000ms timeout With Executors:
May 1, 2009 1:55:19 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithExecutors
SEVERE: The command [/tmp/testcmd.sh] timed out.
Java.util.concurrent.TimeoutException
        at Java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.Java:228)
        at Java.util.concurrent.FutureTask.get(FutureTask.Java:91)
        at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.Java:179)
        at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.Java:19)
Java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
        at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.Java:186)
        at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.Java:19)
Caused by: Java.util.concurrent.TimeoutException
        at Java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.Java:228)
        at Java.util.concurrent.FutureTask.get(FutureTask.Java:91)
        at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.Java:179)
        ... 1 more

Exit value:-1


5000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK

Exit value:0


5000ms timeout With Worker:
May 1, 2009 1:55:34 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithWorker
SEVERE: The command [/tmp/testcmd.sh] timed out.
Java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
        at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithWorker(ProcessUtility.Java:338)
        at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.Java:47)

Exit value:-1


15000ms timeout With Executors:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK

Exit value:0


15000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK

Exit value:0


15000ms timeout With Worker:
OUTPUT>TEST COMMAND RAN OK

Exit value:0

Aus meiner Sicht funktioniert der Ansatz mit einer Worker-Thread-Klasse am besten, da er in beiden Fällen die erwarteten Ergebnisse liefert. Der Ansatz mit Executoren funktioniert ebenfalls wie erwartet, mit der Einschränkung, dass der Befehl im 15000ms-Timeout-Fall zweimal ausgeführt wird (d. H. Die Ausgabe für den Befehl wird zweimal angezeigt). Der Ansatz, der die sleep () -Methode verwendet, setzt den Befehl im Timeout-Fall von 5000 ms nicht wie erwartet ab und zeigt die Ausgabe zweimal an, führt den Befehl jedoch im 15000ms-Timeout-Fall wie erwartet aus.

11
James Adams

Für alle, die das Executor-Framework verwenden: Sie alle vergessen, den Executor herunterzufahren. Ändern Sie es also wie folgt:

ExecutorService service = Executors.newSingleThreadExecutor();
try {
    Future<Integer> ft = service.submit(call);
    try {
        int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
        return exitVal;
    } catch (TimeoutException to) {
        p.destroy();
        throw to;
    }
}
finally {
    service.shutdown();
}

Andernfalls behält Ihr Programm einen aktiven Nicht-Daemon-Thread bei. Stellen Sie sicher, dass Ihr Programm nicht beendet wird, bis Sie System.exit aufrufen

5
Rob Prime

Für diejenigen, die die neue Java 8-Methode waitFor(long timeout, TimeUnit unit) nicht verwenden können (da sie sich auf Android befinden oder einfach kein Upgrade durchführen können), können Sie sie einfach aus dem JDK-Quellcode kopieren und irgendwo in Ihre utils-Datei einfügen:

public boolean waitFor(long timeout, TimeUnit unit, final Process process)
            throws InterruptedException
    {
        long startTime = System.nanoTime();
        long rem = unit.toNanos(timeout);

        do {
            try {
                process.exitValue();
                return true;
            } catch(IllegalThreadStateException ex) {
                if (rem > 0)
                    Thread.sleep(
                            Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
            }
            rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
        } while (rem > 0);
        return false;
    }

Die einzige Änderung, die ich im Quellcode von JDK8 am Original vorgenommen habe, ist der Zusatz des Parameters Process, sodass wir die exitValue -Methode aus dem Prozess aufrufen können. 

Die exitValue -Methode versucht direkt, eine IllegalThreadStateException zurückzugeben oder auszulösen, wenn der Prozess noch nicht beendet ist. In diesem Fall warten wir das empfangene Timeout ab und kündigen.

Die Methode gibt einen booleschen Wert zurück. Wenn sie also false zurückgibt, wissen Sie, dass Sie den Prozess manuell beenden müssen.

Dieser Weg scheint einfacher zu sein als alles, was oben erwähnt wurde (erwarten Sie den direkten Aufruf von waitFor).

Eine leichte Lösung für kleine Apps:

public class Test {
    public static void main(String[] args) throws Java.io.IOException, InterruptedException {   
        Process process = new ProcessBuilder().command("sleep", "10").start();

        int i=0;
        boolean deadYet = false;
        do {
            Thread.sleep(1000);
            try {
                process.exitValue();
                deadYet = true;
            } catch (IllegalThreadStateException e) {
                System.out.println("Not done yet...");
                if (++i >= 5) throw new RuntimeException("timeout");
            }
        } while (!deadYet);
    }
}
3
Janus Troelsen

Implementieren Sie als Delegat, und schlagen Sie den Aufruf fehl, wenn der Abschluss über Ihrer Schwelle liegt.

2
Chris Ballance

Versuchen Sie es mit einem Timer (oder Sleep ()), in einem separaten Thread oder in Ihrer Ereigniswarteschlange, falls Sie einen haben.

2
Macke

Ich habe auch die Worker-Implementierung getestet und funktioniert wie ein Zauber. Im Bearbeitungsprozess Io habe ich Threads hinzugefügt, um Stde und Stdo zu behandeln. Wenn der Worker-Thread abgelaufen ist, verlasse ich auch die IO-Threads. 

Process p = Runtime.getRuntime().exec(cmd.trim());

            //setup error and output stream threads
            CommandStreamThread eStream = new CommandStreamThread(p.getErrorStream(), "STDE");            
            CommandStreamThread oStream = new CommandStreamThread(p.getInputStream(), "STDO");

            // kick them off
            eStream.start();
            oStream.start();

            //setup a worker thread so we can time it out when we need
            CommandWorkerThread worker=new CommandWorkerThread(p);
            worker.start();

            try {
                worker.join(this.getTimeout());
                if (worker.getExit() != null)
                    return worker.getExit();
                else
                    throw new TimeoutException("Timeout reached:"+this.getTimeout()+" ms");
            } catch(InterruptedException ex) {
                eStream.interrupt();
                oStream.interrupt();
                worker.interrupt();
                Thread.currentThread().interrupt();
                throw ex;
            } finally {
                p.destroy();
            }
2
Peco

Zunächst einige Hintergrundinformationen. Während der Ausführung eines Befehls trat ein Zeitlimit auf, da das Programm, das ich ausführen wollte, niemals Debug- oder Fehlerinformationen ausgab, wenn ein Fehler auftrat, und es intern immer wieder versuchte, was dazu führte, dass der Prozess stecken blieb weil beim erneuten Versuch nie ein Fehler oder ein Ausgabestream aufgetreten ist.

Also nach process.exec() oder process.start(),

Es würde für immer an dieser Linie stecken bleiben,

BufferedReader input = new BufferedReader(newInputStreamReader(process.getInputStream()));

Gemäß Java 1.8 mit der public boolean waitFor(long timeout,TimeUnit unit) -Methode sollte das Zeitlimit nach dem angegebenen Zeitlimit "ideal" abgelaufen sein, aber in meinem Fall kann es aus irgendeinem Grund sein, dass das Zeitlimit nicht abgelaufen ist, weil ich es war Ausführen der Anwendung als Windows-Dienst (Ich habe die Benutzerberechtigungen und alles auf dem Konto überprüft, aber es hat nicht geholfen).

Also habe ich versucht, es mit der folgenden Logik zu implementieren, bei der wir den Eingabestream mit input.ready() und einem Timeout-Flag weiter überprüfen. Diese einfache Lösung hat sich im Vergleich zu allen anderen als reizvoll erwiesen.

Code:

public boolean runCommand() throws IOException, InterruptedException, Exception {
    StringBuilder rawResponse = new StringBuilder();
    System.out.println("Running Command " + Arrays.toString(command));
    ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList(command));
    processBuilder.redirectErrorStream(true);
    Process process = processBuilder.start(); //Executing the process
    BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
    waitForTimeout(input, process); //Waiting for Timout
    String line;
    while ((line = input.readLine()) != null) {
        rawResponse.append(line).append("\n");
    }
    return true;
}


//Timeout method 
private void waitForTimeout(BufferedReader input, Process process) throws InterruptedException, Exception {
    int timeout = 5;
    while (timeout > 0) {
        if (input.ready()) {
            break;
        } else {
            timeout--;
            Thread.sleep(1000);
            if (timeout == 0 && !input.ready()) {
                destroyProcess(process);
                throw new Exception("Timeout in executing the command "+Arrays.toString(command));
            }
        }
    }
}
2
xxnations

Es gibt verschiedene Möglichkeiten, dies zu tun, aber ich würde in Betracht ziehen, einen Executor zu verwenden - er hilft Ihnen einfach, den Exit-Wert oder die Exception aus dem Thread an den ursprünglichen Aufrufer zurückzuleiten.

    final Process p = ...        
    Callable<Integer> call = new Callable<Integer>() {
    public Integer call() throws Exception {
        p.waitFor();
        return p.exitValue();
      }
    };
    Future<Integer> ft = Executors.newSingleThreadExecutor().submit(call);
    try {
      int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
      return exitVal;
    } catch (TimeoutException to) {
      p.destroy();
      throw to;
    }

Ich denke, Sie können die Bedingung des Rennens nicht umgehen, wodurch das Warten abläuft und der Prozess dann beendet wird, bevor Sie destroy () aufrufen.

2
Neil Coffey

Ich weiß, das ist ein wirklich alter Beitrag. Ich brauchte Hilfe bei einem ähnlichen Projekt, also dachte ich, ich könnte etwas von meinem Code geben, den ich gearbeitet habe, und denen, die funktionieren.

long current = System.currentTimeMillis();

ProcessBuilder pb  = new ProcessBuilder(arguments);
try{
    pb.redirectErrorStream(true);
    process = pb.start();
    int c ;
    while((c = process.getInputStream().read()) != -1 )
        if(System.currentTimeMillis() - current < timeOutMilli) 
            result += (char)c;
        else throw new Exception();
    return result.trim();
    }catch(Exception e){
        e.printStackTrace();
    }
    return result;

Hoffe, das hilft der Zukunft: D

0
sabbibJAVA

Apache Commons Exec kann Ihnen dabei helfen.

Siehe http://commons.Apache.org/proper/commons-exec/tutorial.html

String line = "your command line";
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
executor.setWatchdog(watchdog);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
executor.setStreamHandler(streamHandler);
int exitValue = executor.execute(cmdLine);
System.out.println(exitValue);
System.out.println(outputStream.toString());
0
xxg

Sie können einen Thread starten, der sich für die gewünschte Zeit im Ruhezustand befindet, und nach dem Schlaf ein Boolean ändern, für das Sie eine Schleife in der Methode executeCommandLine erstellen.

So etwas (weder getestet noch kompiliert, diese Lösung ist ein Prototyp, den Sie umgestalten sollten, wenn Sie es benötigen):

public static int executeCommandLine(final String commandLine,
                                     final boolean printOutput,
                                     final boolean printError)
    throws IOException, InterruptedException
{
    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec(commandLine);

    if (printOutput)
    {
        BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        System.out.println("Output:  " + outputReader.readLine());
    }

    if (printError)
    {
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        System.out.println("Error:  " + errorReader.readLine());
    }

    ret = -1;
    final[] b = {true};
    new Thread(){
       public void run(){
           Thread.sleep(2000); //to adapt
           b[0] = false;
       }
    }.start();
    while(b[0])
    {
          ret = process.waitFor();
    }

    return ret;
}
0

Bei Verwendung von Java 8 würde ich mit Aleksander Blomskøld antworten, d. H. P.waitFor (1, TimeUnit.MINUTE)

wenn Sie Java 6/7 verwenden und Swing verwenden, können Sie einen SwingWorker verwenden: 

   final Process process = ...
   SwingWorker<Integer, Integer> sw = new SwingWorker<>() {
       @Override
       protected Integer doInBackground() throws Exception {
          process.waitFor();
          return process.exitValue();
       }
   };
   sw.execute();                
   int exitValue = sw.get(1, TimeUnit.SECONDS);
   if (exitValue == 0) {
       //everything was fine
   } else {
       //process exited with issues
   }
0
sproger

und hier ist das StreamThread

public class CommandStreamThread extends Thread{
        private InputStream iStream;
        private String cPrompt;

        CommandStreamThread (InputStream is, String cPrompt)
        {
            this.iStream = is;
            this.cPrompt = cPrompt;
        }

        public void run()
        {
            try
            {
                InputStreamReader streamReader= new InputStreamReader(this.iStream);
                BufferedReader reader = new BufferedReader(streamReader);


                String linesep=System.getProperty("line.separator");
                String line=null;
                while ((line=reader.readLine())!=null){
                    System.out.println(line);
                    //Process the next line seperately in case this is EOF is not preceded by EOL
                    int in;
                    char[] buffer=new char[linesep.length()];
                    while ( (in = reader.read(buffer)) != -1){
                        String bufferValue=String.valueOf(buffer, 0, in);
                        System.out.print(bufferValue);
                        if (bufferValue.equalsIgnoreCase(linesep))
                            break;
                    }
                }

                //Or the easy way out with commons utils!
                //IOUtils.copy(this.iStream, System.out);


              } catch (Exception e){
                    e.printStackTrace();  
              }
        }

        public InputStream getIStream() {
            return iStream;
        }

        public void setIStream(InputStream stream) {
            iStream = stream;
        }

        public String getCPrompt() {
            return cPrompt;
        }

        public void setCPrompt(String Prompt) {
            cPrompt = Prompt;
        }


}
0
Peco