fix "IDEA-221944 Deadlock on opening second project" and support preloading for proje...
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / startup / ProjectStartupTaskManager.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.execution.startup;
3
4 import com.intellij.execution.RunManager;
5 import com.intellij.execution.RunnerAndConfigurationSettings;
6 import com.intellij.execution.impl.RunManagerImpl;
7 import com.intellij.notification.NotificationGroup;
8 import com.intellij.openapi.Disposable;
9 import com.intellij.openapi.project.Project;
10 import com.intellij.openapi.project.impl.ProjectLifecycleListener;
11 import com.intellij.openapi.ui.MessageType;
12 import com.intellij.openapi.util.Disposer;
13 import org.jetbrains.annotations.NonNls;
14 import org.jetbrains.annotations.NotNull;
15
16 import java.util.*;
17
18 public final class ProjectStartupTaskManager {
19   public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.logOnlyGroup("Project Startup Tasks Messages");
20   @NonNls public static final String PREFIX = "Project Startup Tasks: ";
21   private final Project myProject;
22   private final ProjectStartupSharedConfiguration myShared;
23   private final ProjectStartupLocalConfiguration myLocal;
24
25   public static ProjectStartupTaskManager getInstance(@NotNull Project project) {
26     return project.getService(ProjectStartupTaskManager.class);
27   }
28
29   public ProjectStartupTaskManager(@NotNull Project project) {
30     myProject = project;
31     myShared = myProject.getService(ProjectStartupSharedConfiguration.class);
32     myLocal = myProject.getService(ProjectStartupLocalConfiguration.class);
33     verifyState();
34   }
35
36   // This method is called from a post-startup activity so the 'post-startup activities done'
37   // event always happens after this method is done executing
38   public void waitForExecutionReady(Runnable runnable) {
39     Disposable readyDisposable = Disposer.newDisposable();
40     Disposer.register(myProject, readyDisposable);
41     myProject.getMessageBus().connect(readyDisposable).subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() {
42       @Override
43       public void postStartupActivitiesPassed(@NotNull Project project) {
44         if (project == myProject) {
45           runnable.run();
46           Disposer.dispose(readyDisposable);
47         }
48       }
49     });
50   }
51
52   private void verifyState() {
53     if (myShared.isEmpty()) {
54       return;
55     }
56
57     final Collection<RunnerAndConfigurationSettings> sharedConfigurations = getSharedConfigurations();
58     final List<RunnerAndConfigurationSettings> canNotBeShared = new ArrayList<>();
59     final Iterator<RunnerAndConfigurationSettings> iterator = sharedConfigurations.iterator();
60     while (iterator.hasNext()) {
61       final RunnerAndConfigurationSettings configuration = iterator.next();
62       if (!configuration.isShared()) {
63         iterator.remove();
64         canNotBeShared.add(configuration);
65       }
66     }
67     if (! canNotBeShared.isEmpty()) {
68       canNotBeShared.addAll(getLocalConfigurations());
69       setStartupConfigurations(sharedConfigurations, canNotBeShared);
70     }
71   }
72
73   public Collection<RunnerAndConfigurationSettings> getSharedConfigurations() {
74     return getConfigurations(myShared);
75   }
76
77   public Collection<RunnerAndConfigurationSettings> getLocalConfigurations() {
78     return getConfigurations(myLocal);
79   }
80
81   private Collection<RunnerAndConfigurationSettings> getConfigurations(ProjectStartupConfigurationBase configuration) {
82     if (configuration.isEmpty()) return Collections.emptyList();
83
84     final List<RunnerAndConfigurationSettings> result = new ArrayList<>();
85     final List<ProjectStartupConfigurationBase.ConfigurationDescriptor> list = configuration.getList();
86     RunManagerImpl runManager = (RunManagerImpl)RunManager.getInstance(myProject);
87     for (ProjectStartupConfigurationBase.ConfigurationDescriptor descriptor : list) {
88       final RunnerAndConfigurationSettings settings = runManager.getConfigurationById(descriptor.getId());
89       if (settings != null && settings.getName().equals(descriptor.getName())) {
90         result.add(settings);
91       } else {
92         NOTIFICATION_GROUP.createNotification(PREFIX + " Run Configuration '" + descriptor.getName() + "' not found, removed from list.",
93                                               MessageType.WARNING).notify(myProject);
94       }
95     }
96     return result;
97   }
98
99   public void rename(final String oldId, RunnerAndConfigurationSettings settings) {
100     if (myShared.rename(oldId, settings)) {
101       return;
102     }
103     myLocal.rename(oldId, settings);
104
105   }
106
107   public void delete(final String id) {
108     if (myShared.deleteConfiguration(id)) {
109       return;
110     }
111     myLocal.deleteConfiguration(id);
112   }
113
114   public void setStartupConfigurations(final @NotNull Collection<? extends RunnerAndConfigurationSettings> shared,
115                                        final @NotNull Collection<? extends RunnerAndConfigurationSettings> local) {
116     myShared.setConfigurations(shared);
117     myLocal.setConfigurations(local);
118   }
119
120   public boolean isEmpty() {
121     return myShared.isEmpty() && myLocal.isEmpty();
122   }
123
124   public void checkOnChange(RunnerAndConfigurationSettings settings) {
125     if (!settings.isShared()) {
126       final Collection<RunnerAndConfigurationSettings> sharedConfigurations = getSharedConfigurations();
127       if (sharedConfigurations.remove(settings)) {
128         final List<RunnerAndConfigurationSettings> localConfigurations = new ArrayList<>(getLocalConfigurations());
129         localConfigurations.add(settings);
130         setStartupConfigurations(sharedConfigurations, localConfigurations);
131
132         NOTIFICATION_GROUP.createNotification(PREFIX + " configuration was made \"not shared\", since included Run Configuration '" +
133                                               settings.getName() + "' is not shared.", MessageType.WARNING).notify(myProject);
134       }
135     }
136   }
137 }