GTW-4243 Refactor notifications flow for remote sessions

Instead of subscribing to the notification topic and sending received notifications to a client, choose what notification should process the notification.
That would prevent notification duplication in RD mode (we were processing the same notification twice - by backend and local managers)

To achieve described behavior:
- Introduce NotificationRouter as an extension poitn and different implementations for local/controller/guest scenarios. The notification will be processed by the first applicable router
- Move NotificationsListener outside NotificationsManagerImpl, so we could process notifications by the right router
- add showNotification method to NotificationsManager abstract class, so we can access it from outside
- BackendNotificationsHost becomes application-level service (instead of project-level)

Notification routers:
- Guest: If notifications is sent under client's id, we send notification to that client
- Controller: If notification is sent under local id, we check is there an active controller session (we are in RD mode). If there is, we sent all notifications to that controller
- Local: Otherwise we process notification by the local manager

GitOrigin-RevId: c6f9e365c15acfea81145a9acf33ff8bb82013a5
This commit is contained in:
Kate Botsman
2023-07-24 13:40:31 +02:00
committed by intellij-monorepo-bot
parent 20f970b928
commit 3f17df6bc4
8 changed files with 95 additions and 27 deletions

View File

@@ -0,0 +1,27 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.notification
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
/**
* Interface for a notification router.
*
* This interface defines a method for routing a notification to some notification manager
* Implementations of this interface can be registered as extension points.
*/
interface NotificationRouter {
/**
* Routes a notification to some notification manager to be processed
*
* @param notification The notification to be routed.
* @param project The project notification should be shown for
* @return Returns true if the notification is routed to some manager, false otherwise.
*/
fun routeNotification(notification: Notification, project: Project?): Boolean
companion object {
val EP_NAME: ExtensionPointName<NotificationRouter> = ExtensionPointName("com.intellij.notificationRouter")
}
}

View File

@@ -11,6 +11,8 @@ public abstract class NotificationsManager {
return ApplicationManager.getApplication().getService(NotificationsManager.class);
}
public abstract void showNotification(@NotNull Notification notification, @Nullable Project project);
public abstract void expire(@NotNull Notification notification);
public abstract <T extends Notification> T @NotNull [] getNotificationsOfType(@NotNull Class<T> klass, @Nullable Project project);

View File

@@ -0,0 +1,13 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.notification.impl
import com.intellij.notification.Notification
import com.intellij.notification.NotificationRouter
import com.intellij.openapi.project.Project
class LocalNotificationRouter: NotificationRouter {
override fun routeNotification(notification: Notification, project: Project?): Boolean {
NotificationsManagerImpl.getNotificationsManager().showNotification(notification, project)
return true
}
}

View File

@@ -0,0 +1,43 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.notification.impl
import com.intellij.notification.Notification
import com.intellij.notification.NotificationRouter
import com.intellij.notification.Notifications
import com.intellij.notification.impl.NotificationsManagerImpl.isDummyEnvironment
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.ExtensionNotApplicableException
import com.intellij.openapi.project.Project
class NotificationsListener : Notifications {
private val project: Project?
@Suppress("unused")
constructor() {
project = null
}
@Suppress("unused")
private constructor(project: Project?) {
this.project = project
if (isDummyEnvironment()) {
throw ExtensionNotApplicableException.create()
}
}
override fun notify(notification: Notification) {
for (listener in NotificationRouter.EP_NAME.extensionList) {
try {
if (listener.routeNotification(notification, project)) {
return
}
} catch (e: Throwable) {
LOG.error(e)
}
}
}
companion object {
private val LOG = logger<NotificationsListener>()
}
}

View File

