deprecate projectComponentsInitialized in favour of white-listed EP

Another step to protect loading: `ProjectLifecycleListener.projectComponentsInitialized` is deprecated in favour of a new white-listed EP `ProjectServiceContainerInitializedListener`. As for `ProjectServiceContainerCustomizer` (that was introduced for legacy project model), new EP can be extended only by core plugin (runtime error for non-core).

Message bus must be not used for any control events, that’s why EP is used here (as for any such solutions in our new code). Even more — EP is a more simpler and suitable here. EP allows you to control order of execution — yes, if you want specific order, it means that your code is bad. But not possible for now to fix RunManager.

GitOrigin-RevId: 2493a14678d8604193dfff84008882d5ec4d2a24
This commit is contained in:
Vladimir Krivosheev
2020-01-30 13:40:58 +00:00
committed by intellij-monorepo-bot
parent 7b3fba15f8
commit a0e751cc6e
19 changed files with 239 additions and 182 deletions
@@ -1,7 +1,8 @@
# 16
* add `run manager initialization`
* add `projectComponentsInitialized event handling`
* add `RunManager initialization`
* add `projectComponentCreated event handling`
* add `projectComponentCreated`
## 15
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.
package com.intellij.diagnostic.startUpPerformanceReporter
import com.fasterxml.jackson.core.JsonGenerator
@@ -40,7 +40,7 @@ class StartUpPerformanceReporter : StartupActivity, StartUpPerformanceService {
companion object {
internal val LOG = logger<StartUpMeasurer>()
internal const val VERSION = "15"
internal const val VERSION = "16"
internal fun sortItems(items: MutableList<ActivityImpl>) {
items.sortWith(Comparator { o1, o2 ->
@@ -1,32 +1,24 @@
// Copyright 2000-2020 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.
package com.intellij.execution.impl
import com.intellij.diagnostic.runActivity
import com.intellij.execution.IS_RUN_MANAGER_INITIALIZED
import com.intellij.execution.RunManager
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.impl.ProjectLifecycleListener
import com.intellij.openapi.project.ProjectServiceContainerInitializedListener
internal class ProjectRunConfigurationInitializer(project: Project) {
init {
val connection = project.messageBus.connect()
connection.subscribe(ProjectLifecycleListener.TOPIC, object : ProjectLifecycleListener {
override fun projectComponentsInitialized(eventProject: Project) {
if (project === eventProject) {
requestLoadWorkspaceAndProjectRunConfiguration(project)
}
}
})
}
private fun requestLoadWorkspaceAndProjectRunConfiguration(project: Project) {
private class ProjectRunConfigurationInitializer : ProjectServiceContainerInitializedListener {
override fun serviceCreated(project: Project) {
if (IS_RUN_MANAGER_INITIALIZED.get(project) == true) {
return
}
IS_RUN_MANAGER_INITIALIZED.set(project, true)
// we must not fire beginUpdate here, because message bus will fire queued parent message bus messages (and, so, SOE may occur because all other projectOpened will be processed before us)
// simply, you should not listen changes until project opened
project.service<RunManager>()
runActivity("RunManager initialization") {
IS_RUN_MANAGER_INITIALIZED.set(project, true)
// we must not fire beginUpdate here, because message bus will fire queued parent message bus messages (and, so, SOE may occur because all other projectOpened will be processed before us)
// simply, you should not listen changes until project opened
project.service<RunManager>()
}
}
}
@@ -2,7 +2,6 @@
package com.intellij.execution
import com.intellij.execution.configurations.*
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
@@ -24,7 +23,7 @@ abstract class RunManager {
// https://gist.github.com/develar/5bcf39b3f0ec08f507ec112d73375f2b
LOG.debug("Must be not called before project components initialized")
}
return ServiceManager.getService(project, RunManager::class.java)
return project.getService(RunManager::class.java)
}
@JvmStatic
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.
package com.intellij.openapi.module.impl;
import com.intellij.ProjectTopics;
@@ -11,11 +11,12 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.UnknownModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.project.impl.ProjectLifecycleListener;
import com.intellij.openapi.project.ProjectServiceContainerInitializedListener;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
@@ -25,9 +26,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author yole
*/
@State(
name = ModuleManagerImpl.COMPONENT_NAME,
storages = @Storage("modules.xml"),
@@ -48,21 +46,25 @@ public class ModuleManagerComponent extends ModuleManagerImpl {
return;
}
myMessageBusConnection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() {
@Override
public void projectComponentsInitialized(@NotNull final Project project) {
if (project != myProject) return;
Activity activity = StartUpMeasurer.startMainActivity("module loading");
loadModules(myModuleModel);
activity.end();
activity.setDescription("module count: " + myModuleModel.getModules().length);
}
});
myMessageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new ModuleFileListener(this));
}
static class MyProjectServiceContainerInitializedListener implements ProjectServiceContainerInitializedListener {
@Override
public void serviceCreated(@NotNull Project project) {
Activity activity = StartUpMeasurer.startMainActivity("module loading");
ModuleManager moduleManager = getInstance(project);
if (!(moduleManager instanceof ModuleManagerImpl)) {
return;
}
ModuleManagerImpl manager = (ModuleManagerImpl)moduleManager;
manager.loadModules(manager.myModuleModel);
activity.end();
activity.setDescription("module count: " + manager.myModuleModel.getModules().length);
}
}
@Override
protected void unloadNewlyAddedModulesIfPossible(@NotNull Set<ModulePath> modulesToLoad, @NotNull List<UnloadedModuleDescriptionImpl> modulesToUnload) {
UnloadedModulesListChange change = AutomaticModuleUnloader.getInstance(myProject).processNewModules(modulesToLoad, modulesToUnload);
@@ -1,7 +1,8 @@
// 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.
// Copyright 2000-2020 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.
package com.intellij.ide;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.ApiStatus;
/**
* Use extension point {@code com.intellij.applicationInitializedListener} to register listener.
@@ -9,6 +10,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
* <p>
* Not part of {@link ApplicationLoadListener} to avoid class loading before application initialization.
*/
@ApiStatus.Internal
public interface ApplicationInitializedListener {
/**
* Invoked when all application level components are initialized in the same thread where components are initializing (EDT is not guaranteed).
@@ -1,27 +0,0 @@
// 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.
package com.intellij.openapi.project
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.impl.ExtensionPointImpl
import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl
import org.jetbrains.annotations.ApiStatus
/**
* Usage requires IJ Platform team approval (including plugin into white-list).
*/
@ApiStatus.Internal
interface ProjectServiceContainerCustomizer {
companion object {
@JvmStatic
fun getEp(): ExtensionPointImpl<ProjectServiceContainerCustomizer> {
return (ApplicationManager.getApplication().extensionArea as ExtensionsAreaImpl)
.getExtensionPoint("com.intellij.projectServiceContainerCustomizer")
}
}
/**
* Invoked after implementation classes for project's components were determined (and loaded),
* but before components are instantiated.
*/
fun serviceContainerInitialized(project: Project)
}
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.
package com.intellij.openapi.project.impl;
import com.intellij.openapi.project.Project;
@@ -13,6 +13,10 @@ import org.jetbrains.annotations.NotNull;
public interface ProjectLifecycleListener {
Topic<ProjectLifecycleListener> TOPIC = Topic.create("Various stages of project lifecycle notifications", ProjectLifecycleListener.class);
/**
* @deprecated Do not use.
*/
@Deprecated
default void projectComponentsInitialized(@NotNull Project project) {
}
@@ -8,6 +8,7 @@ import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ComponentConfig;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.ProjectLoadHelper;
import com.intellij.openapi.project.impl.ProjectImpl;
import com.intellij.openapi.project.impl.ProjectLifecycleListener;
import com.intellij.openapi.roots.ProjectFileIndex;
@@ -21,8 +22,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
class LightEditProject extends ProjectImpl {
final class LightEditProject extends ProjectImpl {
private static final Logger LOG = Logger.getInstance(LightEditProject.class);
private static final String NAME = "LightEditProject";
private static final Set<String> ALLOWED_CLASSES = ContainerUtil.newHashSet(
@@ -38,7 +38,8 @@ class LightEditProject extends ProjectImpl {
private LightEditProject(@NotNull Path projectPath) {
super(projectPath, NAME);
registerComponents();
ProjectLoadHelper.registerComponents(this);
customizeRegisteredComponents();
getStateStore().setPath(projectPath, false, null);
init(null);
@@ -2,8 +2,6 @@
package com.intellij.openapi.project.impl;
import com.intellij.configurationStore.StoreUtil;
import com.intellij.diagnostic.Activity;
import com.intellij.diagnostic.StartUpMeasurer;
import com.intellij.ide.plugins.ContainerDescriptor;
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
import com.intellij.ide.plugins.PluginManagerCore;
@@ -22,11 +20,10 @@ import com.intellij.openapi.components.impl.stores.IProjectStore;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.impl.ModuleManagerImpl;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectLoadHelper;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectServiceContainerCustomizer;
import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.startup.StartupManager;
@@ -257,37 +254,6 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project
return workspaceFilePath == null ? null : LocalFileSystem.getInstance().findFileByPath(workspaceFilePath);
}
public final void registerComponents() {
String activityNamePrefix = activityNamePrefix();
Activity activity = (activityNamePrefix == null || !StartUpMeasurer.isEnabled()) ? null : StartUpMeasurer.startMainActivity(activityNamePrefix + StartUpMeasurer.Activities.REGISTER_COMPONENTS_SUFFIX);
// at this point of time plugins are already loaded by application - no need to pass indicator to getLoadedPlugins call
//noinspection unchecked
registerComponents((List<IdeaPluginDescriptorImpl>)PluginManagerCore.getLoadedPlugins(), false);
if (activity != null) {
activity = activity.endAndStart("projectComponentRegistered");
}
ProjectServiceContainerCustomizer.getEp().processWithPluginDescriptor((customizer, pluginDescriptor) -> {
if (pluginDescriptor.getPluginId() != PluginManagerCore.CORE_ID) {
LOG.error("Plugin " + pluginDescriptor + " is not approved to add ProjectServiceContainerCustomizer");
}
try {
customizer.serviceContainerInitialized(this);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Throwable e) {
LOG.error(e);
}
});
if (activity != null) {
activity.end();
}
}
public void init(@Nullable ProgressIndicator indicator) {
Application application = ApplicationManager.getApplication();
@@ -311,7 +277,8 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project
if (myName == null) {
myName = getStateStore().getProjectName();
}
application.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(this);
ProjectLoadHelper.notifyThatComponentCreated(this);
}
@Override
@@ -308,7 +308,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
boolean succeed = false;
try {
project.registerComponents();
ProjectLoadHelper.registerComponents(project);
project.getStateStore().setPath(file, isRefreshVfsNeeded, template);
project.init(indicator);
succeed = true;
@@ -0,0 +1,108 @@
// Copyright 2000-2020 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.
package com.intellij.openapi.project
import com.intellij.diagnostic.Activity
import com.intellij.diagnostic.PluginException
import com.intellij.diagnostic.StartUpMeasurer
import com.intellij.diagnostic.StartUpMeasurer.Activities
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.impl.ExtensionPointImpl
import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.impl.ProjectImpl
import com.intellij.openapi.project.impl.ProjectLifecycleListener
import org.jetbrains.annotations.ApiStatus
// Code maybe located in a ProjectImpl, but it is not possible due to non-technical reasons to convert ProjectImpl into modern language.
// Wrap into class as it is not possible to use internal modifier for top-level functions from Java (but we have to reduce scope).
@ApiStatus.Internal
internal class ProjectLoadHelper {
companion object {
@JvmStatic
fun registerComponents(project: ProjectImpl) {
var activity = createActivity(project) { "project ${Activities.REGISTER_COMPONENTS_SUFFIX}" }
// at this point of time plugins are already loaded by application - no need to pass indicator to getLoadedPlugins call
@Suppress("UNCHECKED_CAST")
project.registerComponents(PluginManagerCore.getLoadedPlugins() as List<IdeaPluginDescriptorImpl>, notifyListeners = false)
activity = activity?.endAndStart("projectComponentRegistered")
runHandler(ProjectServiceContainerCustomizer.getEp()) {
it.serviceRegistered(project)
}
activity?.end()
}
@JvmStatic
fun notifyThatComponentCreated(project: ProjectImpl) {
var activity = createActivity(project) { "projectComponentCreated event handling" }
val app = ApplicationManager.getApplication()
@Suppress("DEPRECATION")
app.messageBus.syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(project)
activity = activity?.endAndStart("projectComponentCreated")
runHandler((app.extensionArea as ExtensionsAreaImpl).getExtensionPoint<ProjectServiceContainerInitializedListener>("com.intellij.projectServiceContainerInitializedListener")) {
it.serviceCreated(project)
}
activity?.end()
}
}
}
private val LOG = logger<ProjectImpl>()
private inline fun createActivity(project: ProjectImpl, message: () -> String): Activity? {
return if (project.isDefault || !StartUpMeasurer.isEnabled()) null else StartUpMeasurer.startActivity(message())
}
private inline fun <T : Any> runHandler(ep: ExtensionPointImpl<T>, crossinline executor: (T) -> Unit) {
ep.processWithPluginDescriptor { handler, pluginDescriptor ->
if (pluginDescriptor.pluginId != PluginManagerCore.CORE_ID) {
LOG.error(PluginException("Plugin $pluginDescriptor is not approved to add ${ep.name}", pluginDescriptor.pluginId))
}
try {
executor(handler)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: Throwable) {
LOG.error(PluginException(e, pluginDescriptor.pluginId))
}
}
}
/**
* Usage requires IJ Platform team approval (including plugin into white-list).
*/
@ApiStatus.Internal
interface ProjectServiceContainerCustomizer {
companion object {
@JvmStatic
fun getEp(): ExtensionPointImpl<ProjectServiceContainerCustomizer> {
return (ApplicationManager.getApplication().extensionArea as ExtensionsAreaImpl)
.getExtensionPoint("com.intellij.projectServiceContainerCustomizer")
}
}
/**
* Invoked after implementation classes for project's components were determined (and loaded),
* but before components are instantiated.
*/
fun serviceRegistered(project: Project)
}
/**
* Usage requires IJ Platform team approval (including plugin into white-list).
*/
@ApiStatus.Internal
interface ProjectServiceContainerInitializedListener {
/**
* Invoked after implementation classes for project's components were determined (and loaded),
* but before components are instantiated.
*/
fun serviceCreated(project: Project)
}
@@ -1,5 +1,7 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<projectServiceContainerInitializedListener implementation="com.intellij.openapi.module.impl.ModuleManagerComponent$MyProjectServiceContainerInitializedListener" order="first"/>
<applicationService serviceInterface="com.intellij.openapi.project.impl.ProjectStoreFactory"
serviceImplementation="com.intellij.configurationStore.PlatformLangProjectStoreFactory"
overrides="true"/>
@@ -346,7 +346,9 @@
<extensionPoint name="editorFactoryMouseMotionListener" interface="com.intellij.openapi.editor.event.EditorMouseMotionListener" dynamic="true"/>
<extensionPoint name="projectCloseHandler" interface="com.intellij.openapi.project.ProjectCloseHandler"/>
<extensionPoint name="projectServiceContainerCustomizer" interface="com.intellij.openapi.project.ProjectServiceContainerCustomizer"/>
<extensionPoint name="projectServiceContainerCustomizer" interface="com.intellij.openapi.project.ProjectServiceContainerCustomizer" dynamic="true"/>
<extensionPoint name="projectServiceContainerInitializedListener" interface="com.intellij.openapi.project.ProjectServiceContainerInitializedListener" dynamic="true"/>
<extensionPoint name="actionConfigurationCustomizer" interface="com.intellij.openapi.actionSystem.impl.ActionConfigurationCustomizer" dynamic="false"/>
@@ -533,6 +533,7 @@
<projectService serviceInterface="com.intellij.execution.RunManager" serviceImplementation="com.intellij.execution.impl.RunManagerImpl"/>
<projectService serviceImplementation="com.intellij.execution.impl.IprRunManagerImpl"/>
<projectServiceContainerInitializedListener implementation="com.intellij.execution.impl.ProjectRunConfigurationInitializer" order="last"/>
<fileEditorProvider implementation="com.intellij.openapi.fileEditor.impl.http.HttpFileEditorProvider"/>
<editorActionHandler action="EditorEscape" implementationClass="com.intellij.codeInsight.hint.EscapeHandler" id="hide-hints"/>
@@ -21,10 +21,6 @@
<implementation-class>com.intellij.psi.search.scope.packageSet.UpdatingScopeOnProjectStructureChangeListener</implementation-class>
</component>
<component>
<implementation-class>com.intellij.execution.impl.ProjectRunConfigurationInitializer</implementation-class>
</component>
<component>
<interface-class>com.intellij.openapi.fileEditor.FileEditorManager</interface-class>
<implementation-class>com.intellij.openapi.fileEditor.impl.PsiAwareFileEditorManagerImpl</implementation-class>
@@ -9,8 +9,8 @@
<cachesInvalidator implementation="com.intellij.workspace.ide.WorkspaceModelCachesInvalidator"/>
<projectServiceContainerCustomizer
implementation="com.intellij.workspace.legacyBridge.intellij.LegacyBridgeProjectLifecycleListener"/>
<projectServiceContainerCustomizer implementation="com.intellij.workspace.legacyBridge.intellij.LegacyBridgeProjectLifecycleListener"/>
<projectServiceContainerInitializedListener implementation="com.intellij.workspace.legacyBridge.intellij.LegacyBridgeModuleManagerComponent$MyProjectServiceContainerInitializedListener"/>
<applicationService serviceInterface="com.intellij.configurationStore.StoreReloadManager"
serviceImplementation="com.intellij.workspace.jps.LegacyBridgeStoreReloadManager"
@@ -1,3 +1,4 @@
// Copyright 2000-2020 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.
package com.intellij.workspace.legacyBridge.intellij
import com.intellij.ProjectTopics
@@ -8,7 +9,6 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.components.stateStore
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
@@ -16,7 +16,9 @@ import com.intellij.openapi.diagnostic.runAndLogException
import com.intellij.openapi.module.*
import com.intellij.openapi.module.impl.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.impl.ProjectLifecycleListener
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.ProjectManagerListener
import com.intellij.openapi.project.ProjectServiceContainerInitializedListener
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
import com.intellij.openapi.util.Disposer
@@ -38,58 +40,61 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
@Suppress("ComponentNotRegistered")
class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleManagerEx(), ProjectComponent, Disposable {
class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleManagerEx(), Disposable {
val outOfTreeModulesPath: String =
FileUtilRt.toSystemIndependentName(File(PathManager.getTempPath(), "outOfTreeProjectModules-${project.locationHash}").path)
private val LOG = Logger.getInstance(javaClass)
private val idToModule: ConcurrentMap<ModuleId, LegacyBridgeModule> = ConcurrentHashMap()
internal val unloadedModules: MutableMap<String, UnloadedModuleDescriptionImpl> = mutableMapOf()
private val newModuleInstances = mutableMapOf<ModuleId, LegacyBridgeModule>()
override fun dispose() {
val modules = modulesMap.values.toList()
modulesMap.clear()
val modules = idToModule.values.toList()
idToModule.clear()
for (module in modules) {
Disposer.dispose(module)
}
}
private val modulesMap: ConcurrentMap<ModuleId, LegacyBridgeModule> = ConcurrentHashMap()
internal val unloadedModules: MutableMap<String, UnloadedModuleDescriptionImpl> = mutableMapOf()
private val newModuleInstances = mutableMapOf<ModuleId, LegacyBridgeModule>()
internal class MyProjectServiceContainerInitializedListener : ProjectServiceContainerInitializedListener {
override fun serviceCreated(project: Project) {
val manager = ModuleManagerComponent.getInstance(project) as? LegacyBridgeModuleManagerComponent ?: return
@ApiStatus.Internal
internal fun setNewModuleInstances(addedInstances: List<LegacyBridgeModule>) {
if (newModuleInstances.isNotEmpty()) error("newModuleInstances are not empty")
for (instance in addedInstances) {
newModuleInstances[instance.moduleEntityId] = instance
val unloadedNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet()
val entities = manager.entityStore.current.entities(ModuleEntity::class.java)
.filter { !unloadedNames.contains(it.name) }
.toList()
manager.loadModules(entities)
}
}
private fun getModuleRootComponentByLibrary(entity: LibraryEntity): LegacyBridgeModuleRootComponent {
val tableId = entity.tableId as LibraryTableId.ModuleLibraryTableId
val module = modulesMap[tableId.moduleId] ?: error("Could not find module for module library: ${entity.persistentId()}")
return LegacyBridgeModuleRootComponent.getInstance(module)
}
init {
// default project doesn't have modules
if (!project.isDefault) {
val myMessageBusConnection = project.messageBus.connect(this)
myMessageBusConnection.subscribe(ProjectLifecycleListener.TOPIC, object : ProjectLifecycleListener {
override fun projectComponentsInitialized(listenedProject: Project) {
if (project !== listenedProject) return
val busConnection = project.messageBus.connect(this)
busConnection.subscribe(ProjectManager.TOPIC, object : ProjectManagerListener {
override fun projectOpened(eventProject: Project) {
if (project == eventProject) {
fireModulesAdded()
for (module in idToModule.values) {
(module as ModuleEx).projectOpened()
}
}
}
val unloadedNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet()
val entities = entityStore.current.entities(ModuleEntity::class.java)
.filter { !unloadedNames.contains(it.name) }
.toList()
loadModules(entities)
override fun projectClosed(eventProject: Project) {
if (project == eventProject) {
for (module in idToModule.values) {
(module as ModuleEx).projectClosed()
}
}
}
})
myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
busConnection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
override fun changed(event: EntityStoreChanged) = LOG.bracket("ModuleManagerComponent.EntityStoreChange") {
val moduleLibraryChanges = event.getChanges(LibraryEntity::class.java).filterModuleLibraryChanges()
val changes = event.getChanges(ModuleEntity::class.java)
@@ -152,7 +157,8 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
alreadyCreatedModule.diff = null
addModule(alreadyCreatedModule)
alreadyCreatedModule
} else {
}
else {
if (change.entity.name in unloadedModules.keys) {
// Skip unloaded modules if it was not added via API
continue@nextChange
@@ -174,7 +180,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
unloadedModulesSet.remove(change.newEntity.name)
unloadedModules.remove(change.newEntity.name)
renameModule(oldId, newId)
oldModuleNames[modulesMap.getValue(newId)] = oldId.name
oldModuleNames[idToModule.getValue(newId)] = oldId.name
}
}
}
@@ -237,12 +243,26 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
}
}
})
myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project))
busConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project))
}
}
@ApiStatus.Internal
internal fun setNewModuleInstances(addedInstances: List<LegacyBridgeModule>) {
if (newModuleInstances.isNotEmpty()) error("newModuleInstances are not empty")
for (instance in addedInstances) {
newModuleInstances[instance.moduleEntityId] = instance
}
}
private fun getModuleRootComponentByLibrary(entity: LibraryEntity): LegacyBridgeModuleRootComponent {
val tableId = entity.tableId as LibraryTableId.ModuleLibraryTableId
val module = idToModule[tableId.moduleId] ?: error("Could not find module for module library: ${entity.persistentId()}")
return LegacyBridgeModuleRootComponent.getInstance(module)
}
internal fun addModule(moduleEntity: ModuleEntity): LegacyBridgeModule {
if (modulesMap.containsKey(moduleEntity.persistentId())) {
if (idToModule.containsKey(moduleEntity.persistentId())) {
error("Module ${moduleEntity.name} (id:'${moduleEntity.persistentId()}') is already added")
}
@@ -252,27 +272,27 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
}
internal fun addModule(module: LegacyBridgeModule) {
val oldValue = modulesMap.put(module.moduleEntityId, module)
val oldValue = idToModule.put(module.moduleEntityId, module)
if (oldValue != null) {
LOG.warn("Duplicate module name: ${module.name}")
}
}
internal fun removeModuleAndFireEvent(moduleEntityId: ModuleId) {
val moduleImpl = modulesMap.remove(moduleEntityId) ?: error("Module $moduleEntityId does not exist")
val moduleImpl = idToModule.remove(moduleEntityId) ?: error("Module $moduleEntityId does not exist")
project.messageBus.syncPublisher(ProjectTopics.MODULES).moduleRemoved(project, moduleImpl)
Disposer.dispose(moduleImpl)
}
internal fun fireBeforeModuleRemoved(moduleEntityId: ModuleId) {
val moduleImpl = modulesMap[moduleEntityId] ?: error("Module $moduleEntityId does not exist")
val moduleImpl = idToModule[moduleEntityId] ?: error("Module $moduleEntityId does not exist")
project.messageBus.syncPublisher(ProjectTopics.MODULES).beforeModuleRemoved(project, moduleImpl)
}
internal fun renameModule(oldId: ModuleId, newId: ModuleId) {
val moduleImpl = modulesMap.remove(oldId) ?: error("Module $oldId does not exist")
val moduleImpl = idToModule.remove(oldId) ?: error("Module $oldId does not exist")
val replacedModuleImplById = modulesMap.put(newId, moduleImpl)
val replacedModuleImplById = idToModule.put(newId, moduleImpl)
if (replacedModuleImplById != null) {
error("ModuleId $newId already exists")
}
@@ -289,7 +309,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
override fun moduleGraph(): Graph<Module> = moduleGraph(includeTests = true)
override fun moduleGraph(includeTests: Boolean): Graph<Module> {
return GraphGenerator.generate(CachingSemiGraph.cache(object : InboundSemiGraph<Module> {
override fun getNodes(): Collection<Module> = this@LegacyBridgeModuleManagerComponent.modulesMap.values.toMutableList()
override fun getNodes(): Collection<Module> = this@LegacyBridgeModuleManagerComponent.idToModule.values.toMutableList()
override fun getIn(m: Module): Iterator<Module> {
val dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests)
@@ -319,22 +339,8 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
}
}
override fun projectOpened() {
fireModulesAdded()
for (module in modulesMap.values) {
(module as ModuleEx).projectOpened()
}
}
override fun projectClosed() {
for (module in modulesMap.values) {
(module as ModuleEx).projectClosed()
}
}
private fun fireModulesAdded() {
for (module in modulesMap.values) {
for (module in idToModule.values) {
fireModuleAddedInWriteAction(module as ModuleEx)
}
}
@@ -386,7 +392,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
ModuleRootManager.getInstance(module).isDependsOn(onModule)
override fun getAllModuleDescriptions(): MutableCollection<ModuleDescription> =
(modulesMap.values.map { module ->
(idToModule.values.map { module ->
object : ModuleDescription {
override fun getName(): String = module.name
@@ -427,17 +433,17 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
return getUnloadedModuleDescription(moduleEntity)
}
override fun getModules(): Array<Module> = modulesMap.values.toTypedArray()
override fun getModules(): Array<Module> = idToModule.values.toTypedArray()
private val sortedModulesValue = CachedValueWithParameter<Set<ModuleId>, Array<Module>> { _, _ ->
val allModules = modulesMap.values.toTypedArray<Module>()
val allModules = idToModule.values.toTypedArray<Module>()
Arrays.sort(allModules, moduleDependencyComparator())
return@CachedValueWithParameter allModules
}
override fun getSortedModules(): Array<Module> = entityStore.cachedValue(sortedModulesValue, modulesMap.keys.toSet())
override fun getSortedModules(): Array<Module> = entityStore.cachedValue(sortedModulesValue, idToModule.keys.toSet())
override fun findModuleByName(name: String): Module? = modulesMap[ModuleId(name)]
override fun findModuleByName(name: String): Module? = idToModule[ModuleId(name)]
override fun disposeModule(module: Module) = ApplicationManager.getApplication().runWriteAction {
val modifiableModel = modifiableModel
@@ -567,8 +573,9 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
companion object {
@JvmStatic
fun getInstance(project: Project): LegacyBridgeModuleManagerComponent =
ModuleManagerComponent.getInstance(project) as LegacyBridgeModuleManagerComponent
fun getInstance(project: Project): LegacyBridgeModuleManagerComponent {
return ModuleManagerComponent.getInstance(project) as LegacyBridgeModuleManagerComponent
}
private fun List<EntityChange<LibraryEntity>>.filterModuleLibraryChanges() =
filter {
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.
package com.intellij.workspace.legacyBridge.intellij
import com.intellij.ide.plugins.PluginManagerCore
@@ -30,7 +30,7 @@ class LegacyBridgeProjectLifecycleListener : ProjectServiceContainerCustomizer {
fun enabled(project: Project) = ModuleManager.getInstance(project) is LegacyBridgeModuleManagerComponent
}
override fun serviceContainerInitialized(project: Project) {
override fun serviceRegistered(project: Project) {
val enabled = Registry.`is`(ENABLED_REGISTRY_KEY) || WorkspaceModelInitialTestContent.peek() != null
if (!enabled) {
LOG.info("Using legacy project model to open project")