mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
External system notification manager Project leak fix
This commit is contained in:
+120
-92
@@ -52,9 +52,10 @@ import com.intellij.ui.content.ContentFactory;
|
||||
import com.intellij.ui.content.MessageView;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.concurrency.SequentialTaskExecutor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.update.MergingUpdateQueue;
|
||||
import com.intellij.util.ui.update.Update;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -63,7 +64,6 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* This class is responsible for ide user by external system integration-specific events.
|
||||
@@ -77,11 +77,10 @@ import java.util.concurrent.ExecutorService;
|
||||
* @since 3/21/12 4:04 PM
|
||||
*/
|
||||
public class ExternalSystemNotificationManager implements Disposable {
|
||||
|
||||
@NotNull private static final Key<Pair<NotificationSource, ProjectSystemId>> CONTENT_ID_KEY = Key.create("CONTENT_ID");
|
||||
|
||||
@NotNull private final ExecutorService myUpdater = SequentialTaskExecutor.createSequentialApplicationPoolExecutor("ExternalSystemNotificationManager pool");
|
||||
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final MergingUpdateQueue myUpdateQueue;
|
||||
@SuppressWarnings("StatefulEp") @Nullable private volatile Project myProject;
|
||||
@NotNull private final Set<Notification> myNotifications;
|
||||
@NotNull private final Map<Key, Notification> myUniqueNotifications;
|
||||
@NotNull private final Set<ProjectSystemId> initializedExternalSystem;
|
||||
@@ -93,6 +92,7 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
myUniqueNotifications = ContainerUtil.newConcurrentMap();
|
||||
initializedExternalSystem = ContainerUtil.newConcurrentSet();
|
||||
myMessageCounter = new MessageCounter();
|
||||
myUpdateQueue = new MergingUpdateQueue(getClass() + " updates", 500, true, null, this, null, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -103,9 +103,12 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
public void processExternalProjectRefreshError(@NotNull Throwable error,
|
||||
@NotNull String externalProjectName,
|
||||
@NotNull ProjectSystemId externalSystemId) {
|
||||
if (myProject.isDisposed() || !myProject.isOpen()) {
|
||||
if (isDisposedOrNotOpen()) {
|
||||
return;
|
||||
}
|
||||
assert myProject != null;
|
||||
Project project = myProject;
|
||||
|
||||
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
|
||||
if (!(manager instanceof ExternalSystemConfigurableAware)) {
|
||||
return;
|
||||
@@ -138,10 +141,10 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
if (!externalSystemId.equals(targetExternalSystemId) && !targetExternalSystemId.equals(ProjectSystemId.IDE)) {
|
||||
continue;
|
||||
}
|
||||
extension.customize(notificationData, myProject, error);
|
||||
extension.customize(notificationData, project, error);
|
||||
}
|
||||
|
||||
EditorNotifications.getInstance(myProject).updateAllNotifications();
|
||||
EditorNotifications.getInstance(project).updateAllNotifications();
|
||||
showNotification(externalSystemId, notificationData);
|
||||
}
|
||||
|
||||
@@ -157,6 +160,7 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
public void showNotification(@NotNull final ProjectSystemId externalSystemId,
|
||||
@NotNull final NotificationData notificationData,
|
||||
@Nullable Key<String> notificationKey) {
|
||||
Disposer.register(this, notificationData);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
PrintStream out = notificationData.getNotificationCategory() == NotificationCategory.INFO ? System.out : System.err;
|
||||
@@ -165,54 +169,60 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
}
|
||||
|
||||
if (notificationKey != null && isNotificationActive(notificationKey)) return;
|
||||
myUpdater.submit(() -> {
|
||||
if (myProject.isDisposed()) return;
|
||||
myUpdateQueue.queue(new Update(new Object()) {
|
||||
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
Runnable action = () -> {
|
||||
if (!initializedExternalSystem.contains(externalSystemId)) {
|
||||
app.runWriteAction(() -> {
|
||||
if (myProject.isDisposed()) return;
|
||||
ExternalSystemUtil.ensureToolWindowContentInitialized(myProject, externalSystemId);
|
||||
initializedExternalSystem.add(externalSystemId);
|
||||
});
|
||||
}
|
||||
if (myProject.isDisposed()) return;
|
||||
NotificationGroup group;
|
||||
if (notificationData.getBalloonGroup() == null) {
|
||||
ExternalProjectsView externalProjectsView =
|
||||
ExternalProjectsManagerImpl.getInstance(myProject).getExternalProjectsView(externalSystemId);
|
||||
group = externalProjectsView instanceof ExternalProjectsViewImpl ?
|
||||
((ExternalProjectsViewImpl)externalProjectsView).getNotificationGroup() : null;
|
||||
}
|
||||
else {
|
||||
final NotificationGroup registeredGroup = NotificationGroup.findRegisteredGroup(notificationData.getBalloonGroup());
|
||||
group = registeredGroup != null ? registeredGroup : NotificationGroup.balloonGroup(notificationData.getBalloonGroup());
|
||||
}
|
||||
if (group == null) return;
|
||||
@Override
|
||||
public void run() {
|
||||
if (isDisposedOrNotOpen()) return;
|
||||
assert myProject != null;
|
||||
Project project = myProject;
|
||||
|
||||
final Notification notification = group.createNotification(
|
||||
notificationData.getTitle(), notificationData.getMessage(),
|
||||
notificationData.getNotificationCategory().getNotificationType(), notificationData.getListener());
|
||||
|
||||
if (notificationKey == null) {
|
||||
myNotifications.add(notification);
|
||||
}
|
||||
else {
|
||||
Notification oldNotification = myUniqueNotifications.put(notificationKey, notification);
|
||||
if (oldNotification != null) {
|
||||
oldNotification.expire();
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
Runnable action = () -> {
|
||||
if (!initializedExternalSystem.contains(externalSystemId)) {
|
||||
app.runWriteAction(() -> {
|
||||
if (isDisposedOrNotOpen()) return;
|
||||
ExternalSystemUtil.ensureToolWindowContentInitialized(project, externalSystemId);
|
||||
initializedExternalSystem.add(externalSystemId);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isDisposedOrNotOpen()) return;
|
||||
NotificationGroup group;
|
||||
if (notificationData.getBalloonGroup() == null) {
|
||||
ExternalProjectsView externalProjectsView =
|
||||
ExternalProjectsManagerImpl.getInstance(project).getExternalProjectsView(externalSystemId);
|
||||
group = externalProjectsView instanceof ExternalProjectsViewImpl ?
|
||||
((ExternalProjectsViewImpl)externalProjectsView).getNotificationGroup() : null;
|
||||
}
|
||||
else {
|
||||
final NotificationGroup registeredGroup = NotificationGroup.findRegisteredGroup(notificationData.getBalloonGroup());
|
||||
group = registeredGroup != null ? registeredGroup : NotificationGroup.balloonGroup(notificationData.getBalloonGroup());
|
||||
}
|
||||
if (group == null) return;
|
||||
|
||||
if (notificationData.isBalloonNotification()) {
|
||||
applyNotification(notification);
|
||||
}
|
||||
else {
|
||||
addMessage(notification, externalSystemId, notificationData);
|
||||
}
|
||||
};
|
||||
app.invokeLater(action, ModalityState.defaultModalityState(), myProject.getDisposed());
|
||||
final Notification notification = group.createNotification(
|
||||
notificationData.getTitle(), notificationData.getMessage(),
|
||||
notificationData.getNotificationCategory().getNotificationType(), notificationData.getListener());
|
||||
|
||||
if (notificationKey == null) {
|
||||
myNotifications.add(notification);
|
||||
}
|
||||
else {
|
||||
Notification oldNotification = myUniqueNotifications.put(notificationKey, notification);
|
||||
if (oldNotification != null) {
|
||||
oldNotification.expire();
|
||||
}
|
||||
}
|
||||
|
||||
if (notificationData.isBalloonNotification()) {
|
||||
applyNotification(notification);
|
||||
}
|
||||
else {
|
||||
addMessage(notification, externalSystemId, notificationData);
|
||||
}
|
||||
};
|
||||
app.invokeLater(action, ModalityState.defaultModalityState(), project.getDisposed());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -231,46 +241,52 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
myMessageCounter.remove(groupName, notificationSource, externalSystemId);
|
||||
if(ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
|
||||
myUpdater.submit(() -> {
|
||||
if (myProject.isDisposed()) return;
|
||||
for (Iterator<Notification> iterator = myNotifications.iterator(); iterator.hasNext(); ) {
|
||||
Notification notification = iterator.next();
|
||||
if (groupName == null || groupName.equals(notification.getGroupId())) {
|
||||
notification.expire();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
final Pair<NotificationSource, ProjectSystemId> contentIdPair = Pair.create(notificationSource, externalSystemId);
|
||||
myUpdateQueue.queue(new Update(new Object()) {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isDisposedOrNotOpen()) return;
|
||||
assert myProject != null;
|
||||
Project project = myProject;
|
||||
|
||||
List<Key> toRemove = new SmartList<>();
|
||||
myUniqueNotifications.forEach((key, notification) -> {
|
||||
if (groupName == null || groupName.equals(notification.getGroupId())) {
|
||||
notification.expire();
|
||||
toRemove.add(key);
|
||||
}
|
||||
});
|
||||
toRemove.forEach(myUniqueNotifications::remove);
|
||||
|
||||
final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
|
||||
if (toolWindow == null) return;
|
||||
|
||||
final Pair<NotificationSource, ProjectSystemId> contentIdPair = Pair.create(notificationSource, externalSystemId);
|
||||
final MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
|
||||
UIUtil.invokeLaterIfNeeded(() -> {
|
||||
if (myProject.isDisposed()) return;
|
||||
for (Content content : messageView.getContentManager().getContents()) {
|
||||
if (!content.isPinned() && contentIdPair.equals(content.getUserData(CONTENT_ID_KEY))) {
|
||||
if (groupName == null) {
|
||||
messageView.getContentManager().removeContent(content, true);
|
||||
}
|
||||
else {
|
||||
assert content.getComponent() instanceof NewEditableErrorTreeViewPanel;
|
||||
NewEditableErrorTreeViewPanel errorTreeView = (NewEditableErrorTreeViewPanel)content.getComponent();
|
||||
ErrorViewStructure errorViewStructure = errorTreeView.getErrorViewStructure();
|
||||
errorViewStructure.removeGroup(groupName);
|
||||
}
|
||||
for (Iterator<Notification> iterator = myNotifications.iterator(); iterator.hasNext(); ) {
|
||||
Notification notification = iterator.next();
|
||||
if (groupName == null || groupName.equals(notification.getGroupId())) {
|
||||
notification.expire();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List<Key> toRemove = new SmartList<>();
|
||||
myUniqueNotifications.forEach((key, notification) -> {
|
||||
if (groupName == null || groupName.equals(notification.getGroupId())) {
|
||||
notification.expire();
|
||||
toRemove.add(key);
|
||||
}
|
||||
});
|
||||
toRemove.forEach(myUniqueNotifications::remove);
|
||||
|
||||
final ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
|
||||
if (toolWindow == null) return;
|
||||
|
||||
final MessageView messageView = ServiceManager.getService(project, MessageView.class);
|
||||
UIUtil.invokeLaterIfNeeded(() -> {
|
||||
if (project.isDisposed()) return;
|
||||
for (Content content : messageView.getContentManager().getContents()) {
|
||||
if (!content.isPinned() && contentIdPair.equals(content.getUserData(CONTENT_ID_KEY))) {
|
||||
if (groupName == null) {
|
||||
messageView.getContentManager().removeContent(content, true);
|
||||
}
|
||||
else {
|
||||
assert content.getComponent() instanceof NewEditableErrorTreeViewPanel;
|
||||
NewEditableErrorTreeViewPanel errorTreeView = (NewEditableErrorTreeViewPanel)content.getComponent();
|
||||
ErrorViewStructure errorViewStructure = errorTreeView.getErrorViewStructure();
|
||||
errorViewStructure.removeGroup(groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -303,9 +319,14 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
final int guiLine = line < 0 ? -1 : line + 1;
|
||||
final int guiColumn = column < 0 ? 0 : column + 1;
|
||||
|
||||
if (isDisposedOrNotOpen()) return;
|
||||
assert myProject != null;
|
||||
Project project = myProject;
|
||||
final Navigatable navigatable = notificationData.getNavigatable() != null
|
||||
? notificationData.getNavigatable()
|
||||
: virtualFile != null ? new OpenFileDescriptor(myProject, virtualFile, line, column) : NonNavigatable.INSTANCE;
|
||||
: virtualFile != null
|
||||
? new OpenFileDescriptor(project, virtualFile, line, column)
|
||||
: NonNavigatable.INSTANCE;
|
||||
|
||||
final ErrorTreeElementKind kind =
|
||||
ErrorTreeElementKind.convertMessageFromCompilerErrorType(notificationData.getNotificationCategory().getMessageCategory());
|
||||
@@ -347,7 +368,7 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
}
|
||||
|
||||
private void applyNotification(@NotNull final Notification notification) {
|
||||
if (!myProject.isDisposed() && myProject.isOpen()) {
|
||||
if (!isDisposedOrNotOpen()) {
|
||||
notification.notify(myProject);
|
||||
}
|
||||
}
|
||||
@@ -363,6 +384,7 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
final Pair<NotificationSource, ProjectSystemId> contentIdPair = Pair.create(notificationSource, externalSystemId);
|
||||
Content targetContent = findContent(contentIdPair, contentDisplayName);
|
||||
|
||||
assert myProject != null;
|
||||
final MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
|
||||
if (targetContent == null || !contentIdPair.equals(targetContent.getUserData(CONTENT_ID_KEY))) {
|
||||
errorTreeView = new NewEditableErrorTreeViewPanel(myProject, null, true, true, null);
|
||||
@@ -388,6 +410,7 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
@Nullable
|
||||
private Content findContent(@NotNull Pair<NotificationSource, ProjectSystemId> contentIdPair, @NotNull String contentDisplayName) {
|
||||
Content targetContent = null;
|
||||
assert myProject != null;
|
||||
final MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
|
||||
for (Content content : messageView.getContentManager().getContents()) {
|
||||
if (contentIdPair.equals(content.getUserData(CONTENT_ID_KEY))
|
||||
@@ -419,8 +442,13 @@ public class ExternalSystemNotificationManager implements Disposable {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myProject = null;
|
||||
myNotifications.clear();
|
||||
myUniqueNotifications.clear();
|
||||
initializedExternalSystem.clear();
|
||||
}
|
||||
|
||||
private boolean isDisposedOrNotOpen() {
|
||||
return myProject == null || myProject.isDisposed() || !myProject.isOpen();
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -17,6 +17,7 @@ package com.intellij.openapi.externalSystem.service.notification;
|
||||
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -30,7 +31,7 @@ import java.util.Map;
|
||||
* @author Vladislav.Soroka
|
||||
* @since 3/28/14
|
||||
*/
|
||||
public class NotificationData {
|
||||
public class NotificationData implements Disposable {
|
||||
|
||||
@NotNull private String myTitle;
|
||||
@NotNull private String myMessage;
|
||||
@@ -183,4 +184,9 @@ public class NotificationData {
|
||||
public void setBalloonGroup(@Nullable String balloonGroup) {
|
||||
myBalloonGroup = balloonGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myListenerMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user