284c0f06c0f245e390747a34004d6e69fc90280b
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / ExecutionHelper.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution;
18
19 import com.intellij.execution.executors.DefaultRunExecutor;
20 import com.intellij.execution.process.OSProcessHandler;
21 import com.intellij.execution.process.ProcessHandler;
22 import com.intellij.execution.ui.RunContentDescriptor;
23 import com.intellij.execution.ui.RunContentManagerImpl;
24 import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.command.CommandProcessor;
27 import com.intellij.openapi.components.ServiceManager;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.progress.ProgressIndicator;
30 import com.intellij.openapi.progress.ProgressManager;
31 import com.intellij.openapi.progress.Task;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.ui.Messages;
34 import com.intellij.openapi.util.Disposer;
35 import com.intellij.openapi.util.text.StringUtil;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.openapi.wm.ToolWindow;
38 import com.intellij.openapi.wm.ToolWindowId;
39 import com.intellij.openapi.wm.ToolWindowManager;
40 import com.intellij.ui.content.Content;
41 import com.intellij.ui.content.ContentFactory;
42 import com.intellij.ui.content.MessageView;
43 import com.intellij.util.Function;
44 import com.intellij.util.NotNullFunction;
45 import com.intellij.util.concurrency.Semaphore;
46 import com.intellij.util.ui.ErrorTreeView;
47 import com.intellij.util.ui.MessageCategory;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
50
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.List;
55
56 /**
57  * Created by IntelliJ IDEA.
58  *
59  * @author: Roman Chernyatchik
60  * @date: Oct 4, 2007
61  */
62 public class ExecutionHelper {
63   private static final Logger LOG = Logger.getInstance(ExecutionHelper.class.getName());
64
65   private ExecutionHelper() {
66   }
67
68   public static void showErrors(@NotNull final Project myProject,
69                                 @NotNull final List<Exception> exceptionList,
70                                 @NotNull final String tabDisplayName,
71                                 @Nullable final VirtualFile file) {
72     if (ApplicationManager.getApplication().isUnitTestMode() && !exceptionList.isEmpty()) {
73       throw new RuntimeException(exceptionList.get(0));
74     }
75     ApplicationManager.getApplication().invokeLater(new Runnable() {
76       public void run() {
77         if (myProject.isDisposed()) return;
78         if (exceptionList.isEmpty()) {
79           removeContents(null, myProject, tabDisplayName);
80           return;
81         }
82
83         final RailsErrorViewPanel errorTreeView = new RailsErrorViewPanel(myProject);
84         try {
85           openMessagesView(errorTreeView, myProject, tabDisplayName);
86         }
87         catch (NullPointerException e) {
88           final StringBuilder builder = new StringBuilder();
89           builder.append("Exceptions occured:");
90           for (final Exception exception : exceptionList) {
91             builder.append("\n");
92             builder.append(exception.getMessage());
93           }
94           Messages.showErrorDialog(builder.toString(), "Execution Error");
95           return;
96         }
97         for (final Exception exception : exceptionList) {
98           String[] messages = StringUtil.splitByLines(exception.getMessage());
99           if (messages.length == 0) messages = new String[]{"Unknown Error"};
100           errorTreeView.addMessage(MessageCategory.ERROR, messages, file, -1, -1, null);
101         }
102
103         ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW).activate(null);
104       }
105     });
106   }
107
108   private static void openMessagesView(@NotNull final RailsErrorViewPanel errorTreeView,
109                                        @NotNull final Project myProject,
110                                        @NotNull final String tabDisplayName) {
111     CommandProcessor commandProcessor = CommandProcessor.getInstance();
112     commandProcessor.executeCommand(myProject, new Runnable() {
113       public void run() {
114         final MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
115         final Content content = ContentFactory.SERVICE.getInstance().createContent(errorTreeView, tabDisplayName, true);
116         messageView.getContentManager().addContent(content);
117         Disposer.register(content, errorTreeView);
118         messageView.getContentManager().setSelectedContent(content);
119         removeContents(content, myProject, tabDisplayName);
120       }
121     }, "Open message view", null);
122   }
123
124   private static void removeContents(@Nullable final Content notToRemove,
125                                      @NotNull final Project myProject,
126                                      @NotNull final String tabDisplayName) {
127     MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
128     Content[] contents = messageView.getContentManager().getContents();
129     for (Content content : contents) {
130       LOG.assertTrue(content != null);
131       if (content.isPinned()) continue;
132       if (tabDisplayName.equals(content.getDisplayName()) && content != notToRemove) {
133         ErrorTreeView listErrorView = (ErrorTreeView)content.getComponent();
134         if (listErrorView != null) {
135           if (messageView.getContentManager().removeContent(content, true)) {
136             content.release();
137           }
138         }
139       }
140     }
141   }
142
143   public static Collection<RunContentDescriptor> findRunningConsole(final Project project,
144                                                                     @NotNull final NotNullFunction<String, Boolean> cmdLineMatcher) {
145     final ExecutionManager executionManager = ExecutionManager.getInstance(project);
146     final ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(DefaultRunExecutor.getRunExecutorInstance().getId());
147     if (runToolWindow != null && runToolWindow.isVisible()) {
148       final RunContentDescriptor selectedContent = executionManager.getContentManager().getSelectedContent();
149       if (selectedContent != null){
150         final ProcessHandler processHandler = selectedContent.getProcessHandler();
151         if (processHandler instanceof OSProcessHandler && !processHandler.isProcessTerminated()) {
152           final String commandLine = ((OSProcessHandler)processHandler).getCommandLine();
153           if (cmdLineMatcher.fun(commandLine).booleanValue()) {
154             return Collections.singletonList(selectedContent);
155           }
156         }
157       }
158     }
159
160     final ArrayList<RunContentDescriptor> result = new ArrayList<RunContentDescriptor>();
161     final RunContentDescriptor[] runContentDescriptors = ((RunContentManagerImpl)executionManager.getContentManager()).getAllDescriptors();
162     for (RunContentDescriptor runContentDescriptor : runContentDescriptors) {
163       final ProcessHandler processHandler = runContentDescriptor.getProcessHandler();
164       if (processHandler instanceof OSProcessHandler && !processHandler.isProcessTerminated()) {
165         final String commandLine = ((OSProcessHandler)processHandler).getCommandLine();
166         if (cmdLineMatcher.fun(commandLine).booleanValue()) {
167           result.add(runContentDescriptor);
168         }
169       }
170     }
171     return result;
172   }
173
174   public static class RailsErrorViewPanel extends NewErrorTreeViewPanel {
175     public RailsErrorViewPanel(final Project project) {
176       super(project, "reference.toolWindows.messages");
177     }
178
179     protected boolean canHideWarnings() {
180       return false;
181     }
182   }
183
184
185   public static void executeExternalProcess(@Nullable final Project myProject,
186                                             @NotNull final OSProcessHandler processHandler,
187                                             @NotNull final ExecutionMode mode) {
188     final String title = mode.getTitle() != null ? mode.getTitle() : "Please wait...";
189     assert title != null;
190
191     final Runnable process;
192     if (mode.cancelable()) {
193       process = createCancelableExecutionProcess(processHandler, mode.shouldCancelFun());
194     }
195     else {
196       if (mode.getTimeout() <= 0) {
197         process = new Runnable() {
198           public void run() {
199             processHandler.waitFor();
200           }
201         };
202       } else {
203         process = createTimelimitedExecutionProcess(processHandler, mode.getTimeout());
204       }
205     }
206     if (mode.withModalProgress()) {
207       ProgressManager.getInstance().runProcessWithProgressSynchronously(process, title, mode.cancelable(), myProject,
208                                                                         mode.getProgressParentComponent());
209     }
210     else if (mode.inBackGround()) {
211       final Task task = new Task.Backgroundable(myProject, title, mode.cancelable()) {
212         public void run(@NotNull final ProgressIndicator indicator) {
213           process.run();
214         }
215       };
216       ProgressManager.getInstance().run(task);
217     }
218     else {
219       final String title2 = mode.getTitle2();
220       final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
221       if (indicator != null && title2 != null) {
222         indicator.setText2(title2);
223       }
224       process.run();
225     }
226   }
227
228   private static Runnable createCancelableExecutionProcess(final ProcessHandler processHandler,
229                                                            final Function<Object, Boolean> cancelableFun) {
230     return new Runnable() {
231       private ProgressIndicator myProgressIndicator;
232       private final Semaphore mySemaphore = new Semaphore();
233
234       private final Runnable myWaitThread = new Runnable() {
235         public void run() {
236           try {
237             processHandler.waitFor();
238           }
239           finally {
240             mySemaphore.up();
241           }
242         }
243       };
244
245       private final Runnable myCancelListener = new Runnable() {
246         public void run() {
247           for (; ;) {
248             if ((myProgressIndicator != null && (myProgressIndicator.isCanceled()
249                                                  || !myProgressIndicator.isRunning()))
250                 || (cancelableFun != null && cancelableFun.fun(null).booleanValue())
251                 || processHandler.isProcessTerminated()) {
252
253               if (!processHandler.isProcessTerminated()) {
254                 try {
255                   processHandler.destroyProcess();
256                 }
257                 finally {
258                   mySemaphore.up();
259                 }
260               }
261               break;
262             }
263             try {
264               synchronized (this) {
265                 wait(1000);
266               }
267             }
268             catch (InterruptedException e) {
269               //Do nothing
270             }
271           }
272         }
273       };
274
275       public void run() {
276         myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
277         if (myProgressIndicator != null && StringUtil.isEmpty(myProgressIndicator.getText())) {
278           myProgressIndicator.setText("Please wait...");
279         }
280
281         LOG.assertTrue(myProgressIndicator != null || cancelableFun != null,
282                        "Cancelable process must have an opportunity to be canceled!");
283         mySemaphore.down();
284         ApplicationManager.getApplication().executeOnPooledThread(myWaitThread);
285         ApplicationManager.getApplication().executeOnPooledThread(myCancelListener);
286
287         mySemaphore.waitFor();
288       }
289     };
290   }
291
292   private static Runnable createTimelimitedExecutionProcess(final OSProcessHandler processHandler,
293                                                             final int timeout) {
294     return new Runnable() {
295       private final Semaphore mySemaphore = new Semaphore();
296
297       private final Runnable myProcessThread = new Runnable() {
298         public void run() {
299           try {
300             final boolean finished = processHandler.waitFor(1000 * timeout);
301             if (!finished) {
302               LOG.error("Timeout (" + timeout + " sec) on executing: " + processHandler.getCommandLine());
303               processHandler.destroyProcess();
304             }
305           } finally {
306             mySemaphore.up();
307           }
308         }
309       };
310
311       public void run() {
312         mySemaphore.down();
313         ApplicationManager.getApplication().executeOnPooledThread(myProcessThread);
314
315         mySemaphore.waitFor();
316       }
317     };
318   }
319 }