/******************************************************************************* * 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.workspace; import com.google.gwt.core.client.Callback; import com.google.gwt.core.client.Scheduler; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.core.model.workspace.Workspace; import org.eclipse.che.api.core.model.workspace.WorkspaceConfig; import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.component.Component; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.notification.StatusNotification; import org.eclipse.che.ide.api.preferences.PreferencesManager; import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient; import org.eclipse.che.ide.api.workspace.event.WorkspaceStartedEvent; import org.eclipse.che.ide.api.workspace.event.WorkspaceStartingEvent; import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent; import org.eclipse.che.ide.context.BrowserAddress; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.ui.loaders.LoaderPresenter; import org.eclipse.che.ide.util.loging.Log; import org.eclipse.che.ide.workspace.create.CreateWorkspacePresenter; import org.eclipse.che.ide.workspace.start.StartWorkspacePresenter; import java.util.Map; import static org.eclipse.che.api.workspace.shared.Constants.CHE_WORKSPACE_AUTO_START; import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE; import static org.eclipse.che.ide.ui.loaders.LoaderPresenter.Phase.CREATING_WORKSPACE_SNAPSHOT; import static org.eclipse.che.ide.ui.loaders.LoaderPresenter.Phase.STARTING_WORKSPACE_RUNTIME; import static org.eclipse.che.ide.ui.loaders.LoaderPresenter.Phase.WORKSPACE_STOPPED; /** * @author Evgen Vidolob * @author Dmitry Shnurenko * @author Yevhenii Voevodin */ public abstract class WorkspaceComponent implements Component, WsAgentStateHandler, WorkspaceStoppedEvent.Handler { private static final String WS_STATUS_ERROR_MSG = "Tried to subscribe to workspace status events, but got error"; private static final String WS_AGENT_OUTPUT_ERROR_MSG = "Tried to subscribe to workspace agent output, but got error"; private static final String ENV_STATUS_ERROR_MSG = "Tried to subscribe to environment status events, but got error"; protected final WorkspaceServiceClient workspaceServiceClient; protected final CoreLocalizationConstant locale; protected final CreateWorkspacePresenter createWorkspacePresenter; protected final DtoUnmarshallerFactory dtoUnmarshallerFactory; protected final AppContext appContext; protected final BrowserAddress browserAddress; protected final DialogFactory dialogFactory; protected final PreferencesManager preferencesManager; protected final DtoFactory dtoFactory; protected final NotificationManager notificationManager; protected final StartWorkspacePresenter startWorkspacePresenter; private final EventBus eventBus; private final LoaderPresenter loader; private final RequestTransmitter transmitter; protected Callback<Component, Exception> callback; protected boolean needToReloadComponents; public WorkspaceComponent(WorkspaceServiceClient workspaceServiceClient, CreateWorkspacePresenter createWorkspacePresenter, StartWorkspacePresenter startWorkspacePresenter, CoreLocalizationConstant locale, DtoUnmarshallerFactory dtoUnmarshallerFactory, EventBus eventBus, AppContext appContext, NotificationManager notificationManager, BrowserAddress browserAddress, DialogFactory dialogFactory, PreferencesManager preferencesManager, DtoFactory dtoFactory, LoaderPresenter loader, RequestTransmitter transmitter) { this.workspaceServiceClient = workspaceServiceClient; this.createWorkspacePresenter = createWorkspacePresenter; this.startWorkspacePresenter = startWorkspacePresenter; this.locale = locale; this.dtoUnmarshallerFactory = dtoUnmarshallerFactory; this.eventBus = eventBus; this.appContext = appContext; this.notificationManager = notificationManager; this.browserAddress = browserAddress; this.dialogFactory = dialogFactory; this.preferencesManager = preferencesManager; this.dtoFactory = dtoFactory; this.loader = loader; this.transmitter = transmitter; this.needToReloadComponents = true; eventBus.addHandler(WsAgentStateEvent.TYPE, this); eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this); } /** {@inheritDoc} */ @Override public void onWorkspaceStopped(WorkspaceStoppedEvent event) { setCurrentWorkspace(null); } /** {@inheritDoc} */ @Override public void onWsAgentStarted(WsAgentStateEvent event) { notificationManager.notify(locale.startedWs(), StatusNotification.Status.SUCCESS, FLOAT_MODE); } /** {@inheritDoc} */ @Override public void onWsAgentStopped(WsAgentStateEvent event) { } /** * Sets workspace to app context as current. * * @param workspace * workspace which will be current */ public void setCurrentWorkspace(Workspace workspace) { appContext.setWorkspace(workspace); if (needToReloadComponents) { callback.onSuccess(WorkspaceComponent.this); needToReloadComponents = false; } if (workspace != null) { browserAddress.setAddress(workspace.getNamespace(), workspace.getConfig().getName()); } } /** * Listens message bus and handles workspace events. * * @param workspace * workspace to listen * @param callback * callback * @param restoreFromSnapshot * restore or not the workspace from snapshot */ public void handleWorkspaceEvents(final WorkspaceDto workspace, final Callback<Component, Exception> callback, final Boolean restoreFromSnapshot) { loader.show(STARTING_WORKSPACE_RUNTIME); setCurrentWorkspace(workspace); String workspaceId = appContext.getWorkspaceId(); subscribe(WS_STATUS_ERROR_MSG, "event:workspace-status:subscribe", workspaceId); subscribe(WS_AGENT_OUTPUT_ERROR_MSG, "event:ws-agent-output:subscribe", workspaceId); subscribe(ENV_STATUS_ERROR_MSG, "event:environment-status:subscribe", workspaceId); if (appContext.getActiveRuntime() != null) { appContext.getActiveRuntime().getMachines().forEach(machine -> subscribeEnvironmentOutput(machine.getDisplayName())); } WorkspaceStatus workspaceStatus = workspace.getStatus(); switch (workspaceStatus) { case SNAPSHOTTING: loader.show(CREATING_WORKSPACE_SNAPSHOT); break; case STARTING: eventBus.fireEvent(new WorkspaceStartingEvent(workspace)); break; case RUNNING: Scheduler.get().scheduleDeferred(() -> { loader.setSuccess(STARTING_WORKSPACE_RUNTIME); eventBus.fireEvent(new WorkspaceStartedEvent(workspace)); }); break; default: workspaceServiceClient.getSettings() .then((Function<Map<String, String>, Map<String, String>>)settings -> { if (Boolean.parseBoolean(settings.getOrDefault(CHE_WORKSPACE_AUTO_START, "true"))) { final WorkspaceConfig config = workspace.getConfig(); config.getEnvironments().get(config.getDefaultEnv()).getMachines().keySet() .forEach(machine -> subscribeEnvironmentOutput(machine)); startWorkspaceById(workspaceId, config.getDefaultEnv(), restoreFromSnapshot); } else { loader.show(WORKSPACE_STOPPED); } return settings; }); } } private void subscribeEnvironmentOutput(String machine) { String endpointId = "ws-master"; String subscribeByName = "event:environment-output:subscribe-by-machine-name"; String workspaceIdPlusMachineName = appContext.getWorkspaceId() + "::" + machine; transmitter.newRequest() .endpointId(endpointId) .methodName(subscribeByName) .paramsAsString(workspaceIdPlusMachineName) .sendAndSkipResult(); } private void subscribe(String it, String methodName, String id) { workspaceServiceClient.getWorkspace(browserAddress.getWorkspaceKey()) .then((Operation<WorkspaceDto>)skip -> transmitter.newRequest() .endpointId("ws-master") .methodName(methodName) .paramsAsString(id) .sendAndSkipResult()) .catchError((Operation<PromiseError>)error -> Log.error(getClass(), it + ": " + error.getMessage())); } /** * Starts workspace. * * @param workspaceID * workspace ID to start * @param callback * callback * @param restoreFromSnapshot * restore or not the workspace from snapshot */ public void startWorkspace(final String workspaceID, final Callback<Component, Exception> callback, final Boolean restoreFromSnapshot) { workspaceServiceClient.getWorkspace(workspaceID).then(new Operation<WorkspaceDto>() { @Override public void apply(WorkspaceDto workspace) throws OperationException { handleWorkspaceEvents(workspace, callback, restoreFromSnapshot); } }); } /** * Starts workspace by id when web socket connected. * * @param workspace * workspace which will be started * @param callback * callback to be executed */ public void startWorkspace(final Workspace workspace, final Callback<Component, Exception> callback) { startWorkspace(workspace.getId(), callback, null); } /** * Sends a message to the parent frame to inform that IDE application can be shown. */ private native void notifyShowIDE() /*-{ $wnd.parent.postMessage("show-ide", "*"); }-*/; /** * Starts specified workspace if it's {@link WorkspaceStatus} different of {@code RUNNING} */ protected Operation<WorkspaceDto> startWorkspace() { return new Operation<WorkspaceDto>() { @Override public void apply(WorkspaceDto workspaceToStart) throws OperationException { startWorkspace(workspaceToStart, callback); } }; } abstract void tryStartWorkspace(); private void startWorkspaceById(String workspaceId, String defaultEnvironment, Boolean restoreFromSnapshot) { loader.show(STARTING_WORKSPACE_RUNTIME); workspaceServiceClient.startById(workspaceId, defaultEnvironment, restoreFromSnapshot).catchError(new Operation<PromiseError>() { @Override public void apply(PromiseError error) throws OperationException { notificationManager.notify(locale.startWsErrorTitle(), error.getMessage(), StatusNotification.Status.FAIL, FLOAT_MODE); loader.setError(STARTING_WORKSPACE_RUNTIME); } }); } }