From 9de7aff540beabd77ee0cac2ae6b9fa7e6d60638 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 25 Oct 2011 19:02:35 +0200 Subject: [PATCH] Extracted base functionality from OSProcessHandler; moved relevant classes to 'util' module --- .../intellij/compiler/JpsServerManager.java | 6 +- .../intellij/execution/OutputListener.java | 1 - .../intellij/tools/ToolProcessAdapter.java | 5 +- .../execution/process/OSProcessHandler.java | 299 +--------------- .../process/BaseOSProcessHandler.java | 331 ++++++++++++++++++ .../execution/process/ProcessAdapter.java | 0 .../execution/process/ProcessEvent.java | 0 .../execution/process/ProcessHandler.java | 0 .../execution/process/ProcessListener.java | 0 .../execution/process/ProcessOutputTypes.java | 0 .../uiDesigner/actions/PreviewFormAction.java | 1 - 11 files changed, 346 insertions(+), 297 deletions(-) create mode 100644 platform/util/src/com/intellij/execution/process/BaseOSProcessHandler.java rename platform/{platform-api => util}/src/com/intellij/execution/process/ProcessAdapter.java (100%) rename platform/{platform-api => util}/src/com/intellij/execution/process/ProcessEvent.java (100%) rename platform/{platform-api => util}/src/com/intellij/execution/process/ProcessHandler.java (100%) rename platform/{platform-api => util}/src/com/intellij/execution/process/ProcessListener.java (100%) rename platform/{platform-api => util}/src/com/intellij/execution/process/ProcessOutputTypes.java (100%) diff --git a/java/compiler/impl/src/com/intellij/compiler/JpsServerManager.java b/java/compiler/impl/src/com/intellij/compiler/JpsServerManager.java index 52280823727a..242f7f260676 100644 --- a/java/compiler/impl/src/com/intellij/compiler/JpsServerManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/JpsServerManager.java @@ -175,7 +175,11 @@ public class JpsServerManager implements ApplicationComponent{ final int port = NetUtils.findAvailableSocketPort(); final Process process = launchServer(port); - final OSProcessHandler processHandler = new OSProcessHandler(process, null); + final OSProcessHandler processHandler = new OSProcessHandler(process, null) { + protected boolean shouldDestroyProcessRecursively() { + return true; + } + }; final Ref serverStartMessage = new Ref(null); final Semaphore semaphore = new Semaphore(); semaphore.down(); diff --git a/platform/lang-impl/src/com/intellij/execution/OutputListener.java b/platform/lang-impl/src/com/intellij/execution/OutputListener.java index a10658238d6b..35ac3a13e87b 100644 --- a/platform/lang-impl/src/com/intellij/execution/OutputListener.java +++ b/platform/lang-impl/src/com/intellij/execution/OutputListener.java @@ -4,7 +4,6 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.util.Key; -import org.apache.commons.lang.text.StrBuilder; import org.jetbrains.annotations.NotNull; /** diff --git a/platform/lang-impl/src/com/intellij/tools/ToolProcessAdapter.java b/platform/lang-impl/src/com/intellij/tools/ToolProcessAdapter.java index 1f1f6febcc49..aeed409c935f 100644 --- a/platform/lang-impl/src/com/intellij/tools/ToolProcessAdapter.java +++ b/platform/lang-impl/src/com/intellij/tools/ToolProcessAdapter.java @@ -17,10 +17,9 @@ package com.intellij.tools; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectBundle; -import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.WindowManager; diff --git a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java index 790997889aa3..0870c5b6dd9c 100644 --- a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java @@ -19,46 +19,19 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.encoding.EncodingManager; -import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.concurrent.*; +import java.util.concurrent.Future; -public class OSProcessHandler extends ProcessHandler { +public class OSProcessHandler extends BaseOSProcessHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.OSProcessHandler"); - @NotNull - private final Process myProcess; - - @Nullable - private final String myCommandLine; - - private final ProcessWaitFor myWaitFor; - - private static class ExecutorServiceHolder { - private static final ExecutorService ourThreadExecutorsService = createServiceImpl(); - - private static ThreadPoolExecutor createServiceImpl() { - return new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() { - @SuppressWarnings({"HardCodedStringLiteral"}) - public Thread newThread(Runnable r) { - return new Thread(r, "OSProcessHandler pooled thread"); - } - }); - } + public OSProcessHandler(@NotNull final Process process, @Nullable final String commandLine) { + super(process, commandLine, EncodingManager.getInstance().getDefaultCharset()); } - /** - * Override this method in order to execute the task with a custom pool - * - * @param task a task to run - */ + @Override protected Future executeOnPooledThread(Runnable task) { final Application application = ApplicationManager.getApplication(); @@ -66,133 +39,7 @@ public class OSProcessHandler extends ProcessHandler { return application.executeOnPooledThread(task); } - return ExecutorServiceHolder.ourThreadExecutorsService.submit(task); - } - - public OSProcessHandler(@NotNull final Process process, @Nullable final String commandLine) { - myProcess = process; - myCommandLine = commandLine; - myWaitFor = new ProcessWaitFor(process); - } - - private class ProcessWaitFor { - private final Future myWaitForThreadFuture; - private final BlockingQueue> myTerminationCallback = new ArrayBlockingQueue>(1); - - public void detach() { - myWaitForThreadFuture.cancel(true); - } - - - public ProcessWaitFor(final Process process) { - myWaitForThreadFuture = executeOnPooledThread(new Runnable() { - public void run() { - int exitCode = 0; - try { - while (true) { - try { - exitCode = process.waitFor(); - break; - } - catch (InterruptedException e) { - LOG.debug(e); - } - } - } - finally { - try { - myTerminationCallback.take().consume(exitCode); - } - catch (InterruptedException e) { - LOG.info(e); - } - } - } - }); - } - - public void setTerminationCallback(Consumer r) { - myTerminationCallback.offer(r); - } - } - - public Process getProcess() { - return myProcess; - } - - public void startNotify() { - final ReadProcessThread stdoutThread = new ReadProcessThread(createProcessOutReader()) { - protected void textAvailable(String s) { - notifyTextAvailable(s, ProcessOutputTypes.STDOUT); - } - }; - - final ReadProcessThread stderrThread = new ReadProcessThread(createProcessErrReader()) { - protected void textAvailable(String s) { - notifyTextAvailable(s, ProcessOutputTypes.STDERR); - } - }; - - if (myCommandLine != null) { - notifyTextAvailable(myCommandLine + '\n', ProcessOutputTypes.SYSTEM); - } - - addProcessListener(new ProcessAdapter() { - public void startNotified(final ProcessEvent event) { - try { - final Future stdOutReadingFuture = executeOnPooledThread(stdoutThread); - final Future stdErrReadingFuture = executeOnPooledThread(stderrThread); - - myWaitFor.setTerminationCallback(new Consumer() { - @Override - public void consume(Integer exitCode) { - try { - // tell threads that no more attempts to read process' output should be made - stderrThread.setProcessTerminated(true); - stdoutThread.setProcessTerminated(true); - - stdErrReadingFuture.get(); - stdOutReadingFuture.get(); - } - catch (InterruptedException ignored) { - } - catch (ExecutionException e) { - LOG.error(e); - } - finally { - onOSProcessTerminated(exitCode); - } - } - }); - } - finally { - removeProcessListener(this); - } - } - }); - - super.startNotify(); - } - - protected void onOSProcessTerminated(final int exitCode) { - notifyProcessTerminated(exitCode); - } - - protected Reader createProcessOutReader() { - return new InputStreamReader(myProcess.getInputStream(), getCharset()); - } - - protected Reader createProcessErrReader() { - return new InputStreamReader(myProcess.getErrorStream(), getCharset()); - } - - protected void destroyProcessImpl() { - try { - closeStreams(); - } - finally { - doDestroyProcess(); - } + return super.executeOnPooledThread(task); } protected boolean shouldDestroyProcessRecursively(){ @@ -207,141 +54,12 @@ public class OSProcessHandler extends ProcessHandler { final Process process = getProcess(); if (shouldDestroyProcessRecursively()) { killProcessTree(process); - } else { + } + else { process.destroy(); } } - protected void detachProcessImpl() { - final Runnable runnable = new Runnable() { - public void run() { - closeStreams(); - - myWaitFor.detach(); - notifyProcessDetached(); - } - }; - - executeOnPooledThread(runnable); - } - - protected void closeStreams() { - try { - myProcess.getOutputStream().close(); - } - catch (IOException e) { - LOG.error(e); - } - } - - public boolean detachIsDefault() { - return false; - } - - public OutputStream getProcessInput() { - return myProcess.getOutputStream(); - } - - // todo: to remove - @Nullable - public String getCommandLine() { - return myCommandLine; - } - - - public Charset getCharset() { - return EncodingManager.getInstance().getDefaultCharset(); - } - - private abstract static class ReadProcessThread implements Runnable { - private final Reader myReader; - private boolean skipLF = false; - - private boolean myIsProcessTerminated = false; - private final char[] myBuffer = new char[8192]; - - public ReadProcessThread(final Reader reader) { - myReader = reader; - } - - public synchronized void setProcessTerminated(boolean isProcessTerminated) { - myIsProcessTerminated = isProcessTerminated; - } - - public void run() { - try { - while (true) { - final int rc = readAvailable(); - if (rc == DONE) break; - Thread.sleep(rc == READ_SOME ? 1 : 5); // give other threads a chance - } - } - catch (InterruptedException ignore) { - } - catch (IOException e) { - LOG.info(e); - } - catch (Exception e) { - LOG.error(e); - } - } - - private static final int DONE = 0; - private static final int READ_SOME = 1; - private static final int READ_NONE = 2; - - private synchronized int readAvailable() throws IOException { - char[] buffer = myBuffer; - StringBuilder token = new StringBuilder(); - int rc = READ_NONE; - while (myReader.ready()) { - int n = myReader.read(buffer); - if (n <= 0) break; - rc = READ_SOME; - - for (int i = 0; i < n; i++) { - char c = buffer[i]; - if (skipLF && c != '\n') { - token.append('\r'); - } - - if (c == '\r') { - skipLF = true; - } - else { - skipLF = false; - token.append(c); - } - - if (c == '\n') { - textAvailable(token.toString()); - token.setLength(0); - } - } - } - - if (token.length() != 0) { - textAvailable(token.toString()); - token.setLength(0); - } - - if (myIsProcessTerminated) { - try { - myReader.close(); - } - catch (IOException e1) { - // supressed - } - - return DONE; - } - - return rc; - } - - protected abstract void textAvailable(final String s); - } - /** * Kill whole process tree. * @param process Process @@ -357,5 +75,4 @@ public class OSProcessHandler extends ProcessHandler { } return destroyed; } - } diff --git a/platform/util/src/com/intellij/execution/process/BaseOSProcessHandler.java b/platform/util/src/com/intellij/execution/process/BaseOSProcessHandler.java new file mode 100644 index 000000000000..361ef73d0811 --- /dev/null +++ b/platform/util/src/com/intellij/execution/process/BaseOSProcessHandler.java @@ -0,0 +1,331 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.process; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.concurrent.*; + +public class BaseOSProcessHandler extends ProcessHandler { + private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.OSProcessHandlerBase"); + @NotNull + protected final Process myProcess; + @Nullable + protected final String myCommandLine; + protected final ProcessWaitFor myWaitFor; + @Nullable + private final Charset myCharset; + + public BaseOSProcessHandler(@NotNull final Process process, @Nullable final String commandLine, @Nullable Charset charset) { + myProcess = process; + myCommandLine = commandLine; + myCharset = charset; + myWaitFor = new ProcessWaitFor(process); + } + + /** + * Override this method in order to execute the task with a custom pool + * + * @param task a task to run + */ + protected Future executeOnPooledThread(Runnable task) { + return ExecutorServiceHolder.ourThreadExecutorsService.submit(task); + } + + public Process getProcess() { + return myProcess; + } + + public void startNotify() { + final ReadProcessThread stdoutThread = new ReadProcessThread(createProcessOutReader()) { + protected void textAvailable(String s) { + notifyTextAvailable(s, ProcessOutputTypes.STDOUT); + } + }; + + final ReadProcessThread stderrThread = new ReadProcessThread(createProcessErrReader()) { + protected void textAvailable(String s) { + notifyTextAvailable(s, ProcessOutputTypes.STDERR); + } + }; + + if (myCommandLine != null) { + notifyTextAvailable(myCommandLine + '\n', ProcessOutputTypes.SYSTEM); + } + + addProcessListener(new ProcessAdapter() { + public void startNotified(final ProcessEvent event) { + try { + final Future stdOutReadingFuture = executeOnPooledThread(stdoutThread); + final Future stdErrReadingFuture = executeOnPooledThread(stderrThread); + + myWaitFor.setTerminationCallback(new Consumer() { + @Override + public void consume(Integer exitCode) { + try { + // tell threads that no more attempts to read process' output should be made + stderrThread.setProcessTerminated(true); + stdoutThread.setProcessTerminated(true); + + stdErrReadingFuture.get(); + stdOutReadingFuture.get(); + } + catch (InterruptedException ignored) { + } + catch (ExecutionException e) { + LOG.error(e); + } + finally { + onOSProcessTerminated(exitCode); + } + } + }); + } + finally { + removeProcessListener(this); + } + } + }); + + super.startNotify(); + } + + protected void onOSProcessTerminated(final int exitCode) { + notifyProcessTerminated(exitCode); + } + + protected Reader createProcessOutReader() { + return createInputStreamReader(myProcess.getInputStream()); + } + + protected Reader createProcessErrReader() { + return createInputStreamReader(myProcess.getErrorStream()); + } + + private Reader createInputStreamReader(InputStream streamToRead) { + final Charset charset = getCharset(); + if (charset == null) { + // use default charset + return new InputStreamReader(streamToRead); + } + return new InputStreamReader(streamToRead, charset); + } + + protected void destroyProcessImpl() { + try { + closeStreams(); + } + finally { + doDestroyProcess(); + } + } + + protected void doDestroyProcess() { + getProcess().destroy(); + } + + protected void detachProcessImpl() { + final Runnable runnable = new Runnable() { + public void run() { + closeStreams(); + + myWaitFor.detach(); + notifyProcessDetached(); + } + }; + + executeOnPooledThread(runnable); + } + + protected void closeStreams() { + try { + myProcess.getOutputStream().close(); + } + catch (IOException e) { + LOG.error(e); + } + } + + public boolean detachIsDefault() { + return false; + } + + public OutputStream getProcessInput() { + return myProcess.getOutputStream(); + } + + // todo: to remove + @Nullable + public String getCommandLine() { + return myCommandLine; + } + + @Nullable + public Charset getCharset() { + return myCharset; + } + + private static class ExecutorServiceHolder { + private static final ExecutorService ourThreadExecutorsService = createServiceImpl(); + + private static ThreadPoolExecutor createServiceImpl() { + return new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() { + @SuppressWarnings({"HardCodedStringLiteral"}) + public Thread newThread(Runnable r) { + return new Thread(r, "OSProcessHandler pooled thread"); + } + }); + } + } + + private abstract static class ReadProcessThread implements Runnable { + private final Reader myReader; + private boolean skipLF = false; + + private boolean myIsProcessTerminated = false; + private final char[] myBuffer = new char[8192]; + + public ReadProcessThread(final Reader reader) { + myReader = reader; + } + + public synchronized void setProcessTerminated(boolean isProcessTerminated) { + myIsProcessTerminated = isProcessTerminated; + } + + public void run() { + try { + while (true) { + final int rc = readAvailable(); + if (rc == DONE) { + break; + } + //noinspection BusyWait + Thread.sleep(rc == READ_SOME ? 1L : 5L); // give other threads a chance + } + } + catch (InterruptedException ignore) { + } + catch (IOException e) { + LOG.info(e); + } + catch (Exception e) { + LOG.error(e); + } + } + + private static final int DONE = 0; + private static final int READ_SOME = 1; + private static final int READ_NONE = 2; + + private synchronized int readAvailable() throws IOException { + char[] buffer = myBuffer; + StringBuilder token = new StringBuilder(); + int rc = READ_NONE; + while (myReader.ready()) { + int n = myReader.read(buffer); + if (n <= 0) break; + rc = READ_SOME; + + for (int i = 0; i < n; i++) { + char c = buffer[i]; + if (skipLF && c != '\n') { + token.append('\r'); + } + + if (c == '\r') { + skipLF = true; + } + else { + skipLF = false; + token.append(c); + } + + if (c == '\n') { + textAvailable(token.toString()); + token.setLength(0); + } + } + } + + if (token.length() != 0) { + textAvailable(token.toString()); + token.setLength(0); + } + + if (myIsProcessTerminated) { + try { + myReader.close(); + } + catch (IOException e1) { + // supressed + } + + return DONE; + } + + return rc; + } + + protected abstract void textAvailable(final String s); + } + + protected class ProcessWaitFor { + private final Future myWaitForThreadFuture; + private final BlockingQueue> myTerminationCallback = new ArrayBlockingQueue>(1); + + public void detach() { + myWaitForThreadFuture.cancel(true); + } + + + public ProcessWaitFor(final Process process) { + myWaitForThreadFuture = executeOnPooledThread(new Runnable() { + public void run() { + int exitCode = 0; + try { + while (true) { + try { + exitCode = process.waitFor(); + break; + } + catch (InterruptedException e) { + LOG.debug(e); + } + } + } + finally { + try { + myTerminationCallback.take().consume(exitCode); + } + catch (InterruptedException e) { + LOG.info(e); + } + } + } + }); + } + + public void setTerminationCallback(Consumer r) { + myTerminationCallback.offer(r); + } + } +} diff --git a/platform/platform-api/src/com/intellij/execution/process/ProcessAdapter.java b/platform/util/src/com/intellij/execution/process/ProcessAdapter.java similarity index 100% rename from platform/platform-api/src/com/intellij/execution/process/ProcessAdapter.java rename to platform/util/src/com/intellij/execution/process/ProcessAdapter.java diff --git a/platform/platform-api/src/com/intellij/execution/process/ProcessEvent.java b/platform/util/src/com/intellij/execution/process/ProcessEvent.java similarity index 100% rename from platform/platform-api/src/com/intellij/execution/process/ProcessEvent.java rename to platform/util/src/com/intellij/execution/process/ProcessEvent.java diff --git a/platform/platform-api/src/com/intellij/execution/process/ProcessHandler.java b/platform/util/src/com/intellij/execution/process/ProcessHandler.java similarity index 100% rename from platform/platform-api/src/com/intellij/execution/process/ProcessHandler.java rename to platform/util/src/com/intellij/execution/process/ProcessHandler.java diff --git a/platform/platform-api/src/com/intellij/execution/process/ProcessListener.java b/platform/util/src/com/intellij/execution/process/ProcessListener.java similarity index 100% rename from platform/platform-api/src/com/intellij/execution/process/ProcessListener.java rename to platform/util/src/com/intellij/execution/process/ProcessListener.java diff --git a/platform/platform-api/src/com/intellij/execution/process/ProcessOutputTypes.java b/platform/util/src/com/intellij/execution/process/ProcessOutputTypes.java similarity index 100% rename from platform/platform-api/src/com/intellij/execution/process/ProcessOutputTypes.java rename to platform/util/src/com/intellij/execution/process/ProcessOutputTypes.java diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java b/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java index cf3bddc4c965..15bb312a9e48 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java @@ -38,7 +38,6 @@ import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil;