diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/context/AppContextImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/context/AppContextImpl.java index a03f48375b9..c9834c72abc 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/context/AppContextImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/context/AppContextImpl.java @@ -13,6 +13,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Lists.newArrayList; import static java.util.Collections.addAll; +import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STOPPING; import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO; @@ -31,7 +32,9 @@ import java.util.Map; import java.util.Optional; import org.eclipse.che.api.core.model.workspace.Workspace; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; import org.eclipse.che.api.factory.shared.dto.FactoryDto; +import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.app.StartUpAction; @@ -55,6 +58,7 @@ import org.eclipse.che.ide.api.selection.Selection; import org.eclipse.che.ide.api.workspace.WorkspaceReadyEvent; import org.eclipse.che.ide.api.workspace.event.WorkspaceStartedEvent; +import org.eclipse.che.ide.api.workspace.event.WorkspaceStatusChangedEvent; import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent; import org.eclipse.che.ide.project.node.SyntheticNode; import org.eclipse.che.ide.resource.Path; @@ -76,9 +80,6 @@ public class AppContextImpl implements AppContext, SelectionChangedHandler, ResourceChangedHandler, - WindowActionHandler, - WorkspaceStartedEvent.Handler, - WorkspaceStoppedEvent.Handler, ResourceManagerInitializer { private final QueryParameters queryParameters; private final List projectsInImport; @@ -121,10 +122,13 @@ public AppContextImpl( projectsInImport = new ArrayList<>(); + WorkspaceStateHandler workspaceStateHandler = new WorkspaceStateHandler(); + eventBus.addHandler(SelectionChangedEvent.TYPE, this); eventBus.addHandler(ResourceChangedEvent.getType(), this); - eventBus.addHandler(WindowActionEvent.TYPE, this); - eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this); + eventBus.addHandler(WindowActionEvent.TYPE, workspaceStateHandler); + eventBus.addHandler(WorkspaceStoppedEvent.TYPE, workspaceStateHandler); + eventBus.addHandler(WorkspaceStatusChangedEvent.TYPE, workspaceStateHandler); //in some cases IDE doesn't save preferences on window close //so try to save if window lost focus @@ -224,12 +228,7 @@ public void initResourceManager(final Callback callb callback.onFailure(new NullPointerException("Dev machine is not initialized")); } - if (!rootProjects.isEmpty()) { - for (Project project : rootProjects) { - eventBus.fireEvent(new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED))); - } - rootProjects.clear(); - } + clearProjects(); resourceManager = resourceManagerFactory.newResourceManager(runtime.getDevMachine()); resourceManager @@ -421,41 +420,19 @@ public Project getRootProject() { } } - @Override - public void onWindowClosing(WindowActionEvent event) { - appStateManager.get().persistWorkspaceState(getWorkspaceId()); - } - - @Override - public void onWorkspaceStarted(WorkspaceStartedEvent event) { - setWorkspace(event.getWorkspace()); - } - - @Override - public void onWorkspaceStopped(WorkspaceStoppedEvent event) { - appStateManager - .get() - .persistWorkspaceState(getWorkspaceId()) - .then( - ignored -> { - for (Project project : rootProjects) { - eventBus.fireEvent( - new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED))); - } - - rootProjects.clear(); - resourceManager = null; - }); - - clearRuntime(); - } - private void clearRuntime() { runtime = null; } - @Override - public void onWindowClosed(WindowActionEvent event) {} + private void clearProjects() { + if (!rootProjects.isEmpty()) { + rootProjects.forEach( + project -> + eventBus.fireEvent( + new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED)))); + rootProjects.clear(); + } + } @Override public String getMasterEndpoint() { @@ -493,4 +470,51 @@ public Map getProperties() { } return properties; } + + private class WorkspaceStateHandler + implements WindowActionHandler, + WorkspaceStartedEvent.Handler, + WorkspaceStoppedEvent.Handler, + WorkspaceStatusChangedEvent.Handler { + + Promise persistWorkspaceStatePromise; + + @Override + public void onWindowClosing(WindowActionEvent event) { + appStateManager.get().persistWorkspaceState(getWorkspaceId()); + } + + @Override + public void onWindowClosed(WindowActionEvent event) {} + + @Override + public void onWorkspaceStarted(WorkspaceStartedEvent event) { + setWorkspace(event.getWorkspace()); + } + + @Override + public void onWorkspaceStatusChangedEvent(WorkspaceStatusChangedEvent event) { + WorkspaceStatus workspaceStatus = event.getWorkspaceStatusEvent().getStatus(); + if (STOPPING == workspaceStatus) { + persistWorkspaceStatePromise = + appStateManager.get().persistWorkspaceState(getWorkspaceId()); + } + } + + @Override + public void onWorkspaceStopped(WorkspaceStoppedEvent event) { + if (persistWorkspaceStatePromise != null) { + persistWorkspaceStatePromise.then( + arg -> { + clearProjects(); + resourceManager = null; + clearRuntime(); + }); + } else { + clearProjects(); + resourceManager = null; + clearRuntime(); + } + } + } } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/pom.xml b/plugins/plugin-docker/che-plugin-docker-machine/pom.xml index dca7053d3cc..944f82881db 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/pom.xml +++ b/plugins/plugin-docker/che-plugin-docker-machine/pom.xml @@ -25,6 +25,10 @@ false + + com.google.code.gson + gson + com.google.guava guava @@ -73,6 +77,10 @@ org.eclipse.che.core che-core-api-model + + org.eclipse.che.core + che-core-api-user + org.eclipse.che.core che-core-api-workspace diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineModule.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineModule.java index f8dd9d00e55..98eb71e399f 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineModule.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineModule.java @@ -18,6 +18,7 @@ import java.util.Set; import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.environment.server.TypeSpecificEnvironmentParser; +import org.eclipse.che.plugin.docker.machine.cleaner.AppStatesPreferenceCleaner; import org.eclipse.che.plugin.docker.machine.parser.DockerImageEnvironmentParser; import org.eclipse.che.plugin.docker.machine.parser.DockerfileEnvironmentParser; @@ -33,6 +34,7 @@ protected void configure() { bind( org.eclipse.che.plugin.docker.machine.cleaner .RemoveWorkspaceFilesAfterRemoveWorkspaceEventSubscriber.class); + bind(AppStatesPreferenceCleaner.class); @SuppressWarnings("unused") Multibinder devMachineEnvVars = diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/cleaner/AppStatesPreferenceCleaner.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/cleaner/AppStatesPreferenceCleaner.java new file mode 100644 index 00000000000..878e0b879fb --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/cleaner/AppStatesPreferenceCleaner.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.plugin.docker.machine.cleaner; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.Map; +import javax.annotation.PostConstruct; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.core.model.workspace.Workspace; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.api.user.server.PreferenceManager; +import org.eclipse.che.api.user.server.UserManager; +import org.eclipse.che.api.workspace.server.event.WorkspaceRemovedEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Handler for clean up app state preference when workspace is removed. */ +@Singleton +public class AppStatesPreferenceCleaner implements EventSubscriber { + private static final Logger LOG = LoggerFactory.getLogger(AppStatesPreferenceCleaner.class); + + /** The name of the property for the mappings in user preferences. */ + public static final String APP_STATES_PREFERENCE_PROPERTY = "IdeAppStates"; + + private JsonParser jsonParser; + private EventService eventService; + private UserManager userManager; + private PreferenceManager preferenceManager; + + @Inject + public AppStatesPreferenceCleaner( + JsonParser jsonParser, + EventService eventService, + UserManager userManager, + PreferenceManager preferenceManager) { + this.jsonParser = jsonParser; + this.eventService = eventService; + this.userManager = userManager; + this.preferenceManager = preferenceManager; + } + + @PostConstruct + public void subscribe() { + eventService.subscribe(this); + } + + @Override + public void onEvent(WorkspaceRemovedEvent workspaceRemovedEvent) { + try { + Workspace workspace = workspaceRemovedEvent.getWorkspace(); + User user = userManager.getByName(workspace.getNamespace()); + if (user == null) { + return; + } + + String userId = user.getId(); + Map preferences = preferenceManager.find(userId); + String appStates = preferences.get(APP_STATES_PREFERENCE_PROPERTY); + if (appStates == null) { + return; + } + + JsonObject workspaces = jsonParser.parse(appStates).getAsJsonObject(); + JsonElement removedWorkspacePreferences = workspaces.remove(workspace.getId()); + if (removedWorkspacePreferences != null) { + preferences.put(APP_STATES_PREFERENCE_PROPERTY, workspaces.toString()); + preferenceManager.save(userId, preferences); + } + } catch (NotFoundException | ServerException e) { + Workspace workspace = workspaceRemovedEvent.getWorkspace(); + LOG.error( + "Unable to clean up preferences for owner of the workspace {} with namespace {}", + workspace.getId(), + workspace.getNamespace()); + } + } +}