From 3b077539f1b5224a89d7e8d2b03d041ef6045f43 Mon Sep 17 00:00:00 2001
From: ribeaudc <ribeaudc>
Date: Thu, 22 May 2008 10:51:33 +0000
Subject: [PATCH] change: - 'ProcessResult' should no longer access the
 Process.

SVN: 6244
---
 .../process/ProcessExecutionHelper.java       |  63 ++++++++---
 .../cisd/common/process/ProcessResult.java    | 103 +++++++++---------
 2 files changed, 97 insertions(+), 69 deletions(-)

diff --git a/common/source/java/ch/systemsx/cisd/common/process/ProcessExecutionHelper.java b/common/source/java/ch/systemsx/cisd/common/process/ProcessExecutionHelper.java
index 86d6d774ce8..851d68e3bfd 100644
--- a/common/source/java/ch/systemsx/cisd/common/process/ProcessExecutionHelper.java
+++ b/common/source/java/ch/systemsx/cisd/common/process/ProcessExecutionHelper.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.commons.io.IOUtils;
@@ -202,45 +203,58 @@ public final class ProcessExecutionHelper
     {
         assert millisoWaitForCompletion > 0L : "Unspecified time out.";
         final ProcessWatchdog processWatchdog = new ProcessWatchdog(millisoWaitForCompletion);
+        Process process = null;
         try
         {
-            final Process process = launchProcess(commandLine);
+            process = launchProcess(commandLine);
             processWatchdog.start(process);
             try
             {
                 process.waitFor();
                 processWatchdog.stop();
+                return createResult(commandLine, process, processWatchdog.isProcessKilled(),
+                        readProcessOutputLines(process, machineLog));
             } catch (final InterruptedException e)
             {
-                process.destroy();
                 operationLog.warn(String.format("Execution of %s interrupted after timeout.",
                         commandLine));
+                return createResult(commandLine, process, processWatchdog.isProcessKilled(),
+                        Collections.<String> emptyList());
             }
-            return createResult(commandLine, process, processWatchdog.isProcessKilled());
         } catch (final IOException ex)
         {
             return createNotStartedResult(commandLine, ex);
+        } finally
+        {
+            closeStreams(process);
+            process.destroy();
         }
     }
 
     private final ProcessResult runWithoutWatchdog(final List<String> commandLine)
     {
+        Process process = null;
         try
         {
-            final Process process = launchProcess(commandLine);
+            process = launchProcess(commandLine);
             try
             {
                 process.waitFor();
+                return createResult(commandLine, process, false, readProcessOutputLines(process,
+                        machineLog));
             } catch (final InterruptedException e)
             {
-                process.destroy();
                 operationLog.warn(String.format("Execution of %s interrupted after timeout.",
                         commandLine));
+                return createResult(commandLine, process, true, Collections.<String> emptyList());
             }
-            return createResult(commandLine, process, false);
         } catch (final IOException ex)
         {
             return createNotStartedResult(commandLine, ex);
+        } finally
+        {
+            closeStreams(process);
+            process.destroy();
         }
     }
 
@@ -252,11 +266,7 @@ public final class ProcessExecutionHelper
         {
             operationLog.debug("Executing command: " + commandLine);
         }
-        // NOTE 2008-02-04, Tomasz Pylak: This operation can get blocked. I've observed it when ln
-        // was executed on NAS
-        // file system mounted locally.
-        final Process process = processBuilder.start();
-        return process;
+        return processBuilder.start();
     }
 
     private final ProcessResult createNotStartedResult(final List<String> commandLine,
@@ -267,20 +277,29 @@ public final class ProcessExecutionHelper
     }
 
     private final ProcessResult createResult(final List<String> commandLine,
-            final Process processOrNull, final boolean isInterrupted)
+            final Process processOrNull, final boolean isInterrupted, final List<String> outputLines)
     {
         if (processOrNull == null)
         {
             return ProcessResult.createNotStarted(commandLine, operationLog, machineLog);
         } else
         {
+            final List<String> lines;
+            if (outputLines == null)
+            {
+                lines = readProcessOutputLines(processOrNull, machineLog);
+            } else
+            {
+                lines = outputLines;
+            }
             if (isInterrupted)
             {
                 return ProcessResult.createWaitingInterrupted(processOrNull, commandLine,
-                        operationLog, machineLog);
+                        operationLog, machineLog, lines);
             } else
             {
-                return ProcessResult.create(processOrNull, commandLine, operationLog, machineLog);
+                return ProcessResult.create(processOrNull, commandLine, operationLog, machineLog,
+                        lines);
             }
         }
     }