@@ -126,7 +126,8 @@ public final class NotificationsManagerImpl extends NotificationsManager {
return ArrayUtil.toObjectArray(result, klass);
}
private void doNotify(Notification notification, @Nullable Project project) {
@Override
public void showNotification(@NotNull Notification notification, @Nullable Project project) {
NotificationsConfigurationImpl configuration = NotificationsConfigurationImpl.getInstanceImpl();
NotificationSettings settings = NotificationsConfigurationImpl.getSettings(notification.getGroupId());
@@ -166,7 +167,7 @@ public final class NotificationsManagerImpl extends NotificationsManager {
}
@RequiresEdt
private void showNotification(Notification notification, @Nullable Project project) {
private void showNotificationInner(Notification notification, @Nullable Project project) {
if (LOG.isDebugEnabled()) LOG.debug("incoming: " + notification + ", project=" + project);
if (myEarlyNotifications != null) {
@@ -297,7 +298,7 @@ public final class NotificationsManagerImpl extends NotificationsManager {
span.setAttribute("project", project.toString());
}
span.setAttribute("notification", notification.toString());
showNotification(notification, project);
showNotificationInner(notification, project);
});
}
@@ -1135,7 +1136,7 @@ public final class NotificationsManagerImpl extends NotificationsManager {
return text.getPreferredSize().height;
}
private static boolean isDummyEnvironment() {
static boolean isDummyEnvironment() {
Application app = ApplicationManager.getApplication();
return app.isUnitTestMode() || app.isCommandLine();
}
@@ -1234,27 +1235,6 @@ public final class NotificationsManagerImpl extends NotificationsManager {
return null;
}
static final class MyNotificationListener implements Notifications {
private final Project project;
@SuppressWarnings("unused")
MyNotificationListener() {
project = null;
}
@SuppressWarnings("unused")
private MyNotificationListener(@Nullable Project project) {
this.project = project;
if (isDummyEnvironment()) {
throw ExtensionNotApplicableException.create();
}
}
@Override
public void notify(@NotNull Notification notification) {
((NotificationsManagerImpl)NotificationsManager.getNotificationsManager()).doNotify(notification, project);
}
}
private static @Nullable Point getCollapsedTextEndLocation(JEditorPane text, BalloonLayoutData layoutData) {
try {

View File

@@ -524,5 +524,6 @@
<extensionPoint name="meetNewUiCustomization" interface="com.intellij.ide.ui.experimental.meetNewUi.MeetNewUiCustomization" dynamic="true"/>
<extensionPoint name="notificationRouter" interface="com.intellij.notification.NotificationRouter" dynamic="true"/>
</extensionPoints>
</idea-plugin>

View File

@@ -1552,6 +1552,8 @@
<toolbarQuickAction implementationClass="com.intellij.openapi.wm.impl.headertoolbar.BuildQuickAction" listGroupID="MainToolbarQuickActions.Run" />
<toolbarQuickAction implementationClass="com.intellij.openapi.wm.impl.headertoolbar.CoverageQuickAction" listGroupID="MainToolbarQuickActions.Run" />
<toolbarQuickAction implementationClass="com.intellij.openapi.wm.impl.headertoolbar.ProfilerQuickAction" listGroupID="MainToolbarQuickActions.Run" />
<notificationRouter implementation="com.intellij.notification.impl.LocalNotificationRouter" order="last"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains">
@@ -1612,7 +1614,7 @@
</applicationListeners>
<projectListeners>
<listener class="com.intellij.notification.impl.NotificationsManagerImpl$MyNotificationListener"
<listener class="com.intellij.notification.impl.NotificationsListener"
activeInHeadlessMode="false"
activeInTestMode="false"
topic="com.intellij.notification.Notifications"/>

View File

@@ -182,7 +182,7 @@
<listener class="com.intellij.notification.impl.NotificationsToolWindowNotificationListener"
topic="com.intellij.notification.Notifications"/>
<listener class="com.intellij.notification.impl.NotificationsManagerImpl$MyNotificationListener"
<listener class="com.intellij.notification.impl.NotificationsListener"
topic="com.intellij.notification.Notifications"/>
<listener class="com.intellij.notification.impl.NotificationsConfigurationImpl$MyNotificationListener"
topic="com.intellij.notification.Notifications"/>