82f7d430db52ea5f0f312cd8e20f580fd876d54b
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / impl / ExecutionManagerImpl.java
1 /*
2  * Copyright 2000-2016 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 package com.intellij.execution.impl;
17
18 import com.intellij.CommonBundle;
19 import com.intellij.execution.*;
20 import com.intellij.execution.configuration.CompatibilityAwareRunProfile;
21 import com.intellij.execution.configurations.RunConfiguration;
22 import com.intellij.execution.configurations.RunProfile;
23 import com.intellij.execution.configurations.RunProfileState;
24 import com.intellij.execution.process.ProcessAdapter;
25 import com.intellij.execution.process.ProcessEvent;
26 import com.intellij.execution.process.ProcessHandler;
27 import com.intellij.execution.runners.ExecutionEnvironment;
28 import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
29 import com.intellij.execution.runners.ExecutionUtil;
30 import com.intellij.execution.runners.ProgramRunner;
31 import com.intellij.execution.ui.RunContentDescriptor;
32 import com.intellij.execution.ui.RunContentManager;
33 import com.intellij.execution.ui.RunContentManagerImpl;
34 import com.intellij.ide.SaveAndSyncHandler;
35 import com.intellij.openapi.Disposable;
36 import com.intellij.openapi.actionSystem.DataContext;
37 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
38 import com.intellij.openapi.application.ApplicationManager;
39 import com.intellij.openapi.application.ModalityState;
40 import com.intellij.openapi.components.ServiceManager;
41 import com.intellij.openapi.diagnostic.Logger;
42 import com.intellij.openapi.progress.ProcessCanceledException;
43 import com.intellij.openapi.project.DumbService;
44 import com.intellij.openapi.project.IndexNotReadyException;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.ui.DialogWrapper;
47 import com.intellij.openapi.ui.Messages;
48 import com.intellij.openapi.util.Condition;
49 import com.intellij.openapi.util.Disposer;
50 import com.intellij.openapi.util.Key;
51 import com.intellij.openapi.util.Trinity;
52 import com.intellij.openapi.util.registry.Registry;
53 import com.intellij.openapi.util.text.StringUtil;
54 import com.intellij.ui.docking.DockManager;
55 import com.intellij.util.Alarm;
56 import com.intellij.util.SmartList;
57 import com.intellij.util.containers.ContainerUtil;
58 import org.jetbrains.annotations.NotNull;
59 import org.jetbrains.annotations.Nullable;
60 import org.jetbrains.annotations.TestOnly;
61
62 import javax.swing.*;
63 import java.util.*;
64
65 public class ExecutionManagerImpl extends ExecutionManager implements Disposable {
66   public static final Key<Object> EXECUTION_SESSION_ID_KEY = Key.create("EXECUTION_SESSION_ID_KEY");
67   public static final Key<Boolean> EXECUTION_SKIP_RUN = Key.create("EXECUTION_SKIP_RUN");
68
69   private static final Logger LOG = Logger.getInstance(ExecutionManagerImpl.class);
70   private static final ProcessHandler[] EMPTY_PROCESS_HANDLERS = new ProcessHandler[0];
71
72   private final Project myProject;
73   private final Alarm myAwaitingTerminationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
74   private final Map<RunProfile, ExecutionEnvironment> myAwaitingRunProfiles = ContainerUtil.newHashMap();
75   private final List<Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor>> myRunningConfigurations =
76     ContainerUtil.createLockFreeCopyOnWriteList();
77   private RunContentManagerImpl myContentManager;
78   private volatile boolean myForceCompilationInTests;
79
80   @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
81   @NotNull
82   public static ExecutionManagerImpl getInstance(@NotNull Project project) {
83     return (ExecutionManagerImpl)ServiceManager.getService(project, ExecutionManager.class);
84   }
85
86   protected ExecutionManagerImpl(@NotNull Project project) {
87     myProject = project;
88   }
89
90   @NotNull
91   private static ExecutionEnvironmentBuilder createEnvironmentBuilder(@NotNull Project project,
92                                                                       @NotNull Executor executor,
93                                                                       @Nullable RunnerAndConfigurationSettings configuration) {
94     ExecutionEnvironmentBuilder builder = new ExecutionEnvironmentBuilder(project, executor);
95
96     ProgramRunner runner =
97       RunnerRegistry.getInstance().getRunner(executor.getId(), configuration != null ? configuration.getConfiguration() : null);
98     if (runner == null && configuration != null) {
99       LOG.error("Cannot find runner for " + configuration.getName());
100     }
101     else if (runner != null) {
102       assert configuration != null;
103       builder.runnerAndSettings(runner, configuration);
104     }
105     return builder;
106   }
107
108   public static boolean isProcessRunning(@Nullable RunContentDescriptor descriptor) {
109     ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
110     return processHandler != null && !processHandler.isProcessTerminated();
111   }
112
113   private static void start(@NotNull ExecutionEnvironment environment) {
114     RunnerAndConfigurationSettings settings = environment.getRunnerAndConfigurationSettings();
115     ProgramRunnerUtil.executeConfiguration(environment, settings != null && settings.isEditBeforeRun(), true);
116   }
117
118   private static boolean userApprovesStopForSameTypeConfigurations(Project project, String configName, int instancesCount) {
119     RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
120     final RunManagerConfig config = runManager.getConfig();
121     if (!config.isRestartRequiresConfirmation()) return true;
122
123     DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
124       @Override
125       public boolean isToBeShown() {
126         return config.isRestartRequiresConfirmation();
127       }
128
129       @Override
130       public void setToBeShown(boolean value, int exitCode) {
131         config.setRestartRequiresConfirmation(value);
132       }
133
134       @Override
135       public boolean canBeHidden() {
136         return true;
137       }
138
139       @Override
140       public boolean shouldSaveOptionsOnCancel() {
141         return false;
142       }
143
144       @NotNull
145       @Override
146       public String getDoNotShowMessage() {
147         return CommonBundle.message("dialog.options.do.not.show");
148       }
149     };
150     return Messages.showOkCancelDialog(
151       project,
152       ExecutionBundle.message("rerun.singleton.confirmation.message", configName, instancesCount),
153       ExecutionBundle.message("process.is.running.dialog.title", configName),
154       ExecutionBundle.message("rerun.confirmation.button.text"),
155       CommonBundle.message("button.cancel"),
156       Messages.getQuestionIcon(), option) == Messages.OK;
157   }
158
159   private static boolean userApprovesStopForIncompatibleConfigurations(Project project,
160                                                                        String configName,
161                                                                        List<RunContentDescriptor> runningIncompatibleDescriptors) {
162     RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
163     final RunManagerConfig config = runManager.getConfig();
164     if (!config.isStopIncompatibleRequiresConfirmation()) return true;
165
166     DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
167       @Override
168       public boolean isToBeShown() {
169         return config.isStopIncompatibleRequiresConfirmation();
170       }
171
172       @Override
173       public void setToBeShown(boolean value, int exitCode) {
174         config.setStopIncompatibleRequiresConfirmation(value);
175       }
176
177       @Override
178       public boolean canBeHidden() {
179         return true;
180       }
181
182       @Override
183       public boolean shouldSaveOptionsOnCancel() {
184         return false;
185       }
186
187       @NotNull
188       @Override
189       public String getDoNotShowMessage() {
190         return CommonBundle.message("dialog.options.do.not.show");
191       }
192     };
193
194     final StringBuilder names = new StringBuilder();
195     for (final RunContentDescriptor descriptor : runningIncompatibleDescriptors) {
196       String name = descriptor.getDisplayName();
197       if (names.length() > 0) {
198         names.append(", ");
199       }
200       names.append(StringUtil.isEmpty(name) ? ExecutionBundle.message("run.configuration.no.name")
201                                             : String.format("'%s'", name));
202     }
203
204     //noinspection DialogTitleCapitalization
205     return Messages.showOkCancelDialog(
206       project,
207       ExecutionBundle.message("stop.incompatible.confirmation.message",
208                               configName, names.toString(), runningIncompatibleDescriptors.size()),
209       ExecutionBundle.message("incompatible.configuration.is.running.dialog.title", runningIncompatibleDescriptors.size()),
210       ExecutionBundle.message("stop.incompatible.confirmation.button.text"),
211       CommonBundle.message("button.cancel"),
212       Messages.getQuestionIcon(), option) == Messages.OK;
213   }
214
215   private static void stop(@Nullable RunContentDescriptor descriptor) {
216     ProcessHandler processHandler = descriptor != null ? descriptor.getProcessHandler() : null;
217     if (processHandler == null) {
218       return;
219     }
220
221     if (processHandler instanceof KillableProcess && processHandler.isProcessTerminating()) {
222       ((KillableProcess)processHandler).killProcess();
223       return;
224     }
225
226     if (!processHandler.isProcessTerminated()) {
227       if (processHandler.detachIsDefault()) {
228         processHandler.detachProcess();
229       }
230       else {
231         processHandler.destroyProcess();
232       }
233     }
234   }
235
236   @Override
237   public void dispose() {
238     for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
239       Disposer.dispose(trinity.first);
240     }
241     myRunningConfigurations.clear();
242   }
243
244   @NotNull
245   @Override
246   public RunContentManager getContentManager() {
247     if (myContentManager == null) {
248       myContentManager = new RunContentManagerImpl(myProject, DockManager.getInstance(myProject));
249       Disposer.register(myProject, myContentManager);
250     }
251     return myContentManager;
252   }
253
254   @NotNull
255   @Override
256   public ProcessHandler[] getRunningProcesses() {
257     if (myContentManager == null) return EMPTY_PROCESS_HANDLERS;
258     List<ProcessHandler> handlers = null;
259     for (RunContentDescriptor descriptor : getContentManager().getAllDescriptors()) {
260       ProcessHandler processHandler = descriptor.getProcessHandler();
261       if (processHandler != null) {
262         if (handlers == null) {
263           handlers = new SmartList<ProcessHandler>();
264         }
265         handlers.add(processHandler);
266       }
267     }
268     return handlers == null ? EMPTY_PROCESS_HANDLERS : handlers.toArray(new ProcessHandler[handlers.size()]);
269   }
270
271   @Override
272   public void compileAndRun(@NotNull final Runnable startRunnable,
273                             @NotNull final ExecutionEnvironment environment,
274                             @Nullable final RunProfileState state,
275                             @Nullable final Runnable onCancelRunnable) {
276     long id = environment.getExecutionId();
277     if (id == 0) {
278       id = environment.assignNewExecutionId();
279     }
280
281     RunProfile profile = environment.getRunProfile();
282     if (!(profile instanceof RunConfiguration)) {
283       startRunnable.run();
284       return;
285     }
286
287     final RunConfiguration runConfiguration = (RunConfiguration)profile;
288     final List<BeforeRunTask> beforeRunTasks = RunManagerEx.getInstanceEx(myProject).getBeforeRunTasks(runConfiguration);
289     if (beforeRunTasks.isEmpty()) {
290       startRunnable.run();
291     }
292     else {
293       DataContext context = environment.getDataContext();
294       final DataContext projectContext = context != null ? context : SimpleDataContext.getProjectContext(myProject);
295       final long finalId = id;
296       final Long executionSessionId = new Long(id);
297       ApplicationManager.getApplication().executeOnPooledThread((Runnable)() -> {
298         for (BeforeRunTask task : beforeRunTasks) {
299           if (myProject.isDisposed()) {
300             return;
301           }
302           @SuppressWarnings("unchecked")
303           BeforeRunTaskProvider<BeforeRunTask> provider = BeforeRunTaskProvider.getProvider(myProject, task.getProviderId());
304           if (provider == null) {
305             LOG.warn("Cannot find BeforeRunTaskProvider for id='" + task.getProviderId() + "'");
306             continue;
307           }
308           ExecutionEnvironment taskEnvironment = new ExecutionEnvironmentBuilder(environment).contentToReuse(null).build();
309           taskEnvironment.setExecutionId(finalId);
310           EXECUTION_SESSION_ID_KEY.set(taskEnvironment, executionSessionId);
311           if (!provider.executeTask(projectContext, runConfiguration, taskEnvironment, task)) {
312             if (onCancelRunnable != null) {
313               SwingUtilities.invokeLater(onCancelRunnable);
314             }
315             return;
316           }
317         }
318
319         doRun(environment, startRunnable);
320       });
321     }
322   }
323
324   protected void doRun(@NotNull final ExecutionEnvironment environment, @NotNull final Runnable startRunnable) {
325     Boolean allowSkipRun = environment.getUserData(EXECUTION_SKIP_RUN);
326     if (allowSkipRun != null && allowSkipRun) {
327       environment.getProject().getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(environment.getExecutor().getId(),
328                                                                                                 environment);
329     }
330     else {
331       // important! Do not use DumbService.smartInvokeLater here because it depends on modality state
332       // and execution of startRunnable could be skipped if modality state check fails
333       //noinspection SSBasedInspection
334       SwingUtilities.invokeLater(() -> {
335         if (!myProject.isDisposed()) {
336           if (!Registry.is("dumb.aware.run.configurations")) {
337             DumbService.getInstance(myProject).runWhenSmart(startRunnable);
338           } else {
339             try {
340               DumbService.getInstance(myProject).setAlternativeResolveEnabled(true);
341               startRunnable.run();
342             } catch (IndexNotReadyException ignored) {
343               ExecutionUtil.handleExecutionError(environment, new ExecutionException("cannot start while indexing is in progress."));
344             } finally {
345               DumbService.getInstance(myProject).setAlternativeResolveEnabled(false);
346             }
347           }
348         }
349       });
350     }
351   }
352
353   @Override
354   public void startRunProfile(@NotNull final RunProfileStarter starter,
355                               @NotNull final RunProfileState state,
356                               @NotNull final ExecutionEnvironment environment) {
357     final Project project = environment.getProject();
358     RunContentDescriptor reuseContent = getContentManager().getReuseContent(environment);
359     if (reuseContent != null) {
360       reuseContent.setExecutionId(environment.getExecutionId());
361       environment.setContentToReuse(reuseContent);
362     }
363
364     final Executor executor = environment.getExecutor();
365     project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStartScheduled(executor.getId(), environment);
366
367     Runnable startRunnable;
368     startRunnable = () -> {
369       if (project.isDisposed()) {
370         return;
371       }
372
373       RunProfile profile = environment.getRunProfile();
374       boolean started = false;
375       try {
376         project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarting(executor.getId(), environment);
377
378         final RunContentDescriptor descriptor = starter.execute(state, environment);
379         if (descriptor != null) {
380           final Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity =
381             Trinity.create(descriptor, environment.getRunnerAndConfigurationSettings(), executor);
382           myRunningConfigurations.add(trinity);
383           Disposer.register(descriptor, () -> myRunningConfigurations.remove(trinity));
384           getContentManager().showRunContent(executor, descriptor, environment.getContentToReuse());
385           final ProcessHandler processHandler = descriptor.getProcessHandler();
386           if (processHandler != null) {
387             if (!processHandler.isStartNotified()) {
388               processHandler.startNotify();
389             }
390             project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarted(executor.getId(), environment, processHandler);
391             started = true;
392             processHandler.addProcessListener(new ProcessExecutionListener(project, profile, processHandler));
393           }
394           environment.setContentToReuse(descriptor);
395         }
396       }
397       catch (ProcessCanceledException e) {
398         LOG.info(e);
399       }
400       catch (ExecutionException e) {
401         ExecutionUtil.handleExecutionError(project, executor.getToolWindowId(), profile, e);
402         LOG.info(e);
403       }
404       finally {
405         if (!started) {
406           project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(), environment);
407         }
408       }
409     };
410
411     if (ApplicationManager.getApplication().isUnitTestMode() && !myForceCompilationInTests) {
412       startRunnable.run();
413     }
414     else {
415       compileAndRun(startRunnable, environment, state, () -> {
416         if (!project.isDisposed()) {
417           project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(), environment);
418         }
419       });
420     }
421   }
422
423   @Override
424   public void restartRunProfile(@NotNull Project project,
425                                 @NotNull Executor executor,
426                                 @NotNull ExecutionTarget target,
427                                 @Nullable RunnerAndConfigurationSettings configuration,
428                                 @Nullable ProcessHandler processHandler) {
429     ExecutionEnvironmentBuilder builder = createEnvironmentBuilder(project, executor, configuration);
430     if (processHandler != null) {
431       for (RunContentDescriptor descriptor : getContentManager().getAllDescriptors()) {
432         if (descriptor.getProcessHandler() == processHandler) {
433           builder.contentToReuse(descriptor);
434           break;
435         }
436       }
437     }
438     restartRunProfile(builder.target(target).build());
439   }
440
441   @Override
442   public void restartRunProfile(@NotNull final ExecutionEnvironment environment) {
443     RunnerAndConfigurationSettings configuration = environment.getRunnerAndConfigurationSettings();
444
445     List<RunContentDescriptor> runningIncompatible;
446     if (configuration == null) {
447       runningIncompatible = Collections.emptyList();
448     }
449     else {
450       runningIncompatible = getIncompatibleRunningDescriptors(configuration);
451     }
452
453     RunContentDescriptor contentToReuse = environment.getContentToReuse();
454     final List<RunContentDescriptor> runningOfTheSameType = new SmartList<RunContentDescriptor>();
455     if (configuration != null && configuration.isSingleton()) {
456       runningOfTheSameType.addAll(getRunningDescriptorsOfTheSameConfigType(configuration));
457     }
458     else if (isProcessRunning(contentToReuse)) {
459       runningOfTheSameType.add(contentToReuse);
460     }
461
462     List<RunContentDescriptor> runningToStop = ContainerUtil.concat(runningOfTheSameType, runningIncompatible);
463     if (!runningToStop.isEmpty()) {
464       if (configuration != null) {
465         if (!runningOfTheSameType.isEmpty()
466             && (runningOfTheSameType.size() > 1 || contentToReuse == null || runningOfTheSameType.get(0) != contentToReuse) &&
467             !userApprovesStopForSameTypeConfigurations(environment.getProject(), configuration.getName(), runningOfTheSameType.size())) {
468           return;
469         }
470         if (!runningIncompatible.isEmpty()
471             && !userApprovesStopForIncompatibleConfigurations(myProject, configuration.getName(), runningIncompatible)) {
472           return;
473         }
474       }
475
476       for (RunContentDescriptor descriptor : runningToStop) {
477         stop(descriptor);
478       }
479     }
480
481     if (myAwaitingRunProfiles.get(environment.getRunProfile()) == environment) {
482       // defense from rerunning exactly the same ExecutionEnvironment
483       return;
484     }
485     myAwaitingRunProfiles.put(environment.getRunProfile(), environment);
486
487     awaitTermination(new Runnable() {
488       @Override
489       public void run() {
490         if (myAwaitingRunProfiles.get(environment.getRunProfile()) != environment) {
491           // a new rerun has been requested before starting this one, ignore this rerun
492           return;
493         }
494         if ((DumbService.getInstance(myProject).isDumb() && !Registry.is("dumb.aware.run.configurations")) || ExecutorRegistry.getInstance().isStarting(environment)) {
495           awaitTermination(this, 100);
496           return;
497         }
498
499         for (RunContentDescriptor descriptor : runningOfTheSameType) {
500           ProcessHandler processHandler = descriptor.getProcessHandler();
501           if (processHandler != null && !processHandler.isProcessTerminated()) {
502             awaitTermination(this, 100);
503             return;
504           }
505         }
506         myAwaitingRunProfiles.remove(environment.getRunProfile());
507         start(environment);
508       }
509     }, 50);
510   }
511
512   private void awaitTermination(@NotNull Runnable request, long delayMillis) {
513     if (ApplicationManager.getApplication().isUnitTestMode()) {
514       ApplicationManager.getApplication().invokeLater(request, ModalityState.any());
515     }
516     else {
517       myAwaitingTerminationAlarm.addRequest(request, delayMillis);
518     }
519   }
520
521   @TestOnly
522   public void setForceCompilationInTests(boolean forceCompilationInTests) {
523     myForceCompilationInTests = forceCompilationInTests;
524   }
525
526   @NotNull
527   private List<RunContentDescriptor> getRunningDescriptorsOfTheSameConfigType(@NotNull final RunnerAndConfigurationSettings configurationAndSettings) {
528     return getRunningDescriptors(runningConfigurationAndSettings -> configurationAndSettings == runningConfigurationAndSettings);
529   }
530
531   @NotNull
532   private List<RunContentDescriptor> getIncompatibleRunningDescriptors(@NotNull RunnerAndConfigurationSettings configurationAndSettings) {
533     final RunConfiguration configurationToCheckCompatibility = configurationAndSettings.getConfiguration();
534     return getRunningDescriptors(runningConfigurationAndSettings -> {
535       RunConfiguration runningConfiguration = runningConfigurationAndSettings == null ? null : runningConfigurationAndSettings.getConfiguration();
536       if (runningConfiguration == null || !(runningConfiguration instanceof CompatibilityAwareRunProfile)) {
537         return false;
538       }
539       return ((CompatibilityAwareRunProfile)runningConfiguration).mustBeStoppedToRun(configurationToCheckCompatibility);
540     });
541   }
542
543   @NotNull
544   public List<RunContentDescriptor> getRunningDescriptors(@NotNull Condition<RunnerAndConfigurationSettings> condition) {
545     List<RunContentDescriptor> result = new SmartList<RunContentDescriptor>();
546     for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
547       if (condition.value(trinity.getSecond())) {
548         ProcessHandler processHandler = trinity.getFirst().getProcessHandler();
549         if (processHandler != null /*&& !processHandler.isProcessTerminating()*/ && !processHandler.isProcessTerminated()) {
550           result.add(trinity.getFirst());
551         }
552       }
553     }
554     return result;
555   }
556
557   @NotNull
558   public Set<Executor> getExecutors(RunContentDescriptor descriptor) {
559     Set<Executor> result = new HashSet<Executor>();
560     for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
561       if (descriptor == trinity.first) result.add(trinity.third);
562     }
563     return result;
564   }
565
566   private static class ProcessExecutionListener extends ProcessAdapter {
567     private final Project myProject;
568     private final RunProfile myProfile;
569     private final ProcessHandler myProcessHandler;
570
571     public ProcessExecutionListener(Project project, RunProfile profile, ProcessHandler processHandler) {
572       myProject = project;
573       myProfile = profile;
574       myProcessHandler = processHandler;
575     }
576
577     @Override
578     public void processTerminated(ProcessEvent event) {
579       if (myProject.isDisposed()) return;
580
581       myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminated(myProfile, myProcessHandler);
582
583       SaveAndSyncHandler saveAndSyncHandler = SaveAndSyncHandler.getInstance();
584       if (saveAndSyncHandler != null) {
585         saveAndSyncHandler.scheduleRefresh();
586       }
587     }
588
589     @Override
590     public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
591       if (myProject.isDisposed()) return;
592
593       myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminating(myProfile, myProcessHandler);
594     }
595   }
596 }