diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
index 7630132ebc5b..5dd9daef4bb4 100644
--- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
+++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
@@ -71,10 +71,28 @@ public abstract class AbstractExternalSystemSettings<
*
* That's why this method allows to wrap given 'generic listener' into external system-specific one.
*
+ * @param subscription is a disposable to unsubscribe from external system settings events
* @param listener target generic listener to wrap to external system-specific implementation
*/
+ public void subscribe(@NotNull Disposable subscription, @NotNull ExternalSystemSettingsListener listener) {
+ subscribe(listener);
+ }
+
+ /**
+ * @deprecated use {@link AbstractExternalSystemSettings#subscribe(Disposable, ExternalSystemSettingsListener)} instead
+ */
+ @Deprecated
public abstract void subscribe(@NotNull ExternalSystemSettingsListener listener);
+ /**
+ * Generic subscribe implementation
+ *
+ * @see AbstractExternalSystemSettings#subscribe(Disposable, ExternalSystemSettingsListener)
+ */
+ protected void doSubscribe(@NotNull Disposable subscription, @NotNull L listener) {
+ getProject().getMessageBus().connect(subscription).subscribe(getChangesTopic(), listener);
+ }
+
public void copyFrom(@NotNull SS settings) {
for (PS projectSettings : settings.getLinkedProjectsSettings()) {
myLinkedProjectsSettings.put(projectSettings.getExternalProjectPath(), projectSettings);
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java
index dfcacdcc5d01..0feb96abeade 100644
--- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java
+++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java
@@ -3,6 +3,7 @@ package com.intellij.openapi.externalSystem.util;
import com.intellij.execution.rmi.RemoteUtil;
import com.intellij.ide.highlighter.ArchiveFileType;
+import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.*;
import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
@@ -683,7 +684,15 @@ public class ExternalSystemApiUtil {
@NotNull ProjectSystemId systemId,
@NotNull ExternalSystemSettingsListener listener) {
//noinspection unchecked
- getSettings(project, systemId).subscribe(listener);
+ getSettings(project, systemId).subscribe(project, listener);
+ }
+
+ public static void subscribe(@NotNull Project project,
+ @NotNull ProjectSystemId systemId,
+ @NotNull Disposable subscription,
+ @NotNull ExternalSystemSettingsListener listener) {
+ //noinspection unchecked
+ getSettings(project, systemId).subscribe(subscription, listener);
}
@NotNull
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
index 5fa145a953f8..d888783ae1ce 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
@@ -1,6 +1,7 @@
// 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 org.jetbrains.plugins.gradle.settings;
+import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
@@ -43,8 +44,12 @@ public class GradleSettings extends AbstractExternalSystemSettings listener) {
- getProject().getMessageBus().connect(getProject()).subscribe(GradleSettingsListener.TOPIC,
- new DelegatingGradleSettingsListenerAdapter(listener));
+ subscribe(getProject(), listener);
+ }
+
+ @Override
+ public void subscribe(@NotNull Disposable subscription, @NotNull ExternalSystemSettingsListener listener) {
+ doSubscribe(subscription, new DelegatingGradleSettingsListenerAdapter(listener));
}
@Override
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleUnlinkedProjectProcessor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleUnlinkedProjectProcessor.kt
index 6f73ed931133..faae53e849c6 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleUnlinkedProjectProcessor.kt
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleUnlinkedProjectProcessor.kt
@@ -6,12 +6,14 @@ import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationType
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationNamesInfo
+import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
+import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
import org.jetbrains.plugins.gradle.service.project.GradleNotification
@@ -30,7 +32,9 @@ class GradleUnlinkedProjectProcessor : StartupActivity, DumbAware {
}
companion object {
- private const val SHOW_UNLINKED_GRADLE_POPUP = "show.inlinked.gradle.project.popup"
+ private const val SHOW_UNLINKED_GRADLE_POPUP = "show.unlinked.gradle.project.popup"
+
+ private val LOG = Logger.getInstance(GradleUnlinkedProjectProcessor::class.java)
private fun showNotification(project: Project) {
if (!GradleSettings.getInstance(project).linkedProjectsSettings.isEmpty()) return
@@ -61,12 +65,15 @@ class GradleUnlinkedProjectProcessor : StartupActivity, DumbAware {
override fun actionPerformed(e: AnActionEvent) {}
}
+ val subscription = Disposer.newDisposable()
val settingsListener = object : GradleSettingsListenerAdapter() {
override fun onProjectsLinked(settings: MutableCollection) {
notification.expire()
+ debug("Unlinked project notification expired")
+ Disposer.dispose(subscription)
}
}
- ExternalSystemApiUtil.subscribe(project, GradleConstants.SYSTEM_ID, settingsListener)
+ ExternalSystemApiUtil.subscribe(project, GradleConstants.SYSTEM_ID, subscription, settingsListener)
notification.notify(project)
}
@@ -82,5 +89,9 @@ class GradleUnlinkedProjectProcessor : StartupActivity, DumbAware {
fun enableNotifications(project: Project) {
PropertiesComponent.getInstance(project).setValue(SHOW_UNLINKED_GRADLE_POPUP, true, false)
}
+
+ private fun debug(message: String) {
+ if (LOG.isDebugEnabled) LOG.debug(message)
+ }
}
}
\ No newline at end of file
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTest.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTest.kt
new file mode 100644
index 000000000000..173880150f1c
--- /dev/null
+++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTest.kt
@@ -0,0 +1,34 @@
+// 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 org.jetbrains.plugins.gradle.settings
+
+import com.intellij.openapi.util.Disposer
+import org.junit.Test
+
+class GradleSubscriptionTest : GradleSubscriptionTestCase() {
+
+ @Test
+ fun `test external system subscription`() {
+ var linkingCounter = 0
+ onProjectLinked {
+ linkingCounter += 1
+ }
+ linkProject()
+ unlinkProject()
+ linkProject()
+ assertEquals(2, linkingCounter)
+ }
+
+ @Test
+ fun `test external system unsubscription`() {
+ var linkingCounter = 0
+ val subscription = Disposer.newDisposable()
+ onProjectLinked(subscription) {
+ linkingCounter += 1
+ Disposer.dispose(subscription)
+ }
+ linkProject()
+ unlinkProject()
+ linkProject()
+ assertEquals(1, linkingCounter)
+ }
+}
\ No newline at end of file
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTestCase.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTestCase.kt
new file mode 100644
index 000000000000..e395c0822bec
--- /dev/null
+++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/settings/GradleSubscriptionTestCase.kt
@@ -0,0 +1,44 @@
+// 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 org.jetbrains.plugins.gradle.settings
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
+import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
+import org.jetbrains.plugins.gradle.importing.GradleBuildScriptBuilderEx
+import org.jetbrains.plugins.gradle.importing.GradleImportingTestCase
+import org.junit.runners.Parameterized
+
+abstract class GradleSubscriptionTestCase : GradleImportingTestCase() {
+
+ protected fun linkProject() {
+ importProject(GradleBuildScriptBuilderEx().withJavaPlugin().generate())
+ }
+
+ protected fun unlinkProject() {
+ val settings = ExternalSystemApiUtil.getSettings(myProject, externalSystemId)
+ settings.unlinkExternalProject(currentExternalProjectSettings.externalProjectPath)
+ }
+
+ protected fun onProjectLinked(subscription: Disposable, listener: () -> Unit) {
+ val settingsListener = object : GradleSettingsListenerAdapter() {
+ override fun onProjectsLinked(settings: MutableCollection) = listener()
+ }
+ ExternalSystemApiUtil.subscribe(myProject, externalSystemId, subscription, settingsListener)
+ }
+
+ protected fun onProjectLinked(listener: () -> Unit) {
+ val settingsListener = object : GradleSettingsListenerAdapter() {
+ override fun onProjectsLinked(settings: MutableCollection) = listener()
+ }
+ ExternalSystemApiUtil.subscribe(myProject, externalSystemId, settingsListener)
+ }
+
+ companion object {
+ /**
+ * It's sufficient to run the test against one gradle version
+ */
+ @Parameterized.Parameters(name = "with Gradle-{0}")
+ @JvmStatic
+ fun tests(): Collection> = arrayListOf(arrayOf(BASE_GRADLE_VERSION))
+ }
+}
\ No newline at end of file