@@ -296,7 +315,6 @@ public final class ProcessExecutionHelper
             result = runWithoutWatchdog(cmd);
         }
         result.log();
-        result.destroyProcess();
         return result.isOK();
     }
 
@@ -352,4 +370,19 @@ public final class ProcessExecutionHelper
             }
         }
     }
+
+    /**
+     * Close the streams belonging to given <var>Process</var>.
+     */
+    private final static void closeStreams(final Process processOrNull)
+    {
+        if (processOrNull == null)
+        {
+            return;
+        }
+        IOUtils.closeQuietly(processOrNull.getInputStream());
+        IOUtils.closeQuietly(processOrNull.getOutputStream());
+        IOUtils.closeQuietly(processOrNull.getErrorStream());
+    }
+
 }
diff --git a/common/source/java/ch/systemsx/cisd/common/process/ProcessResult.java b/common/source/java/ch/systemsx/cisd/common/process/ProcessResult.java
index e52b3a66ab6..830bd4e5a2d 100644
--- a/common/source/java/ch/systemsx/cisd/common/process/ProcessResult.java
+++ b/common/source/java/ch/systemsx/cisd/common/process/ProcessResult.java
@@ -17,7 +17,7 @@
 package ch.systemsx.cisd.common.process;
 
 import java.io.File;
-import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.log4j.Level;
@@ -28,11 +28,10 @@ import org.apache.log4j.Logger;
  * <p>
  * Since the process output can only ever be read once from a process, it need to be kept around if
  * it is needed more than once. This is what this class is good for.
+ * </p>
  */
 public final class ProcessResult
 {
-    private final Process processOrNull; // null if not started
-
     private final boolean hasBlocked;
 
     private final List<String> commandLine;
@@ -43,63 +42,69 @@ public final class ProcessResult
 
     private final Logger machineLog;
 
-    private List<String> outputLines;
+    private final int exitValue;
 
-    // process finished or was terminated after timeout
-    public static ProcessResult create(final Process process, final List<String> commandLine,
-            final Logger operationLog, final Logger machineLog)
+    private final List<String> outputLines;
+
+    private final boolean run;
+
+    /**
+     * Creates a <code>ProcessResult</code> for a process which normally terminates.
+     */
+    public final static ProcessResult create(final Process process, final List<String> commandLine,
+            final Logger operationLog, final Logger machineLog, final List<String> outputLines)
     {
-        return new ProcessResult(process, false, commandLine, operationLog, machineLog);
+        return new ProcessResult(process, false, commandLine, operationLog, machineLog, outputLines);
     }
 
-    // process could not start at all
+    /**
+     * Creates a <code>ProcessResult</code> for a process which did not start at all.
+     */
     public static ProcessResult createNotStarted(final List<String> commandLine,
             final Logger operationLog, final Logger machineLog)
     {
-        return new ProcessResult(null, false, commandLine, operationLog, machineLog);
+        return new ProcessResult(null, false, commandLine, operationLog, machineLog, Collections
+                .<String> emptyList());
     }
 
-    // process started, but was blocked and could not be terminated, so we stopped waiting for it
+    /**
+     * Creates a <code>ProcessResult</code> for a process which blocked and could not be
+     * terminated. So we stopped waiting for it.
+     */
     public static ProcessResult createWaitingInterrupted(final Process process,
-            final List<String> commandLine, final Logger operationLog, final Logger machineLog)
+            final List<String> commandLine, final Logger operationLog, final Logger machineLog,
+            final List<String> outputLines)
     {
-        return new ProcessResult(process, true, commandLine, operationLog, machineLog);
+        return new ProcessResult(process, true, commandLine, operationLog, machineLog, outputLines);
     }
 
     private ProcessResult(final Process processOrNull, final boolean hasBlocked,
-            final List<String> commandLine, final Logger operationLog, final Logger machineLog)
+            final List<String> commandLine, final Logger operationLog, final Logger machineLog,
+            final List<String> outputLines)
     {
         this.commandLine = commandLine;
         this.commandName = new File(commandLine.get(0)).getName();
-        this.processOrNull = processOrNull;
         this.hasBlocked = hasBlocked;
         this.operationLog = operationLog;
         this.machineLog = machineLog;
-        this.outputLines = null;
+        this.exitValue = getExitValue(processOrNull, hasBlocked);
+        this.outputLines = outputLines;
+        this.run = processOrNull != null;
     }
 
