/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.context; import com.google.gwt.core.client.Callback; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.core.model.workspace.Workspace; import org.eclipse.che.api.factory.shared.dto.FactoryDto; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.app.StartUpAction; import org.eclipse.che.ide.api.data.HasDataObject; import org.eclipse.che.ide.api.editor.EditorAgent; import org.eclipse.che.ide.api.editor.EditorPartPresenter; import org.eclipse.che.ide.api.event.SelectionChangedEvent; import org.eclipse.che.ide.api.event.SelectionChangedHandler; import org.eclipse.che.ide.api.event.WindowActionEvent; import org.eclipse.che.ide.api.event.WindowActionHandler; import org.eclipse.che.ide.api.machine.ActiveRuntime; import org.eclipse.che.ide.api.machine.DevMachine; import org.eclipse.che.ide.api.resources.Container; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.ResourceChangedEvent.ResourceChangedHandler; import org.eclipse.che.ide.api.resources.ResourceDelta; import org.eclipse.che.ide.api.resources.ResourcePathComparator; import org.eclipse.che.ide.api.resources.VirtualFile; 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.WorkspaceStoppedEvent; import org.eclipse.che.ide.project.node.SyntheticNode; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.resources.ResourceManagerInitializer; import org.eclipse.che.ide.resources.impl.ResourceDeltaImpl; import org.eclipse.che.ide.resources.impl.ResourceManager; import org.eclipse.che.ide.statepersistance.AppStateManager; import java.util.ArrayList; import java.util.List; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Lists.newArrayList; import static com.google.gwt.user.client.Random.nextInt; import static java.util.Collections.addAll; 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; import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED; import static org.eclipse.che.ide.api.resources.ResourceDelta.UPDATED; /** * Implementation of {@link AppContext}. * * @author Vitaly Parfonov * @author Artem Zatsarynnyi * @author Vlad Zhukovskyi */ @Singleton public class AppContextImpl implements AppContext, SelectionChangedHandler, ResourceChangedHandler, WindowActionHandler, WorkspaceStartedEvent.Handler, WorkspaceStoppedEvent.Handler, ResourceManagerInitializer { private static final String APP_ID = String.valueOf(nextInt(Integer.MAX_VALUE)); private final QueryParameters queryParameters; private final List<String> projectsInImport; private final EventBus eventBus; private final ResourceManager.ResourceManagerFactory resourceManagerFactory; private final Provider<EditorAgent> editorAgentProvider; private final Provider<AppStateManager> appStateManager; private final List<Project> rootProjects = newArrayList(); private final List<Resource> selectedResources = newArrayList(); private Workspace userWorkspace; private CurrentUser currentUser; private FactoryDto factory; private Path projectsRoot; private ActiveRuntime runtime; private ResourceManager resourceManager; /** * List of actions with parameters which comes from startup URL. * Can be processed after IDE initialization as usual after starting ws-agent. */ private List<StartUpAction> startAppActions; @Inject public AppContextImpl(EventBus eventBus, QueryParameters queryParameters, ResourceManager.ResourceManagerFactory resourceManagerFactory, Provider<EditorAgent> editorAgentProvider, Provider<AppStateManager> appStateManager) { this.eventBus = eventBus; this.queryParameters = queryParameters; this.resourceManagerFactory = resourceManagerFactory; this.editorAgentProvider = editorAgentProvider; this.appStateManager = appStateManager; projectsInImport = new ArrayList<>(); eventBus.addHandler(SelectionChangedEvent.TYPE, this); eventBus.addHandler(ResourceChangedEvent.getType(), this); eventBus.addHandler(WindowActionEvent.TYPE, this); eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this); } private static native String masterFromIDEConfig() /*-{ if ($wnd.IDE && $wnd.IDE.config) { return $wnd.IDE.config.restContext; } else { return null; } }-*/; @Override public Workspace getWorkspace() { return userWorkspace; } @Override public void setWorkspace(Workspace workspace) { if (workspace != null) { userWorkspace = workspace; if (workspace.getRuntime() != null) { runtime = new ActiveRuntime(workspace.getRuntime()); } } else { userWorkspace = null; runtime = null; } } @Override public String getWorkspaceId() { if (userWorkspace == null) { throw new IllegalArgumentException(getClass() + " Workspace can not be null."); } return userWorkspace.getId(); } @Override public CurrentUser getCurrentUser() { return currentUser; } public void setCurrentUser(CurrentUser currentUser) { this.currentUser = currentUser; } @Override public List<String> getImportingProjects() { return projectsInImport; } @Override public void addProjectToImporting(String pathToProject) { projectsInImport.add(pathToProject); } @Override public void removeProjectFromImporting(String pathToProject) { projectsInImport.remove(pathToProject); } @Override public List<StartUpAction> getStartAppActions() { return startAppActions; } @Override public void setStartUpActions(List<StartUpAction> startUpActions) { this.startAppActions = startUpActions; } @Override public FactoryDto getFactory() { return factory; } @Override public void setFactory(FactoryDto factory) { this.factory = factory; } @Override public DevMachine getDevMachine() { return runtime.getDevMachine(); } @Override public void initResourceManager(final Callback<ResourceManager, Exception> callback) { if (runtime.getDevMachine() == null) { //should never happened, but anyway 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(); } resourceManager = resourceManagerFactory.newResourceManager(runtime.getDevMachine()); resourceManager.getWorkspaceProjects().then(projects -> { rootProjects.clear(); addAll(rootProjects, projects); rootProjects.sort(ResourcePathComparator.getInstance()); callback.onSuccess(resourceManager); eventBus.fireEvent(new WorkspaceReadyEvent(projects)); }).catchError(error -> { callback.onFailure((Exception)error.getCause()); }); } @Override public String getWorkspaceName() { return userWorkspace.getConfig().getName(); } /** {@inheritDoc} */ @Override public void onResourceChanged(ResourceChangedEvent event) { final ResourceDelta delta = event.getDelta(); final Resource resource = delta.getResource(); if (delta.getKind() == ADDED) { if ((delta.getFlags() & (MOVED_FROM | MOVED_TO)) != 0) { for (Project rootProject : rootProjects) { if (rootProject.getLocation().equals(delta.getFromPath()) && resource.isProject()) { rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject()); break; } } for (Resource selectedResource : selectedResources) { if (selectedResource.getLocation().equals(delta.getFromPath())) { selectedResources.set(selectedResources.indexOf(selectedResource), resource); break; } } } else if (resource.getLocation().segmentCount() == 1 && resource.isProject()) { boolean exists = rootProjects.stream().anyMatch(it -> it.getLocation().equals(resource.getLocation())); if (!exists) { rootProjects.add(resource.asProject()); rootProjects.sort(ResourcePathComparator.getInstance()); } } } else if (delta.getKind() == REMOVED) { for (Project rootProject : rootProjects) { if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) { rootProjects.remove(rootProjects.indexOf(rootProject)); break; } } for (Resource selectedResource : selectedResources) { if (selectedResource.getLocation().equals(resource.getLocation())) { selectedResources.remove(selectedResources.indexOf(selectedResource)); break; } } } else if (delta.getKind() == UPDATED) { for (Project rootProject : rootProjects) { if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) { rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject()); break; } } for (Resource selectedResource : selectedResources) { if (selectedResource.getLocation().equals(resource.getLocation())) { selectedResources.set(selectedResources.indexOf(selectedResource), resource); break; } } } } @Override public Path getProjectsRoot() { return projectsRoot; } public void setProjectsRoot(Path projectsRoot) { this.projectsRoot = projectsRoot; } @Override public void onSelectionChanged(SelectionChangedEvent event) { final Selection<?> selection = event.getSelection(); if (selection instanceof Selection.NoSelectionProvided) { return; } selectedResources.clear(); if (selection != null) { for (Object o : selection.getAllElements()) { if (o instanceof HasDataObject && ((HasDataObject)o).getData() instanceof Resource) { selectedResources.add((Resource)((HasDataObject)o).getData()); } else if (o instanceof Resource) { selectedResources.add((Resource)o); } } } } @Override public Project[] getProjects() { return rootProjects.toArray(new Project[rootProjects.size()]); } @Override public Container getWorkspaceRoot() { checkState(resourceManager != null, "Workspace configuration has not been received yet"); return resourceManager.getWorkspaceRoot(); } @Override public Resource getResource() { return selectedResources.isEmpty() ? null : selectedResources.get(0); } @Override public Resource[] getResources() { return selectedResources.toArray(new Resource[selectedResources.size()]); } @Override public Project getRootProject() { if (rootProjects.isEmpty()) { return null; } if (selectedResources.isEmpty()) { EditorAgent editorAgent = editorAgentProvider.get(); if (editorAgent == null) { return null; } final EditorPartPresenter editor = editorAgent.getActiveEditor(); if (editor == null) { return null; } final VirtualFile file = editor.getEditorInput().getFile(); if (file instanceof SyntheticNode) { final Path projectPath = ((SyntheticNode)file).getProject(); for (Project project : rootProjects) { if (project.getLocation().equals(projectPath)) { return project; } } } return null; } else { Project root = null; for (Project project : rootProjects) { if (project.getLocation().isPrefixOf(selectedResources.get(0).getLocation())) { root = project; break; } } if (root == null) { return null; } for (int i = 1; i < selectedResources.size(); i++) { if (!root.getLocation().isPrefixOf(selectedResources.get(i).getLocation())) { return null; } } return root; } } @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) { } @Override public String getMasterEndpoint() { String fromUrl = queryParameters.getByName("master"); if (fromUrl == null || fromUrl.isEmpty()) return masterFromIDEConfig(); else return fromUrl; } @Override public String getDevAgentEndpoint() { String fromUrl = queryParameters.getByName("agent"); if (fromUrl == null || fromUrl.isEmpty()) return runtime.getDevMachine().getWsAgentBaseUrl(); else return fromUrl; } @Override public String getAppId() { return APP_ID; } @Override public ActiveRuntime getActiveRuntime() { return runtime; } }