util: redirect stderr to stdout to spawn fewer threads; reuse BaseOutputReader (IDEA...
authorSergey Simonchik <sergey.simonchik@jetbrains.com>
Tue, 17 Nov 2015 17:05:31 +0000 (20:05 +0300)
committerSergey Simonchik <sergey.simonchik@jetbrains.com>
Tue, 17 Nov 2015 17:05:31 +0000 (20:05 +0300)
platform/util/src/com/intellij/util/EnvironmentUtil.java

index 089e23005b4812ab7576617056e7fc9c68d6c29f..e3aec2e334dcf1b5c6482e428a4a3b45b56d7c43 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.CharsetToolkit;
 import com.intellij.util.concurrency.FixedFuture;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.CharsetToolkit;
 import com.intellij.util.concurrency.FixedFuture;
+import com.intellij.util.io.BaseOutputReader;
 import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
 import gnu.trove.THashMap;
 import org.jetbrains.annotations.NotNull;
 import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
 import gnu.trove.THashMap;
 import org.jetbrains.annotations.NotNull;
@@ -167,17 +168,17 @@ public class EnvironmentUtil {
       String[] command = {shell, "-l", "-i", "-c", ("'" + reader.getAbsolutePath() + "' '" + envFile.getAbsolutePath() + "'")};
       LOG.info("loading shell env: " + StringUtil.join(command, " "));
 
       String[] command = {shell, "-l", "-i", "-c", ("'" + reader.getAbsolutePath() + "' '" + envFile.getAbsolutePath() + "'")};
       LOG.info("loading shell env: " + StringUtil.join(command, " "));
 
-      Process process = Runtime.getRuntime().exec(command);
-      StreamGobbler stdoutGobbler = new StreamGobbler(process.getInputStream(), "stdout");
-      stdoutGobbler.start();
-      StreamGobbler stderrGobbler = new StreamGobbler(process.getErrorStream(), "stderr");
-      stderrGobbler.start();
+      ProcessBuilder processBuilder = new ProcessBuilder(command);
+      processBuilder.redirectErrorStream(true);
+      Process process = processBuilder.start();
+
+      StreamGobbler gobbler = new StreamGobbler(process.getInputStream());
       int rv = waitAndTerminateAfter(process, SHELL_ENV_READING_TIMEOUT);
       int rv = waitAndTerminateAfter(process, SHELL_ENV_READING_TIMEOUT);
-      stdoutGobbler.logIfNotEmpty();
-      stderrGobbler.logIfNotEmpty();
+      gobbler.stop();
 
       String lines = FileUtil.loadFile(envFile);
       if (rv != 0 || lines.isEmpty()) {
 
       String lines = FileUtil.loadFile(envFile);
       if (rv != 0 || lines.isEmpty()) {
+        LOG.info("shell process output: " + StringUtil.trimEnd(gobbler.getText(), '\n'));
         throw new Exception("rv:" + rv + " text:" + lines.length());
       }
       return parseEnv(lines);
         throw new Exception("rv:" + rv + " text:" + lines.length());
       }
       return parseEnv(lines);
@@ -300,67 +301,33 @@ public class EnvironmentUtil {
     }
   }
 
     }
   }
 
-  private static class StreamGobbler implements Runnable {
-    private final BufferedReader myReader;
-    private final String myStreamType;
-    private final StringBuffer myBuffer = new StringBuffer();
-    private final Semaphore myFinishSemaphore = new Semaphore(0);
+  private static class StreamGobbler extends BaseOutputReader {
 
 
-    public StreamGobbler(@NotNull InputStream stream, @NotNull String streamType) {
-      myReader = new BufferedReader(new InputStreamReader(stream));
-      myStreamType = streamType;
-    }
+    private final StringBuffer myBuffer;
 
 
-    public void start() {
-      Thread thread = new Thread(this, "shell process " + myStreamType + " reader");
-      thread.start();
+    public StreamGobbler(@NotNull InputStream stream) {
+      super(stream, CharsetToolkit.getDefaultSystemCharset());
+      myBuffer = new StringBuffer();
+      start("stdout/stderr streams of shell env loading process");
     }
 
     }
 
+    @NotNull
     @Override
     @Override
-    public void run() {
-      int ch;
-      try {
-        while ((ch = myReader.read()) != -1) {
-          myBuffer.append((char)ch);
-        }
-      }
-      catch (IOException e) {
-        LOG.warn("Error reading shell process " + myStreamType, e);
-      }
-      finally {
-        myFinishSemaphore.release(1);
-        close();
-      }
+    protected Future<?> executeOnPooledThread(@NotNull Runnable runnable) {
+      ExecutorService executor = ConcurrencyUtil.newSingleThreadExecutor("shell process streams gobbler");
+      Future<?> future = executor.submit(runnable);
+      executor.shutdown();
+      return future;
     }
 
     }
 
-    private void close() {
-      try {
-        myReader.close();
-      }
-      catch (IOException e) {
-        LOG.warn("Error closing shell process " + myStreamType, e);
-      }
+    @Override
+    protected void onTextAvailable(@NotNull String text) {
+      myBuffer.append(text);
     }
 
     @NotNull
     }
 
     @NotNull
-    private String getText() {
-      try {
-        if (!myFinishSemaphore.tryAcquire(1, 2, TimeUnit.SECONDS)) {
-          LOG.warn("closing shell process " + myStreamType + " forcibly");
-          close();
-        }
-      }
-      catch (InterruptedException e) {
-        LOG.info(e);
-      }
+    public String getText() {
       return myBuffer.toString();
     }
       return myBuffer.toString();
     }
-
-    public void logIfNotEmpty() {
-      String text = getText();
-      if (!text.isEmpty()) {
-        LOG.info("shell process " + myStreamType + ":" + StringUtil.trimEnd(text, '\n'));
-      }
-    }
   }
 }
   }
 }