-    /**
-     * Calls the {@link Process#destroy()} method which explicitly closes some file handles.
-     * <p>
-     * <i>Note that one must not call {#link {@link #getProcessOutput()} for the first time after
-     * this method has been called.</i>
-     * <p>
-     * Whether it is necessary to call this method depends on the JRE. For some JREs there occur
-     * {@link IOException}s with code 24 ("Too many open files") when running processes with high
-     * frequency without calling this method.
-     */
-    public void destroyProcess()
+    private final static int getExitValue(final Process processOrNull, final boolean hasBlocked)
     {
-        if (processOrNull != null)
+        if (processOrNull != null && hasBlocked == false)
         {
-            processOrNull.destroy();
+            return processOrNull.exitValue();
         }
+        return ProcessExecutionHelper.NO_EXIT_VALUE;
     }
 
     /**
      * Returns the command line that belongs to this process.
      */
-    public List<String> getCommandLine()
+    public final List<String> getCommandLine()
     {
         return commandLine;
     }
@@ -107,7 +112,7 @@ public final class ProcessResult
     /**
      * Returns the name of the command that belongs to this process.
      */
-    public String getCommandName()
+    public final String getCommandName()
     {
         return commandName;
     }
@@ -115,27 +120,17 @@ public final class ProcessResult
     /**
      * Returns the lines of the process output.
      */
-    public List<String> getProcessOutput()
+    public final List<String> getProcessOutput()
     {
-        if (outputLines == null)
-        {
-            outputLines = ProcessExecutionHelper.readProcessOutputLines(processOrNull, machineLog);
-        }
         return outputLines;
     }
 
-    public int exitValue()
+    public final int exitValue()
     {
-        if (hasBlocked == false && processOrNull != null)
-        {
-            return processOrNull.exitValue();
-        } else
-        {
-            return ProcessExecutionHelper.NO_EXIT_VALUE;
-        }
+        return exitValue;
     }
 
-    public boolean isOK()
+    public final boolean isOK()
     {
         return exitValue() == ProcessExecutionHelper.EXIT_VALUE_OK;
     }
@@ -143,31 +138,31 @@ public final class ProcessResult
     /**
      * Returns <code>true</code> if the process has been run at all.
      */
-    public boolean isRun()
+    public final boolean isRun()
     {
-        return processOrNull != null;
+        return run;
     }
 
     /**
      * Returns <code>true</code> if the process could not been terminated after the timeout and we
      * stopped waiting for it.
      */
-    public boolean hasBlocked()
+    public final boolean hasBlocked()
     {
         return hasBlocked;
     }
 
     /**
-     * Returns <code>true</code> if the process has been terminated on the Operating System level.
+     * Returns <code>true</code> if the process has been terminated on the <i>Operating System</i>
+     * level.
      */
-    public boolean isTerminated()
+    public final boolean isTerminated()
     {
         return ProcessExecutionHelper.isProcessTerminated(exitValue());
     }
 
-    public void log()
+    public final void log()
     {
-
         if (isOK() == false)
         {
             logProcessExitValue(Level.WARN);
@@ -179,7 +174,7 @@ public final class ProcessResult
         }
     }
 
-    private void logProcessExitValue(final Level logLevel)
+    private final void logProcessExitValue(final Level logLevel)
     {
         if (isRun() == false)
         {
@@ -199,7 +194,7 @@ public final class ProcessResult
         }
     }
 
-    private void logProcessOutput(final Level logLevel)
+    private final void logProcessOutput(final Level logLevel)
     {
         assert logLevel != null;
 
-- 
GitLab