diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties index 30a382ca90..9b39fee9ec 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties @@ -210,9 +210,14 @@ che.docker.ip.external=NULL # - 'docker-local': internal address is address of container within docker network, and exposed ports # are used. # - 'custom': The evaluation strategy may be customized through a template property. +# - 'docker-local-custom': internal address is set as in docker-local strategy, external address is composed +# as in the custom strategy with the 'template' and the 'external.protocol' properties. + # The 'docker-local' strategy may be useful if a firewall prevents communication between che-server and # workspace containers, but will prevent communication when che-server and workspace containers are not # on the same Docker network. +# The 'docker-local-custom' strategy may be useful when Che and the workspace servers need to be exposed on the +# same single TCP port. che.docker.server_evaluation_strategy=default @@ -317,12 +322,52 @@ che.openshift.project=eclipse-che che.openshift.serviceaccountname=cheserviceaccount che.openshift.liveness.probe.delay=300 che.openshift.liveness.probe.timeout=1 +che.openshift.workspaces.pvc.name=claim-che-workspace +che.openshift.workspaces.pvc.quantity=10Gi +# Create secure route against HTTPS +# NOTE: In order to create routes against HTTPS +# Property 'strategy.che.docker.server_evaluation_strategy.secure.external.urls' should be also set to true +che.openshift.secure.routes=false +# Pod that is launched when performing persistent volume claim maintenance jobs on OpenShift +che.openshift.jobs.image=centos:centos7 +che.openshift.jobs.memorylimit=250Mi + +# Run job to create workspace subpath directories in persistent volume before launching workspace. +# Necessary in some versions of OpenShift/Kubernetes as workspace subpath volumemounts are created +# with root permissions, and thus cannot be modified by workspaces running as user (presents as error +# importing projects into workspace in Che). Default is "true", but should be set to false if version +# of Openshift/Kubernetes creates subdirectories with user permissions. +# Relevant issue: https://github.com/kubernetes/kubernetes/issues/41638 +che.openshift.precreate.workspace.dirs=true + +# Specifications of compute resources that can be consumed +# by the workspace container: +# +# - Amount of memory required for a workspace container to run e.g. 512Mi +che.openshift.workspace.memory.request=NULL +# +# - Maximum amount of memory a workspace container can use e.g. 1.3Gi +che.openshift.workspace.memory.override=NULL + +# The Openshift will idle the server if no workspace is run for +# this length of time. +che.openshift.server.inactive.stop.timeout.ms=1800000 + +# +# +# Be aware that setting che.openshift.workspace.memory.override +# will override Che memory limits +# +# More information about setting Compute Resources in OpenShift can be +# found here: https://docs.openshift.org/latest/dev_guide/compute_resources.html#dev-compute-resources # Which implementation of DockerConnector to use in managing containers. In general, # the base implementation of DockerConnector is appropriate, but OpenShiftConnector # is necessary for deploying Che on OpenShift. Options: # - 'default' : Use DockerConnector # - 'openshift' : use OpenShiftConnector +# Note that if 'openshift' connector is used, the property che.docker.ip.external +# MUST be set. che.docker.connector=default # Defines whether stacks loaded once or each time server starts. diff --git a/core/che-core-api-core/pom.xml b/core/che-core-api-core/pom.xml index b2978ed389..2cabd3299a 100644 --- a/core/che-core-api-core/pom.xml +++ b/core/che-core-api-core/pom.xml @@ -255,6 +255,17 @@ + + com.mycila + license-maven-plugin + + + + **/ServerIdleEvent.java + + + + diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/event/ServerIdleEvent.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/event/ServerIdleEvent.java new file mode 100644 index 0000000000..c904583f34 --- /dev/null +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/event/ServerIdleEvent.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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.api.core.event; +/** + * Event informing about idling the che server. + */ +public class ServerIdleEvent { + private long timeout; + + /** + * Implements the handler to handle idling. + */ + public ServerIdleEvent(long timeout) { + super(); + this.timeout = timeout; + } + + + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } +} diff --git a/dockerfiles/che/Dockerfile.centos b/dockerfiles/che/Dockerfile.centos index 2fca8eb300..3ea613af59 100644 --- a/dockerfiles/che/Dockerfile.centos +++ b/dockerfiles/che/Dockerfile.centos @@ -48,3 +48,6 @@ EXPOSE 8000 8080 COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ADD eclipse-che.tar.gz /home/user/ +RUN mkdir /logs && chmod 0777 /logs +RUN chmod -R 0777 /home/user/ +RUN mkdir /data && chmod 0777 /data diff --git a/dockerfiles/che/entrypoint.sh b/dockerfiles/che/entrypoint.sh index 964634d5a4..6ac3fda6d6 100755 --- a/dockerfiles/che/entrypoint.sh +++ b/dockerfiles/che/entrypoint.sh @@ -51,7 +51,7 @@ Variables: export CHE_REGISTRY_HOST=${CHE_REGISTRY_HOST:-${DEFAULT_CHE_REGISTRY_HOST}} DEFAULT_CHE_PORT=8080 - CHE_PORT=${CHE_PORT:-${DEFAULT_CHE_PORT}} + export CHE_PORT=${CHE_PORT:-${DEFAULT_CHE_PORT}} DEFAULT_CHE_IP= CHE_IP=${CHE_IP:-${DEFAULT_CHE_IP}} @@ -246,18 +246,40 @@ init() { fi ### Are we going to use the embedded che.properties or one provided by user?` ### CHE_LOCAL_CONF_DIR is internal Che variable that sets where to load - export CHE_LOCAL_CONF_DIR="/conf" - if [ -f "/conf/che.properties" ]; then - echo "Found custom che.properties..." - if [ "$CHE_USER" != "root" ]; then - sudo chown -R ${CHE_USER} ${CHE_LOCAL_CONF_DIR} + # check if we have permissions to create /conf folder. + if [ -w / ]; then + export CHE_LOCAL_CONF_DIR="/conf" + if [ -f "/conf/che.properties" ]; then + echo "Found custom che.properties..." + if [ "$CHE_USER" != "root" ]; then + sudo chown -R ${CHE_USER} ${CHE_LOCAL_CONF_DIR} + fi + else + if [ ! -d ${CHE_LOCAL_CONF_DIR} ]; then + mkdir -p ${CHE_LOCAL_CONF_DIR} + fi + if [ -w ${CHE_LOCAL_CONF_DIR} ];then + echo "ERROR: user ${CHE_USER} does OK have write permissions to ${CHE_LOCAL_CONF_DIR}" + echo "Using embedded che.properties... Copying template to ${CHE_LOCAL_CONF_DIR}/che.properties" + cp -rf "${CHE_HOME}/conf/che.properties" ${CHE_LOCAL_CONF_DIR}/che.properties + else + echo "ERROR: user ${CHE_USER} does not have write permissions to ${CHE_LOCAL_CONF_DIR}" + exit 1 + fi fi else - if [ ! -d /conf ]; then - mkdir -p /conf + echo "WARN: parent dir is not writeable, CHE_LOCAL_CONF_DIR will be set to ${CHE_DATA}/conf" + export CHE_LOCAL_CONF_DIR="${CHE_DATA}/conf" + if [ ! -d ${CHE_LOCAL_CONF_DIR} ]; then + mkdir -p ${CHE_LOCAL_CONF_DIR} + fi + if [ -w ${CHE_LOCAL_CONF_DIR} ];then + echo "Using embedded che.properties... Copying template to ${CHE_LOCAL_CONF_DIR}/che.properties" + cp -rf "${CHE_HOME}/conf/che.properties" ${CHE_LOCAL_CONF_DIR}/che.properties + else + echo "ERROR: user ${CHE_USER} does not have write permissions to ${CHE_LOCAL_CONF_DIR}" + exit 1 fi - echo "Using embedded che.properties... Copying template to ${CHE_LOCAL_CONF_DIR}/che.properties" - cp -rf "${CHE_HOME}/conf/che.properties" ${CHE_LOCAL_CONF_DIR}/che.properties fi # Update the provided che.properties with the location of the /data mounts diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/ModificationTracker.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/ModificationTracker.java index 03017aa6a5..52e64d6139 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/ModificationTracker.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/ModificationTracker.java @@ -30,4 +30,12 @@ public interface ModificationTracker { * @return modification tracker value */ String getModificationStamp(); + + /** + * Update modification tracker value by content. Modification tracker is a value is changed by any modification of the content + * of the file. + * + * @param content actual file content + */ + void updateModificationStamp(String content); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/manager/CommandManagerImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/manager/CommandManagerImpl.java index dcd723e0b3..a00f24039e 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/manager/CommandManagerImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/manager/CommandManagerImpl.java @@ -87,9 +87,9 @@ public class CommandManagerImpl implements CommandManager { this.commandNameGenerator = commandNameGenerator; commands = new HashMap<>(); + registerNative(); - // FIXME: spi - // Temporary solution while a better mechanism of obtaining appContext.getProjects() with Promises is being considered... + // TODO (spi ide): Temporary solution while a better mechanism of obtaining appContext.getProjects() with Promises is being considered... eventBus.addHandler(WorkspaceReadyEvent.getType(), e -> fetchCommands()); eventBus.addHandler(WorkspaceStoppedEvent.TYPE, e -> { commands.clear(); @@ -380,4 +380,17 @@ public class CommandManagerImpl implements CommandManager { private void notifyCommandUpdated(CommandImpl prevCommand, CommandImpl command) { eventBus.fireEvent(new CommandUpdatedEvent(prevCommand, command)); } + + /* Expose Command Manager's internal API to the world, to allow selenium tests or clients that use IDE to refresh commands. */ + private native void registerNative() /*-{ + var that = this; + + var CommandManager = {}; + + CommandManager.refresh = $entry(function () { + that.@org.eclipse.che.ide.command.manager.CommandManagerImpl::fetchCommands()(); + }); + + $wnd.IDE.CommandManager = CommandManager; + }-*/; } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/synchronization/EditorGroupSynchronizationImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/synchronization/EditorGroupSynchronizationImpl.java index 252966daff..38f2402287 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/synchronization/EditorGroupSynchronizationImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/synchronization/EditorGroupSynchronizationImpl.java @@ -168,7 +168,7 @@ public class EditorGroupSynchronizationImpl implements EditorGroupSynchronizatio }); } - private void updateContent(String newContent, String oldStamp, VirtualFile virtualFile) { + private void updateContent(String newContent, String eventModificationStamp, VirtualFile virtualFile) { final DocumentHandle documentHandle = getDocumentHandleFor(groupLeaderEditor); if (documentHandle == null) { return; @@ -184,14 +184,14 @@ public class EditorGroupSynchronizationImpl implements EditorGroupSynchronizatio } final File file = (File)virtualFile; - final String newStamp = file.getModificationStamp(); + final String currentStamp = file.getModificationStamp(); - if (oldStamp == null && !Objects.equals(newContent, oldContent)) { + if (eventModificationStamp == null && !Objects.equals(newContent, oldContent)) { replaceContent(document, newContent, oldContent, cursorPosition); return; } - if (!Objects.equals(oldStamp, newStamp)) { + if (!Objects.equals(eventModificationStamp, currentStamp)) { replaceContent(document, newContent, oldContent, cursorPosition); notificationManager.notify("External operation", "File '" + file.getName() + "' is updated", SUCCESS, EMERGE_MODE); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/factory/utils/FactoryProjectImporter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/factory/utils/FactoryProjectImporter.java index 2014ee6ae4..daa799e4c3 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/factory/utils/FactoryProjectImporter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/factory/utils/FactoryProjectImporter.java @@ -13,6 +13,8 @@ package org.eclipse.che.ide.factory.utils; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.core.model.workspace.config.SourceStorage; import org.eclipse.che.api.git.shared.GitCheckoutEvent; import org.eclipse.che.api.promises.client.Function; @@ -37,19 +39,17 @@ import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.user.AskCredentialsDialog; import org.eclipse.che.ide.api.user.Credentials; import org.eclipse.che.ide.api.workspace.model.ProjectConfigImpl; +import org.eclipse.che.ide.projectimport.wizard.ProjectImportOutputJsonRpcNotifier; import org.eclipse.che.ide.resource.Path; -import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.util.ExceptionUtils; import org.eclipse.che.ide.util.StringUtils; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.MessageBusProvider; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.SubscriptionHandler; import org.eclipse.che.security.oauth.OAuthStatus; +import javax.inject.Singleton; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -73,17 +73,18 @@ import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUC * @author Valeriy Svydenko * @author Anton Korneta */ +@Singleton public class FactoryProjectImporter extends AbstractImporter { - private static final String CHANNEL = "git:checkout:"; + private final AskCredentialsDialog askCredentialsDialog; + private final CoreLocalizationConstant locale; + private final NotificationManager notificationManager; + private final String restContext; + private final DialogFactory dialogFactory; + private final OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry; + private final RequestTransmitter requestTransmitter; + private final ProjectImportOutputJsonRpcNotifier subscriber; - private final MessageBusProvider messageBusProvider; - private final AskCredentialsDialog askCredentialsDialog; - private final CoreLocalizationConstant locale; - private final NotificationManager notificationManager; - private final String restContext; - private final DialogFactory dialogFactory; - private final OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry; - private final DtoUnmarshallerFactory dtoUnmarshallerFactory; + private final Map checkoutContextRegistry = new HashMap<>(); private FactoryImpl factory; private AsyncCallback callback; @@ -96,8 +97,8 @@ public class FactoryProjectImporter extends AbstractImporter { ImportProjectNotificationSubscriberFactory subscriberFactory, DialogFactory dialogFactory, OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry, - MessageBusProvider messageBusProvider, - DtoUnmarshallerFactory dtoUnmarshallerFactory) { + RequestTransmitter requestTransmitter, + ProjectImportOutputJsonRpcNotifier subscriber) { super(appContext, subscriberFactory); this.notificationManager = notificationManager; this.askCredentialsDialog = askCredentialsDialog; @@ -105,8 +106,34 @@ public class FactoryProjectImporter extends AbstractImporter { this.restContext = appContext.getMasterEndpoint(); this.dialogFactory = dialogFactory; this.oAuth2AuthenticatorRegistry = oAuth2AuthenticatorRegistry; - this.messageBusProvider = messageBusProvider; - this.dtoUnmarshallerFactory = dtoUnmarshallerFactory; + this.requestTransmitter = requestTransmitter; + this.subscriber = subscriber; + } + + @Inject + private void configure(RequestHandlerConfigurator requestHandlerConfigurator) { + requestHandlerConfigurator.newConfiguration() + .methodName("git/checkoutOutput") + .paramsAsDto(GitCheckoutEvent.class) + .noResult() + .withConsumer(this::consumeGitCheckoutEvent); + } + + private void consumeGitCheckoutEvent(GitCheckoutEvent event) { + CheckoutContext context = checkoutContextRegistry.get(event.getWorkspaceId() + event.getProjectName()); + if (context == null) { + return; + } + + String projectName = context.projectName; + String reference = event.isCheckoutOnly() ? event.getBranchRef() : context.startPoint; + String repository = context.repository; + String branch = context.branch; + + String title = locale.clonedSource(projectName); + String content = locale.clonedSourceWithCheckout(projectName, repository, reference, branch); + + notificationManager.notify(title, content, SUCCESS, FLOAT_MODE); } public void startImporting(FactoryImpl factory, AsyncCallback callback) { @@ -183,7 +210,6 @@ public class FactoryProjectImporter extends AbstractImporter { @NotNull final SourceStorage sourceStorage) { final String projectName = pathToProject.lastSegment(); final StatusNotification notification = notificationManager.notify(locale.cloningSource(projectName), null, PROGRESS, FLOAT_MODE); - final ProjectNotificationSubscriber subscriber = subscriberFactory.createSubscriber(); subscriber.subscribe(projectName, notification); String location = sourceStorage.getLocation(); // it's needed for extract repository name from repository url e.g https://github.com/codenvy/che-core.git @@ -192,37 +218,8 @@ public class FactoryProjectImporter extends AbstractImporter { final Map parameters = firstNonNull(sourceStorage.getParameters(), Collections.emptyMap()); final String branch = parameters.get("branch"); final String startPoint = parameters.get("startPoint"); - final MessageBus messageBus = messageBusProvider.getMachineMessageBus(); - final String channel = CHANNEL + appContext.getWorkspaceId() + ':' + projectName; - final SubscriptionHandler successImportHandler = new SubscriptionHandler( - dtoUnmarshallerFactory.newWSUnmarshaller(GitCheckoutEvent.class)) { - @Override - protected void onMessageReceived(GitCheckoutEvent result) { - if (result.isCheckoutOnly()) { - notificationManager.notify(locale.clonedSource(projectName), - locale.clonedSourceWithCheckout(projectName, repository, result.getBranchRef(), branch), - SUCCESS, - FLOAT_MODE); - } else { - notificationManager.notify(locale.clonedSource(projectName), - locale.clonedWithCheckoutOnStartPoint(projectName, repository, startPoint, branch), - SUCCESS, - FLOAT_MODE); - } - } - @Override - protected void onErrorReceived(Throwable e) { - try { - messageBus.unsubscribe(channel, this); - } catch (WebSocketException ignore) { - } - } - }; - try { - messageBus.subscribe(channel, successImportHandler); - } catch (WebSocketException ignore) { - } + subscribe(projectName, repository, branch, startPoint); MutableProjectConfig importConfig = new MutableProjectConfig(); importConfig.setPath(pathToProject.toString()); @@ -236,6 +233,8 @@ public class FactoryProjectImporter extends AbstractImporter { @Override public Project apply(Project project) throws FunctionException { subscriber.onSuccess(); + unsubscribe(projectName); + notification.setContent(locale.clonedSource(projectName)); notification.setStatus(SUCCESS); @@ -246,9 +245,11 @@ public class FactoryProjectImporter extends AbstractImporter { @Override public Promise apply(PromiseError err) throws FunctionException { final int errorCode = ExceptionUtils.getErrorCode(err.getCause()); + unsubscribe(projectName); switch (errorCode) { case UNAUTHORIZED_GIT_OPERATION: subscriber.onFailure(err.getMessage()); + final Map attributes = ExceptionUtils.getAttributes(err.getCause()); final String providerName = attributes.get(PROVIDER_NAME); final String authenticateUrl = attributes.get(AUTHENTICATE_URL); @@ -296,6 +297,28 @@ public class FactoryProjectImporter extends AbstractImporter { }); } + private void subscribe(String projectName, String repository, String branch, String startPoint) { + String key = appContext.getWorkspaceId() + projectName; + + checkoutContextRegistry.put(key, new CheckoutContext(projectName, repository, branch, startPoint)); + requestTransmitter.newRequest() + .endpointId("ws-agent") + .methodName("git/checkoutOutput/subscribe") + .paramsAsString(key) + .sendAndSkipResult(); + } + + private void unsubscribe(String projectName) { + String key = appContext.getWorkspaceId() + projectName; + + checkoutContextRegistry.remove(key); + requestTransmitter.newRequest() + .endpointId("ws-agent") + .methodName("git/checkoutOutput/unsubscribe") + .paramsAsString(key) + .sendAndSkipResult(); + } + private Promise tryAuthenticateAndRepeatImport(@NotNull final String providerName, @NotNull final String authenticateUrl, @NotNull final Path pathToProject, @@ -337,4 +360,18 @@ public class FactoryProjectImporter extends AbstractImporter { callback.onFailure(error.getCause()); }); } + + private class CheckoutContext { + private final String projectName; + private final String repository; + private final String branch; + private final String startPoint; + + private CheckoutContext(String projectName, String repository, String branch, String startPoint) { + this.projectName = projectName; + this.repository = repository; + this.branch = branch; + this.startPoint = startPoint; + } + } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/part/explorer/project/ProjectExplorerPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/part/explorer/project/ProjectExplorerPresenter.java index 4ad888e1e7..39f19dc37e 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/part/explorer/project/ProjectExplorerPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/part/explorer/project/ProjectExplorerPresenter.java @@ -28,7 +28,6 @@ import org.eclipse.che.ide.api.data.tree.TreeExpander; import org.eclipse.che.ide.api.data.tree.settings.NodeSettings; import org.eclipse.che.ide.api.data.tree.settings.SettingsProvider; import org.eclipse.che.ide.api.extension.ExtensionsInitializedEvent; -import org.eclipse.che.ide.api.extension.ExtensionsInitializedEvent.ExtensionsInitializedHandler; import org.eclipse.che.ide.api.mvp.View; import org.eclipse.che.ide.api.parts.base.BasePresenter; import org.eclipse.che.ide.api.resources.Container; @@ -49,12 +48,6 @@ import org.eclipse.che.ide.resources.reveal.RevealResourceEvent; import org.eclipse.che.ide.resources.tree.ResourceNode; import org.eclipse.che.ide.ui.smartTree.NodeDescriptor; import org.eclipse.che.ide.ui.smartTree.Tree; -import org.eclipse.che.ide.ui.smartTree.event.BeforeExpandNodeEvent; -import org.eclipse.che.ide.ui.smartTree.event.CollapseNodeEvent; -import org.eclipse.che.ide.ui.smartTree.event.ExpandNodeEvent; -import org.eclipse.che.ide.ui.smartTree.event.PostLoadEvent; -import org.eclipse.che.ide.ui.smartTree.event.SelectionChangedEvent; -import org.eclipse.che.ide.ui.smartTree.event.SelectionChangedEvent.SelectionChangedHandler; import org.eclipse.che.providers.DynaObject; import org.vectomatic.dom.svg.ui.SVGResource; @@ -90,6 +83,7 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel private final CoreLocalizationConstant locale; private final Resources resources; private final TreeExpander treeExpander; + private final AppContext appContext; private final RequestTransmitter requestTransmitter; private final DtoFactory dtoFactory; private UpdateTask updateTask = new UpdateTask(); @@ -112,6 +106,7 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel this.settingsProvider = settingsProvider; this.locale = locale; this.resources = resources; + this.appContext = appContext; this.requestTransmitter = requestTransmitter; this.dtoFactory = dtoFactory; this.view.setDelegate(this); @@ -119,40 +114,22 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel eventBus.addHandler(ResourceChangedEvent.getType(), this); eventBus.addHandler(MarkerChangedEvent.getType(), this); eventBus.addHandler(SyntheticNodeUpdateEvent.getType(), this); - eventBus.addHandler(WorkspaceStoppedEvent.TYPE, new WorkspaceStoppedEvent.Handler() { - @Override - public void onWorkspaceStopped(WorkspaceStoppedEvent event) { - getTree().getNodeStorage().clear(); + eventBus.addHandler(WorkspaceStoppedEvent.TYPE, event -> getTree().getNodeStorage().clear()); + + view.getTree().getSelectionModel().addSelectionChangedHandler(event -> setSelection(new Selection<>(event.getSelection()))); + + view.getTree().addBeforeExpandHandler(event -> { + NodeDescriptor nodeDescriptor = view.getTree().getNodeDescriptor(event.getNode()); + + if (event.getNode() instanceof SyntheticNode && nodeDescriptor != null && nodeDescriptor.isExpandDeep()) { + event.setCancelled(true); } }); - view.getTree().getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler() { - @Override - public void onSelectionChanged(SelectionChangedEvent event) { - setSelection(new Selection<>(event.getSelection())); - } - }); - - view.getTree().addBeforeExpandHandler(new BeforeExpandNodeEvent.BeforeExpandNodeHandler() { - @Override - public void onBeforeExpand(BeforeExpandNodeEvent event) { - final NodeDescriptor nodeDescriptor = view.getTree().getNodeDescriptor(event.getNode()); - - if (event.getNode() instanceof SyntheticNode && nodeDescriptor != null && nodeDescriptor.isExpandDeep()) { - event.setCancelled(true); - } - } - }); - - view.getTree().getNodeLoader().addPostLoadHandler(new PostLoadEvent.PostLoadHandler() { - @Override - public void onPostLoad(PostLoadEvent event) { - for (Node node : event.getReceivedNodes()) { - - if (node instanceof ResourceNode && expandQueue.remove(((ResourceNode)node).getData().getLocation())) { - view.getTree().setExpanded(node, true); - } - + view.getTree().getNodeLoader().addPostLoadHandler(event -> { + for (Node node : event.getReceivedNodes()) { + if (node instanceof ResourceNode && expandQueue.remove(((ResourceNode)node).getData().getLocation())) { + view.getTree().setExpanded(node, true); } } }); @@ -162,12 +139,9 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel registerNative(); // when ide has already initialized, then we force set focus to the current part - eventBus.addHandler(ExtensionsInitializedEvent.getType(), new ExtensionsInitializedHandler() { - @Override - public void onExtensionsInitialized(ExtensionsInitializedEvent event) { - if (partStack != null) { - partStack.setActivePart(ProjectExplorerPresenter.this); - } + eventBus.addHandler(ExtensionsInitializedEvent.getType(), event -> { + if (partStack != null) { + partStack.setActivePart(ProjectExplorerPresenter.this); } }); } @@ -177,41 +151,35 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel final String endpointId = "ws-agent"; final String method = "track:project-tree"; - getTree().addExpandHandler(new ExpandNodeEvent.ExpandNodeHandler() { - @Override - public void onExpand(ExpandNodeEvent event) { - Node node = event.getNode(); + getTree().addExpandHandler(event -> { + Node node = event.getNode(); - if (node instanceof ResourceNode) { - Resource data = ((ResourceNode)node).getData(); - requestTransmitter.newRequest() - .endpointId(endpointId) - .methodName(method) - .paramsAsDto(dtoFactory.createDto(ProjectTreeTrackingOperationDto.class) - .withPath(data.getLocation().toString()) - .withType(START)) - .sendAndSkipResult(); + if (node instanceof ResourceNode) { + Resource data = ((ResourceNode)node).getData(); + requestTransmitter.newRequest() + .endpointId(endpointId) + .methodName(method) + .paramsAsDto(dtoFactory.createDto(ProjectTreeTrackingOperationDto.class) + .withPath(data.getLocation().toString()) + .withType(START)) + .sendAndSkipResult(); - } } }); - getTree().addCollapseHandler(new CollapseNodeEvent.CollapseNodeHandler() { - @Override - public void onCollapse(CollapseNodeEvent event) { - Node node = event.getNode(); + getTree().addCollapseHandler(event -> { + Node node = event.getNode(); - if (node instanceof ResourceNode) { - Resource data = ((ResourceNode)node).getData(); - requestTransmitter.newRequest() - .endpointId(endpointId) - .methodName(method) - .paramsAsDto(dtoFactory.createDto(ProjectTreeTrackingOperationDto.class) - .withPath(data.getLocation().toString()) - .withType(STOP)) - .sendAndSkipResult(); + if (node instanceof ResourceNode) { + Resource data = ((ResourceNode)node).getData(); + requestTransmitter.newRequest() + .endpointId(endpointId) + .methodName(method) + .paramsAsDto(dtoFactory.createDto(ProjectTreeTrackingOperationDto.class) + .withPath(data.getLocation().toString()) + .withType(STOP)) + .sendAndSkipResult(); - } } }); } @@ -232,7 +200,11 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel ProjectExplorer.reveal = $entry(function (path) { that.@org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter::doReveal(*)(path); - }) + }); + + ProjectExplorer.refresh = $entry(function () { + that.@org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter::doRefresh()(); + }); $wnd.IDE.ProjectExplorer = ProjectExplorer; }-*/; @@ -253,6 +225,10 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel eventBus.fireEvent(new RevealResourceEvent(Path.valueOf(path))); } + private void doRefresh() { + appContext.getWorkspaceRoot().synchronize(); + } + @Override @SuppressWarnings("unchecked") public void onResourceChanged(ResourceChangedEvent event) { diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/FileImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/FileImpl.java index 6dd53a794a..e01f0cfa74 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/FileImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/FileImpl.java @@ -110,7 +110,7 @@ class FileImpl extends ResourceImpl implements File { /** {@inheritDoc} */ @Override public Promise updateContent(String content) { - setModificationStamp(TextUtils.md5(content)); + updateModificationStamp(content); return resourceManager.write(this, content); } @@ -140,4 +140,9 @@ class FileImpl extends ResourceImpl implements File { public String getModificationStamp() { return modificationStamp; } + + @Override + public void updateModificationStamp(String content) { + this.modificationStamp = TextUtils.md5(content); + } } diff --git a/ide/che-core-ide-stacks/src/main/resources/stacks.json b/ide/che-core-ide-stacks/src/main/resources/stacks.json index b5ef5b350a..cd1d35f8d8 100644 --- a/ide/che-core-ide-stacks/src/main/resources/stacks.json +++ b/ide/che-core-ide-stacks/src/main/resources/stacks.json @@ -2618,5 +2618,69 @@ } ], "creator": "Dharmit Shah" +}, +{ + "id": "centos-go", + "creator": "Dharmit Shah", + "name": "CentOS Go", + "description": "CentOS based Go Stack", + "scope": "advanced", + "tags": [ + "CentOS", + "Go" + ], + "components": [ + { + "name": "CentOS", + "version": "7.3" + }, + { + "name": "Go", + "version": "1.6.2" + } + ], + "source": { + "type": "image", + "origin": "registry.centos.org/che-stacks/centos-go" + }, + "workspaceConfig": { + "environments": { + "default": { + "machines": { + "dev-machine": { + "agents": [ + "org.eclipse.che.exec", "org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh" + ], + "servers": {}, + "attributes" : { + "memoryLimitBytes": "2147483648" + } + } + }, + "recipe": { + "location": "registry.centos.org/che-stacks/centos-go", + "type": "dockerimage" + } + } + }, + "name": "default", + "defaultEnv": "default", + "description": null, + "commands": [ + { + "name": "run", + "type": "custom", + "commandLine": "cd ${current.project.path} && go get -d && go run main.go", + "attributes": { + "previewUrl": "http://${server.port.8080}", + "goal": "Run" + } + } + ] + }, + "stackIcon": { + "name": "type-go.svg", + "mediaType": "image/svg+xml" + } } ] diff --git a/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionDocument.java b/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionDocument.java index 5c8635774f..aec1b91114 100644 --- a/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionDocument.java +++ b/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionDocument.java @@ -12,6 +12,8 @@ package org.eclipse.che.ide.editor.orion.client; import com.google.web.bindery.event.shared.HandlerRegistration; +import org.eclipse.che.ide.api.resources.File; +import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.editor.orion.client.jso.ModelChangedEventOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionPixelPositionOverlay; @@ -197,6 +199,7 @@ public class OrionDocument extends AbstractDocument { public void replace(int offset, int length, String text) { this.editorOverlay.getModel().setText(text, offset, offset + length); + updateModificationTimeStamp(); } @Override @@ -205,6 +208,14 @@ public class OrionDocument extends AbstractDocument { int lineStart = model.getLineStart(startLine); int lineEnd = model.getLineStart(endLine); editorOverlay.setText(text, lineStart + startChar, lineEnd + endChar); + updateModificationTimeStamp(); + } + + private void updateModificationTimeStamp() { + VirtualFile file = this.getFile(); + if (file instanceof File) { + ((File)file).updateModificationStamp(editorOverlay.getText()); + } } public int getContentsCharCount() { diff --git a/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionEditorPresenter.java b/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionEditorPresenter.java index b6485e42bf..a318f893db 100644 --- a/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionEditorPresenter.java +++ b/ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionEditorPresenter.java @@ -1015,7 +1015,13 @@ public class OrionEditorPresenter extends AbstractEditorPresenter implements Tex editorView.setEditorWidget(editorWidget); document = editorWidget.getDocument(); - document.setFile(input.getFile()); + final VirtualFile file = input.getFile(); + document.setFile(file); + + if (file instanceof File) { + ((File)file).updateModificationStamp(content); + } + cursorModel = new OrionCursorModel(document); editorWidget.setTabSize(configuration.getTabWidth()); @@ -1032,7 +1038,7 @@ public class OrionEditorPresenter extends AbstractEditorPresenter implements Tex if (delayedFocus) { editorWidget.refresh(); editorWidget.setFocus(); - setSelection(new Selection<>(input.getFile())); + setSelection(new Selection<>(file)); delayedFocus = false; } diff --git a/infrastructures/docker/pom.xml b/infrastructures/docker/pom.xml index 3894c7da57..96f3909992 100644 --- a/infrastructures/docker/pom.xml +++ b/infrastructures/docker/pom.xml @@ -70,6 +70,10 @@ javax.ws.rs javax.ws.rs-api + + org.antlr + ST4 + org.eclipse.che.core che-core-api-core @@ -198,8 +202,11 @@ **/DefaultServerEvaluationStrategyTest.java **/LocalDockerServerEvaluationStrategy.java **/LocalDockerServerEvaluationStrategyTest.java + **/LocalDockerCustomServerEvaluationStrategy.java + **/LocalDockerCustomServerEvaluationStrategyTest.java **/DockerInstanceRuntimeInfo.java **/DockerInstanceRuntimeInfoTest.java + **/ServerIdleDetector.java diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java index 55aae69ea1..976972cc28 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java @@ -614,6 +614,12 @@ public class DockerMachineStarter { env = commonMachineEnvVariables; volumes = commonMachineSystemVolumes; } + // register workspace ID and Machine Name + env.put(CHE_WORKSPACE_ID, workspaceId); + // FIXME: spi +// env.put(DockerInstanceRuntimeInfo.CHE_MACHINE_NAME, machineName); +// env.put(DockerInstanceRuntimeInfo.CHE_IS_DEV_MACHINE, Boolean.toString(isDev)); + containerConfig.getExpose().addAll(portsToExpose); containerConfig.getEnvironment().putAll(env); containerConfig.getVolumes().addAll(volumes); diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerServerEvaluationStrategy.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerServerEvaluationStrategy.java index f03184cf69..86b438a5cd 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerServerEvaluationStrategy.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerServerEvaluationStrategy.java @@ -16,15 +16,11 @@ import com.google.inject.name.Named; import org.eclipse.che.api.workspace.server.model.impl.ServerImpl; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.plugin.docker.client.json.ContainerInfo; -import org.eclipse.che.plugin.docker.client.json.PortBinding; +import org.eclipse.che.workspace.infrastructure.docker.strategy.BaseServerEvaluationStrategy; import org.eclipse.che.workspace.infrastructure.docker.strategy.ServerEvaluationStrategy; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import static com.google.common.base.Strings.isNullOrEmpty; - /** * Represents a server evaluation strategy for the configuration where the workspace server and * workspace containers are running on the same Docker network. Calling @@ -37,63 +33,11 @@ import static com.google.common.base.Strings.isNullOrEmpty; * @author Angel Misevski * @see ServerEvaluationStrategy */ -public class LocalDockerServerEvaluationStrategy extends ServerEvaluationStrategy { - - /** - * Used to store the address set by property {@code che.docker.ip}, if applicable. - */ - protected String internalAddressProperty; - - /** - * Used to store the address set by property {@code che.docker.ip.external}. if applicable. - */ - protected String externalAddressProperty; +public class LocalDockerServerEvaluationStrategy extends BaseServerEvaluationStrategy { @Inject public LocalDockerServerEvaluationStrategy(@Nullable @Named("che.docker.ip") String internalAddress, @Nullable @Named("che.docker.ip.external") String externalAddress) { - this.internalAddressProperty = internalAddress; - this.externalAddressProperty = externalAddress; - } - - @Override - protected Map getInternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { - String internalAddressContainer = containerInfo.getNetworkSettings().getIpAddress(); - - String internalAddress; - boolean useExposedPorts = true; - if (!isNullOrEmpty(internalAddressContainer)) { - internalAddress = internalAddressContainer; - } else { - internalAddress = internalHost; - useExposedPorts = false; - } - - Map> portBindings = containerInfo.getNetworkSettings().getPorts(); - - Map addressesAndPorts = new HashMap<>(); - for (Map.Entry> portEntry : portBindings.entrySet()) { - String exposedPort = portEntry.getKey().split("/")[0]; - String ephemeralPort = portEntry.getValue().get(0).getHostPort(); - if (useExposedPorts) { - addressesAndPorts.put(portEntry.getKey(), internalAddress + ":" + exposedPort); - } else { - addressesAndPorts.put(portEntry.getKey(), internalAddress + ":" + ephemeralPort); - } - } - return addressesAndPorts; - } - - @Override - protected Map getExternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { - String externalAddressContainer = containerInfo.getNetworkSettings().getGateway(); - - String externalAddress = externalAddressProperty != null ? - externalAddressProperty : - !isNullOrEmpty(externalAddressContainer) ? - externalAddressContainer : - internalHost; - - return getExposedPortsToAddressPorts(externalAddress, containerInfo.getNetworkSettings().getPorts()); + super(internalAddress, externalAddress, null, null, null, true); } } diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/BaseServerEvaluationStrategy.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/BaseServerEvaluationStrategy.java new file mode 100644 index 0000000000..e1c29c0fff --- /dev/null +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/BaseServerEvaluationStrategy.java @@ -0,0 +1,514 @@ +/******************************************************************************* + * 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.workspace.infrastructure.docker.strategy; + +import com.google.common.base.Strings; + +import org.eclipse.che.api.workspace.server.model.impl.ServerImpl; +import org.eclipse.che.plugin.docker.client.json.ContainerInfo; +import org.eclipse.che.plugin.docker.client.json.PortBinding; +import org.stringtemplate.v4.ST; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * Represents a server evaluation strategy for the configuration where the strategy can be customized through template properties. + * + * @author Florent Benoit + * @see ServerEvaluationStrategy + */ +public abstract class BaseServerEvaluationStrategy extends ServerEvaluationStrategy { + + /** + * Regexp to extract port (under the form 22/tcp or 4401/tcp, etc.) from label references + */ + public static final String LABEL_CHE_SERVER_REF_KEY = "^che:server:(.*):ref$"; + + /** + * Name of the property for getting the workspace ID. + */ + public static final String CHE_WORKSPACE_ID_PROPERTY = "CHE_WORKSPACE_ID="; + + /** + * Name of the property to get the machine name property + */ + public static final String CHE_MACHINE_NAME_PROPERTY = "CHE_MACHINE_NAME="; + + /** + * Name of the property to get the property that indicates if the machine is the dev machine + */ + public static final String CHE_IS_DEV_MACHINE_PROPERTY = "CHE_IS_DEV_MACHINE="; + + /** + * Prefix added in front of the generated name to build the workspaceId + */ + public static final String CHE_WORKSPACE_ID_PREFIX = "workspace"; + + + /** + * name of the macro that indicates if the machine is the dev machine + */ + public static final String IS_DEV_MACHINE_MACRO = "isDevMachine"; + + /** + * Used to store the address set by property {@code che.docker.ip}, if applicable. + */ + protected String cheDockerIp; + + /** + * Used to store the address set by property {@code che.docker.ip.external}. if applicable. + */ + protected String cheDockerIpExternal; + + /** + * The current port of che. + */ + private final String chePort; + + /** + * Secured or not ? (for example https vs http) + */ + private final String cheDockerCustomExternalProtocol; + + /** + * Template for external addresses. + */ + private String cheDockerCustomExternalTemplate; + + /** + * Option to enable the use of the container address, when searching for addresses. + */ + private boolean localDockerMode; + + + /** + * Option to tell if an exception should be thrown when the host defined in the `externalAddress` isn't known + * and cannot be converted into a valid IP. + */ + private boolean throwOnUnknownHost = true; + + /** + * Default constructor + */ + public BaseServerEvaluationStrategy(String cheDockerIp, + String cheDockerIpExternal, + String cheDockerCustomExternalTemplate, + String cheDockerCustomExternalProtocol, + String chePort) { + this(cheDockerIp, cheDockerIpExternal, cheDockerCustomExternalTemplate, cheDockerCustomExternalProtocol, chePort, false); + } + + /** + * Constructor to be called by derived strategies + */ + public BaseServerEvaluationStrategy(String cheDockerIp, + String cheDockerIpExternal, + String cheDockerCustomExternalTemplate, + String cheDockerCustomExternalProtocol, + String chePort, + boolean localDockerMode) { + this.cheDockerIp = cheDockerIp; + this.cheDockerIpExternal = cheDockerIpExternal; + this.chePort = chePort; + this.cheDockerCustomExternalTemplate = cheDockerCustomExternalTemplate; + this.cheDockerCustomExternalProtocol = cheDockerCustomExternalProtocol; + this.localDockerMode = localDockerMode; + } + + @Override + protected Map getInternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { + final String internalAddressContainer = containerInfo.getNetworkSettings().getIpAddress(); + + final String internalAddress; + + if (localDockerMode) { + internalAddress = !isNullOrEmpty(internalAddressContainer) ? + internalAddressContainer : + internalHost; + } else { + internalAddress = + cheDockerIp != null ? + cheDockerIp : + internalHost; + } + + boolean useExposedPorts = localDockerMode && internalAddress != internalHost; + + return getExposedPortsToAddressPorts(internalAddress, containerInfo.getNetworkSettings().getPorts(), useExposedPorts); + } + + + /** + * Override the host for all ports by using the external template. + */ + @Override + protected Map getExternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { + + // create Rendering evaluation + RenderingEvaluation renderingEvaluation = getOnlineRenderingEvaluation(containerInfo, internalHost); + + // get current ports + Map> ports = containerInfo.getNetworkSettings().getPorts(); + + if (isNullOrEmpty(cheDockerCustomExternalTemplate)) { + return getExposedPortsToAddressPorts(renderingEvaluation.getExternalAddress(), ports, false); + } + + return ports.keySet().stream() + .collect(Collectors.toMap(portKey -> portKey, + portKey -> renderingEvaluation.render(cheDockerCustomExternalTemplate, portKey))); + } + + + /** + * Constructs a map of {@link ServerImpl} from provided parameters, using selected strategy + * for evaluating addresses and ports. + * + *

Keys consist of port number and transport protocol (tcp or udp) separated by + * a forward slash (e.g. 8080/tcp) + * + * @param containerInfo + * the {@link ContainerInfo} describing the container. + * @param internalHost + * alternative hostname to use, if address cannot be obtained from containerInfo + * @param serverConfMap + * additional Map of {@link OldServerConfImpl}. Configurations here override those found + * in containerInfo. + * @return a Map of the servers exposed by the container. + */ + public Map getServers(ContainerInfo containerInfo, + String internalHost, + Map serverConfMap) { + Map servers = super.getServers(containerInfo, internalHost, serverConfMap); + return servers.entrySet().stream().collect(Collectors.toMap(map -> map.getKey(), map -> updateServer(map.getValue()))); + } + + + /** + * Updates the protocol for the given server by using given protocol (like https) for http URLs. + * @param server the server to update + * @return updated server object + */ + protected ServerImpl updateServer(ServerImpl server) { + if (!Strings.isNullOrEmpty(cheDockerCustomExternalProtocol)) { + // FIXME: spi +// if ("http".equals(server.getProtocol())) { +// server.setProtocol(cheDockerCustomExternalProtocol); +// String url = server.getUrl(); +// int length = "http".length(); +// server.setUrl(cheDockerCustomExternalProtocol.concat(url.substring(length))); +// } + } + return server; + } + + + /** + * Allow to get the rendering outside of the evaluation strategies. + * It is called online as in this case we have access to container info + */ + public RenderingEvaluation getOnlineRenderingEvaluation(ContainerInfo containerInfo, String internalHost) { + return new OnlineRenderingEvaluation(containerInfo).withInternalHost(internalHost); + } + + /** + * Allow to get the rendering outside of the evaluation strategies. + * It is called offline as without container info, user need to provide merge of container and images data + */ + public RenderingEvaluation getOfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + return new OfflineRenderingEvaluation(labels, exposedPorts, env); + } + + /** + * Simple interface for performing the rendering for a given portby using the given template + * + * @author Florent Benoit + */ + public interface RenderingEvaluation { + /** + * Gets the template rendering for the given port and using the given template + * + * @param template + * which can include + * @param port + * the port for the mapping + * @return the rendering of the template + */ + String render(String template, String port); + + /** + * Gets default external address. + */ + String getExternalAddress(); + } + + /** + * Online implementation (using the container info) + */ + protected class OnlineRenderingEvaluation extends OfflineRenderingEvaluation implements RenderingEvaluation { + + private String gatewayAddressContainer; + private String internalHost; + + protected OnlineRenderingEvaluation(ContainerInfo containerInfo) { + super(containerInfo.getConfig().getLabels(), containerInfo.getConfig().getExposedPorts().keySet(), + containerInfo.getConfig().getEnv()); + this.gatewayAddressContainer = containerInfo.getNetworkSettings().getGateway(); + } + + protected OnlineRenderingEvaluation withInternalHost(String internalHost) { + this.internalHost = internalHost; + return this; + } + + @Override + public String getExternalAddress() { + if (localDockerMode) { + return cheDockerIpExternal != null ? + cheDockerIpExternal : + !isNullOrEmpty(gatewayAddressContainer) ? + gatewayAddressContainer : + this.internalHost; + } + + return cheDockerIpExternal != null ? + cheDockerIpExternal : + cheDockerIp != null ? + cheDockerIp : + !isNullOrEmpty(gatewayAddressContainer) ? + gatewayAddressContainer : + this.internalHost; + + } + } + + /** + * Offline implementation (container not yet created) + */ + protected class OfflineRenderingEvaluation extends DefaultRenderingEvaluation implements RenderingEvaluation { + + public OfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + super(labels, exposedPorts, env); + } + } + + /** + * Inner class used to perform the rendering + */ + protected abstract class DefaultRenderingEvaluation implements RenderingEvaluation { + + /** + * Labels + */ + private Map labels; + + /** + * Ports + */ + private Set exposedPorts; + + /** + * Environment variables + */ + private final String[] env; + + /** + * Map with properties for all ports + */ + private Map globalPropertiesMap = new HashMap<>(); + + /** + * Mapping between a port and the server ref name + */ + private Map portsToRefName; + + /** + * Data initialized ? + */ + private boolean initialized; + + /** + * Default constructor. + */ + protected DefaultRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + this.labels = labels; + this.exposedPorts = exposedPorts; + this.env = env; + } + + /** + * Initialize data + */ + protected void init() { + this.initPortMapping(); + this.populateGlobalProperties(); + } + + /** + * Compute port mapping with server ref name + */ + protected void initPortMapping() { + // ok, so now we have a map of labels and a map of exposed ports + // need to extract the name of the ref (if defined in a label) or then pickup default name "Server--" + Pattern pattern = Pattern.compile(LABEL_CHE_SERVER_REF_KEY); + Map portsToKnownRefName = labels.entrySet().stream() + .filter(map -> pattern.matcher(map.getKey()).matches()) + .collect(Collectors.toMap(p -> { + Matcher matcher = pattern.matcher(p.getKey()); + matcher.matches(); + String val = matcher.group(1); + return val.contains("/") ? val : val.concat("/tcp"); + }, p -> p.getValue())); + + // add to this map only port without a known ref name + Map portsToUnkownRefName = + exposedPorts.stream().filter((port) -> !portsToKnownRefName.containsKey(port)) + .collect(Collectors.toMap(p -> p, p -> "server-" + p.replace('/', '-'))); + + // list of all ports with refName (known/unknown) + this.portsToRefName = new HashMap(portsToKnownRefName); + portsToRefName.putAll(portsToUnkownRefName); + } + + /** + * Gets default external address. + */ + public String getExternalAddress() { + return cheDockerIpExternal != null ? + cheDockerIpExternal : cheDockerIp; + } + + /** + * Populate the template properties + */ + protected void populateGlobalProperties() { + String externalAddress = getExternalAddress(); + String externalIP = getExternalIp(externalAddress); + globalPropertiesMap.put("internalIp", cheDockerIp); + globalPropertiesMap.put("externalAddress", externalAddress); + globalPropertiesMap.put("externalIP", externalIP); + globalPropertiesMap.put("workspaceId", getWorkspaceId()); + globalPropertiesMap.put("workspaceIdWithoutPrefix", getWorkspaceId().replaceFirst(CHE_WORKSPACE_ID_PREFIX,"")); + globalPropertiesMap.put("machineName", getMachineName()); + globalPropertiesMap.put("wildcardNipDomain", getWildcardNipDomain(externalAddress)); + globalPropertiesMap.put("wildcardXipDomain", getWildcardXipDomain(externalAddress)); + globalPropertiesMap.put("chePort", chePort); + globalPropertiesMap.put(IS_DEV_MACHINE_MACRO, getIsDevMachine()); + } + + /** + * Rendering + */ + @Override + public String render(String template, String port) { + if (!this.initialized) { + init(); + this.initialized = true; + } + ST stringTemplate = new ST(template); + globalPropertiesMap.forEach((key, value) -> stringTemplate.add(key, + IS_DEV_MACHINE_MACRO.equals(key) ? + Boolean.parseBoolean(value) + : value)); + stringTemplate.add("serverName", portsToRefName.get(port)); + return stringTemplate.render(); + } + + /** + * returns if the current machine is the dev machine + * + * @return true if the curent machine is the dev machine + */ + protected String getIsDevMachine() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_IS_DEV_MACHINE_PROPERTY)) + .map(s -> s.substring(CHE_IS_DEV_MACHINE_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the workspace ID from the config of the given container + * + * @return workspace ID + */ + protected String getWorkspaceId() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_WORKSPACE_ID_PROPERTY)) + .map(s -> s.substring(CHE_WORKSPACE_ID_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the workspace Machine Name from the config of the given container + * + * @return machine name of the workspace + */ + protected String getMachineName() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_MACHINE_NAME_PROPERTY)) + .map(s -> s.substring(CHE_MACHINE_NAME_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the IP address of the external address + * + * @return IP Address + */ + protected String getExternalIp(String externalAddress) { + try { + return InetAddress.getByName(externalAddress).getHostAddress(); + } catch (UnknownHostException e) { + if (throwOnUnknownHost) { + throw new UnsupportedOperationException("Unable to find the IP for the address '" + externalAddress + "'", e); + } + } + return null; + } + + /** + * Gets a Wildcard domain based on the ip using an external provider nip.io + * + * @return wildcard domain + */ + protected String getWildcardNipDomain(String externalAddress) { + return String.format("%s.%s", getExternalIp(externalAddress), "nip.io"); + } + + /** + * Gets a Wildcard domain based on the ip using an external provider xip.io + * + * @return wildcard domain + */ + protected String getWildcardXipDomain(String externalAddress) { + return String.format("%s.%s", getExternalIp(externalAddress), "xip.io"); + } + + } + + @Override + protected boolean useHttpsForExternalUrls() { + return "https".equals(cheDockerCustomExternalProtocol); + } + + public BaseServerEvaluationStrategy withThrowOnUnknownHost(boolean throwOnUnknownHost) { + this.throwOnUnknownHost = throwOnUnknownHost; + return this; + } +} diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/DefaultServerEvaluationStrategy.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/DefaultServerEvaluationStrategy.java index 1ba583f841..1ebc5aac05 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/DefaultServerEvaluationStrategy.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/DefaultServerEvaluationStrategy.java @@ -31,42 +31,11 @@ import java.util.Map; * @author Alexander Garagatyi * @see ServerEvaluationStrategy */ -public class DefaultServerEvaluationStrategy extends ServerEvaluationStrategy { - - /** - * Used to store the address set by property {@code che.docker.ip}, if applicable. - */ - protected String internalAddressProperty; - - /** - * Used to store the address set by property {@code che.docker.ip.external}. if applicable. - */ - protected String externalAddressProperty; +public class DefaultServerEvaluationStrategy extends BaseServerEvaluationStrategy { @Inject public DefaultServerEvaluationStrategy(@Nullable @Named("che.docker.ip") String internalAddress, @Nullable @Named("che.docker.ip.external") String externalAddress) { - this.internalAddressProperty = internalAddress; - this.externalAddressProperty = externalAddress; - } - - @Override - protected Map getInternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { - String internalAddress = internalAddressProperty != null ? - internalAddressProperty : - internalHost; - - return getExposedPortsToAddressPorts(internalAddress, containerInfo.getNetworkSettings().getPorts()); - } - - @Override - protected Map getExternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { - String externalAddress = externalAddressProperty != null ? - externalAddressProperty : - internalAddressProperty != null ? - internalAddressProperty : - internalHost; - - return super.getExposedPortsToAddressPorts(externalAddress, containerInfo.getNetworkSettings().getPorts()); + super(internalAddress, externalAddress, null, null, null, false); } } diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/ServerEvaluationStrategy.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/ServerEvaluationStrategy.java index 975f747705..c2d0792c58 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/ServerEvaluationStrategy.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/strategy/ServerEvaluationStrategy.java @@ -10,16 +10,16 @@ *******************************************************************************/ package org.eclipse.che.workspace.infrastructure.docker.strategy; -import org.eclipse.che.api.workspace.server.model.impl.ServerImpl; -import org.eclipse.che.plugin.docker.client.json.ContainerInfo; -import org.eclipse.che.plugin.docker.client.json.PortBinding; - import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.eclipse.che.api.workspace.server.model.impl.ServerImpl; +import org.eclipse.che.plugin.docker.client.json.ContainerInfo; +import org.eclipse.che.plugin.docker.client.json.PortBinding; + /** * Represents a strategy for resolving Servers associated with workspace containers. * Used to extract relevant information from e.g. {@link ContainerInfo} into a map of @@ -27,14 +27,26 @@ import java.util.Map; * * @author Angel Misevski * @author Alexander Garagatyi + * @author Ilya Buziuk * @see ServerEvaluationStrategyProvider */ public abstract class ServerEvaluationStrategy { + private static final String HTTP = "http"; + private static final String HTTPS = "https"; protected static final String SERVER_CONF_LABEL_REF_KEY = "che:server:%s:ref"; protected static final String SERVER_CONF_LABEL_PROTOCOL_KEY = "che:server:%s:protocol"; protected static final String SERVER_CONF_LABEL_PATH_KEY = "che:server:%s:path"; + + /** + * @return true if external addresses need to be exposed against https, false otherwise + */ + protected boolean useHttpsForExternalUrls() { + return false; + } + + /** * Gets a map of all internal addresses exposed by the container in the form of * {@code "

:"} @@ -109,7 +121,11 @@ public abstract class ServerEvaluationStrategy { // Add protocol and path to internal/external address, if applicable String internalUrl = null; String externalUrl = null; - if (serverConf.getProtocol() != null) { + + String internalProtocol = serverConf.getProtocol(); + String externalProtocol = getProtocolForExternalUrl(internalProtocol); + + if (internalProtocol != null) { String pathSuffix = serverConf.getPath(); if (pathSuffix != null && !pathSuffix.isEmpty()) { if (pathSuffix.charAt(0) != '/') { @@ -118,8 +134,9 @@ public abstract class ServerEvaluationStrategy { } else { pathSuffix = ""; } - internalUrl = serverConf.getProtocol() + "://" + internalAddressAndPort + pathSuffix; - externalUrl = serverConf.getProtocol() + "://" + externalAddressAndPort + pathSuffix; + + internalUrl = internalProtocol + "://" + internalAddressAndPort + pathSuffix; + externalUrl = externalProtocol + "://" + externalAddressAndPort + pathSuffix; } servers.put(serverConf.getRef(), new ServerImpl(externalUrl)); @@ -215,14 +232,37 @@ public abstract class ServerEvaluationStrategy { * "9090/udp" : "my-host.com:32722" * } * } + * */ - protected Map getExposedPortsToAddressPorts(String address, Map> ports) { + protected Map getExposedPortsToAddressPorts(String address, Map> ports, boolean useExposedPorts) { Map addressesAndPorts = new HashMap<>(); for (Map.Entry> portEntry : ports.entrySet()) { + String exposedPort = portEntry.getKey().split("/")[0]; // there is one value always - String port = portEntry.getValue().get(0).getHostPort(); - addressesAndPorts.put(portEntry.getKey(), address + ":" + port); + String ephemeralPort = portEntry.getValue().get(0).getHostPort(); + if (useExposedPorts) { + addressesAndPorts.put(portEntry.getKey(), address + ":" + exposedPort); + } else { + addressesAndPorts.put(portEntry.getKey(), address + ":" + ephemeralPort); + } } return addressesAndPorts; } + + protected Map getExposedPortsToAddressPorts(String address, Map> ports) { + return getExposedPortsToAddressPorts(address, ports, false); + } + + + /** + * @param protocolForInternalUrl + * @return https, if {@link #useHttpsForExternalUrls()} method in sub-class returns true and protocol for internal Url is http + */ + private String getProtocolForExternalUrl(final String protocolForInternalUrl) { + if (useHttpsForExternalUrls() && HTTP.equals(protocolForInternalUrl)) { + return HTTPS; + } + return protocolForInternalUrl; + } + } diff --git a/infrastructures/docker/src/test/java/org/eclipse/che/workspace/infrastructure/docker/old/ServerEvaluationStrategyTest.java b/infrastructures/docker/src/test/java/org/eclipse/che/workspace/infrastructure/docker/old/ServerEvaluationStrategyTest.java index 27fd3ee5fa..e58adf7835 100644 --- a/infrastructures/docker/src/test/java/org/eclipse/che/workspace/infrastructure/docker/old/ServerEvaluationStrategyTest.java +++ b/infrastructures/docker/src/test/java/org/eclipse/che/workspace/infrastructure/docker/old/ServerEvaluationStrategyTest.java @@ -60,7 +60,7 @@ public class ServerEvaluationStrategyTest { expected.put("9090/udp", DEFAULT_HOSTNAME + ":" + "32101"); // when - Map actual = strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports); + Map actual = strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports, false); // then assertEquals(actual, expected); @@ -85,7 +85,7 @@ public class ServerEvaluationStrategyTest { expected.put("9090/udp", DEFAULT_HOSTNAME + ":" + "32101"); // when - Map actual = strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports); + Map actual = strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports, false); // then assertEquals(actual, expected); @@ -352,7 +352,7 @@ public class ServerEvaluationStrategyTest { .withHostPort("32101"))); when(networkSettings.getPorts()).thenReturn(ports); Map exposedPortsToAddressPorts = - strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports); + strategy.getExposedPortsToAddressPorts(DEFAULT_HOSTNAME, ports, false); when(strategy.getExternalAddressesAndPorts(containerInfo, DEFAULT_HOSTNAME)) .thenReturn(exposedPortsToAddressPorts); when(strategy.getInternalAddressesAndPorts(containerInfo, DEFAULT_HOSTNAME)) @@ -373,6 +373,11 @@ public class ServerEvaluationStrategyTest { String internalAddress) { return null; } + + @Override + protected boolean useHttpsForExternalUrls() { + return false; + } } */ } diff --git a/plugins/plugin-composer/che-plugin-composer-server/src/main/java/org/eclipse/che/plugin/composer/server/projecttype/ComposerValueProviderFactory.java b/plugins/plugin-composer/che-plugin-composer-server/src/main/java/org/eclipse/che/plugin/composer/server/projecttype/ComposerValueProviderFactory.java index 7c2d35bc1a..fb792312cf 100644 --- a/plugins/plugin-composer/che-plugin-composer-server/src/main/java/org/eclipse/che/plugin/composer/server/projecttype/ComposerValueProviderFactory.java +++ b/plugins/plugin-composer/che-plugin-composer-server/src/main/java/org/eclipse/che/plugin/composer/server/projecttype/ComposerValueProviderFactory.java @@ -54,7 +54,7 @@ public class ComposerValueProviderFactory implements ValueProviderFactory { JsonObject model = readModel(projectFolder); String value = ""; - if (attributeName.equals(PACKAGE)) { + if (attributeName.equals(PACKAGE) && model.has("name")) { value = model.get("name").getAsString(); } diff --git a/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/Exec.java b/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/Exec.java index 8eddc50920..fda34f8e0a 100644 --- a/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/Exec.java +++ b/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/Exec.java @@ -20,7 +20,7 @@ public class Exec { private final String[] command; private final String id; - Exec(String[] command, String id) { + public Exec(String[] command, String id) { this.command = command; this.id = id; } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/BaseServerEvaluationStrategy.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/BaseServerEvaluationStrategy.java new file mode 100644 index 0000000000..73dee6634b --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/BaseServerEvaluationStrategy.java @@ -0,0 +1,517 @@ +/******************************************************************************* + * 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.plugin.docker.machine; + +import com.google.common.base.Strings; +import com.google.inject.Inject; +import com.google.inject.name.Named; + +import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; +import org.eclipse.che.api.machine.server.model.impl.ServerImpl; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.plugin.docker.client.json.ContainerInfo; +import org.eclipse.che.plugin.docker.client.json.PortBinding; +import org.stringtemplate.v4.ST; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * Represents a server evaluation strategy for the configuration where the strategy can be customized through template properties. + * + * @author Florent Benoit + * @see ServerEvaluationStrategy + */ +public abstract class BaseServerEvaluationStrategy extends ServerEvaluationStrategy { + + /** + * Regexp to extract port (under the form 22/tcp or 4401/tcp, etc.) from label references + */ + public static final String LABEL_CHE_SERVER_REF_KEY = "^che:server:(.*):ref$"; + + /** + * Name of the property for getting the workspace ID. + */ + public static final String CHE_WORKSPACE_ID_PROPERTY = "CHE_WORKSPACE_ID="; + + /** + * Name of the property to get the machine name property + */ + public static final String CHE_MACHINE_NAME_PROPERTY = "CHE_MACHINE_NAME="; + + /** + * Name of the property to get the property that indicates if the machine is the dev machine + */ + public static final String CHE_IS_DEV_MACHINE_PROPERTY = "CHE_IS_DEV_MACHINE="; + + /** + * Prefix added in front of the generated name to build the workspaceId + */ + public static final String CHE_WORKSPACE_ID_PREFIX = "workspace"; + + + /** + * name of the macro that indicates if the machine is the dev machine + */ + public static final String IS_DEV_MACHINE_MACRO = "isDevMachine"; + + /** + * Used to store the address set by property {@code che.docker.ip}, if applicable. + */ + protected String cheDockerIp; + + /** + * Used to store the address set by property {@code che.docker.ip.external}. if applicable. + */ + protected String cheDockerIpExternal; + + /** + * The current port of che. + */ + private final String chePort; + + /** + * Secured or not ? (for example https vs http) + */ + private final String cheDockerCustomExternalProtocol; + + /** + * Template for external addresses. + */ + private String cheDockerCustomExternalTemplate; + + /** + * Option to enable the use of the container address, when searching for addresses. + */ + private boolean localDockerMode; + + + /** + * Option to tell if an exception should be thrown when the host defined in the `externalAddress` isn't known + * and cannot be converted into a valid IP. + */ + private boolean throwOnUnknownHost = true; + + /** + * Default constructor + */ + public BaseServerEvaluationStrategy(String cheDockerIp, + String cheDockerIpExternal, + String cheDockerCustomExternalTemplate, + String cheDockerCustomExternalProtocol, + String chePort) { + this(cheDockerIp, cheDockerIpExternal, cheDockerCustomExternalTemplate, cheDockerCustomExternalProtocol, chePort, false); + } + + /** + * Constructor to be called by derived strategies + */ + public BaseServerEvaluationStrategy(String cheDockerIp, + String cheDockerIpExternal, + String cheDockerCustomExternalTemplate, + String cheDockerCustomExternalProtocol, + String chePort, + boolean localDockerMode) { + this.cheDockerIp = cheDockerIp; + this.cheDockerIpExternal = cheDockerIpExternal; + this.chePort = chePort; + this.cheDockerCustomExternalTemplate = cheDockerCustomExternalTemplate; + this.cheDockerCustomExternalProtocol = cheDockerCustomExternalProtocol; + this.localDockerMode = localDockerMode; + } + + @Override + protected Map getInternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { + final String internalAddressContainer = containerInfo.getNetworkSettings().getIpAddress(); + + final String internalAddress; + + if (localDockerMode) { + internalAddress = !isNullOrEmpty(internalAddressContainer) ? + internalAddressContainer : + internalHost; + } else { + internalAddress = + cheDockerIp != null ? + cheDockerIp : + internalHost; + } + + boolean useExposedPorts = localDockerMode && internalAddress != internalHost; + + return getExposedPortsToAddressPorts(internalAddress, containerInfo.getNetworkSettings().getPorts(), useExposedPorts); + } + + + /** + * Override the host for all ports by using the external template. + */ + @Override + protected Map getExternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { + + // create Rendering evaluation + RenderingEvaluation renderingEvaluation = getOnlineRenderingEvaluation(containerInfo, internalHost); + + // get current ports + Map> ports = containerInfo.getNetworkSettings().getPorts(); + + if (isNullOrEmpty(cheDockerCustomExternalTemplate)) { + return getExposedPortsToAddressPorts(renderingEvaluation.getExternalAddress(), ports, false); + } + + return ports.keySet().stream() + .collect(Collectors.toMap(portKey -> portKey, + portKey -> renderingEvaluation.render(cheDockerCustomExternalTemplate, portKey))); + } + + + /** + * Constructs a map of {@link ServerImpl} from provided parameters, using selected strategy + * for evaluating addresses and ports. + * + *

Keys consist of port number and transport protocol (tcp or udp) separated by + * a forward slash (e.g. 8080/tcp) + * + * @param containerInfo + * the {@link ContainerInfo} describing the container. + * @param internalHost + * alternative hostname to use, if address cannot be obtained from containerInfo + * @param serverConfMap + * additional Map of {@link ServerConfImpl}. Configurations here override those found + * in containerInfo. + * @return a Map of the servers exposed by the container. + */ + public Map getServers(ContainerInfo containerInfo, + String internalHost, + Map serverConfMap) { + Map servers = super.getServers(containerInfo, internalHost, serverConfMap); + return servers.entrySet().stream().collect(Collectors.toMap(map -> map.getKey(), map -> updateServer(map.getValue()))); + } + + + /** + * Updates the protocol for the given server by using given protocol (like https) for http URLs. + * @param server the server to update + * @return updated server object + */ + protected ServerImpl updateServer(ServerImpl server) { + if (!Strings.isNullOrEmpty(cheDockerCustomExternalProtocol)) { + if ("http".equals(server.getProtocol())) { + server.setProtocol(cheDockerCustomExternalProtocol); + String url = server.getUrl(); + int length = "http".length(); + server.setUrl(cheDockerCustomExternalProtocol.concat(url.substring(length))); + } + } + return server; + } + + + /** + * Allow to get the rendering outside of the evaluation strategies. + * It is called online as in this case we have access to container info + */ + public RenderingEvaluation getOnlineRenderingEvaluation(ContainerInfo containerInfo, String internalHost) { + return new OnlineRenderingEvaluation(containerInfo).withInternalHost(internalHost); + } + + /** + * Allow to get the rendering outside of the evaluation strategies. + * It is called offline as without container info, user need to provide merge of container and images data + */ + public RenderingEvaluation getOfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + return new OfflineRenderingEvaluation(labels, exposedPorts, env); + } + + /** + * Simple interface for performing the rendering for a given portby using the given template + * + * @author Florent Benoit + */ + public interface RenderingEvaluation { + /** + * Gets the template rendering for the given port and using the given template + * + * @param template + * which can include + * @param port + * the port for the mapping + * @return the rendering of the template + */ + String render(String template, String port); + + /** + * Gets default external address. + */ + String getExternalAddress(); + } + + /** + * Online implementation (using the container info) + */ + protected class OnlineRenderingEvaluation extends OfflineRenderingEvaluation implements RenderingEvaluation { + + private String gatewayAddressContainer; + private String internalHost; + + protected OnlineRenderingEvaluation(ContainerInfo containerInfo) { + super(containerInfo.getConfig().getLabels(), containerInfo.getConfig().getExposedPorts().keySet(), + containerInfo.getConfig().getEnv()); + this.gatewayAddressContainer = containerInfo.getNetworkSettings().getGateway(); + } + + protected OnlineRenderingEvaluation withInternalHost(String internalHost) { + this.internalHost = internalHost; + return this; + } + + @Override + public String getExternalAddress() { + if (localDockerMode) { + return cheDockerIpExternal != null ? + cheDockerIpExternal : + !isNullOrEmpty(gatewayAddressContainer) ? + gatewayAddressContainer : + this.internalHost; + } + + return cheDockerIpExternal != null ? + cheDockerIpExternal : + cheDockerIp != null ? + cheDockerIp : + !isNullOrEmpty(gatewayAddressContainer) ? + gatewayAddressContainer : + this.internalHost; + + } + } + + /** + * Offline implementation (container not yet created) + */ + protected class OfflineRenderingEvaluation extends DefaultRenderingEvaluation implements RenderingEvaluation { + + public OfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + super(labels, exposedPorts, env); + } + } + + /** + * Inner class used to perform the rendering + */ + protected abstract class DefaultRenderingEvaluation implements RenderingEvaluation { + + /** + * Labels + */ + private Map labels; + + /** + * Ports + */ + private Set exposedPorts; + + /** + * Environment variables + */ + private final String[] env; + + /** + * Map with properties for all ports + */ + private Map globalPropertiesMap = new HashMap<>(); + + /** + * Mapping between a port and the server ref name + */ + private Map portsToRefName; + + /** + * Data initialized ? + */ + private boolean initialized; + + /** + * Default constructor. + */ + protected DefaultRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { + this.labels = labels; + this.exposedPorts = exposedPorts; + this.env = env; + } + + /** + * Initialize data + */ + protected void init() { + this.initPortMapping(); + this.populateGlobalProperties(); + } + + /** + * Compute port mapping with server ref name + */ + protected void initPortMapping() { + // ok, so now we have a map of labels and a map of exposed ports + // need to extract the name of the ref (if defined in a label) or then pickup default name "Server--" + Pattern pattern = Pattern.compile(LABEL_CHE_SERVER_REF_KEY); + Map portsToKnownRefName = labels.entrySet().stream() + .filter(map -> pattern.matcher(map.getKey()).matches()) + .collect(Collectors.toMap(p -> { + Matcher matcher = pattern.matcher(p.getKey()); + matcher.matches(); + String val = matcher.group(1); + return val.contains("/") ? val : val.concat("/tcp"); + }, p -> p.getValue())); + + // add to this map only port without a known ref name + Map portsToUnkownRefName = + exposedPorts.stream().filter((port) -> !portsToKnownRefName.containsKey(port)) + .collect(Collectors.toMap(p -> p, p -> "server-" + p.replace('/', '-'))); + + // list of all ports with refName (known/unknown) + this.portsToRefName = new HashMap(portsToKnownRefName); + portsToRefName.putAll(portsToUnkownRefName); + } + + /** + * Gets default external address. + */ + public String getExternalAddress() { + return cheDockerIpExternal != null ? + cheDockerIpExternal : cheDockerIp; + } + + /** + * Populate the template properties + */ + protected void populateGlobalProperties() { + String externalAddress = getExternalAddress(); + String externalIP = getExternalIp(externalAddress); + globalPropertiesMap.put("internalIp", cheDockerIp); + globalPropertiesMap.put("externalAddress", externalAddress); + globalPropertiesMap.put("externalIP", externalIP); + globalPropertiesMap.put("workspaceId", getWorkspaceId()); + globalPropertiesMap.put("workspaceIdWithoutPrefix", getWorkspaceId().replaceFirst(CHE_WORKSPACE_ID_PREFIX,"")); + globalPropertiesMap.put("machineName", getMachineName()); + globalPropertiesMap.put("wildcardNipDomain", getWildcardNipDomain(externalAddress)); + globalPropertiesMap.put("wildcardXipDomain", getWildcardXipDomain(externalAddress)); + globalPropertiesMap.put("chePort", chePort); + globalPropertiesMap.put(IS_DEV_MACHINE_MACRO, getIsDevMachine()); + } + + /** + * Rendering + */ + @Override + public String render(String template, String port) { + if (!this.initialized) { + init(); + this.initialized = true; + } + ST stringTemplate = new ST(template); + globalPropertiesMap.forEach((key, value) -> stringTemplate.add(key, + IS_DEV_MACHINE_MACRO.equals(key) ? + Boolean.parseBoolean(value) + : value)); + stringTemplate.add("serverName", portsToRefName.get(port)); + return stringTemplate.render(); + } + + /** + * returns if the current machine is the dev machine + * + * @return true if the curent machine is the dev machine + */ + protected String getIsDevMachine() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_IS_DEV_MACHINE_PROPERTY)) + .map(s -> s.substring(CHE_IS_DEV_MACHINE_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the workspace ID from the config of the given container + * + * @return workspace ID + */ + protected String getWorkspaceId() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_WORKSPACE_ID_PROPERTY)) + .map(s -> s.substring(CHE_WORKSPACE_ID_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the workspace Machine Name from the config of the given container + * + * @return machine name of the workspace + */ + protected String getMachineName() { + return Arrays.stream(env).filter(env -> env.startsWith(CHE_MACHINE_NAME_PROPERTY)) + .map(s -> s.substring(CHE_MACHINE_NAME_PROPERTY.length())) + .findFirst().get(); + } + + /** + * Gets the IP address of the external address + * + * @return IP Address + */ + protected String getExternalIp(String externalAddress) { + try { + return InetAddress.getByName(externalAddress).getHostAddress(); + } catch (UnknownHostException e) { + if (throwOnUnknownHost) { + throw new UnsupportedOperationException("Unable to find the IP for the address '" + externalAddress + "'", e); + } + } + return null; + } + + /** + * Gets a Wildcard domain based on the ip using an external provider nip.io + * + * @return wildcard domain + */ + protected String getWildcardNipDomain(String externalAddress) { + return String.format("%s.%s", getExternalIp(externalAddress), "nip.io"); + } + + /** + * Gets a Wildcard domain based on the ip using an external provider xip.io + * + * @return wildcard domain + */ + protected String getWildcardXipDomain(String externalAddress) { + return String.format("%s.%s", getExternalIp(externalAddress), "xip.io"); + } + + } + + @Override + protected boolean useHttpsForExternalUrls() { + return "https".equals(cheDockerCustomExternalProtocol); + } + + public BaseServerEvaluationStrategy withThrowOnUnknownHost(boolean throwOnUnknownHost) { + this.throwOnUnknownHost = throwOnUnknownHost; + return this; + } +} diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategy.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategy.java index 07086fbb1c..314a88c96b 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategy.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategy.java @@ -10,29 +10,10 @@ *******************************************************************************/ package org.eclipse.che.plugin.docker.machine; -import com.google.common.base.Strings; import com.google.inject.Inject; import com.google.inject.name.Named; -import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; -import org.eclipse.che.api.machine.server.model.impl.ServerImpl; import org.eclipse.che.commons.annotation.Nullable; -import org.eclipse.che.plugin.docker.client.json.ContainerInfo; -import org.eclipse.che.plugin.docker.client.json.PortBinding; -import org.stringtemplate.v4.ST; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static com.google.common.base.Strings.isNullOrEmpty; /** * Represents a server evaluation strategy for the configuration where the strategy can be customized through template properties. @@ -40,39 +21,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; * @author Florent Benoit * @see ServerEvaluationStrategy */ -public class CustomServerEvaluationStrategy extends DefaultServerEvaluationStrategy { - - /** - * Regexp to extract port (under the form 22/tcp or 4401/tcp, etc.) from label references - */ - public static final String LABEL_CHE_SERVER_REF_KEY = "^che:server:(.*):ref$"; - - /** - * Name of the property for getting the workspace ID. - */ - public static final String CHE_WORKSPACE_ID_PROPERTY = "CHE_WORKSPACE_ID="; - - /** - * Name of the property to get the machine name property - */ - public static final String CHE_MACHINE_NAME_PROPERTY = "CHE_MACHINE_NAME="; - - /** - * The current port of che. - */ - private final String chePort; - - /** - * Secured or not ? (for example https vs http) - */ - private final String cheDockerCustomExternalProtocol; - - /** - * Template for external addresses. - */ - private String cheDockerCustomExternalTemplate; - - +public class CustomServerEvaluationStrategy extends BaseServerEvaluationStrategy { /** * Default constructor */ @@ -82,317 +31,6 @@ public class CustomServerEvaluationStrategy extends DefaultServerEvaluationStrat @Nullable @Named("che.docker.server_evaluation_strategy.custom.template") String cheDockerCustomExternalTemplate, @Nullable @Named("che.docker.server_evaluation_strategy.custom.external.protocol") String cheDockerCustomExternalProtocol, @Named("che.port") String chePort) { - super(cheDockerIp, cheDockerIpExternal); - this.chePort = chePort; - this.cheDockerCustomExternalTemplate = cheDockerCustomExternalTemplate; - this.cheDockerCustomExternalProtocol = cheDockerCustomExternalProtocol; + super(cheDockerIp, cheDockerIpExternal, cheDockerCustomExternalTemplate, cheDockerCustomExternalProtocol, chePort, false); } - - /** - * Override the host for all ports by using the external template. - */ - @Override - protected Map getExternalAddressesAndPorts(ContainerInfo containerInfo, String internalHost) { - - // create Rendering evaluation - RenderingEvaluation renderingEvaluation = getOnlineRenderingEvaluation(containerInfo, internalHost); - - // get current ports - Map> ports = containerInfo.getNetworkSettings().getPorts(); - - return ports.keySet().stream() - .collect(Collectors.toMap(portKey -> portKey, - portKey -> renderingEvaluation.render(cheDockerCustomExternalTemplate, portKey))); - } - - - /** - * Constructs a map of {@link ServerImpl} from provided parameters, using selected strategy - * for evaluating addresses and ports. - * - *

Keys consist of port number and transport protocol (tcp or udp) separated by - * a forward slash (e.g. 8080/tcp) - * - * @param containerInfo - * the {@link ContainerInfo} describing the container. - * @param internalHost - * alternative hostname to use, if address cannot be obtained from containerInfo - * @param serverConfMap - * additional Map of {@link ServerConfImpl}. Configurations here override those found - * in containerInfo. - * @return a Map of the servers exposed by the container. - */ - public Map getServers(ContainerInfo containerInfo, - String internalHost, - Map serverConfMap) { - Map servers = super.getServers(containerInfo, internalHost, serverConfMap); - return servers.entrySet().stream().collect(Collectors.toMap(map -> map.getKey(), map -> updateServer(map.getValue()))); - } - - - /** - * Updates the protocol for the given server by using given protocol (like https) for http URLs. - * @param server the server to update - * @return updated server object - */ - protected ServerImpl updateServer(ServerImpl server) { - if (!Strings.isNullOrEmpty(cheDockerCustomExternalProtocol)) { - if ("http".equals(server.getProtocol())) { - server.setProtocol(cheDockerCustomExternalProtocol); - String url = server.getUrl(); - int length = "http".length(); - server.setUrl(cheDockerCustomExternalProtocol.concat(url.substring(length))); - } - } - return server; - } - - - /** - * Allow to get the rendering outside of the evaluation strategies. - * It is called online as in this case we have access to container info - */ - public RenderingEvaluation getOnlineRenderingEvaluation(ContainerInfo containerInfo, String internalHost) { - return new OnlineRenderingEvaluation(containerInfo).withInternalHost(internalHost); - } - - /** - * Allow to get the rendering outside of the evaluation strategies. - * It is called offline as without container info, user need to provide merge of container and images data - */ - public RenderingEvaluation getOfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { - return new OfflineRenderingEvaluation(labels, exposedPorts, env); - } - - /** - * Simple interface for performing the rendering for a given portby using the given template - * - * @author Florent Benoit - */ - public interface RenderingEvaluation { - /** - * Gets the template rendering for the given port and using the given template - * - * @param template - * which can include - * @param port - * the port for the mapping - * @return the rendering of the template - */ - String render(String template, String port); - } - - /** - * Online implementation (using the container info) - */ - protected class OnlineRenderingEvaluation extends OfflineRenderingEvaluation implements RenderingEvaluation { - - private String gatewayAddressContainer; - private String internalHost; - - protected OnlineRenderingEvaluation(ContainerInfo containerInfo) { - super(containerInfo.getConfig().getLabels(), containerInfo.getConfig().getExposedPorts().keySet(), - containerInfo.getConfig().getEnv()); - this.gatewayAddressContainer = containerInfo.getNetworkSettings().getGateway(); - } - - protected OnlineRenderingEvaluation withInternalHost(String internalHost) { - this.internalHost = internalHost; - return this; - } - - @Override - protected String getExternalAddress() { - return externalAddressProperty != null ? - externalAddressProperty : - internalAddressProperty != null ? - internalAddressProperty : - !isNullOrEmpty(gatewayAddressContainer) ? - gatewayAddressContainer : - this.internalHost; - } - } - - /** - * Offline implementation (container not yet created) - */ - protected class OfflineRenderingEvaluation extends DefaultRenderingEvaluation implements RenderingEvaluation { - - public OfflineRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { - super(labels, exposedPorts, env); - } - } - - /** - * Inner class used to perform the rendering - */ - protected abstract class DefaultRenderingEvaluation implements RenderingEvaluation { - - /** - * Labels - */ - private Map labels; - - /** - * Ports - */ - private Set exposedPorts; - - /** - * Environment variables - */ - private final String[] env; - - /** - * Map with properties for all ports - */ - private Map globalPropertiesMap = new HashMap<>(); - - /** - * Mapping between a port and the server ref name - */ - private Map portsToRefName; - - /** - * Data initialized ? - */ - private boolean initialized; - - /** - * Default constructor. - */ - protected DefaultRenderingEvaluation(Map labels, Set exposedPorts, String[] env) { - this.labels = labels; - this.exposedPorts = exposedPorts; - this.env = env; - } - - /** - * Initialize data - */ - protected void init() { - this.initPortMapping(); - this.populateGlobalProperties(); - } - - /** - * Compute port mapping with server ref name - */ - protected void initPortMapping() { - // ok, so now we have a map of labels and a map of exposed ports - // need to extract the name of the ref (if defined in a label) or then pickup default name "Server--" - Pattern pattern = Pattern.compile(LABEL_CHE_SERVER_REF_KEY); - Map portsToKnownRefName = labels.entrySet().stream() - .filter(map -> pattern.matcher(map.getKey()).matches()) - .collect(Collectors.toMap(p -> { - Matcher matcher = pattern.matcher(p.getKey()); - matcher.matches(); - String val = matcher.group(1); - return val.contains("/") ? val : val.concat("/tcp"); - }, p -> p.getValue())); - - // add to this map only port without a known ref name - Map portsToUnkownRefName = - exposedPorts.stream().filter((port) -> !portsToKnownRefName.containsKey(port)) - .collect(Collectors.toMap(p -> p, p -> "Server-" + p.replace('/', '-'))); - - // list of all ports with refName (known/unknown) - this.portsToRefName = new HashMap(portsToKnownRefName); - portsToRefName.putAll(portsToUnkownRefName); - } - - /** - * Gets default external address. - */ - protected String getExternalAddress() { - return externalAddressProperty != null ? - externalAddressProperty : internalAddressProperty; - } - - /** - * Populate the template properties - */ - protected void populateGlobalProperties() { - String externalAddress = getExternalAddress(); - String externalIP = getExternalIp(externalAddress); - globalPropertiesMap.put("internalIp", internalAddressProperty); - globalPropertiesMap.put("externalAddress", externalAddress); - globalPropertiesMap.put("externalIP", externalIP); - globalPropertiesMap.put("workspaceId", getWorkspaceId()); - globalPropertiesMap.put("machineName", getMachineName()); - globalPropertiesMap.put("wildcardNipDomain", getWildcardNipDomain(externalAddress)); - globalPropertiesMap.put("wildcardXipDomain", getWildcardXipDomain(externalAddress)); - globalPropertiesMap.put("chePort", chePort); - } - - /** - * Rendering - */ - @Override - public String render(String template, String port) { - if (!this.initialized) { - init(); - this.initialized = true; - } - ST stringTemplate = new ST(template); - globalPropertiesMap.forEach((key, value) -> stringTemplate.add(key, value)); - stringTemplate.add("serverName", portsToRefName.get(port)); - return stringTemplate.render(); - } - - /** - * Gets the workspace ID from the config of the given container - * - * @return workspace ID - */ - protected String getWorkspaceId() { - return Arrays.stream(env).filter(env -> env.startsWith(CHE_WORKSPACE_ID_PROPERTY)) - .map(s -> s.substring(CHE_WORKSPACE_ID_PROPERTY.length())) - .findFirst().get(); - } - - /** - * Gets the workspace Machine Name from the config of the given container - * - * @return machine name of the workspace - */ - protected String getMachineName() { - return Arrays.stream(env).filter(env -> env.startsWith(CHE_MACHINE_NAME_PROPERTY)) - .map(s -> s.substring(CHE_MACHINE_NAME_PROPERTY.length())) - .findFirst().get(); - } - - /** - * Gets the IP address of the external address - * - * @return IP Address - */ - protected String getExternalIp(String externalAddress) { - try { - return InetAddress.getByName(externalAddress).getHostAddress(); - } catch (UnknownHostException e) { - throw new UnsupportedOperationException("Unable to find the IP for the address '" + externalAddress + "'", e); - } - } - - /** - * Gets a Wildcard domain based on the ip using an external provider nip.io - * - * @return wildcard domain - */ - protected String getWildcardNipDomain(String externalAddress) { - return String.format("%s.%s", getExternalIp(externalAddress), "nip.io"); - } - - /** - * Gets a Wildcard domain based on the ip using an external provider xip.io - * - * @return wildcard domain - */ - protected String getWildcardXipDomain(String externalAddress) { - return String.format("%s.%s", getExternalIp(externalAddress), "xip.io"); - } - - } - } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategy.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategy.java new file mode 100644 index 0000000000..22330d416e --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategy.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2016-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; + +import com.google.inject.Inject; +import com.google.inject.name.Named; + +import org.eclipse.che.api.machine.server.model.impl.ServerImpl; +import org.eclipse.che.commons.annotation.Nullable; + + +/** + * Represents a server evaluation strategy for the configuration where the workspace server and workspace + * containers are running on the same Docker network and are exposed through the same single port. + * + * This server evaluation strategy will return a completed {@link ServerImpl} with internal addresses set + * as {@link LocalDockerServerEvaluationStrategy} does. Contrary external addresses will be managed by the + * `custom` evaluation strategy,and its template property `che.docker.server_evaluation_strategy.custom.template` + * + *

cheExternalAddress can be set using property {@code che.docker.ip.external}. + * This strategy is useful when Che and the workspace servers need to be exposed on the same single TCP port + * + * @author Mario Loriedo + * @see ServerEvaluationStrategy + */ +public class LocalDockerCustomServerEvaluationStrategy extends BaseServerEvaluationStrategy { + + @Inject + public LocalDockerCustomServerEvaluationStrategy(@Nullable @Named("che.docker.ip") String internalAddress, + @Nullable @Named("che.docker.ip.external") String externalAddress, + @Nullable @Named("che.docker.server_evaluation_strategy.custom.template") String cheDockerCustomExternalTemplate, + @Nullable @Named("che.docker.server_evaluation_strategy.custom.external.protocol") String cheDockerCustomExternalProtocol, + @Named("che.port") String chePort) { + super(internalAddress, externalAddress, cheDockerCustomExternalTemplate, cheDockerCustomExternalProtocol, chePort, true); + } +} diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/idle/ServerIdleDetector.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/idle/ServerIdleDetector.java new file mode 100644 index 0000000000..0694849296 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/idle/ServerIdleDetector.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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.idle; + +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.che.api.core.event.ServerIdleEvent; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.api.workspace.server.WorkspaceManager; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Inject; +/** + * Notifies about idling the che server + * Fires {@link org.eclipse.che.api.core.event.ServerIdleEvent} if no workspace + * is run for che.openshift.server.inactive.stop.timeout.ms milliseconds + */ +@Singleton +public class ServerIdleDetector implements EventSubscriber { + private static final Logger LOG = LoggerFactory.getLogger(ServerIdleDetector.class); + private static final String IDLING_CHE_SERVER_SCHEDULED = "Idling che server scheduled [timeout=%s] seconds]"; + + private final long timeout; + private ScheduledFuture future; + private ScheduledExecutorService executor; + private WorkspaceManager workspaceManager; + private final EventService eventService; + + @Inject + public ServerIdleDetector(WorkspaceManager workspaceManager, + EventService eventService, + @Named("che.openshift.server.inactive.stop.timeout.ms") long timeout) { + this.timeout = timeout; + this.eventService = eventService; + this.workspaceManager = workspaceManager; + if (timeout > 0) { + this.executor = Executors.newSingleThreadScheduledExecutor(); + this.future = executor.schedule(this::run, timeout, TimeUnit.MILLISECONDS); + LOG.info(String.format(IDLING_CHE_SERVER_SCHEDULED, timeout/1000)); + } + } + + @Override + public void onEvent(WorkspaceStatusEvent event) { + if (future != null) { + String workspaceId = event.getWorkspaceId(); + switch (event.getEventType()) { + case RUNNING: + if (!future.isCancelled()) { + future.cancel(true); + LOG.info("Idling che server canceled"); + } + break; + case STOPPED: + Set ids = workspaceManager.getRunningWorkspacesIds(); + ids.remove(workspaceId); + if (ids.size() <= 0) { + if (!future.isCancelled()) { + future.cancel(true); + } + future = executor.schedule(this::run, timeout, TimeUnit.MILLISECONDS); + LOG.info(String.format(IDLING_CHE_SERVER_SCHEDULED, timeout/1000)); + } + break; + default: + break; + } + } + } + + private void run() { + Set ids = workspaceManager.getRunningWorkspacesIds(); + if (ids.size() <= 0) { + eventService.publish(new ServerIdleEvent(timeout)); + } + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this); + } + + @PreDestroy + private void unsubscribe() { + eventService.unsubscribe(this); + if (future != null && !future.isCancelled()) { + future.cancel(true); + } + if (executor != null && !executor.isShutdown()) { + executor.shutdown(); + } + } + +} diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategyTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategyTest.java index 1e8fc9aef0..4200cb14c2 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategyTest.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/CustomServerEvaluationStrategyTest.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static org.eclipse.che.plugin.docker.machine.CustomServerEvaluationStrategy.CHE_WORKSPACE_ID_PREFIX; import static org.mockito.Mockito.when; /** @@ -43,11 +44,19 @@ public class CustomServerEvaluationStrategyTest { private static final String ALL_IP_ADDRESS = "0.0.0.0"; - private static final String WORKSPACE_ID_VALUE = "work123"; - private static final String WORKSPACE_ID_PROPERTY = "CHE_WORKSPACE_ID=" + WORKSPACE_ID_VALUE; + private static final String WORKSPACE_ID_WITHOUT_PREFIX_VALUE = "ABCDEFG"; + + private static final String WORKSPACE_ID_VALUE = CHE_WORKSPACE_ID_PREFIX + WORKSPACE_ID_WITHOUT_PREFIX_VALUE; + private static final String WORKSPACE_ID_PROPERTY_PREFIX = "CHE_WORKSPACE_ID="; + private static final String WORKSPACE_ID_PROPERTY = WORKSPACE_ID_PROPERTY_PREFIX + WORKSPACE_ID_VALUE; private static final String MACHINE_NAME_VALUE = "myMachine"; - private static final String MACHINE_NAME_PROPERTY = "CHE_MACHINE_NAME=" + MACHINE_NAME_VALUE; + private static final String MACHINE_NAME_PROPERTY_PREFIX = "CHE_MACHINE_NAME="; + private static final String MACHINE_NAME_PROPERTY = MACHINE_NAME_PROPERTY_PREFIX + MACHINE_NAME_VALUE; + + private static final String IS_DEV_MACHINE_PROPERTY_PREFIX = "CHE_IS_DEV_MACHINE="; + private static final String IS_DEV_MACHINE_PROPERTY_TRUE = IS_DEV_MACHINE_PROPERTY_PREFIX + "true"; + private static final String IS_DEV_MACHINE_PROPERTY_FALSE = IS_DEV_MACHINE_PROPERTY_PREFIX + "false"; @Mock private ContainerConfig containerConfig; @@ -76,7 +85,7 @@ public class CustomServerEvaluationStrategyTest { when(containerConfig.getLabels()).thenReturn(containerLabels); when(containerConfig.getExposedPorts()).thenReturn(containerExposedPorts); - envContainerConfig = new String[]{WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY}; + envContainerConfig = new String[]{WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY_TRUE}; when(containerConfig.getEnv()).thenReturn(envContainerConfig); when(containerInfo.getNetworkSettings()).thenReturn(networkSettings); @@ -126,6 +135,52 @@ public class CustomServerEvaluationStrategyTest { Assert.assertEquals(portMapping.get("4401/tcp"), WORKSPACE_ID_VALUE); } + /** + * Check workspace Id without prefix template + */ + @Test + public void testWorkspaceIdWithoutPrefixRule() throws Throwable { + this.customServerEvaluationStrategy = + new CustomServerEvaluationStrategy("10.0.0.1", "192.168.1.1", "", "http", "8080"); + + Map portMapping = this.customServerEvaluationStrategy.getExternalAddressesAndPorts(containerInfo, "localhost"); + + Assert.assertTrue(portMapping.containsKey("4401/tcp")); + Assert.assertEquals(portMapping.get("4401/tcp"), WORKSPACE_ID_WITHOUT_PREFIX_VALUE); + } + + /** + * Check the isDevMachine macro in template + */ + @Test + public void testIsDevMachineWhenTrue() throws Throwable { + this.customServerEvaluationStrategy = + new CustomServerEvaluationStrategy("10.0.0.1", "192.168.1.1", + "", "http", "8080"); + + Map portMapping = this.customServerEvaluationStrategy.getExternalAddressesAndPorts(containerInfo, "localhost"); + + Assert.assertTrue(portMapping.containsKey("4401/tcp")); + Assert.assertEquals(portMapping.get("4401/tcp"), WORKSPACE_ID_VALUE); + } + + /** + * Check the isDevMachine macro in template + */ + @Test + public void testIsDevMachineWhenFalse() throws Throwable { + this.envContainerConfig = new String[]{WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY_FALSE}; + when(containerConfig.getEnv()).thenReturn(envContainerConfig); + + this.customServerEvaluationStrategy = + new CustomServerEvaluationStrategy("10.0.0.1", "192.168.1.1", + "", "http", "8080"); + + Map portMapping = this.customServerEvaluationStrategy.getExternalAddressesAndPorts(containerInfo, "localhost"); + + Assert.assertTrue(portMapping.containsKey("4401/tcp")); + Assert.assertEquals(portMapping.get("4401/tcp"), MACHINE_NAME_VALUE); + } /** * Check workspace Id template @@ -213,7 +268,7 @@ public class CustomServerEvaluationStrategyTest { exposedPorts.add("4401/tcp"); exposedPorts.add("4411/tcp"); exposedPorts.add("8080/tcp"); - List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY); + List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY_TRUE); this.customServerEvaluationStrategy = new CustomServerEvaluationStrategy("127.0.0.1", null, "-", "https", "8080"); CustomServerEvaluationStrategy.RenderingEvaluation renderingEvaluation = this.customServerEvaluationStrategy @@ -233,7 +288,7 @@ public class CustomServerEvaluationStrategyTest { exposedPorts.add("4401/tcp"); exposedPorts.add("4411/tcp"); exposedPorts.add("8080/tcp"); - List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY); + List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY_TRUE); this.customServerEvaluationStrategy = new CustomServerEvaluationStrategy("127.0.0.1", "127.0.0.1", "-", "https", "8080"); CustomServerEvaluationStrategy.RenderingEvaluation renderingEvaluation = this.customServerEvaluationStrategy @@ -253,7 +308,7 @@ public class CustomServerEvaluationStrategyTest { exposedPorts.add("4401/tcp"); exposedPorts.add("4411/tcp"); exposedPorts.add("8080/tcp"); - List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY); + List env = Arrays.asList(WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY_TRUE); this.customServerEvaluationStrategy = new CustomServerEvaluationStrategy("127.0.0.1", "300.300.300.300", "-", "https", "8080"); CustomServerEvaluationStrategy.RenderingEvaluation renderingEvaluation = this.customServerEvaluationStrategy diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategyTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategyTest.java new file mode 100644 index 0000000000..7d297c4bf7 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/LocalDockerCustomServerEvaluationStrategyTest.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2016-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; + +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; +import org.eclipse.che.api.machine.server.model.impl.ServerImpl; +import org.eclipse.che.api.machine.server.model.impl.ServerPropertiesImpl; +import org.eclipse.che.plugin.docker.client.json.ContainerConfig; +import org.eclipse.che.plugin.docker.client.json.ContainerInfo; +import org.eclipse.che.plugin.docker.client.json.NetworkSettings; +import org.eclipse.che.plugin.docker.client.json.PortBinding; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class LocalDockerCustomServerEvaluationStrategyTest { + + private static final String CHE_DOCKER_IP_EXTERNAL = "container-host-ext.com"; + private static final String ALL_IP_ADDRESS = "0.0.0.0"; + private static final String CONTAINERCONFIG_HOSTNAME = "che-ws-y6jwknht0efzczit-4086112300-fm0aj"; + private static final String WORKSPACE_ID = "79rfwhqaztq2ru2k"; + + private static final String WORKSPACE_ID_VALUE = WORKSPACE_ID; + private static final String WORKSPACE_ID_PROPERTY = "CHE_WORKSPACE_ID=" + WORKSPACE_ID_VALUE; + + private static final String MACHINE_NAME_VALUE = "myMachine"; + private static final String MACHINE_NAME_PROPERTY = "CHE_MACHINE_NAME=" + MACHINE_NAME_VALUE; + + private static final String IS_DEV_MACHINE_VALUE = "true"; + private static final String IS_DEV_MACHINE_PROPERTY = "CHE_IS_DEV_MACHINE=" + IS_DEV_MACHINE_VALUE; + + private static final String CHE_DOCKER_SERVER_EVALUATION_STRATEGY_CUSTOM_TEMPLATE = "--"; + + @Mock + private ContainerInfo containerInfo; + @Mock + private ContainerConfig containerConfig; + @Mock + private NetworkSettings networkSettings; + + private ServerEvaluationStrategy strategy; + + private Map serverConfs; + + private Map> ports; + + private Map labels; + + private String[] env; + + private String[] envContainerConfig; + + + @BeforeMethod + public void setUp() { + + serverConfs = new HashMap<>(); + serverConfs.put("4301/tcp", new ServerConfImpl("sysServer1-tcp", "4301/tcp", "http", "/some/path1")); + serverConfs.put("4305/udp", new ServerConfImpl("devSysServer1-udp", "4305/udp", null, "some/path4")); + + ports = new HashMap<>(); + ports.put("4301/tcp", Collections.singletonList(new PortBinding().withHostIp(ALL_IP_ADDRESS ) + .withHostPort("32100"))); + ports.put("4305/udp", Collections.singletonList(new PortBinding().withHostIp(ALL_IP_ADDRESS ) + .withHostPort("32103"))); + + labels = new HashMap<>(); + labels.put("che:server:4301/tcp:ref", "sysServer1-tcp"); + labels.put("che:server:4305/udp:ref", "devSysServer1-udp"); + + env = new String[]{"CHE_WORKSPACE_ID="+ WORKSPACE_ID}; + + when(containerInfo.getNetworkSettings()).thenReturn(networkSettings); + when(networkSettings.getIpAddress()).thenReturn(CONTAINERCONFIG_HOSTNAME); + when(networkSettings.getPorts()).thenReturn(ports); + when(containerInfo.getConfig()).thenReturn(containerConfig); + when(containerConfig.getHostname()).thenReturn(CONTAINERCONFIG_HOSTNAME); + when(containerConfig.getEnv()).thenReturn(env); + when(containerConfig.getLabels()).thenReturn(labels); + + envContainerConfig = new String[]{WORKSPACE_ID_PROPERTY, MACHINE_NAME_PROPERTY, IS_DEV_MACHINE_PROPERTY}; + when(containerConfig.getEnv()).thenReturn(envContainerConfig); + + } + + /** + * Test: single port strategy should use . + * @throws Exception + */ + @Test + public void shouldUseServerRefToBuildAddressWhenAvailable() throws Exception { + // given + strategy = new LocalDockerCustomServerEvaluationStrategy(null, null, CHE_DOCKER_SERVER_EVALUATION_STRATEGY_CUSTOM_TEMPLATE, "http", null).withThrowOnUnknownHost(false); + + final Map expectedServers = getExpectedServers(CHE_DOCKER_IP_EXTERNAL, + CONTAINERCONFIG_HOSTNAME, + true); + + // when + final Map servers = strategy.getServers(containerInfo, + CHE_DOCKER_IP_EXTERNAL, + serverConfs); + + // then + assertEquals(servers, expectedServers); + } + + private Map getExpectedServers(String externalAddress, + String internalAddress, + boolean useExposedPorts) { + String port1; + String port2; + if (useExposedPorts) { + port1 = ":4301"; + port2 = ":4305"; + } else { + port1 = ":32100"; + port2 = ":32103"; + } + Map expectedServers = new HashMap<>(); + expectedServers.put("4301/tcp", new ServerImpl("sysServer1-tcp", + "http", + "sysServer1-tcp-" + WORKSPACE_ID + "-" + externalAddress, + "http://" + "sysServer1-tcp-" + WORKSPACE_ID + "-" + externalAddress + "/some/path1", + new ServerPropertiesImpl("/some/path1", + internalAddress + port1, + "http://" + internalAddress + port1 + "/some/path1"))); + expectedServers.put("4305/udp", new ServerImpl("devSysServer1-udp", + null, + "devSysServer1-udp-" + WORKSPACE_ID + "-" + externalAddress, + null, + new ServerPropertiesImpl("some/path4", + internalAddress + port2, + null))); + return expectedServers; + } + +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/pom.xml b/plugins/plugin-docker/che-plugin-openshift-client/pom.xml index 80111272a7..29ea7465c0 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/pom.xml +++ b/plugins/plugin-docker/che-plugin-openshift-client/pom.xml @@ -55,6 +55,22 @@ javax.inject javax.inject + + org.eclipse.che.core + che-core-api-core + + + org.eclipse.che.core + che-core-api-model + + + org.eclipse.che.core + che-core-api-workspace + + + org.eclipse.che.core + che-core-commons-annotations + org.eclipse.che.plugin che-plugin-docker-client diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java index 42c80507c3..d4e1b2acb7 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java @@ -11,16 +11,29 @@ package org.eclipse.che.plugin.openshift.client; +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -28,10 +41,16 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.eclipse.che.api.core.event.ServerIdleEvent; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.plugin.docker.client.DockerApiVersionPathPrefixProvider; import org.eclipse.che.plugin.docker.client.DockerConnector; import org.eclipse.che.plugin.docker.client.DockerConnectorConfiguration; import org.eclipse.che.plugin.docker.client.DockerRegistryAuthResolver; +import org.eclipse.che.plugin.docker.client.Exec; +import org.eclipse.che.plugin.docker.client.LogMessage; import org.eclipse.che.plugin.docker.client.MessageProcessor; import org.eclipse.che.plugin.docker.client.ProgressMonitor; import org.eclipse.che.plugin.docker.client.connection.DockerConnectionFactory; @@ -40,31 +59,35 @@ import org.eclipse.che.plugin.docker.client.json.ContainerConfig; import org.eclipse.che.plugin.docker.client.json.ContainerCreated; import org.eclipse.che.plugin.docker.client.json.ContainerInfo; import org.eclipse.che.plugin.docker.client.json.ContainerListEntry; +import org.eclipse.che.plugin.docker.client.json.ContainerState; import org.eclipse.che.plugin.docker.client.json.Event; import org.eclipse.che.plugin.docker.client.json.Filters; import org.eclipse.che.plugin.docker.client.json.HostConfig; -import org.eclipse.che.plugin.docker.client.json.ImageConfig; import org.eclipse.che.plugin.docker.client.json.ImageInfo; import org.eclipse.che.plugin.docker.client.json.NetworkCreated; import org.eclipse.che.plugin.docker.client.json.NetworkSettings; import org.eclipse.che.plugin.docker.client.json.PortBinding; import org.eclipse.che.plugin.docker.client.json.network.ContainerInNetwork; +import org.eclipse.che.plugin.docker.client.json.network.EndpointConfig; import org.eclipse.che.plugin.docker.client.json.network.Ipam; import org.eclipse.che.plugin.docker.client.json.network.IpamConfig; import org.eclipse.che.plugin.docker.client.json.network.Network; import org.eclipse.che.plugin.docker.client.params.CommitParams; import org.eclipse.che.plugin.docker.client.params.CreateContainerParams; +import org.eclipse.che.plugin.docker.client.params.CreateExecParams; +import org.eclipse.che.plugin.docker.client.params.GetContainerLogsParams; import org.eclipse.che.plugin.docker.client.params.GetEventsParams; import org.eclipse.che.plugin.docker.client.params.GetResourceParams; +import org.eclipse.che.plugin.docker.client.params.InspectImageParams; import org.eclipse.che.plugin.docker.client.params.KillContainerParams; +import org.eclipse.che.plugin.docker.client.params.PullParams; import org.eclipse.che.plugin.docker.client.params.PutResourceParams; import org.eclipse.che.plugin.docker.client.params.RemoveContainerParams; import org.eclipse.che.plugin.docker.client.params.RemoveImageParams; import org.eclipse.che.plugin.docker.client.params.network.RemoveNetworkParams; import org.eclipse.che.plugin.docker.client.params.StartContainerParams; +import org.eclipse.che.plugin.docker.client.params.StartExecParams; import org.eclipse.che.plugin.docker.client.params.StopContainerParams; -import org.eclipse.che.plugin.docker.client.params.InspectImageParams; -import org.eclipse.che.plugin.docker.client.params.PullParams; import org.eclipse.che.plugin.docker.client.params.TagParams; import org.eclipse.che.plugin.docker.client.params.network.ConnectContainerToNetworkParams; import org.eclipse.che.plugin.docker.client.params.network.CreateNetworkParams; @@ -74,7 +97,9 @@ import org.eclipse.che.plugin.docker.client.params.network.InspectNetworkParams; import org.eclipse.che.plugin.openshift.client.exception.OpenShiftException; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesContainer; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesEnvVar; +import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesExecHolder; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesLabelConverter; +import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesOutputAdapter; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesService; import org.eclipse.che.plugin.openshift.client.kubernetes.KubernetesStringUtils; import org.slf4j.Logger; @@ -82,12 +107,24 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.ContainerStateRunning; +import io.fabric8.kubernetes.api.model.ContainerStateTerminated; +import io.fabric8.kubernetes.api.model.ContainerStateWaiting; +import io.fabric8.kubernetes.api.model.ContainerStatus; +import io.fabric8.kubernetes.api.model.DoneableEndpoints; +import io.fabric8.kubernetes.api.model.Endpoints; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimList; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSourceBuilder; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodList; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.PodSpecBuilder; import io.fabric8.kubernetes.api.model.Probe; import io.fabric8.kubernetes.api.model.ProbeBuilder; +import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.ServiceList; import io.fabric8.kubernetes.api.model.ServicePort; @@ -97,12 +134,23 @@ import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.VolumeMountBuilder; import io.fabric8.kubernetes.api.model.extensions.Deployment; import io.fabric8.kubernetes.api.model.extensions.DeploymentBuilder; +import io.fabric8.kubernetes.api.model.extensions.ReplicaSet; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.dsl.ExecWatch; +import io.fabric8.kubernetes.client.dsl.LogWatch; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.utils.InputStreamPumper; +import io.fabric8.openshift.api.model.DeploymentConfig; +import io.fabric8.openshift.api.model.DoneableDeploymentConfig; +import io.fabric8.openshift.api.model.Image; import io.fabric8.openshift.api.model.ImageStream; import io.fabric8.openshift.api.model.ImageStreamTag; +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.api.model.RouteList; import io.fabric8.openshift.client.DefaultOpenShiftClient; import io.fabric8.openshift.client.OpenShiftClient; - -import static com.google.common.base.Strings.isNullOrEmpty; +import io.fabric8.openshift.client.dsl.DeployableScalableResource; /** * Client for OpenShift API. @@ -114,47 +162,137 @@ import static com.google.common.base.Strings.isNullOrEmpty; @Singleton public class OpenShiftConnector extends DockerConnector { private static final Logger LOG = LoggerFactory.getLogger(OpenShiftConnector.class); + public static final String CHE_OPENSHIFT_RESOURCES_PREFIX = "che-ws-"; + public static final String OPENSHIFT_DEPLOYMENT_LABEL = "deployment"; + private static final String CHE_CONTAINER_IDENTIFIER_LABEL_KEY = "cheContainerIdentifier"; private static final String CHE_DEFAULT_EXTERNAL_ADDRESS = "172.17.0.1"; - private static final String CHE_OPENSHIFT_RESOURCES_PREFIX = "che-ws-"; private static final String CHE_WORKSPACE_ID_ENV_VAR = "CHE_WORKSPACE_ID"; + private static final String CHE_IS_DEV_MACHINE_ENV_VAR = "CHE_IS_DEV_MACHINE"; private static final int CHE_WORKSPACE_AGENT_PORT = 4401; private static final int CHE_TERMINAL_AGENT_PORT = 4411; private static final String DOCKER_PROTOCOL_PORT_DELIMITER = "/"; - private static final String OPENSHIFT_SERVICE_TYPE_NODE_PORT = "NodePort"; private static final int OPENSHIFT_WAIT_POD_DELAY = 1000; private static final int OPENSHIFT_WAIT_POD_TIMEOUT = 240; private static final int OPENSHIFT_IMAGESTREAM_WAIT_DELAY = 2000; private static final int OPENSHIFT_IMAGESTREAM_MAX_WAIT_COUNT = 30; private static final String OPENSHIFT_POD_STATUS_RUNNING = "Running"; - private static final String OPENSHIFT_DEPLOYMENT_LABEL = "deployment"; + private static final String OPENSHIFT_VOLUME_STORAGE_CLASS = "volume.beta.kubernetes.io/storage-class"; + private static final String OPENSHIFT_VOLUME_STORAGE_CLASS_NAME = "che-workspace"; private static final String OPENSHIFT_IMAGE_PULL_POLICY_IFNOTPRESENT = "IfNotPresent"; - private static final Long UID_ROOT = Long.valueOf(0); - private static final Long UID_USER = Long.valueOf(1000); - private final OpenShiftClient openShiftClient; + private static final String IDLING_ALPHA_OPENSHIFT_IO_IDLED_AT = "idling.alpha.openshift.io/idled-at"; + private static final String IDLING_ALPHA_OPENSHIFT_IO_PREVIOUS_SCALE = "idling.alpha.openshift.io/previous-scale"; + private static final String OPENSHIFT_CHE_SERVER_DEPLOYMENT_NAME = "che"; + private static final String OPENSHIFT_CHE_SERVER_SERVICE_NAME = "che-host"; + private static final String IDLING_ALPHA_OPENSHIFT_IO_UNIDLE_TARGETS = "idling.alpha.openshift.io/unidle-targets"; + private static final String ISO_8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssX"; + + private Map execMap = new HashMap<>(); + private final String openShiftCheProjectName; - private final String openShiftCheServiceAccount; private final int openShiftLivenessProbeDelay; private final int openShiftLivenessProbeTimeout; + private final String workspacesPersistentVolumeClaim; + private final String workspacesPvcQuantity; + private final String cheWorkspaceStorage; + private final String cheWorkspaceProjectsStorage; + private final String cheServerExternalAddress; + private final String cheWorkspaceMemoryLimit; + private final String cheWorkspaceMemoryRequest; + private final boolean secureRoutes; + private final boolean createWorkspaceDirs; + private final OpenShiftPvcHelper openShiftPvcHelper; @Inject public OpenShiftConnector(DockerConnectorConfiguration connectorConfiguration, DockerConnectionFactory connectionFactory, DockerRegistryAuthResolver authResolver, DockerApiVersionPathPrefixProvider dockerApiVersionPathPrefixProvider, + OpenShiftPvcHelper openShiftPvcHelper, + EventService eventService, + @Nullable @Named("che.docker.ip.external") String cheServerExternalAddress, @Named("che.openshift.project") String openShiftCheProjectName, - @Named("che.openshift.serviceaccountname") String openShiftCheServiceAccount, @Named("che.openshift.liveness.probe.delay") int openShiftLivenessProbeDelay, - @Named("che.openshift.liveness.probe.timeout") int openShiftLivenessProbeTimeout) { + @Named("che.openshift.liveness.probe.timeout") int openShiftLivenessProbeTimeout, + @Named("che.openshift.workspaces.pvc.name") String workspacesPersistentVolumeClaim, + @Named("che.openshift.workspaces.pvc.quantity") String workspacesPvcQuantity, + @Named("che.workspace.storage") String cheWorkspaceStorage, + @Named("che.workspace.projects.storage") String cheWorkspaceProjectsStorage, + @Nullable @Named("che.openshift.workspace.memory.request") String cheWorkspaceMemoryRequest, + @Nullable @Named("che.openshift.workspace.memory.override") String cheWorkspaceMemoryLimit, + @Named("che.openshift.secure.routes") boolean secureRoutes, + @Named("che.openshift.precreate.workspace.dirs") boolean createWorkspaceDirs) { super(connectorConfiguration, connectionFactory, authResolver, dockerApiVersionPathPrefixProvider); + this.cheServerExternalAddress = cheServerExternalAddress; this.openShiftCheProjectName = openShiftCheProjectName; - this.openShiftCheServiceAccount = openShiftCheServiceAccount; this.openShiftLivenessProbeDelay = openShiftLivenessProbeDelay; this.openShiftLivenessProbeTimeout = openShiftLivenessProbeTimeout; + this.workspacesPersistentVolumeClaim = workspacesPersistentVolumeClaim; + this.workspacesPvcQuantity = workspacesPvcQuantity; + this.cheWorkspaceStorage = cheWorkspaceStorage; + this.cheWorkspaceProjectsStorage = cheWorkspaceProjectsStorage; + this.cheWorkspaceMemoryRequest = cheWorkspaceMemoryRequest; + this.cheWorkspaceMemoryLimit = cheWorkspaceMemoryLimit; + this.secureRoutes = secureRoutes; + this.createWorkspaceDirs = createWorkspaceDirs; + this.openShiftPvcHelper = openShiftPvcHelper; + eventService.subscribe(new EventSubscriber() { - this.openShiftClient = new DefaultOpenShiftClient(); + @Override + public void onEvent(ServerIdleEvent event) { + idleCheServer(event); + } + }); + } + + private void idleCheServer(ServerIdleEvent event) { + try (DefaultOpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + DeployableScalableResource deploymentConfigResource = openShiftClient.deploymentConfigs() + .inNamespace(openShiftCheProjectName) + .withName(OPENSHIFT_CHE_SERVER_DEPLOYMENT_NAME); + DeploymentConfig deploymentConfig = deploymentConfigResource.get(); + if (deploymentConfig == null) { + LOG.warn(String.format("Deployment config %s not found", OPENSHIFT_CHE_SERVER_DEPLOYMENT_NAME)); + return; + } + Integer replicas = deploymentConfig.getSpec().getReplicas(); + if (replicas != null && replicas > 0) { + Resource endpointResource = openShiftClient.endpoints() + .inNamespace(openShiftCheProjectName) + .withName(OPENSHIFT_CHE_SERVER_SERVICE_NAME); + Endpoints endpoint = endpointResource.get(); + if (endpoint == null) { + LOG.warn(String.format("Endpoint %s not found", OPENSHIFT_CHE_SERVER_SERVICE_NAME)); + return; + } + Map annotations = deploymentConfig.getMetadata().getAnnotations(); + if (annotations == null) { + annotations = new HashMap<>(); + deploymentConfig.getMetadata().setAnnotations(annotations); + } + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat(ISO_8601_DATE_FORMAT); + df.setTimeZone(tz); + String idle = df.format(new Date()); + annotations.put(IDLING_ALPHA_OPENSHIFT_IO_IDLED_AT, idle); + annotations.put(IDLING_ALPHA_OPENSHIFT_IO_PREVIOUS_SCALE, "1"); + deploymentConfig.getSpec().setReplicas(0); + deploymentConfigResource.patch(deploymentConfig); + Map endpointAnnotations = endpoint.getMetadata().getAnnotations(); + if (endpointAnnotations == null) { + endpointAnnotations = new HashMap<>(); + endpoint.getMetadata().setAnnotations(endpointAnnotations); + } + endpointAnnotations.put(IDLING_ALPHA_OPENSHIFT_IO_IDLED_AT, idle); + endpointAnnotations.put(IDLING_ALPHA_OPENSHIFT_IO_UNIDLE_TARGETS, + "[{\"kind\":\"DeploymentConfig\",\"name\":\"" + OPENSHIFT_CHE_SERVER_DEPLOYMENT_NAME + + "\",\"replicas\":1}]"); + endpointResource.patch(endpoint); + LOG.info("Che server has been idled"); + } + } } /** @@ -167,9 +305,6 @@ public class OpenShiftConnector extends DockerConnector { String containerName = KubernetesStringUtils.convertToContainerName(createContainerParams.getContainerName()); String workspaceID = getCheWorkspaceId(createContainerParams); - // Generate workspaceID if CHE_WORKSPACE_ID env var does not exist - workspaceID = workspaceID.isEmpty() ? KubernetesStringUtils.generateWorkspaceID() : workspaceID; - // imageForDocker is the docker version of the image repository. It's needed for other // OpenShiftConnector API methods, but is not acceptable as an OpenShift name String imageForDocker = createContainerParams.getContainerConfig().getImage(); @@ -186,12 +321,15 @@ public class OpenShiftConnector extends DockerConnector { // Next we need to get the address of the registry where the ImageStreamTag is stored String imageStreamName = KubernetesStringUtils.getImageStreamNameFromPullSpec(imageStreamTagPullSpec); - ImageStream imageStream = openShiftClient.imageStreams() - .inNamespace(openShiftCheProjectName) - .withName(imageStreamName) - .get(); - if (imageStream == null) { - throw new OpenShiftException("ImageStream not found"); + ImageStream imageStream; + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + imageStream = openShiftClient.imageStreams() + .inNamespace(openShiftCheProjectName) + .withName(imageStreamName) + .get(); + if (imageStream == null) { + throw new OpenShiftException("ImageStream not found"); + } } String registryAddress = imageStream.getStatus() .getDockerImageRepository() @@ -207,35 +345,72 @@ public class OpenShiftConnector extends DockerConnector { .getConfig().getExposedPorts().keySet(); Set exposedPorts = getExposedPorts(containerExposedPorts, imageExposedPorts); - boolean runContainerAsRoot = runContainerAsRoot(imageForDocker); - String[] envVariables = createContainerParams.getContainerConfig().getEnv(); String[] volumes = createContainerParams.getContainerConfig().getHostConfig().getBinds(); Map additionalLabels = createContainerParams.getContainerConfig().getLabels(); + String networkName = createContainerParams.getContainerConfig().getHostConfig().getNetworkMode(); + EndpointConfig endpointConfig = createContainerParams.getContainerConfig().getNetworkingConfig().getEndpointsConfig().get(networkName); + String[] endpointAliases = endpointConfig != null ? endpointConfig.getAliases() : new String[0]; + + Map resourceLimits = new HashMap<>(); + if (!isNullOrEmpty(cheWorkspaceMemoryLimit)) { + LOG.info("Che property 'che.openshift.workspace.memory.override' " + + "used to override workspace memory limit to {}.", cheWorkspaceMemoryLimit); + resourceLimits.put("memory", new Quantity(cheWorkspaceMemoryLimit)); + } else { + long memoryLimitBytes = createContainerParams.getContainerConfig().getHostConfig().getMemory(); + String memoryLimit = Long.toString(memoryLimitBytes / 1048576) + "Mi"; + LOG.info("Creating workspace pod with memory limit of {}.", memoryLimit); + resourceLimits.put("memory", new Quantity(cheWorkspaceMemoryLimit)); + } + + Map resourceRequests = new HashMap<>(); + if (!isNullOrEmpty(cheWorkspaceMemoryRequest)) { + resourceRequests.put("memory", new Quantity(cheWorkspaceMemoryRequest)); + } + + String deploymentName; + String serviceName; + if (isDevMachine(createContainerParams)) { + serviceName = deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; + } else { + if (endpointAliases.length > 0) { + serviceName = endpointAliases[0]; + deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + serviceName; + } else { + // Should never happen + serviceName = deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + KubernetesStringUtils.generateWorkspaceID(); + } + } + String containerID; - try { - createOpenShiftService(workspaceID, exposedPorts, additionalLabels); - String deploymentName = createOpenShiftDeployment(workspaceID, - dockerPullSpec, - containerName, - exposedPorts, - envVariables, - volumes, - runContainerAsRoot); + OpenShiftClient openShiftClient = new DefaultOpenShiftClient(); + try { + createOpenShiftService(deploymentName, serviceName, exposedPorts, additionalLabels, endpointAliases); + createOpenShiftDeployment(deploymentName, + dockerPullSpec, + containerName, + exposedPorts, + envVariables, + volumes, + resourceLimits, + resourceRequests); containerID = waitAndRetrieveContainerID(deploymentName); if (containerID == null) { throw new OpenShiftException("Failed to get the ID of the container running in the OpenShift pod"); } - } catch (IOException e) { + } catch (IOException | KubernetesClientException e) { // Make sure we clean up deployment and service in case of an error -- otherwise Che can end up // in an inconsistent state. LOG.info("Error while creating Pod, removing deployment"); - String deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; + LOG.info(e.getMessage()); cleanUpWorkspaceResources(deploymentName); openShiftClient.resource(imageStreamTag).delete(); throw e; + } finally { + openShiftClient.close(); } return new ContainerCreated(containerID, null); @@ -367,10 +542,14 @@ public class OpenShiftConnector extends DockerConnector { @Override public Network inspectNetwork(InspectNetworkParams params) throws IOException { String netId = params.getNetworkId(); + ServiceList services; + + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + services = openShiftClient.services() + .inNamespace(this.openShiftCheProjectName) + .list(); + } - ServiceList services = openShiftClient.services() - .inNamespace(this.openShiftCheProjectName) - .list(); Map containers = new HashMap<>(); for (Service svc : services.getItems()) { String selector = svc.getSpec().getSelector().get(OPENSHIFT_DEPLOYMENT_LABEL); @@ -378,10 +557,13 @@ public class OpenShiftConnector extends DockerConnector { continue; } - PodList pods = openShiftClient.pods() - .inNamespace(openShiftCheProjectName) - .withLabel(OPENSHIFT_DEPLOYMENT_LABEL, selector) - .list(); + PodList pods; + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + pods = openShiftClient.pods() + .inNamespace(openShiftCheProjectName) + .withLabel(OPENSHIFT_DEPLOYMENT_LABEL, selector) + .list(); + } for (Pod pod : pods.getItems()) { String podName = pod.getMetadata() @@ -456,28 +638,34 @@ public class OpenShiftConnector extends DockerConnector { String tag = params.getTag(); // e.g. latest, usually String imageStreamName = KubernetesStringUtils.convertPullSpecToImageStreamName(repo); + ImageStream existingImageStream; + + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + existingImageStream = openShiftClient.imageStreams() + .inNamespace(openShiftCheProjectName) + .withName(imageStreamName) + .get(); + } - ImageStream existingImageStream = openShiftClient.imageStreams() - .inNamespace(openShiftCheProjectName) - .withName(imageStreamName) - .get(); if (existingImageStream == null) { - openShiftClient.imageStreams() - .inNamespace(openShiftCheProjectName) - .createNew() - .withNewMetadata() - .withName(imageStreamName) // imagestream id - .endMetadata() - .withNewSpec() - .addNewTag() - .withName(tag) - .endTag() - .withDockerImageRepository(repo) // tracking repo - .endSpec() - .withNewStatus() - .withDockerImageRepository("") - .endStatus() - .done(); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + openShiftClient.imageStreams() + .inNamespace(openShiftCheProjectName) + .createNew() + .withNewMetadata() + .withName(imageStreamName) // imagestream id + .endMetadata() + .withNewSpec() + .addNewTag() + .withName(tag) + .endTag() + .withDockerImageRepository(repo) // tracking repo + .endSpec() + .withNewStatus() + .withDockerImageRepository("") + .endStatus() + .done(); + } } // Wait for Image metadata to be obtained. @@ -489,10 +677,13 @@ public class OpenShiftConnector extends DockerConnector { Thread.currentThread().interrupt(); } - createdImageStream = openShiftClient.imageStreams() - .inNamespace(openShiftCheProjectName) - .withName(imageStreamName) - .get(); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + createdImageStream = openShiftClient.imageStreams() + .inNamespace(openShiftCheProjectName) + .withName(imageStreamName) + .get(); + } + if (createdImageStream != null && createdImageStream.getStatus().getDockerImageRepository() != null) { @@ -566,12 +757,12 @@ public class OpenShiftConnector extends DockerConnector { @Override public void removeImage(final RemoveImageParams params) throws IOException { - String image = KubernetesStringUtils.getImageStreamNameFromPullSpec(params.getImage()); - - String imageStreamTagName = KubernetesStringUtils.convertPullSpecToTagName(image); - ImageStreamTag imageStreamTag = getImageStreamTagFromRepo(imageStreamTagName); - - openShiftClient.resource(imageStreamTag).delete(); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + String image = KubernetesStringUtils.getImageStreamNameFromPullSpec(params.getImage()); + String imageStreamTagName = KubernetesStringUtils.convertPullSpecToTagName(image); + ImageStreamTag imageStreamTag = getImageStreamTagFromRepo(imageStreamTagName); + openShiftClient.resource(imageStreamTag).delete(); + } } /** @@ -604,7 +795,143 @@ public class OpenShiftConnector extends DockerConnector { } @Override - public void getEvents(final GetEventsParams params, MessageProcessor messageProcessor) {} + public void getEvents(final GetEventsParams params, MessageProcessor messageProcessor) { + CountDownLatch waitForClose = new CountDownLatch(1); + Watcher eventWatcher = + new Watcher() { + @Override + public void eventReceived(Action action, io.fabric8.kubernetes.api.model.Event event) { + // Do nothing; + } + + @Override + public void onClose(KubernetesClientException e) { + if (e == null) { + LOG.error("Eventwatch Closed"); + } else { + LOG.error("Eventwatch Closed" + e.getMessage()); + } + waitForClose.countDown(); + } + }; + OpenShiftClient openShiftClient = new DefaultOpenShiftClient(); + openShiftClient.events() + .inNamespace(openShiftCheProjectName) + .watch(eventWatcher); + try { + waitForClose.await(); + } catch (InterruptedException e) { + LOG.error("Thread interrupted while waiting for eventWatcher."); + Thread.currentThread().interrupt(); + } finally { + openShiftClient.close(); + } + } + + @Override + public void getContainerLogs(final GetContainerLogsParams params, MessageProcessor containerLogsProcessor) + throws IOException { + String container = params.getContainer(); // container ID + Pod pod = getChePodByContainerId(container); + if (pod != null) { + String podName = pod.getMetadata().getName(); + boolean[] ret = new boolean[1]; + ret[0] = false; + OpenShiftClient openShiftClient = new DefaultOpenShiftClient(); + try (LogWatch watchLog = openShiftClient.pods().inNamespace(openShiftCheProjectName).withName(podName) + .watchLog()) { + Watcher watcher = new Watcher() { + + @Override + public void eventReceived(Action action, Pod resource) { + if (action == Action.DELETED) { + ret[0] = true; + } + } + + @Override + public void onClose(KubernetesClientException cause) { + ret[0] = true; + } + + }; + openShiftClient.pods().inNamespace(openShiftCheProjectName).withName(podName).watch(watcher); + Thread.sleep(5000); + InputStream is = watchLog.getOutput(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); + while (!ret[0]) { + String line = bufferedReader.readLine(); + containerLogsProcessor.process(new LogMessage(LogMessage.Type.DOCKER, line)); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (IOException e) { + // The kubernetes client throws an exception (Pipe not connected) when pod doesn't contain any logs. + // We can ignore it. + } finally { + openShiftClient.close(); + } + } + } + + @Override + public Exec createExec(final CreateExecParams params) throws IOException { + String[] command = params.getCmd(); + String containerId = params.getContainer(); + + Pod pod = getChePodByContainerId(containerId); + String podName = pod.getMetadata().getName(); + + String execId = KubernetesStringUtils.generateWorkspaceID(); + KubernetesExecHolder execHolder = new KubernetesExecHolder().withCommand(command) + .withPod(podName); + execMap.put(execId, execHolder); + + return new Exec(command, execId); + } + + @Override + public void startExec(final StartExecParams params, + @Nullable MessageProcessor execOutputProcessor) throws IOException { + String execId = params.getExecId(); + + KubernetesExecHolder exec = execMap.get(execId); + + String podName = exec.getPod(); + String[] command = exec.getCommand(); + for (int i = 0; i < command.length; i++) { + command[i] = URLEncoder.encode(command[i], "UTF-8"); + } + + ExecutorService executor = Executors.newFixedThreadPool(2); + OpenShiftClient openShiftClient = new DefaultOpenShiftClient(); + try (ExecWatch watch = openShiftClient.pods() + .inNamespace(openShiftCheProjectName) + .withName(podName) + .redirectingOutput() + .redirectingError() + .exec(command); + InputStreamPumper outputPump = new InputStreamPumper(watch.getOutput(), + new KubernetesOutputAdapter(LogMessage.Type.STDOUT, + execOutputProcessor)); + InputStreamPumper errorPump = new InputStreamPumper(watch.getError(), + new KubernetesOutputAdapter(LogMessage.Type.STDERR, + execOutputProcessor)) + ) { + Future outFuture = executor.submit(outputPump); + Future errFuture = executor.submit(errorPump); + // Short-term worksaround; the Futures above seem to never finish. + Thread.sleep(2500); + } catch (KubernetesClientException e) { + throw new OpenShiftException(e.getMessage()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + execMap.remove(execId); + executor.shutdown(); + openShiftClient.close(); + } + } /** * Gets the ImageStreamTag corresponding to a given tag name (i.e. without the repository) @@ -621,10 +948,13 @@ public class OpenShiftConnector extends DockerConnector { // Note: ideally, ImageStreamTags could be identified with a label, but it seems like // ImageStreamTags do not support labels. - List imageStreams = openShiftClient.imageStreamTags() - .inNamespace(openShiftCheProjectName) - .list() - .getItems(); + List imageStreams; + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + imageStreams = openShiftClient.imageStreamTags() + .inNamespace(openShiftCheProjectName) + .list() + .getItems(); + } // We only get ImageStreamTag names here, since these ImageStreamTags do not include // Docker metadata, for some reason. @@ -645,60 +975,101 @@ public class OpenShiftConnector extends DockerConnector { String imageStreamTag = imageStreamTags.get(0); // Finally, get the ImageStreamTag, with Docker metadata. - return openShiftClient.imageStreamTags() - .inNamespace(openShiftCheProjectName) - .withName(imageStreamTag) - .get(); + return getImageStreamTag(imageStreamTag); + } + + private ImageStreamTag getImageStreamTag(final String imageStreamName) { + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + return openShiftClient.imageStreamTags() + .inNamespace(openShiftCheProjectName) + .withName(imageStreamName) + .get(); + } } private Service getCheServiceBySelector(String selectorKey, String selectorValue) { - ServiceList svcs = openShiftClient.services() - .inNamespace(this.openShiftCheProjectName) - .list(); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + ServiceList svcs = openShiftClient.services() + .inNamespace(this.openShiftCheProjectName) + .list(); - Service svc = svcs.getItems().stream() - .filter(s->s.getSpec().getSelector().containsKey(selectorKey)) - .filter(s->s.getSpec().getSelector().get(selectorKey).equals(selectorValue)).findAny().orElse(null); + Service svc = svcs.getItems().stream() + .filter(s->s.getSpec().getSelector().containsKey(selectorKey)) + .filter(s->s.getSpec().getSelector().get(selectorKey).equals(selectorValue)).findAny().orElse(null); - if (svc == null) { - LOG.warn("No Service with selector {}={} could be found", selectorKey, selectorValue); + if (svc == null) { + LOG.warn("No Service with selector {}={} could be found", selectorKey, selectorValue); + } + return svc; } - - return svc; } private Deployment getDeploymentByName(String deploymentName) throws IOException { - Deployment deployment = openShiftClient - .extensions().deployments() - .inNamespace(this.openShiftCheProjectName) - .withName(deploymentName) - .get(); - if (deployment == null) { - LOG.warn("No Deployment with name {} could be found", deploymentName); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + Deployment deployment = openShiftClient + .extensions().deployments() + .inNamespace(this.openShiftCheProjectName) + .withName(deploymentName) + .get(); + if (deployment == null) { + LOG.warn("No Deployment with name {} could be found", deploymentName); + } + return deployment; + } + } + + private List getRoutesByLabel(String labelKey, String labelValue) throws IOException { + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + RouteList routeList = openShiftClient + .routes() + .inNamespace(this.openShiftCheProjectName) + .withLabel(labelKey, labelValue) + .list(); + + List items = routeList.getItems(); + + if (items.isEmpty()) { + LOG.warn("No Route with label {}={} could be found", labelKey, labelValue); + throw new IOException("No Route with label " + labelKey + "=" + labelValue + " could be found"); + } + + return items; + } + } + + private List getReplicaSetByLabel(String key, String value) { + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + List replicaSets = openShiftClient.extensions() + .replicaSets() + .inNamespace(openShiftCheProjectName) + .withLabel(key, value) + .list().getItems(); + return replicaSets; } - return deployment; } private Pod getChePodByContainerId(String containerId) throws IOException { - PodList pods = openShiftClient.pods() - .inNamespace(this.openShiftCheProjectName) - .withLabel(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, - KubernetesStringUtils.getLabelFromContainerID(containerId)) - .list(); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + PodList pods = openShiftClient.pods() + .inNamespace(this.openShiftCheProjectName) + .withLabel(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, + KubernetesStringUtils.getLabelFromContainerID(containerId)) + .list(); - List items = pods.getItems(); + List items = pods.getItems(); - if (items.isEmpty()) { - LOG.error("An OpenShift Pod with label {}={} could not be found", CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId); - throw new IOException("An OpenShift Pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId +" could not be found"); + if (items.isEmpty()) { + LOG.error("An OpenShift Pod with label {}={} could not be found", CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId); + throw new IOException("An OpenShift Pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId +" could not be found"); + } + + if (items.size() > 1) { + LOG.error("There are {} pod with label {}={} (just one was expected)", items.size(), CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId ); + throw new IOException("There are " + items.size() + " pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId + " (just one was expeced)"); + } + + return items.get(0); } - - if (items.size() > 1) { - LOG.error("There are {} pod with label {}={} (just one was expeced)", items.size(), CHE_CONTAINER_IDENTIFIER_LABEL_KEY, containerId ); - throw new IOException("There are " + items.size() + " pod with label " + CHE_CONTAINER_IDENTIFIER_LABEL_KEY + "=" + containerId + " (just one was expeced)"); - } - - return items.get(0); } /** @@ -714,12 +1085,21 @@ public class OpenShiftConnector extends DockerConnector { // except that the capitalization is inconsistent, breaking deserialization. Top level elements // are lowercased with underscores, while nested elements conform to FieldNamingPolicy.UPPER_CAMEL_CASE. // We're only converting the config fields for brevity; this means that other fields are null. - String dockerImageConfig = imageStreamTag.getImage().getDockerImageConfig(); - ImageInfo info = GSON.fromJson(dockerImageConfig.replaceFirst("config", "Config") - .replaceFirst("container_config", "ContainerConfig"), - ImageInfo.class); + Image tagImage = imageStreamTag.getImage(); + String dockerImageConfig = tagImage.getDockerImageConfig(); - return info; + if (!isNullOrEmpty(dockerImageConfig)) { + LOG.info("imageStreamTag dockerImageConfig is not empty. Using it to get image info"); + ImageInfo info = GSON.fromJson(dockerImageConfig.replaceFirst("config", "Config") + .replaceFirst("container_config", "ContainerConfig"), + ImageInfo.class); + return info; + } else { + LOG.info("imageStreamTag dockerImageConfig empty. Using dockerImageMetadata to get image info"); + String dockerImageMetadata = GSON.toJson(tagImage.getAdditionalProperties().get("dockerImageMetadata")); + ImageInfo info = GSON.fromJson(dockerImageMetadata, ImageInfo.class); + return info; + } } protected String getCheWorkspaceId(CreateContainerParams createContainerParams) { @@ -731,46 +1111,77 @@ public class OpenShiftConnector extends DockerConnector { return workspaceID.replaceFirst("workspace",""); } - private void createOpenShiftService(String workspaceID, + private boolean isDevMachine(CreateContainerParams createContainerParams) { + Stream env = Arrays.stream(createContainerParams.getContainerConfig().getEnv()); + return Boolean.parseBoolean(env.filter(v -> v.startsWith(CHE_IS_DEV_MACHINE_ENV_VAR) && v.contains("=")) + .map(v -> v.split("=",2)[1]) + .findFirst() + .orElse("false")); + } + + private void createOpenShiftService(String deploymentName, + String serviceName, Set exposedPorts, - Map additionalLabels) { - - Map selector = Collections.singletonMap(OPENSHIFT_DEPLOYMENT_LABEL, CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID); + Map additionalLabels, + String[] endpointAliases) { + Map selector = Collections.singletonMap(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); List ports = KubernetesService.getServicePortsFrom(exposedPorts); - Service service = openShiftClient - .services() - .inNamespace(this.openShiftCheProjectName) - .createNew() - .withNewMetadata() - .withName(CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID) + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + Service service = openShiftClient + .services() + .inNamespace(this.openShiftCheProjectName) + .createNew() + .withNewMetadata() + .withName(serviceName) .withAnnotations(KubernetesLabelConverter.labelsToNames(additionalLabels)) - .endMetadata() - .withNewSpec() - .withType(OPENSHIFT_SERVICE_TYPE_NODE_PORT) + .endMetadata() + .withNewSpec() .withSelector(selector) .withPorts(ports) - .endSpec() - .done(); + .endSpec() + .done(); - LOG.info("OpenShift service {} created", service.getMetadata().getName()); + LOG.info("OpenShift service {} created", service.getMetadata().getName()); + + for (ServicePort port : ports) { + createOpenShiftRoute(serviceName, deploymentName, port.getName()); + } + } } - private String createOpenShiftDeployment(String workspaceID, + private void createOpenShiftRoute(String serviceName, + String deploymentName, + String serverRef) { + String routeId = serviceName.replaceFirst(CHE_OPENSHIFT_RESOURCES_PREFIX, ""); + OpenShiftRouteCreator.createRoute(openShiftCheProjectName, + cheServerExternalAddress, + serverRef, + serviceName, + deploymentName, + routeId, + secureRoutes); + } + + private void createOpenShiftDeployment(String deploymentName, String imageName, String sanitizedContainerName, Set exposedPorts, String[] envVariables, String[] volumes, - boolean runContainerAsRoot) { + Map resourceLimits, + Map resourceRequests) throws OpenShiftException { - String deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; LOG.info("Creating OpenShift deployment {}", deploymentName); Map selector = Collections.singletonMap(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); LOG.info("Adding container {} to OpenShift deployment {}", sanitizedContainerName, deploymentName); - Long UID = runContainerAsRoot ? UID_ROOT : UID_USER; + + if (createWorkspaceDirs) { + createWorkspaceDir(volumes); + } + Container container = new ContainerBuilder() .withName(sanitizedContainerName) .withImage(imageName) @@ -778,17 +1189,19 @@ public class OpenShiftConnector extends DockerConnector { .withPorts(KubernetesContainer.getContainerPortsFrom(exposedPorts)) .withImagePullPolicy(OPENSHIFT_IMAGE_PULL_POLICY_IFNOTPRESENT) .withNewSecurityContext() - .withRunAsUser(UID) - .withPrivileged(true) + .withPrivileged(false) .endSecurityContext() .withLivenessProbe(getLivenessProbeFrom(exposedPorts)) - .withVolumeMounts(getVolumeMountsFrom(volumes, workspaceID)) + .withVolumeMounts(getVolumeMountsFrom(volumes)) + .withNewResources() + .withLimits(resourceLimits) + .withRequests(resourceRequests) + .endResources() .build(); PodSpec podSpec = new PodSpecBuilder() .withContainers(container) - .withVolumes(getVolumesFrom(volumes, workspaceID)) - .withServiceAccountName(this.openShiftCheServiceAccount) + .withVolumes(getVolumesFrom(volumes)) .build(); Deployment deployment = new DeploymentBuilder() @@ -810,13 +1223,14 @@ public class OpenShiftConnector extends DockerConnector { .endSpec() .build(); - deployment = openShiftClient.extensions() - .deployments() - .inNamespace(this.openShiftCheProjectName) - .create(deployment); + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + deployment = openShiftClient.extensions() + .deployments() + .inNamespace(this.openShiftCheProjectName) + .create(deployment); + } LOG.info("OpenShift deployment {} created", deploymentName); - return deployment.getMetadata().getName(); } /** @@ -829,7 +1243,8 @@ public class OpenShiftConnector extends DockerConnector { */ private ImageStreamTag createImageStreamTag(String sourceImageWithTag, String imageStreamTagName) throws IOException { - try { + + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { openShiftClient.imageStreamTags() .inNamespace(openShiftCheProjectName) .createOrReplaceWithNew() @@ -876,11 +1291,12 @@ public class OpenShiftConnector extends DockerConnector { * @param pod * @param containerId * @return + * @throws OpenShiftException */ private ContainerInfo createContainerInfo(Service svc, ImageInfo imageInfo, Pod pod, - String containerId) { + String containerId) throws OpenShiftException { // In Che on OpenShift, we only have one container per pod. Container container = pod.getSpec().getContainers().get(0); @@ -889,7 +1305,6 @@ public class OpenShiftConnector extends DockerConnector { // HostConfig HostConfig hostConfig = new HostConfig(); hostConfig.setBinds(new String[0]); - hostConfig.setMemory(imageInfo.getConfig().getMemory()); // Env vars List imageEnv = Arrays.asList(imageContainerConfig.getEnv()); @@ -935,27 +1350,69 @@ public class OpenShiftConnector extends DockerConnector { info.setNetworkSettings(networkSettings); info.setHostConfig(hostConfig); info.setImage(imageInfo.getConfig().getImage()); + + // In Che on OpenShift, we only have one container per pod. + info.setState(getContainerStates(pod).get(0)); return info; } + private List getContainerStates(final Pod pod) throws OpenShiftException { + List containerStates = new ArrayList<>(); + List containerStatuses = pod.getStatus().getContainerStatuses(); + for (ContainerStatus status : containerStatuses) { + io.fabric8.kubernetes.api.model.ContainerState state = status.getState(); + + ContainerStateTerminated terminated = state.getTerminated(); + ContainerStateWaiting waiting = state.getWaiting(); + ContainerStateRunning running = state.getRunning(); + + ContainerState containerState = new ContainerState(); + + if (terminated != null) { + containerState.setStatus("exited"); + } else if (waiting != null) { + containerState.setStatus("paused"); + } else if (running != null) { + containerState.setStatus("running"); + } else { + throw new OpenShiftException("Fail to detect the state of container with id " + status.getContainerID()); + } + containerStates.add(containerState); + } + return containerStates; + } private void cleanUpWorkspaceResources(String deploymentName) throws IOException { Deployment deployment = getDeploymentByName(deploymentName); Service service = getCheServiceBySelector(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); + List routes = getRoutesByLabel(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); + List replicaSets = getReplicaSetByLabel(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName); - if (service != null) { - LOG.info("Removing OpenShift Service {}", service.getMetadata().getName()); - openShiftClient.resource(service).delete(); - } + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + if (routes != null) { + for (Route route: routes) { + LOG.info("Removing OpenShift Route {}", route.getMetadata().getName()); + openShiftClient.resource(route).delete(); + } + } - if (deployment != null) { - LOG.info("Removing OpenShift Deployment {}", deployment.getMetadata().getName()); - openShiftClient.resource(deployment).delete(); - } + if (service != null) { + LOG.info("Removing OpenShift Service {}", service.getMetadata().getName()); + openShiftClient.resource(service).delete(); + } - // Wait for all pods to terminate before returning. - try { + if (deployment != null) { + LOG.info("Removing OpenShift Deployment {}", deployment.getMetadata().getName()); + openShiftClient.resource(deployment).delete(); + } + + if (replicaSets != null && replicaSets.size() > 0) { + LOG.info("Removing OpenShift ReplicaSets for deployment {}", deploymentName); + replicaSets.forEach(rs -> openShiftClient.resource(rs).delete()); + } + + // Wait for all pods to terminate before returning. for (int waitCount = 0; waitCount < OPENSHIFT_WAIT_POD_TIMEOUT; waitCount++) { List pods = openShiftClient.pods() .inNamespace(openShiftCheProjectName) @@ -975,92 +1432,162 @@ public class OpenShiftConnector extends DockerConnector { throw new OpenShiftException("Timeout while waiting for pods to terminate"); } - private List getVolumeMountsFrom(String[] volumes, String workspaceID) { - List vms = new ArrayList<>(); - for (String volume : volumes) { - String mountPath = volume.split(":",3)[1]; - String volumeName = getVolumeName(volume); + private void createWorkspaceDir(String[] volumes) throws OpenShiftException { + PersistentVolumeClaim pvc = getClaimCheWorkspace(); + String workspaceSubpath = getWorkspaceSubpath(volumes); + if (pvc != null && !isNullOrEmpty(workspaceSubpath)) { + LOG.info("Making sure directory exists for workspace {}", workspaceSubpath); + boolean succeeded = openShiftPvcHelper.createJobPod(workspacesPersistentVolumeClaim, + openShiftCheProjectName, + "create-", + OpenShiftPvcHelper.Command.MAKE, + workspaceSubpath); + if (!succeeded) { + LOG.error("Failed to create workspace directory {} in PVC {}", workspaceSubpath, + workspacesPersistentVolumeClaim); + throw new OpenShiftException("Failed to create workspace directory in PVC"); + } + } + } - VolumeMount vm = new VolumeMountBuilder() - .withMountPath(mountPath) - .withName("ws-" + workspaceID + "-" + volumeName) + /** + * Gets the workspace subpath from an array of volumes. Since volumes provided are + * those used when running Che in Docker, most of the volume spec is ignored; this + * method returns the subpath within the hostpath that refers to the workspace. + *

+ * E.g. for a volume {@code /data/workspaces/wksp-8z00:/projects:Z}, this method will return + * "wksp-8z00". + * + * @param volumes + * @return + */ + private String getWorkspaceSubpath(String[] volumes) { + String workspaceSubpath = null; + for (String volume : volumes) { + // Volumes are structured ::. + // We first check that matches the mount path for projects + // and then extract the hostpath directory. The first part of the volume + // String will be structured /workspaceName. + String mountPath = volume.split(":", 3)[1]; + if (cheWorkspaceProjectsStorage.equals(mountPath)) { + workspaceSubpath = volume.split(":", 3)[0].replaceAll(cheWorkspaceStorage, ""); + if (workspaceSubpath.startsWith("/")) { + workspaceSubpath = workspaceSubpath.substring(1); + } + } + } + return workspaceSubpath; + } + + private List getVolumeMountsFrom(String[] volumes) { + List vms = new ArrayList<>(); + PersistentVolumeClaim pvc = getClaimCheWorkspace(); + if (pvc != null) { + String subPath = getWorkspaceSubpath(volumes); + if (subPath != null) { + VolumeMount vm = new VolumeMountBuilder() + .withMountPath(cheWorkspaceProjectsStorage) + .withName(workspacesPersistentVolumeClaim) + .withSubPath(subPath) .build(); - vms.add(vm); + vms.add(vm); + } } return vms; } - private List getVolumesFrom(String[] volumes, String workspaceID) { + private List getVolumesFrom(String[] volumes) { List vs = new ArrayList<>(); - for (String volume : volumes) { - String hostPath = volume.split(":",3)[0]; - String volumeName = getVolumeName(volume); - - Volume v = new VolumeBuilder() - .withNewHostPath(hostPath) - .withName("ws-" + workspaceID + "-" + volumeName) - .build(); - vs.add(v); + PersistentVolumeClaim pvc = getClaimCheWorkspace(); + if (pvc != null) { + for (String volume : volumes) { + String mountPath = volume.split(":",3)[1]; + if (cheWorkspaceProjectsStorage.equals(mountPath)) { + PersistentVolumeClaimVolumeSource pvcs = new PersistentVolumeClaimVolumeSourceBuilder() + .withClaimName(workspacesPersistentVolumeClaim) + .build(); + Volume v = new VolumeBuilder() + .withPersistentVolumeClaim(pvcs) + .withName(workspacesPersistentVolumeClaim) + .build(); + vs.add(v); + } + } } return vs; } - private String getVolumeName(String volume) { - if (volume.contains("ws-agent")) { - return "wsagent-lib"; + private PersistentVolumeClaim getClaimCheWorkspace() { + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + PersistentVolumeClaimList pvcList = openShiftClient.persistentVolumeClaims().inNamespace(openShiftCheProjectName).list(); + for(PersistentVolumeClaim pvc: pvcList.getItems()) { + if (workspacesPersistentVolumeClaim.equals(pvc.getMetadata().getName())) { + return pvc; + } + } + Map requests = new HashMap<>(); + requests.put("storage", new Quantity(workspacesPvcQuantity)); + Map annotations = Collections.singletonMap(OPENSHIFT_VOLUME_STORAGE_CLASS, OPENSHIFT_VOLUME_STORAGE_CLASS_NAME); + PersistentVolumeClaim pvc = new PersistentVolumeClaimBuilder() + .withNewMetadata() + .withName(workspacesPersistentVolumeClaim) + .withAnnotations(annotations) + .endMetadata() + .withNewSpec() + .withAccessModes("ReadWriteOnce") + .withNewResources() + .withRequests(requests) + .endResources() + .endSpec() + .build(); + pvc = openShiftClient.persistentVolumeClaims().inNamespace(openShiftCheProjectName).create(pvc); + LOG.info("Creating OpenShift PVC {}", pvc.getMetadata().getName()); + return pvc; } - - if (volume.contains("terminal")) { - return "terminal"; - } - - if (volume.contains("workspaces")) { - return "project"; - } - - return "unknown-volume"; } private String waitAndRetrieveContainerID(String deploymentName) throws IOException { - for (int i = 0; i < OPENSHIFT_WAIT_POD_TIMEOUT; i++) { - try { - Thread.sleep(OPENSHIFT_WAIT_POD_DELAY); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + for (int i = 0; i < OPENSHIFT_WAIT_POD_TIMEOUT; i++) { + try { + Thread.sleep(OPENSHIFT_WAIT_POD_DELAY); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + + List pods = openShiftClient.pods() + .inNamespace(this.openShiftCheProjectName) + .withLabel(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName) + .list() + .getItems(); + + if (pods.size() < 1) { + throw new OpenShiftException(String.format("Pod with deployment name %s not found", + deploymentName)); + } else if (pods.size() > 1) { + throw new OpenShiftException(String.format("Multiple pods with deployment name %s found", + deploymentName)); + } - List pods = openShiftClient.pods() - .inNamespace(this.openShiftCheProjectName) - .withLabel(OPENSHIFT_DEPLOYMENT_LABEL, deploymentName) - .list() - .getItems(); - - if (pods.size() < 1) { - throw new OpenShiftException(String.format("Pod with deployment name %s not found", - deploymentName)); - } else if (pods.size() > 1) { - throw new OpenShiftException(String.format("Multiple pods with deployment name %s found", - deploymentName)); - } - - Pod pod = pods.get(0); - String status = pod.getStatus().getPhase(); - if (OPENSHIFT_POD_STATUS_RUNNING.equals(status)) { - String containerID = pod.getStatus().getContainerStatuses().get(0).getContainerID(); - String normalizedID = KubernetesStringUtils.normalizeContainerID(containerID); - openShiftClient.pods() - .inNamespace(openShiftCheProjectName) - .withName(pod.getMetadata().getName()) - .edit() - .editMetadata() - .addToLabels(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, - KubernetesStringUtils.getLabelFromContainerID(normalizedID)) - .endMetadata() - .done(); - return normalizedID; + Pod pod = pods.get(0); + String status = pod.getStatus().getPhase(); + if (OPENSHIFT_POD_STATUS_RUNNING.equals(status)) { + String containerID = pod.getStatus().getContainerStatuses().get(0).getContainerID(); + String normalizedID = KubernetesStringUtils.normalizeContainerID(containerID); + openShiftClient.pods() + .inNamespace(openShiftCheProjectName) + .withName(pod.getMetadata().getName()) + .edit() + .editMetadata() + .addToLabels(CHE_CONTAINER_IDENTIFIER_LABEL_KEY, + KubernetesStringUtils.getLabelFromContainerID(normalizedID)) + .endMetadata() + .done(); + return normalizedID; + } } + return null; } - return null; } /** @@ -1127,19 +1654,6 @@ public class OpenShiftConnector extends DockerConnector { return exposedPorts; } - /** - * When container is expected to be run as root, user field from {@link ImageConfig} is empty. - * For non-root user it contains "user" value - * - * @param imageName - * @return true if user property from Image config is empty string, false otherwise - * @throws IOException - */ - private boolean runContainerAsRoot(final String imageName) throws IOException { - String user = inspectImage(InspectImageParams.create(imageName)).getConfig().getUser(); - return user != null && user.isEmpty(); - } - /** * @param exposedPorts * @return true if machine exposes 4411/tcp port used by Terminal agent, diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftPvcHelper.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftPvcHelper.java new file mode 100644 index 0000000000..8bf5af0116 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftPvcHelper.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * 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.openshift.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSourceBuilder; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.Volume; +import io.fabric8.kubernetes.api.model.VolumeBuilder; +import io.fabric8.kubernetes.api.model.VolumeMount; +import io.fabric8.kubernetes.api.model.VolumeMountBuilder; +import io.fabric8.openshift.client.DefaultOpenShiftClient; +import io.fabric8.openshift.client.OpenShiftClient; + +/** + * Helper class for executing simple commands in a Persistent Volume on Openshift. + *

+ * Creates a short-lived Pod using a CentOS image which mounts a specified PVC and + * executes a command (either {@code mkdir -p } or {@code rm -rf + * For mkdir commands, an in-memory list of created workspaces is stored and used to avoid + * calling mkdir unnecessarily. However, this list is not persisted, so dir creation is + * not tracked between restarts. + * + * @author amisevsk + */ +public class OpenShiftPvcHelper { + + private static final Logger LOG = LoggerFactory.getLogger(OpenShiftPvcHelper.class); + + private static final String POD_PHASE_SUCCEEDED = "Succeeded"; + private static final String POD_PHASE_FAILED = "Failed"; + private static final String[] MKDIR_WORKSPACE_COMMAND = new String[] {"mkdir", "-p"}; + private static final String[] RMDIR_WORKSPACE_COMMAND = new String[] {"rm", "-rf"}; + + private static final Set createdWorkspaces = ConcurrentHashMap.newKeySet(); + + private final String jobImage; + private final String jobMemoryLimit; + + protected enum Command {REMOVE, MAKE} + + @Inject + protected OpenShiftPvcHelper(@Named("che.openshift.jobs.image") String jobImage, + @Named("che.openshift.jobs.memorylimit") String jobMemoryLimit) { + this.jobImage = jobImage; + this.jobMemoryLimit = jobMemoryLimit; + } + + /** + * Creates a pod with {@code command} and reports whether it succeeded + * @param workspacesPvcName + * name of the PVC to mount + * @param projectNamespace + * OpenShift namespace + * @param jobNamePrefix + * prefix used for pod metadata name. Name structure will normally + * be {@code } if only one path is passed, or + * {@code batch} if multiple paths are provided + * @param command + * command to execute in PVC. + * @param workspaceDirs + * list of arguments attached to command. A list of directories to + * create/delete. + * @return true if Pod terminates with phase "Succeeded" or mkdir command issued + * for already created worksapce, false otherwise. + * + * @see Command + */ + protected boolean createJobPod(String workspacesPvcName, + String projectNamespace, + String jobNamePrefix, + Command command, + String... workspaceDirs) { + + if (workspaceDirs.length == 0) { + return true; + } + + if (Command.MAKE.equals(command)) { + String[] dirsToCreate = filterDirsToCreate(workspaceDirs); + if (dirsToCreate.length == 0) { + return true; + } + workspaceDirs = dirsToCreate; + } + + VolumeMount vm = new VolumeMountBuilder() + .withMountPath("/projects") + .withName(workspacesPvcName) + .build(); + + PersistentVolumeClaimVolumeSource pvcs = new PersistentVolumeClaimVolumeSourceBuilder() + .withClaimName(workspacesPvcName) + .build(); + + Volume volume = new VolumeBuilder() + .withPersistentVolumeClaim(pvcs) + .withName(workspacesPvcName) + .build(); + + String[] jobCommand = getCommand(command, "/projects/", workspaceDirs); + LOG.info("Executing command {} in PVC {} for {} dirs", jobCommand[0], workspacesPvcName, workspaceDirs.length); + + Map limit = Collections.singletonMap("memory", new Quantity(jobMemoryLimit)); + + String podName = workspaceDirs.length > 1 ? jobNamePrefix + "batch" + : jobNamePrefix + workspaceDirs[0]; + + Container container = new ContainerBuilder().withName(podName) + .withImage(jobImage) + .withImagePullPolicy("IfNotPresent") + .withNewSecurityContext() + .withPrivileged(false) + .endSecurityContext() + .withCommand(jobCommand) + .withVolumeMounts(vm) + .withNewResources() + .withLimits(limit) + .endResources() + .build(); + + Pod podSpec = new PodBuilder().withNewMetadata() + .withName(podName) + .endMetadata() + .withNewSpec() + .withContainers(container) + .withVolumes(volume) + .withRestartPolicy("Never") + .endSpec() + .build(); + + + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()){ + openShiftClient.pods().inNamespace(projectNamespace).create(podSpec); + boolean completed = false; + while(!completed) { + Pod pod = openShiftClient.pods().inNamespace(projectNamespace).withName(podName).get(); + String phase = pod.getStatus().getPhase(); + switch (phase) { + case POD_PHASE_FAILED: + LOG.info("Pod command {} failed", Arrays.toString(jobCommand)); + case POD_PHASE_SUCCEEDED: + openShiftClient.resource(pod).delete(); + updateCreatedDirs(command, phase, workspaceDirs); + return POD_PHASE_SUCCEEDED.equals(phase); + default: + Thread.sleep(1000); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return false; + } + + private String[] getCommand(Command commandType, String mountPath, String... dirs) { + String[] command = new String[0]; + switch (commandType) { + case MAKE : + command = MKDIR_WORKSPACE_COMMAND; + break; + case REMOVE : + command = RMDIR_WORKSPACE_COMMAND; + break; + } + + String[] dirsWithPath = Arrays.asList(dirs).stream() + .map(dir -> mountPath + dir) + .toArray(String[]::new); + + String[] fullCommand = new String[command.length + dirsWithPath.length]; + + System.arraycopy(command, 0, fullCommand, 0, command.length); + System.arraycopy(dirsWithPath, 0, fullCommand, command.length, dirsWithPath.length); + return fullCommand; + } + + private void updateCreatedDirs(Command command, String phase, String... workspaceDirs) { + if (!POD_PHASE_SUCCEEDED.equals(phase)) { + return; + } + List dirs = Arrays.asList(workspaceDirs); + switch (command) { + case MAKE: + createdWorkspaces.addAll(dirs); + break; + case REMOVE: + createdWorkspaces.removeAll(dirs); + break; + } + } + + private String[] filterDirsToCreate(String[] allDirs) { + List dirs = Arrays.asList(allDirs); + List dirsToCreate = new ArrayList<>(); + for(String dir : dirs) { + if (!createdWorkspaces.contains(dir)) { + dirsToCreate.add(dir); + } + } + return dirsToCreate.toArray(new String[dirsToCreate.size()]); + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftRouteCreator.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftRouteCreator.java new file mode 100644 index 0000000000..05a7034ef2 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftRouteCreator.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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.openshift.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.openshift.api.model.DoneableRoute; +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.api.model.RouteFluent.SpecNested; +import io.fabric8.openshift.client.DefaultOpenShiftClient; +import io.fabric8.openshift.client.OpenShiftClient; + +public class OpenShiftRouteCreator { + private static final Logger LOG = LoggerFactory.getLogger(OpenShiftRouteCreator.class); + private static final String TLS_TERMINATION_EDGE = "edge"; + private static final String REDIRECT_INSECURE_EDGE_TERMINATION_POLICY = "Redirect"; + + public static void createRoute (final String namespace, + final String openShiftNamespaceExternalAddress, + final String serverRef, + final String serviceName, + final String deploymentName, + final String routeId, + final boolean enableTls) { + + if (openShiftNamespaceExternalAddress == null) { + throw new IllegalArgumentException("Property che.docker.ip.external must be set when using openshift."); + } + + try (OpenShiftClient openShiftClient = new DefaultOpenShiftClient()) { + String routeName = generateRouteName(routeId, serverRef); + String serviceHost = generateRouteHost(routeName, openShiftNamespaceExternalAddress); + + SpecNested routeSpec = openShiftClient + .routes() + .inNamespace(namespace) + .createNew() + .withNewMetadata() + .withName(routeName) + .addToLabels(OpenShiftConnector.OPENSHIFT_DEPLOYMENT_LABEL, deploymentName) + .endMetadata() + .withNewSpec() + .withHost(serviceHost) + .withNewTo() + .withKind("Service") + .withName(serviceName) + .endTo() + .withNewPort() + .withNewTargetPort() + .withStrVal(serverRef) + .endTargetPort() + .endPort(); + + if (enableTls) { + routeSpec.withNewTls() + .withTermination(TLS_TERMINATION_EDGE) + .withInsecureEdgeTerminationPolicy(REDIRECT_INSECURE_EDGE_TERMINATION_POLICY) + .endTls(); + } + + Route route = routeSpec.endSpec().done(); + + LOG.info("OpenShift route {} created", route.getMetadata().getName()); + } + } + + private static String generateRouteName(final String serviceName, final String serverRef) { + return serverRef + "-" + serviceName; + } + + private static String generateRouteHost(final String routeName, final String openShiftNamespaceExternalAddress) { + return routeName + "-" + openShiftNamespaceExternalAddress; + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleaner.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleaner.java new file mode 100644 index 0000000000..85afdad7e1 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleaner.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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.openshift.client; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.event.ServerIdleEvent; +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.workspace.server.WorkspaceFilesCleaner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Class used to remove workspace directories in Persistent Volume when a workspace + * is delete while running on OpenShift. Deleted workspace directories are stored + * in a list. Upon Che server idling, all of these workspaces are deleted simultaneously + * from the PVC using a {@link OpenShiftPvcHelper} job. + *

+ * Since deleting a workspace does not immediately remove its files, re-creating a workspace + * with a previously used name can result in files from the previous workspace still being + * present. + * + * @see WorkspaceFilesCleaner + * @author amisevsk + */ +@Singleton +public class OpenShiftWorkspaceFilesCleaner implements WorkspaceFilesCleaner { + + private static final Logger LOG = LoggerFactory.getLogger(OpenShiftConnector.class); + private static final Set deleteQueue = ConcurrentHashMap.newKeySet(); + private final String projectNamespace; + private final String workspacesPvcName; + private final OpenShiftPvcHelper openShiftPvcHelper; + + @Inject + public OpenShiftWorkspaceFilesCleaner(EventService eventService, + OpenShiftPvcHelper openShiftPvcHelper, + @Named("che.openshift.project") String projectNamespace, + @Named("che.openshift.workspaces.pvc.name") String workspacesPvcName) { + this.projectNamespace = projectNamespace; + this.workspacesPvcName = workspacesPvcName; + this.openShiftPvcHelper = openShiftPvcHelper; + eventService.subscribe(new EventSubscriber() { + @Override + public void onEvent(ServerIdleEvent event) { + deleteWorkspacesInQueue(event); + } + }); + } + + @Override + public void clear(Workspace workspace) throws IOException, ServerException { + String workspaceName = workspace.getConfig().getName(); + if (isNullOrEmpty(workspaceName)) { + LOG.error("Could not get workspace name for files removal."); + return; + } + deleteQueue.add(workspaceName); + } + + private void deleteWorkspacesInQueue(ServerIdleEvent event) { + List deleteQueueCopy = new ArrayList<>(deleteQueue); + String[] dirsToDelete = deleteQueueCopy.toArray(new String[deleteQueueCopy.size()]); + + LOG.info("Deleting {} workspaces on PVC {}", deleteQueueCopy.size(), workspacesPvcName); + boolean successful = openShiftPvcHelper.createJobPod(workspacesPvcName, + projectNamespace, + "delete-", + OpenShiftPvcHelper.Command.REMOVE, + dirsToDelete); + if (successful) { + deleteQueue.removeAll(deleteQueueCopy); + } + } + + /** + * Clears the list of workspace directories to be deleted. Necessary for testing. + */ + @VisibleForTesting + protected static void clearDeleteQueue() { + deleteQueue.clear(); + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainer.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainer.java index dc4c0a6680..a37b59fc56 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainer.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainer.java @@ -47,7 +47,7 @@ public final class KubernetesContainer { int portNumber = Integer.parseInt(port); String portName = CheServicePorts.get().get(portNumber); - portName = isNullOrEmpty(portName) ? exposedPort.replace("/", "-") : portName; + portName = isNullOrEmpty(portName) ? "server-" + exposedPort.replace("/", "-") : portName; ContainerPort containerPort = new ContainerPortBuilder().withName(portName).withProtocol(protocol) .withContainerPort(portNumber).build(); diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesExecHolder.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesExecHolder.java new file mode 100644 index 0000000000..ad0af92e11 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesExecHolder.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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.openshift.client.kubernetes; + +import java.util.Arrays; + +import org.eclipse.che.plugin.openshift.client.OpenShiftConnector; + +/** + * Holder class for metadata about an exec, to be used with {@link OpenShiftConnector}. + * + *

In OpenShift, {@code createExec()} is not separate from {@code startExec()}, + * so this class has to be used to pass data between {@code createExec()} and + * {@code startExec()} calls. + * + * @see OpenShiftConnector#createExec(org.eclipse.che.plugin.docker.client.params.CreateExecParams) + * @see OpenShiftConnector#startExec(org.eclipse.che.plugin.docker.client.params.StartExecParams, org.eclipse.che.plugin.docker.client.MessageProcessor) + */ +public class KubernetesExecHolder { + + private String[] command; + private String podName; + + public KubernetesExecHolder withCommand(String[] command) { + this.command = command; + return this; + } + + public KubernetesExecHolder withPod(String podName) { + this.podName = podName; + return this; + } + + public String[] getCommand() { + return command; + } + + public String getPod() { + return podName; + } + + public String toString() { + return String.format("KubernetesExecHolder {command=%s, podName=%s}", + Arrays.asList(command).toString(), + podName); + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverter.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverter.java index e499117dec..2931e36ab5 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverter.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverter.java @@ -64,6 +64,9 @@ public final class KubernetesLabelConverter { */ public static Map labelsToNames(Map labels) { Map names = new HashMap<>(); + if (labels == null) { + return names; + } for (Map.Entry label : labels.entrySet()) { if (!hasConversionProblems(label)) { @@ -103,6 +106,9 @@ public final class KubernetesLabelConverter { */ public static Map namesToLabels(Map names) { Map labels = new HashMap<>(); + if (names == null) { + return labels; + } for (Map.Entry entry: names.entrySet()){ String key = entry.getKey(); String value = entry.getValue(); diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapter.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapter.java new file mode 100644 index 0000000000..2d87a77053 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapter.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.openshift.client.kubernetes; + +import io.fabric8.kubernetes.client.Callback; +import io.fabric8.kubernetes.client.utils.InputStreamPumper; + +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.plugin.docker.client.LogMessage; +import org.eclipse.che.plugin.docker.client.MessageProcessor; + +/** + * Adapter class for passing data from a {@code kubernetes-client} output stream (e.g. + * for an exec call) to {@link MessageProcessor}. This class should be passed to a + * {@link InputStreamPumper} along with the output of the exec call. + * + *

Output passed in via the {@link #call(byte[])} method is parsed into lines, + * (respecting {@code '\n'} and {@code CRLF} as line separators), and + * passed to the {@link MessageProcessor} as {@link LogMessage}s. + */ +public class KubernetesOutputAdapter implements Callback { + + private LogMessage.Type type; + private MessageProcessor execOutputProcessor; + private StringBuilder lineBuffer; + + /** + * Create a new KubernetesOutputAdapter + * + * @param type + * the type of LogMessages being passed to the MessageProcessor + * @param processor + * the processor receiving LogMessages. If null, calling {@link #call(byte[])} + * will return immediately. + */ + public KubernetesOutputAdapter(LogMessage.Type type, + @Nullable MessageProcessor processor) { + this.type = type; + this.execOutputProcessor = processor; + this.lineBuffer = new StringBuilder(); + } + + @Override + public void call(byte[] data) { + if (data == null || data.length == 0 || execOutputProcessor == null) { + return; + } + int start = 0; + int offset = 0; + + for (int pos = 0; pos < data.length; pos++) { + if (data[pos] == '\n' || data[pos] == '\r') { + offset = pos - start; + String line = new String(data, start, offset); + lineBuffer.append(line); + execOutputProcessor.process(new LogMessage(type, lineBuffer.toString())); + lineBuffer.setLength(0); + if (data[pos] == '\r') { + pos += 1; + } + start = pos + 1; + } + } + String trailingChars = new String(data, start, data.length - start); + lineBuffer.append(trailingChars); + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesService.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesService.java index 33e62b16e5..df179410df 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesService.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesService.java @@ -47,7 +47,7 @@ public final class KubernetesService { int portNumber = Integer.parseInt(port); String portName = CheServicePorts.get().get(portNumber); - portName = isNullOrEmpty(portName) ? exposedPort.replace("/", "-") : portName; + portName = isNullOrEmpty(portName) ? "server-" + exposedPort.replace("/", "-") : portName; int targetPortNumber = portNumber; ServicePort servicePort = new ServicePort(); diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java index b9949ab36c..3726af54d3 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java @@ -16,6 +16,7 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.plugin.docker.client.DockerApiVersionPathPrefixProvider; import org.eclipse.che.plugin.docker.client.DockerConnectorConfiguration; import org.eclipse.che.plugin.docker.client.DockerRegistryAuthResolver; @@ -31,9 +32,17 @@ import org.testng.annotations.Test; public class OpenShiftConnectorTest { private static final String[] CONTAINER_ENV_VARIABLES = {"CHE_WORKSPACE_ID=abcd1234"}; private static final String CHE_DEFAULT_OPENSHIFT_PROJECT_NAME = "eclipse-che"; - private static final String CHE_DEFAULT_OPENSHIFT_SERVICEACCOUNT = "cheserviceaccount"; private static final int OPENSHIFT_LIVENESS_PROBE_DELAY = 300; private static final int OPENSHIFT_LIVENESS_PROBE_TIMEOUT = 1; + private static final String OPENSHIFT_DEFAULT_WORKSPACE_PERSISTENT_VOLUME_CLAIM = "che_claim_data"; + private static final String OPENSHIFT_DEFAULT_WORKSPACE_QUANTITY = "10Gi"; + private static final String OPENSHIFT_DEFAULT_WORKSPACE_STORAGE = "/data/workspaces"; + private static final String OPENSHIFT_DEFAULT_WORKSPACE_PROJECTS_STORAGE = "/projects"; + private static final String CHE_DEFAULT_SERVER_EXTERNAL_ADDRESS = "che.openshift.mini"; + private static final String CHE_WORKSPACE_CPU_LIMIT = "1"; + private static final boolean SECURE_ROUTES = false; + private static final boolean CREATE_WORKSPACE_DIRS = true; + @Mock private DockerConnectorConfiguration dockerConnectorConfiguration; @@ -45,6 +54,10 @@ public class OpenShiftConnectorTest { private DockerApiVersionPathPrefixProvider dockerApiVersionPathPrefixProvider; @Mock private CreateContainerParams createContainerParams; + @Mock + private EventService eventService; + @Mock + private OpenShiftPvcHelper openShiftPvcHelper; private OpenShiftConnector openShiftConnector; @@ -62,10 +75,20 @@ public class OpenShiftConnectorTest { dockerConnectionFactory, authManager, dockerApiVersionPathPrefixProvider, + openShiftPvcHelper, + eventService, + CHE_DEFAULT_SERVER_EXTERNAL_ADDRESS, CHE_DEFAULT_OPENSHIFT_PROJECT_NAME, - CHE_DEFAULT_OPENSHIFT_SERVICEACCOUNT, OPENSHIFT_LIVENESS_PROBE_DELAY, - OPENSHIFT_LIVENESS_PROBE_TIMEOUT); + OPENSHIFT_LIVENESS_PROBE_TIMEOUT, + OPENSHIFT_DEFAULT_WORKSPACE_PERSISTENT_VOLUME_CLAIM, + OPENSHIFT_DEFAULT_WORKSPACE_QUANTITY, + OPENSHIFT_DEFAULT_WORKSPACE_STORAGE, + OPENSHIFT_DEFAULT_WORKSPACE_PROJECTS_STORAGE, + CHE_WORKSPACE_CPU_LIMIT, + null, + SECURE_ROUTES, + CREATE_WORKSPACE_DIRS); String workspaceID = openShiftConnector.getCheWorkspaceId(createContainerParams); //Then diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleanerTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleanerTest.java new file mode 100644 index 0000000000..828e52ae60 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftWorkspaceFilesCleanerTest.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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.openshift.client; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.event.ServerIdleEvent; +import org.eclipse.che.api.core.model.workspace.Workspace; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; +import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class OpenShiftWorkspaceFilesCleanerTest { + + private static final String CHE_OPENSHIFT_PROJECT = "eclipse-che"; + private static final String WORKSPACES_PVC_NAME = "che-data-volume"; + private static final String WORKSPACE_ONE = "testworkspaceone"; + private static final String WORKSPACE_TWO = "testworkspacetwo"; + + @Mock + private OpenShiftPvcHelper pvcHelper; + @Mock + private ServerIdleEvent serverIdleEvent; + private EventService eventService; + private OpenShiftWorkspaceFilesCleaner cleaner; + + @BeforeMethod + public void setup() { + OpenShiftWorkspaceFilesCleaner.clearDeleteQueue(); + MockitoAnnotations.initMocks(this); + eventService = new EventService(); + cleaner = new OpenShiftWorkspaceFilesCleaner(eventService, + pvcHelper, + CHE_OPENSHIFT_PROJECT, + WORKSPACES_PVC_NAME); + } + + @Test + public void shouldDoNothingWithoutIdleEvent() throws ServerException, IOException { + // Given + Workspace workspace = generateWorkspace(WORKSPACE_ONE); + + // When + cleaner.clear(workspace); + + // Then + verify(pvcHelper, never()).createJobPod(anyString(), + anyString(), + anyString(), + any(OpenShiftPvcHelper.Command.class), + any(String[].class)); + } + + @Test + public void shouldDeleteWorkspaceOnIdleEvent() throws ServerException, IOException { + // Given + Workspace workspace = generateWorkspace(WORKSPACE_ONE); + + // When + cleaner.clear(workspace); + eventService.publish(serverIdleEvent); + + // Then + verify(pvcHelper, times(1)).createJobPod(anyString(), + anyString(), + anyString(), + eq(OpenShiftPvcHelper.Command.REMOVE), + eq(WORKSPACE_ONE)); + } + + @Test + public void shouldDeleteMultipleQueuedWorkspacesAtOnce() throws ServerException, IOException { + // Given + Workspace workspaceOne = generateWorkspace(WORKSPACE_ONE); + Workspace workspaceTwo = generateWorkspace(WORKSPACE_TWO); + String[] expectedDirs = new String[] {WORKSPACE_ONE, WORKSPACE_TWO}; + ArgumentCaptor dirCaptor = ArgumentCaptor.forClass(String.class); + + // When + cleaner.clear(workspaceOne); + cleaner.clear(workspaceTwo); + eventService.publish(serverIdleEvent); + + // Then + verify(pvcHelper, times(1)).createJobPod(anyString(), + anyString(), + anyString(), + eq(OpenShiftPvcHelper.Command.REMOVE), + dirCaptor.capture(), // Varargs capture doesn't seem to work. + dirCaptor.capture()); + + List dirs = dirCaptor.getAllValues(); + String[] actualDirs = dirs.toArray(new String[dirs.size()]); + // Sort arrays to ignore order + Arrays.sort(actualDirs); + Arrays.sort(expectedDirs); + assertEquals(actualDirs, expectedDirs, "Expected all dirs to be deleted when server is idled."); + } + + @Test + public void shouldRetainQueueIfDeletionFails() throws ServerException, IOException { + // Given + Workspace workspaceOne = generateWorkspace(WORKSPACE_ONE); + when(pvcHelper.createJobPod(any(), any(), any(), any(), any())).thenReturn(false); + + // When + cleaner.clear(workspaceOne); + eventService.publish(serverIdleEvent); + + // Then + verify(pvcHelper, times(1)).createJobPod(anyString(), + anyString(), + anyString(), + eq(OpenShiftPvcHelper.Command.REMOVE), + eq(WORKSPACE_ONE)); + + // When + eventService.publish(serverIdleEvent); + + // Then + verify(pvcHelper, times(2)).createJobPod(anyString(), + anyString(), + anyString(), + eq(OpenShiftPvcHelper.Command.REMOVE), + eq(WORKSPACE_ONE)); + } + + @Test + public void shouldUseProjectNamespaceAndPvcNameAsParameters() throws ServerException, IOException { + // Given + Workspace workspaceOne = generateWorkspace(WORKSPACE_ONE); + + // When + cleaner.clear(workspaceOne); + eventService.publish(serverIdleEvent); + + // Then + verify(pvcHelper, times(1)).createJobPod(eq(WORKSPACES_PVC_NAME), + eq(CHE_OPENSHIFT_PROJECT), + anyString(), + eq(OpenShiftPvcHelper.Command.REMOVE), + eq(WORKSPACE_ONE)); + } + + private Workspace generateWorkspace(String id) { + WorkspaceConfigImpl config = new WorkspaceConfigImpl(); + config.setName(id); + return new WorkspaceImpl(id, null, config); + } +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainerTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainerTest.java index d3cd0be897..83c0c1dc2a 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainerTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesContainerTest.java @@ -44,7 +44,7 @@ public class KubernetesContainerTest { map(p -> Integer.toString(p.getContainerPort()) + "/" + p.getProtocol().toLowerCase()).collect(Collectors.toList()); - assertTrue(exposedPorts.stream().anyMatch(portsAndProtocols::contains)); + assertTrue(exposedPorts.stream().allMatch(portsAndProtocols::contains)); } @Test @@ -61,7 +61,7 @@ public class KubernetesContainerTest { map(p -> Integer.toString(p.getContainerPort()) + "/" + p.getProtocol().toLowerCase()).collect(Collectors.toList()); - assertTrue(imageExposedPorts.keySet().stream().anyMatch(portsAndProtocols::contains)); + assertTrue(imageExposedPorts.keySet().stream().allMatch(portsAndProtocols::contains)); } } diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesEnvVarTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesEnvVarTest.java index 36adea1fd2..ccb63d44b1 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesEnvVarTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesEnvVarTest.java @@ -44,7 +44,7 @@ public class KubernetesEnvVarTest { // Then List keysAndValues = env.stream().map(k -> k.getName() + "=" + k.getValue()).collect(Collectors.toList()); - assertTrue(Arrays.stream(envVariables).anyMatch(keysAndValues::contains)); + assertTrue(Arrays.stream(envVariables).allMatch(keysAndValues::contains)); } } diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverterTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverterTest.java index bf43eefc9d..51ad72ac30 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverterTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesLabelConverterTest.java @@ -16,15 +16,15 @@ import static org.testng.Assert.assertTrue; import java.util.HashMap; import java.util.Map; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class KubernetesLabelConverterTest { + private final String prefix = KubernetesLabelConverter.getCheServerLabelPrefix(); + @Test public void shouldConvertLabelsToValidKubernetesLabelNames() { String validLabelRegex = "([A-Za-z0-9][-A-Za-z0-9_\\.]*)?[A-Za-z0-9]"; - String prefix = KubernetesLabelConverter.getCheServerLabelPrefix(); // Given Map labels = new HashMap<>(); @@ -46,7 +46,6 @@ public class KubernetesLabelConverterTest { @Test public void shouldBeAbleToRecoverOriginalLabelsAfterConversion() { // Given - String prefix = KubernetesLabelConverter.getCheServerLabelPrefix(); Map originalLabels = new HashMap<>(); originalLabels.put(prefix + "4401/tcp:path:", "/api"); originalLabels.put(prefix + "8000/tcp:ref:", "tomcat-debug"); @@ -59,4 +58,58 @@ public class KubernetesLabelConverterTest { assertEquals(originalLabels, unconverted); } + @Test + public void shouldIgnoreAndLogProblemLabels() { + // Given + Map originalLabels = new HashMap<>(); + Map validLabels = new HashMap<>(); + validLabels.put(prefix + "4401/tcp:path:", "/api"); + validLabels.put(prefix + "8000/tcp:ref:", "tomcat-debug"); + Map invalidLabels = new HashMap<>(); + invalidLabels.put(prefix + "9999/t.cp:path:", "/api"); + invalidLabels.put(prefix + "1111/tcp:path:", "/a_pi"); + + originalLabels.putAll(validLabels); + originalLabels.putAll(invalidLabels); + + // When + Map converted = KubernetesLabelConverter.labelsToNames(originalLabels); + Map unconverted = KubernetesLabelConverter.namesToLabels(converted); + + // Then + assertTrue(validLabels.entrySet().stream().allMatch(unconverted.entrySet()::contains), + "Valid labels should be there when converting + unconverting"); + assertTrue(invalidLabels.entrySet().stream().noneMatch(unconverted.entrySet()::contains), + "Labels with invalid characters should be ignored"); + } + + @Test + public void shouldIgnoreEmptyValues() { + // Given + Map originalLabels = new HashMap<>(); + originalLabels.put(prefix + "4401/tcp:path:", null); + originalLabels.put(prefix + "4402/tcp:path:", ""); + originalLabels.put(prefix + "4403/tcp:path:", " "); + + // When + Map converted = KubernetesLabelConverter.labelsToNames(originalLabels); + + // Then + assertTrue(converted.isEmpty(), "Labels with null, empty, or whitespace values should be ignored"); + } + + @Test + public void shouldNotIgnoreValuesWithoutPrefix() { + // Given + Map originalLabels = new HashMap<>(); + originalLabels.put("4401/tcp:path:", "/api"); + originalLabels.put(prefix + "8000/tcp:ref:", "tomcat-debug"); + + // When + Map converted = KubernetesLabelConverter.labelsToNames(originalLabels); + + // Then + // Currently we put a warning in the logs but convert these labels anyways. + assertTrue(converted.size() == 2, "Should convert labels even without prefix"); + } } diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapterTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapterTest.java new file mode 100644 index 0000000000..8dd5a571f5 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesOutputAdapterTest.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * 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.openshift.client.kubernetes; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.che.plugin.docker.client.LogMessage; +import org.eclipse.che.plugin.docker.client.MessageProcessor; + + +public class KubernetesOutputAdapterTest { + + private static LogMessage.Type LOG_TYPE = LogMessage.Type.DOCKER; + private testMessageProcessor processor; + private KubernetesOutputAdapter adapter; + + private class testMessageProcessor implements MessageProcessor { + + private List messages; + private LogMessage.Type type = null; + + public testMessageProcessor() { + this.messages = new ArrayList<>(); + } + + @Override + public void process(LogMessage message) { + LogMessage.Type messageType = message.getType(); + if (type == null) { + type = messageType; + } + messages.add(message.getContent()); + } + + public List getMessages() { + return new ArrayList<>(messages); + } + + public LogMessage.Type getType() { + return type; + } + }; + + @BeforeMethod + public void setUp() { + processor = new testMessageProcessor(); + adapter = new KubernetesOutputAdapter(LOG_TYPE, processor); + } + + @Test + public void shouldBreakLinesCorrectly() { + // Given + byte[] input = "line1\nline2\n".getBytes(); + List expected = generateExpected("line1", "line2"); + + // When + adapter.call(input); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should break lines on \\n char"); + } + + @Test + public void shouldCacheUnfinishedLinesBetweenCalls() { + // Given + byte[] firstInput = "line1\nlin".getBytes(); + byte[] secondInput = "e2\nline3\n".getBytes(); + List expected = generateExpected("line1", "line2", "line3"); + + // When + adapter.call(firstInput); + adapter.call(secondInput); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should store unfinished lines between calls"); + } + + @Test + public void shouldUseProvidedLogMessageType() { + for (LogMessage.Type type : LogMessage.Type.values()) { + // Given + byte[] input = "line1\n".getBytes(); + LogMessage.Type expected = type; + processor = new testMessageProcessor(); + adapter = new KubernetesOutputAdapter(type, processor); + + // When + adapter.call(input); + + // Then + LogMessage.Type actual = processor.getType(); + assertEquals(actual, expected, "Should call MessageProcessor with provided type"); + } + } + + @Test + public void shouldBreakLinesNormallyWithCarriageReturn() { + // Given + byte[] input = "line1\r\nline2\n".getBytes(); + List expected = generateExpected("line1", "line2"); + + // When + adapter.call(input); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should break lines normally on \\r\\n characters"); + } + + @Test + public void shouldNotIgnoreEmptyLines() { + // Given + byte[] input = "line1\n\nline2\n".getBytes(); + List expected = generateExpected("line1", "", "line2"); + + // When + adapter.call(input); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should call processor.process() with empty Strings"); + } + + @Test + public void shouldNotCallWithoutFinalNewline() { + // Given + byte[] input = "line1\nline2".getBytes(); // No trailing \n + List firstExpected = generateExpected("line1"); + List secondExpected = generateExpected("line1", "line2"); + + // When + adapter.call(input); + + // Then + List firstActual = processor.getMessages(); + assertEquals(firstActual, firstExpected, "Should only process lines when they are terminated by \\n or \\r\\n"); + + // When + adapter.call("\n".getBytes()); + + // Then + List secondActual = processor.getMessages(); + assertEquals(secondActual, secondExpected, "Should buffer lines until newline is encountered."); + + } + + @Test + public void shouldIgnoreNullCalls() { + // Given + byte[] firstInput = "line1\n".getBytes(); + byte[] secondInput = "line2\n".getBytes(); + List expected = generateExpected("line1", "line2"); + + // When + adapter.call(firstInput); + adapter.call(null); + adapter.call(secondInput); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should ignore calls with null arguments"); + } + + @Test + public void shouldKeepBufferPastNullCalls() { + // Given + byte[] firstInput = "lin".getBytes(); + byte[] secondInput = "e1\nline2\n".getBytes(); + List expected = generateExpected("line1", "line2"); + + // When + adapter.call(firstInput); + adapter.call(null); + adapter.call(secondInput); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "Should ignore calls with null arguments"); + } + + @Test + public void shouldDoNothingWhenExecOutputProcessorIsNull() { + // Given + byte[] firstInput = "line1\n".getBytes(); + byte[] secondInput = "line2\n".getBytes(); + adapter = new KubernetesOutputAdapter(LOG_TYPE, null); + + // When + adapter.call(firstInput); + adapter.call(secondInput); + + // Then + List actual = processor.getMessages(); + assertTrue(actual.isEmpty(), "Should do nothing when ExecOutputProcessor is null"); + } + + @Test + public void shouldIgnoreCallsWhenDataIsEmpty() { + // Given + byte[] emptyInput = "".getBytes(); + byte[] firstInput = "line1\n".getBytes(); + byte[] secondInput = "line2\n".getBytes(); + List expected = generateExpected("line1", "line2"); + + // When + adapter.call(emptyInput); + adapter.call(firstInput); + adapter.call(emptyInput); + adapter.call(secondInput); + adapter.call(emptyInput); + + // Then + List actual = processor.getMessages(); + assertEquals(actual, expected, "KubernetesOutputAdapter ignore empty data calls"); + + } + + private List generateExpected(String... strings) { + List expected = new ArrayList<>(); + for (String string : strings) { + expected.add(string); + } + return expected; + } + + +} diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesServiceTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesServiceTest.java index a1b575415b..ebcc02a9cf 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesServiceTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/kubernetes/KubernetesServiceTest.java @@ -40,7 +40,7 @@ public class KubernetesServiceTest { map(p -> Integer.toString(p.getPort()) + "/" + p.getProtocol()).collect(Collectors.toList()); - assertTrue(imageExposedPorts.keySet().stream().anyMatch(portsAndProtocols::contains)); + assertTrue(imageExposedPorts.keySet().stream().allMatch(portsAndProtocols::contains)); } @Test @@ -60,7 +60,7 @@ public class KubernetesServiceTest { map(p -> Integer.toString(p.getPort()) + "/" + p.getProtocol()).collect(Collectors.toList()); - assertTrue(exposedPorts.keySet().stream().anyMatch(portsAndProtocols::contains)); + assertTrue(exposedPorts.keySet().stream().allMatch(portsAndProtocols::contains)); } @Test @@ -73,13 +73,13 @@ public class KubernetesServiceTest { exposedPorts.put("4411/tcp",null); exposedPorts.put("4412/tcp",null); exposedPorts.put("8080/tcp",null); - exposedPorts.put("8888/tcp",null); + exposedPorts.put("8000/tcp",null); exposedPorts.put("9876/tcp",null); Set expectedPortNames = new HashSet<>(); expectedPortNames.add("sshd"); expectedPortNames.add("wsagent"); - expectedPortNames.add("wsagent-pda"); + expectedPortNames.add("wsagent-jpda"); expectedPortNames.add("terminal"); expectedPortNames.add("exec-agent"); expectedPortNames.add("tomcat"); @@ -92,7 +92,7 @@ public class KubernetesServiceTest { map(p -> p.getName()).collect(Collectors.toList()); // Then - assertTrue(actualPortNames.stream().anyMatch(expectedPortNames::contains)); + assertTrue(actualPortNames.stream().allMatch(expectedPortNames::contains)); } @Test @@ -102,7 +102,7 @@ public class KubernetesServiceTest { exposedPorts.put("55/tcp",null); Set expectedPortNames = new HashSet<>(); - expectedPortNames.add("55-tcp"); + expectedPortNames.add("server-55-tcp"); // When List servicePorts = KubernetesService.getServicePortsFrom(exposedPorts.keySet()); @@ -110,7 +110,7 @@ public class KubernetesServiceTest { map(p -> p.getName()).collect(Collectors.toList()); // Then - assertTrue(actualPortNames.stream().anyMatch(expectedPortNames::contains)); + assertTrue(actualPortNames.stream().allMatch(expectedPortNames::contains)); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClient.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClient.java index 4c3201073d..690a63d7ef 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClient.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClient.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.git.client; -import org.eclipse.che.api.core.model.workspace.config.ProjectConfig; import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.BranchListMode; import org.eclipse.che.api.git.shared.CheckoutRequest; @@ -28,9 +27,6 @@ import org.eclipse.che.api.git.shared.StatusFormat; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.ide.resource.Path; -import org.eclipse.che.ide.rest.AsyncRequestCallback; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.RequestCallback; import java.util.List; import java.util.Map; @@ -40,29 +36,10 @@ import java.util.Map; * * @author Ann Zhuleva * @author Vlad Zhukovskyi + * @author Igor Vinokur */ public interface GitServiceClient { - /** - * Add changes to Git index (temporary storage). Sends request over WebSocket. - * - * @param projectConfig - * project (root of GIT repository) - * @param update - * if true then never stage new files, but stage modified new contents of tracked files and remove files from - * the index if the corresponding files in the working tree have been removed - * @param filePattern - * pattern of the files to be added, default is "." (all files are added) - * @param callback - * callback - * @throws WebSocketException - * @deprecated use {@link #add(Path, boolean, Path[])} - */ - void add(ProjectConfig projectConfig, - boolean update, - List filePattern, - RequestCallback callback) throws WebSocketException; - /** * Add changes to Git index (temporary storage). Sends request over WebSocket. * @@ -73,7 +50,6 @@ public interface GitServiceClient { * the index if the corresponding files in the working tree have been removed * @param paths * pattern of the files to be added, default is "." (all files are added) - * @throws WebSocketException */ Promise add(Path project, boolean update, Path[] paths); @@ -95,7 +71,6 @@ public interface GitServiceClient { * * @param removeDeletedRefs * if true then delete removed refs from local repository - * @throws WebSocketException */ Promise fetch(Path project, String remote, List refspec, boolean removeDeletedRefs); @@ -107,51 +82,42 @@ public interface GitServiceClient { * project (root of GIT repository) * @param mode * get remote branches - * @param callback - * @deprecated use {@link #branchList(Path, BranchListMode)} - */ - @Deprecated - void branchList(ProjectConfig project, - @Nullable BranchListMode mode, - AsyncRequestCallback> callback); - - /** - * Get the list of the branches. For now, all branches cannot be returned at once, so the parameter remote tells to get - * remote branches if true or local ones (if false). - * @param project - * project (root of GIT repository) - * @param mode */ Promise> branchList(Path project, BranchListMode mode); /** * Delete branch. - * @param project + * + * @param project * project (root of GIT repository) * @param name * name of the branch to delete * @param force - * force if true delete branch {@code name} even if it is not fully merged + * force if true delete branch {@code name} even if it is not fully merged */ Promise branchDelete(Path project, String name, boolean force); /** * Checkout the branch with pointed name. - * @param project + * + * @param project * project (root of GIT repository) * @param oldName * branch's current name * @param newName + * branch's new name */ Promise branchRename(Path project, String oldName, String newName); /** * Create new branch with pointed name. - * @param project + * + * @param project * project (root of GIT repository) * @param name * new branch's name * @param startPoint + * name of a commit at which to start the new branch */ Promise branchCreate(Path project, String name, String startPoint); @@ -160,38 +126,10 @@ public interface GitServiceClient { * * @param project * project (root of GIT repository) - * @param checkoutRequest - * checkout request - * @deprecated {@link #checkout(Path, CheckoutRequest)} - */ - @Deprecated - void checkout(ProjectConfig project, - CheckoutRequest checkoutRequest, - AsyncRequestCallback callback); - - /** - * Checkout the branch with pointed name. - * @param project - * project (root of GIT repository) * @param request + * checkout request */ - Promise checkout(Path project, CheckoutRequest request); - - /** - * Get the list of remote repositories for pointed by {@code projectConfig} parameter one. - * - * @param projectConfig - * project (root of GIT repository) - * @param remoteName - * remote repository's name. Can be null in case when it is need to fetch all {@link Remote} - * @param verbose - * If true show remote url and name otherwise show remote name - * @return a promise that provides list {@link Remote} repositories for the {@code workspaceId}, {@code projectConfig}, - * {@code remoteName}, {@code verbose} or rejects with an error. - * @deprecated use {@link #remoteList(Path, String, boolean)} - */ - @Deprecated - Promise> remoteList(ProjectConfig projectConfig, @Nullable String remoteName, boolean verbose); + Promise checkout(Path project, CheckoutRequest request); /** * Get the list of remote repositories for pointed by {@code projectConfig} parameter one. @@ -204,7 +142,6 @@ public interface GitServiceClient { * If true show remote url and name otherwise show remote name * @return a promise that provides list {@link Remote} repositories for the {@code workspaceId}, {@code projectConfig}, * {@code remoteName}, {@code verbose} or rejects with an error. - * @deprecated use {@link #remoteList(Path, String, boolean)} */ Promise> remoteList(Path project, String remote, boolean verbose); @@ -217,21 +154,6 @@ public interface GitServiceClient { * remote repository's name * @param url * remote repository's URL - * @deprecated use {@link #remoteAdd(Path, String, String)} - */ - @Deprecated - void remoteAdd(ProjectConfig project, - String name, - String url, - AsyncRequestCallback callback); - - /** - * Adds remote repository to the list of remote repositories. - * @param project - * project (root of GIT repository) - * @param name - * remote repository's name - * @param url */ Promise remoteAdd(Path project, String name, String url); @@ -242,28 +164,18 @@ public interface GitServiceClient { * project (root of GIT repository) * @param name * remote repository name to delete - * @deprecated use {@link #remoteDelete(Path, String)} - */ - @Deprecated - void remoteDelete(ProjectConfig project, - String name, - AsyncRequestCallback callback); - - /** - * Deletes the pointed(by name) remote repository from the list of repositories. - * @param project - * project (root of GIT repository) - * @param name */ Promise remoteDelete(Path project, String name); /** * Remove items from the working tree and the index. - * @param project + * + * @param project * project (root of GIT repository) * @param items * items to remove * @param cached + * is for removal only from index */ Promise remove(Path project, Path[] items, boolean cached); @@ -272,28 +184,33 @@ public interface GitServiceClient { * 1. Reset files in index - content of files is untouched. Typically it is useful to remove from index mistakenly added files.
* git reset [paths] is the opposite of git add [paths]. 2. Reset the current branch head to [commit] and * possibly updates the index (resetting it to the tree of [commit]) and the working tree depending on [mode]. - * @param project + * + * @param project * project (root of GIT repository) * @param commit * commit to which current head should be reset * @param resetType - * type of the reset + * type of the reset * @param files -* pattern of the files to reset the index. If null then reset the current branch head to [commit], + * pattern of the files to reset the index. If null then reset the current branch head to [commit], + * else reset received files in index. */ Promise reset(Path project, String commit, ResetRequest.ResetType resetType, Path[] files); /** * Initializes new Git repository (over WebSocket). - * @param project + * + * @param project * project (root of GIT repository) * @param bare + * to create bare repository or not */ Promise init(Path project, boolean bare); /** * Pull (fetch and merge) changes from remote repository to local one (sends request over WebSocket). - * @param project + * + * @param project * project (root of GIT repository) * @param refSpec * list of refspec to fetch. @@ -305,6 +222,7 @@ public interface GitServiceClient { *

  • featured - remote branch name.
  • * * @param remote + * remote remote repository's name */ Promise pull(Path project, String refSpec, String remote); @@ -320,50 +238,22 @@ public interface GitServiceClient { * @param force * push refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. If true * disables the check. This can cause the remote repository to lose commits - * @deprecated use {@link #push(Path, List, String, boolean)} - */ - @Deprecated - Promise push(ProjectConfig project, - List refSpec, - String remote, - boolean force); - - /** - * Push changes from local repository to remote one (sends request over WebSocket). - * @param project - * project - * @param refSpec - * list of refspec to push - * @param remote - * remote repository name or url - * @param force -* push refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. If true */ Promise push(Path project, List refSpec, String remote, boolean force); /** - * Performs commit changes from index to repository. The result of the commit is represented by {@link Revision}, which is returned by - * callback in onSuccess(Revision result). Sends request over WebSocket. + * Clones one remote repository to local one (over WebSocket). * * @param project * project (root of GIT repository) - * @param message - * commit log message - * @param all - * automatically stage files that have been modified and deleted - * @param amend - * indicates that previous commit must be overwritten - * @param callback - * callback - * @throws WebSocketException - * @deprecated use {@link #commit(Path, String, boolean, boolean)} + * @param remoteUri + * the location of the remote repository + * @param remoteName + * remote name instead of "origin" */ - @Deprecated - void commit(ProjectConfig project, - String message, - boolean all, - boolean amend, - AsyncRequestCallback callback); + Promise clone(Path project, + String remoteUri, + String remoteName); /** * Performs commit changes from index to repository. The result of the commit is represented by {@link Revision}, which is returned by @@ -377,62 +267,50 @@ public interface GitServiceClient { * automatically stage files that have been modified and deleted * @param amend * indicates that previous commit must be overwritten - * @throws WebSocketException */ Promise commit(Path project, String message, boolean all, boolean amend); /** - * Performs commit for the given files (ignoring git index). + * Performs commit changes from index to repository. * * @param project * project (root of GIT repository) * @param message * commit log message - * @param files - * the list of iles that are commited, ignoring the index * @param amend * indicates that previous commit must be overwritten - * @throws WebSocketException - */ - Promise commit(Path project, String message, Path[] files, boolean amend); - - /** - * Performs commit changes from index to repository. - * @param project - * project (root of GIT repository) - * @param message - * commit log message - * @param all - * automatically stage files that have been modified and deleted * @param files -* the list of files that are committed, ignoring the index - * @param amend + * the list of files that are committed, ignoring the index */ - Promise commit(Path project, String message, boolean all, Path[] files, boolean amend); + Promise commit(Path project, String message, boolean amend, Path[] files); /** * Get repository options. - * @param project + * + * @param project * project (root of GIT repository) * @param requestedConfig + * list of config keys */ Promise> config(Path project, List requestedConfig); /** * Compare two commits, get the diff for pointed file(s) or for the whole project in text format. - * @param project + * + * @param project * project (root of GIT repository) * @param fileFilter * files for which to show changes * @param type - * type of diff format + * type of diff format * @param noRenames -* don't show renamed files + * don't show renamed files * @param renameLimit -* the limit of shown renamed files + * the limit of shown renamed files * @param commitA -* first commit to compare + * first commit to compare * @param commitB + * second commit to be compared */ Promise diff(Path project, List fileFilter, @@ -445,19 +323,21 @@ public interface GitServiceClient { /** * Compare commit with index or working tree (depends on {@code cached}), get the diff for pointed file(s) or for the whole project in * text format. - * @param project + * + * @param project * project (root of GIT repository) * @param files * files for which to show changes * @param type - * type of diff format + * type of diff format * @param noRenames -* don't show renamed files + * don't show renamed files * @param renameLimit -* the limit of shown renamed files + * the limit of shown renamed files * @param commitA -* commit to compare + * commit to compare * @param cached + * if true then compare commit with index, if false, then compare with working tree. */ Promise diff(Path project, List files, @@ -469,47 +349,39 @@ public interface GitServiceClient { /** * Get the file content from specified revision or branch. - * @param project + * + * @param project * project configuration of root GIT repository * @param file * file name with its full path * @param version + * revision or branch where the showed file is present */ Promise showFileContent(Path project, Path file, String version); /** * Get log of commits. * - * Method is deprecated. Use {@link #log(Path, Path[], int, int, boolean)} to pass - * {@code skip} and {@code maxCount} parameters to limit the number of returning entries. - * @param project - * project (root of GIT repository) - * @param fileFilter - * range of files to filter revisions list - * @param plainText - */ - @Deprecated - Promise log(Path project, @Nullable Path[] fileFilter, boolean plainText); - - /** - * Get log of commits. - * @param project + * @param project * project (root of GIT repository) * @param fileFilter * range of files to filter revisions list * @param skip - * the number of commits that will be skipped + * the number of commits that will be skipped * @param maxCount -* the number of commits that will be returned + * the number of commits that will be returned * @param plainText + * if true the loq response will be in text format */ Promise log(Path project, @Nullable Path[] fileFilter, int skip, int maxCount, boolean plainText); /** * Merge the pointed commit with current HEAD. - * @param project + * + * @param project * project (root of GIT repository) * @param commit + * commit's reference to merge with */ Promise merge(Path project, String commit); @@ -535,9 +407,11 @@ public interface GitServiceClient { * D 123.txt * ?? folder/test.css * - * @param project + * + * @param project * project (root of GIT repository) * @param format + * to show in short format or not */ Promise statusText(Path project, StatusFormat format); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClientImpl.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClientImpl.java index 98446a04de..5195d385fd 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClientImpl.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitServiceClientImpl.java @@ -10,17 +10,17 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.git.client; -import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.che.api.core.model.workspace.config.ProjectConfig; import org.eclipse.che.api.git.shared.AddRequest; import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.BranchCreateRequest; import org.eclipse.che.api.git.shared.BranchListMode; import org.eclipse.che.api.git.shared.CheckoutRequest; +import org.eclipse.che.api.git.shared.CloneRequest; import org.eclipse.che.api.git.shared.CommitRequest; +import org.eclipse.che.api.git.shared.Commiters; import org.eclipse.che.api.git.shared.DiffType; import org.eclipse.che.api.git.shared.FetchRequest; import org.eclipse.che.api.git.shared.LogResponse; @@ -37,39 +37,26 @@ import org.eclipse.che.api.git.shared.Revision; import org.eclipse.che.api.git.shared.ShowFileContentResponse; import org.eclipse.che.api.git.shared.Status; import org.eclipse.che.api.git.shared.StatusFormat; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.RequestCall; -import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.ide.MimeType; import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.machine.WsAgentStateController; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.rest.AsyncRequest; -import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.AsyncRequestFactory; import org.eclipse.che.ide.rest.AsyncRequestLoader; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.StringMapUnmarshaller; import org.eclipse.che.ide.rest.StringUnmarshaller; import org.eclipse.che.ide.ui.loaders.request.LoaderFactory; -import org.eclipse.che.ide.websocket.Message; -import org.eclipse.che.ide.websocket.MessageBuilder; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.RequestCallback; -import javax.validation.constraints.NotNull; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.google.gwt.http.client.RequestBuilder.DELETE; -import static com.google.gwt.http.client.RequestBuilder.POST; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; +import static org.eclipse.che.api.git.shared.AddRequest.DEFAULT_PATTERN; import static org.eclipse.che.api.git.shared.StatusFormat.PORCELAIN; -import static org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.createFromAsyncRequest; import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; import static org.eclipse.che.ide.MimeType.TEXT_PLAIN; import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; @@ -83,41 +70,39 @@ import static org.eclipse.che.ide.rest.HTTPHeader.CONTENTTYPE; */ @Singleton public class GitServiceClientImpl implements GitServiceClient { - private static final String ADD = "/git/add"; - private static final String BRANCH = "/git/branch"; - private static final String CHECKOUT = "/git/checkout"; - private static final String COMMIT = "/git/commit"; - private static final String CONFIG = "/git/config"; - private static final String DIFF = "/git/diff"; - private static final String FETCH = "/git/fetch"; - private static final String INIT = "/git/init"; - private static final String LOG = "/git/log"; - private static final String SHOW = "/git/show"; - private static final String MERGE = "/git/merge"; - private static final String STATUS = "/git/status"; - private static final String PUSH = "/git/push"; - private static final String PULL = "/git/pull"; - private static final String REMOTE = "/git/remote"; - private static final String REMOVE = "/git/remove"; - private static final String RESET = "/git/reset"; - private static final String REPOSITORY = "/git/repository"; + private static final String ADD = "/git/add"; + private static final String BRANCH = "/git/branch"; + private static final String CHECKOUT = "/git/checkout"; + private static final String CLONE = "/git/clone"; + private static final String COMMIT = "/git/commit"; + private static final String CONFIG = "/git/config"; + private static final String DIFF = "/git/diff"; + private static final String FETCH = "/git/fetch"; + private static final String INIT = "/git/init"; + private static final String LOG = "/git/log"; + private static final String SHOW = "/git/show"; + private static final String MERGE = "/git/merge"; + private static final String STATUS = "/git/status"; + private static final String PUSH = "/git/push"; + private static final String PULL = "/git/pull"; + private static final String REMOTE = "/git/remote"; + private static final String REMOVE = "/git/remove"; + private static final String RESET = "/git/reset"; + private static final String REPOSITORY = "/git/repository"; /** Loader to be displayed. */ private final AsyncRequestLoader loader; private final DtoFactory dtoFactory; private final DtoUnmarshallerFactory dtoUnmarshallerFactory; private final AsyncRequestFactory asyncRequestFactory; - private final WsAgentStateController wsAgentStateController; private final AppContext appContext; @Inject protected GitServiceClientImpl(LoaderFactory loaderFactory, - WsAgentStateController wsAgentStateController, DtoFactory dtoFactory, AsyncRequestFactory asyncRequestFactory, DtoUnmarshallerFactory dtoUnmarshallerFactory, AppContext appContext) { - this.wsAgentStateController = wsAgentStateController; this.appContext = appContext; this.loader = loaderFactory.newLoader(); this.dtoFactory = dtoFactory; @@ -126,46 +111,30 @@ public class GitServiceClientImpl implements GitServiceClient { } @Override - public Promise init(final Path project, final boolean bare) { - return createFromAsyncRequest(new RequestCall() { - @Override - public void makeCall(final AsyncCallback callback) { - String url = INIT + "?projectPath=" + project.toString() + "&bare=" + bare; - - Message message = new MessageBuilder(POST, url).header(ACCEPT, TEXT_PLAIN).build(); - - sendMessageToWS(message, new RequestCallback() { - @Override - protected void onSuccess(Void result) { - callback.onSuccess(result); - } - - @Override - protected void onFailure(Throwable exception) { - callback.onFailure(exception); - } - }); - } - }); + public Promise init(Path project, boolean bare) { + String url = getWsAgentBaseUrl() + INIT + "?projectPath=" + project + "&bare=" + bare; + return asyncRequestFactory.createPostRequest(url, null).loader(loader).send(); } - private void sendMessageToWS(final @NotNull Message message, final @NotNull RequestCallback callback) { - wsAgentStateController.getMessageBus().then(new Operation() { - @Override - public void apply(MessageBus arg) throws OperationException { - try { - arg.send(message, callback); - } catch (WebSocketException e) { - throw new OperationException(e.getMessage(), e); - } - } - }); + @Override + public Promise clone(Path project, + String remoteUri, + String remoteName) { + CloneRequest cloneRequest = dtoFactory.createDto(CloneRequest.class) + .withRemoteName(remoteName) + .withRemoteUri(remoteUri) + .withWorkingDir(project.toString()); + + String params = "?projectPath=" + project; + String url = CLONE + params; + + return asyncRequestFactory.createPostRequest(url, cloneRequest).loader(loader).send(); } @Override public Promise statusText(Path project, StatusFormat format) { - String params = "?projectPath=" + project.toString() + "&format=" + format; - String url = appContext.getDevAgentEndpoint() + STATUS + params; + String params = "?projectPath=" + project + "&format=" + format; + String url = getWsAgentBaseUrl() + STATUS + params; return asyncRequestFactory.createGetRequest(url) .loader(loader) @@ -175,78 +144,13 @@ public class GitServiceClientImpl implements GitServiceClient { } @Override - public void add(ProjectConfig project, - boolean update, - @Nullable List filePattern, - RequestCallback callback) throws WebSocketException { - AddRequest addRequest = dtoFactory.createDto(AddRequest.class).withUpdate(update); - if (filePattern == null) { - addRequest.setFilePattern(AddRequest.DEFAULT_PATTERN); - } else { - addRequest.setFilePattern(filePattern); - } - String url = ADD + "?projectPath=" + project.getPath(); - - MessageBuilder builder = new MessageBuilder(POST, url); - builder.data(dtoFactory.toJson(addRequest)) - .header(CONTENTTYPE, APPLICATION_JSON); - Message message = builder.build(); - - sendMessageToWS(message, callback); - } - - @Override - public Promise add(final Path project, final boolean update, final Path[] paths) { - return createFromAsyncRequest(new RequestCall() { - @Override - public void makeCall(final AsyncCallback callback) { - final AddRequest addRequest = dtoFactory.createDto(AddRequest.class).withUpdate(update); - - if (paths == null) { - addRequest.setFilePattern(AddRequest.DEFAULT_PATTERN); - } else { - - final List patterns = new ArrayList<>(); //need for compatible with server side - for (Path path : paths) { - patterns.add(path.isEmpty() ? "." : path.toString()); - } - - addRequest.setFilePattern(patterns); - } - - final String url = ADD + "?projectPath=" + project.toString(); - final Message message = new MessageBuilder(POST, url).data(dtoFactory.toJson(addRequest)) - .header(CONTENTTYPE, APPLICATION_JSON) - .build(); - - sendMessageToWS(message, new RequestCallback() { - @Override - protected void onSuccess(Void result) { - callback.onSuccess(result); - } - - @Override - protected void onFailure(Throwable exception) { - callback.onFailure(exception); - } - }); - } - }); - } - - @Override - public void commit(ProjectConfig project, - String message, - boolean all, - boolean amend, - AsyncRequestCallback callback) { - CommitRequest commitRequest = dtoFactory.createDto(CommitRequest.class) - .withMessage(message) - .withAmend(amend) - .withAll(all); - String url = appContext.getDevAgentEndpoint() + COMMIT + "?projectPath=" + project.getPath(); - - asyncRequestFactory.createPostRequest(url, commitRequest).loader(loader).send(callback); + public Promise add(Path project, boolean update, Path[] paths) { + AddRequest addRequest = dtoFactory.createDto(AddRequest.class) + .withUpdate(update); + addRequest.setFilePattern(paths == null ? DEFAULT_PATTERN : + stream(paths).map(path -> path.isEmpty() ? "." : path.toString()).collect(toList())); + String url = getWsAgentBaseUrl() + ADD + "?projectPath=" + project; + return asyncRequestFactory.createPostRequest(url, addRequest).loader(loader).send(); } @Override @@ -255,34 +159,22 @@ public class GitServiceClientImpl implements GitServiceClient { .withMessage(message) .withAmend(amend) .withAll(all); - String url = appContext.getDevAgentEndpoint() + COMMIT + "?projectPath=" + project; - + String url = getWsAgentBaseUrl() + COMMIT + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, commitRequest) .loader(loader) .send(dtoUnmarshallerFactory.newUnmarshaller(Revision.class)); } @Override - public Promise commit(Path project, String message, Path[] files, boolean amend) { - return commit(project, message, false, files, amend); - } - - @Override - public Promise commit(Path project, String message, boolean all, Path[] files, boolean amend) { - List paths = new ArrayList<>(files.length); - - for (Path file : files) { - if (!file.isEmpty()) { - paths.add(file.toString()); - } - } - + public Promise commit(Path project, String message, boolean amend, Path[] files) { CommitRequest commitRequest = dtoFactory.createDto(CommitRequest.class) .withMessage(message) .withAmend(amend) - .withAll(all) - .withFiles(paths); - String url = appContext.getDevAgentEndpoint() + COMMIT + "?projectPath=" + project; + .withFiles(stream(files).filter(file -> !file.isEmpty()) + .map(Path::toString) + .collect(toList())); + + String url = getWsAgentBaseUrl() + COMMIT + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, commitRequest) .loader(loader) @@ -291,80 +183,48 @@ public class GitServiceClientImpl implements GitServiceClient { @Override public Promise> config(Path project, List requestedConfig) { - String params = "?projectPath=" + project.toString(); + String params = "?projectPath=" + project; if (requestedConfig != null) { for (String entry : requestedConfig) { params += "&requestedConfig=" + entry; } } - String url = appContext.getDevAgentEndpoint() + CONFIG + params; + String url = getWsAgentBaseUrl() + CONFIG + params; return asyncRequestFactory.createGetRequest(url).loader(loader).send(new StringMapUnmarshaller()); } - @Override - public Promise push(ProjectConfig project, List refSpec, String remote, boolean force) { - PushRequest pushRequest = dtoFactory.createDto(PushRequest.class) - .withRemote(remote) - .withRefSpec(refSpec) - .withForce(force); - return asyncRequestFactory.createPostRequest(appContext.getDevAgentEndpoint() + PUSH + - "?projectPath=" + project.getPath(), pushRequest) - .send(dtoUnmarshallerFactory.newUnmarshaller(PushResponse.class)); - } - @Override public Promise push(Path project, List refSpec, String remote, boolean force) { PushRequest pushRequest = dtoFactory.createDto(PushRequest.class) .withRemote(remote) .withRefSpec(refSpec) .withForce(force); - return asyncRequestFactory.createPostRequest(appContext.getDevAgentEndpoint() + PUSH + "?projectPath=" + project, pushRequest) + String url = getWsAgentBaseUrl() + PUSH + "?projectPath=" + project; + return asyncRequestFactory.createPostRequest(url, pushRequest) .send(dtoUnmarshallerFactory.newUnmarshaller(PushResponse.class)); } - @Override - public Promise> remoteList(ProjectConfig project, @Nullable String remoteName, boolean verbose) { - String params = "?projectPath=" + project.getPath() + "&verbose=" + String.valueOf(verbose); - if (remoteName != null) { - params += "&remoteName=" + remoteName; - } - String url = appContext.getDevAgentEndpoint() + REMOTE + params; - return asyncRequestFactory.createGetRequest(url) - .loader(loader) - .send(dtoUnmarshallerFactory.newListUnmarshaller(Remote.class)); - } - @Override public Promise> remoteList(Path project, String remoteName, boolean verbose) { - String params = "?projectPath=" + project.toString() + (remoteName != null ? "&remoteName=" + remoteName : "") + + String params = "?projectPath=" + project + + (remoteName != null ? "&remoteName=" + remoteName : "") + "&verbose=" + String.valueOf(verbose); - String url = appContext.getDevAgentEndpoint() + REMOTE + params; + String url = getWsAgentBaseUrl() + REMOTE + params; return asyncRequestFactory.createGetRequest(url) .loader(loader) .send(dtoUnmarshallerFactory.newListUnmarshaller(Remote.class)); } - @Override - @Deprecated - public void branchList(ProjectConfig project, - BranchListMode listMode, - AsyncRequestCallback> callback) { - String url = appContext.getDevAgentEndpoint() + BRANCH + "?projectPath=" + project.getPath() + - (listMode == null ? "" : "&listMode=" + listMode); - asyncRequestFactory.createGetRequest(url).send(callback); - } - @Override public Promise> branchList(Path project, BranchListMode listMode) { - String url = appContext.getDevAgentEndpoint() + BRANCH + "?projectPath=" + project.toString() + - (listMode == null ? "" : "&listMode=" + listMode); + String url = getWsAgentBaseUrl() + BRANCH + "?projectPath=" + project + (listMode == null ? "" : "&listMode=" + listMode); return asyncRequestFactory.createGetRequest(url).send(dtoUnmarshallerFactory.newListUnmarshaller(Branch.class)); } @Override public Promise getStatus(Path project) { - final String params = "?projectPath=" + project.toString() + "&format=" + PORCELAIN; - final String url = appContext.getDevAgentEndpoint() + STATUS + params; + String params = "?projectPath=" + project + "&format=" + PORCELAIN; + String url = getWsAgentBaseUrl() + STATUS + params; return asyncRequestFactory.createGetRequest(url) .loader(loader) .header(CONTENTTYPE, APPLICATION_JSON) @@ -374,15 +234,14 @@ public class GitServiceClientImpl implements GitServiceClient { @Override public Promise branchDelete(Path project, String name, boolean force) { - String url = appContext.getDevAgentEndpoint() + BRANCH + "?projectPath=" + project.toString() - + "&name=" + name + "&force=" + force; + String url = getWsAgentBaseUrl() + BRANCH + "?projectPath=" + project + "&name=" + name + "&force=" + force; return asyncRequestFactory.createDeleteRequest(url).loader(loader).send(); } @Override public Promise branchRename(Path project, String oldName, String newName) { - String params = "?projectPath=" + project.toString() + "&oldName=" + oldName + "&newName=" + newName; - String url = appContext.getDevAgentEndpoint() + BRANCH + params; + String params = "?projectPath=" + project + "&oldName=" + oldName + "&newName=" + newName; + String url = getWsAgentBaseUrl() + BRANCH + params; return asyncRequestFactory.createPostRequest(url, null).loader(loader) .header(CONTENTTYPE, MimeType.APPLICATION_FORM_URLENCODED) .send(); @@ -391,7 +250,7 @@ public class GitServiceClientImpl implements GitServiceClient { @Override public Promise branchCreate(Path project, String name, String startPoint) { BranchCreateRequest branchCreateRequest = dtoFactory.createDto(BranchCreateRequest.class).withName(name).withStartPoint(startPoint); - String url = appContext.getDevAgentEndpoint() + BRANCH + "?projectPath=" + project.toString(); + String url = getWsAgentBaseUrl() + BRANCH + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, branchCreateRequest) .loader(loader) .header(ACCEPT, APPLICATION_JSON) @@ -399,31 +258,21 @@ public class GitServiceClientImpl implements GitServiceClient { } @Override - public void checkout(ProjectConfig project, - CheckoutRequest checkoutRequest, - AsyncRequestCallback callback) { - String url = appContext.getDevAgentEndpoint() + CHECKOUT + "?projectPath=" + project.getPath(); - asyncRequestFactory.createPostRequest(url, checkoutRequest).loader(loader).send(callback); - } - - @Override - public Promise checkout(Path project, - CheckoutRequest request) { - - final String url = appContext.getDevAgentEndpoint() + CHECKOUT + "?projectPath=" + project.toString(); - return asyncRequestFactory.createPostRequest(url, request).loader(loader).send(); + public Promise checkout(Path project, CheckoutRequest request) { + String url = getWsAgentBaseUrl() + CHECKOUT + "?projectPath=" + project; + return asyncRequestFactory.createPostRequest(url, request).loader(loader).send(new StringUnmarshaller()); } @Override public Promise remove(Path project, Path[] items, boolean cached) { - String params = "?projectPath=" + project.toString(); + String params = "?projectPath=" + project; if (items != null) { for (Path item : items) { params += "&items=" + item.toString(); } } params += "&cached=" + String.valueOf(cached); - String url = appContext.getDevAgentEndpoint() + REMOVE + params; + String url = getWsAgentBaseUrl() + REMOVE + params; return asyncRequestFactory.createDeleteRequest(url).loader(loader).send(); } @@ -434,32 +283,24 @@ public class GitServiceClientImpl implements GitServiceClient { resetRequest.setType(resetType); } if (files != null) { - List fileList = new ArrayList<>(files.length); - for (Path file : files) { - fileList.add(file.isEmpty() ? "." : file.toString()); - } - resetRequest.setFilePattern(fileList); + resetRequest.setFilePattern(stream(files).map(file -> file.isEmpty() ? "." : file.toString()).collect(toList())); } - String url = appContext.getDevAgentEndpoint() + RESET + "?projectPath=" + project; + String url = getWsAgentBaseUrl() + RESET + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, resetRequest).loader(loader).send(); } - @Override - public Promise log(Path project, Path[] fileFilter, boolean plainText) { - return log(project, fileFilter, -1, -1, plainText); - } - @Override public Promise log(Path project, Path[] fileFilter, int skip, int maxCount, boolean plainText) { - StringBuilder params = new StringBuilder().append("?projectPath=").append(project.toString()); + StringBuilder params = new StringBuilder().append("?projectPath=") + .append(project) + .append("&skip=") + .append(skip) + .append("&maxCount=") + .append(maxCount); if (fileFilter != null) { - for (Path file : fileFilter) { - params.append("&fileFilter=").append(file.toString()); - } + stream(fileFilter).forEach(file -> params.append("&fileFilter=").append(file)); } - params.append("&skip=").append(skip); - params.append("&maxCount=").append(maxCount); - String url = appContext.getDevAgentEndpoint() + LOG + params; + String url = getWsAgentBaseUrl() + LOG + params; if (plainText) { return asyncRequestFactory.createGetRequest(url) .send(dtoUnmarshallerFactory.newUnmarshaller(LogResponse.class)); @@ -470,34 +311,16 @@ public class GitServiceClientImpl implements GitServiceClient { } } - @Override - public void remoteAdd(ProjectConfig project, - String name, - String repositoryURL, - AsyncRequestCallback callback) { - RemoteAddRequest remoteAddRequest = dtoFactory.createDto(RemoteAddRequest.class).withName(name).withUrl(repositoryURL); - String url = appContext.getDevAgentEndpoint() + REMOTE + "?projectPath=" + project.getPath(); - asyncRequestFactory.createPutRequest(url, remoteAddRequest).loader(loader).send(callback); - } - @Override public Promise remoteAdd(Path project, String name, String url) { RemoteAddRequest remoteAddRequest = dtoFactory.createDto(RemoteAddRequest.class).withName(name).withUrl(url); - String requestUrl = appContext.getDevAgentEndpoint() + REMOTE + "?projectPath=" + project.toString(); + String requestUrl = getWsAgentBaseUrl() + REMOTE + "?projectPath=" + project; return asyncRequestFactory.createPutRequest(requestUrl, remoteAddRequest).loader(loader).send(); } - @Override - public void remoteDelete(ProjectConfig project, - String name, - AsyncRequestCallback callback) { - String url = appContext.getDevAgentEndpoint() + REMOTE + '/' + name + "?projectPath=" + project.getPath(); - asyncRequestFactory.createDeleteRequest(url).loader(loader).send(callback); - } - @Override public Promise remoteDelete(Path project, String name) { - String url = appContext.getDevAgentEndpoint() + REMOTE + '/' + name + "?projectPath=" + project.toString(); + String url = getWsAgentBaseUrl() + REMOTE + '/' + name + "?projectPath=" + project; return asyncRequestFactory.createDeleteRequest(url).loader(loader).send(); } @@ -507,37 +330,49 @@ public class GitServiceClientImpl implements GitServiceClient { .withRefSpec(refspec) .withRemote(remote) .withRemoveDeletedRefs(removeDeletedRefs); - String url = appContext.getDevAgentEndpoint() + FETCH + "?projectPath=" + project; + String url = getWsAgentBaseUrl() + FETCH + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, fetchRequest).send(); } @Override public Promise pull(Path project, String refSpec, String remote) { PullRequest pullRequest = dtoFactory.createDto(PullRequest.class).withRemote(remote).withRefSpec(refSpec); - String url = appContext.getDevAgentEndpoint() + PULL + "?projectPath=" + project; + String url = getWsAgentBaseUrl() + PULL + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, pullRequest).send(dtoUnmarshallerFactory.newUnmarshaller(PullResponse.class)); } @Override - public Promise diff(Path project, - List fileFilter, + public Promise diff(Path project, List fileFilter, DiffType type, boolean noRenames, int renameLimit, String commitA, String commitB) { - return diff(project, fileFilter, type, noRenames, renameLimit, commitA, commitB, false).send(new StringUnmarshaller()); + return diff(project, + fileFilter, + type, + noRenames, + renameLimit, + commitA, + commitB, + false).send(new StringUnmarshaller()); } @Override - public Promise diff(Path project, - List files, + public Promise diff(Path project, List files, DiffType type, boolean noRenames, int renameLimit, String commitA, boolean cached) { - return diff(project, files, type, noRenames, renameLimit, commitA, null, cached).send(new StringUnmarshaller()); + return diff(project, + files, + type, + noRenames, + renameLimit, + commitA, + null, + cached).send(new StringUnmarshaller()); } private AsyncRequest diff(Path project, @@ -548,36 +383,31 @@ public class GitServiceClientImpl implements GitServiceClient { String commitA, String commitB, boolean cached) { - StringBuilder params = new StringBuilder().append("?projectPath=").append(project.toString()); + StringBuilder params = new StringBuilder().append("?projectPath=") + .append(project) + .append("&noRenames=").append(noRenames) + .append("&renameLimit=").append(renameLimit) + .append("&cached=").append(cached); if (fileFilter != null) { - for (String file : fileFilter) { - if (file.isEmpty()) { - continue; - } - params.append("&fileFilter=").append(file); - } + fileFilter.stream().filter(file -> !file.isEmpty()).forEach(file -> params.append("&fileFilter=").append(file)); } if (type != null) { params.append("&diffType=").append(type); } - params.append("&noRenames=").append(noRenames); - params.append("&renameLimit=").append(renameLimit); if (commitA != null) { params.append("&commitA=").append(commitA); } if (commitB != null) { params.append("&commitB=").append(commitB); } - params.append("&cached=").append(cached); - - String url = appContext.getDevAgentEndpoint() + DIFF + params; + String url = getWsAgentBaseUrl() + DIFF + params; return asyncRequestFactory.createGetRequest(url).loader(loader); } @Override public Promise showFileContent(Path project, Path file, String version) { - String params = "?projectPath=" + project.toString() + "&file=" + file + "&version=" + version ; - String url = appContext.getDevAgentEndpoint() + SHOW + params; + String params = "?projectPath=" + project + "&file=" + file + "&version=" + version; + String url = getWsAgentBaseUrl() + SHOW + params; return asyncRequestFactory.createGetRequest(url) .loader(loader) .send(dtoUnmarshallerFactory.newUnmarshaller(ShowFileContentResponse.class)); @@ -586,7 +416,7 @@ public class GitServiceClientImpl implements GitServiceClient { @Override public Promise merge(Path project, String commit) { MergeRequest mergeRequest = dtoFactory.createDto(MergeRequest.class).withCommit(commit); - String url = appContext.getDevAgentEndpoint() + MERGE + "?projectPath=" + project; + String url = getWsAgentBaseUrl() + MERGE + "?projectPath=" + project; return asyncRequestFactory.createPostRequest(url, mergeRequest) .loader(loader) .header(ACCEPT, APPLICATION_JSON) @@ -594,26 +424,12 @@ public class GitServiceClientImpl implements GitServiceClient { } @Override - public Promise deleteRepository(final Path project) { - return createFromAsyncRequest(new RequestCall() { - @Override - public void makeCall(final AsyncCallback callback) { - String url = REPOSITORY + "?projectPath=" + project.toString(); - final Message message = new MessageBuilder(DELETE, url).header(ACCEPT, TEXT_PLAIN) - .build(); + public Promise deleteRepository(Path project) { + String url = getWsAgentBaseUrl() + REPOSITORY + "?projectPath=" + project; + return asyncRequestFactory.createDeleteRequest(url).loader(loader).send(); + } - sendMessageToWS(message, new RequestCallback() { - @Override - protected void onSuccess(Void result) { - callback.onSuccess(result); - } - - @Override - protected void onFailure(Throwable exception) { - callback.onFailure(exception); - } - }); - } - }); + private String getWsAgentBaseUrl() { + return appContext.getDevMachine().getWsAgentBaseUrl(); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/AddToIndexAction.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/AddToIndexAction.java index 1a33d30b59..d3a2627875 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/AddToIndexAction.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/AddToIndexAction.java @@ -109,9 +109,8 @@ public class AddToIndexAction extends GitAction { } private boolean containsInSelected(List items) { - Resource[] appContextResources = appContext.getResources(); for (String item : items) { - for (Resource selectedItem : appContextResources) { + for (Resource selectedItem : appContext.getResources()) { String selectedItemPath = selectedItem.getLocation() .removeFirstSegments(1) // remove project name from path .toString(); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/CompareWithLatestAction.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/CompareWithLatestAction.java index 7026d1a0e9..74f9cd445c 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/CompareWithLatestAction.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/CompareWithLatestAction.java @@ -10,18 +10,13 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.git.client.action; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.ide.ext.git.client.GitServiceClient; -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.ide.api.action.ActionEvent; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.notification.NotificationManager; -import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; @@ -89,44 +84,33 @@ public class CompareWithLatestAction extends GitAction { .removeTrailingSeparator() .toString(); - service.diff( - project.getLocation(), - selectedItemPath.isEmpty() ? null : singletonList(selectedItemPath), - NAME_STATUS, false, 0, REVISION, false) - .then(new Operation() { - @Override - public void apply(String diff) throws OperationException { - if (diff.isEmpty()) { - dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), - locale.compareMessageIdenticalContentText(), null).show(); - } else { - final String[] changedFiles = diff.split("\n"); - if (changedFiles.length == 1) { - project.getFile(changedFiles[0].substring(2)).then(new Operation>() { - @Override - public void apply(Optional file) throws OperationException { - if (file.isPresent()) { - comparePresenter.showCompareWithLatest(file.get(), - defineStatus(changedFiles[0].substring(0, 1)), - REVISION); - } - } - }); - } else { - Map items = new HashMap<>(); - for (String item : changedFiles) { - items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); + service.diff(project.getLocation(), + selectedItemPath.isEmpty() ? null : singletonList(selectedItemPath), NAME_STATUS, false, 0, REVISION, false) + .then(diff -> { + if (diff.isEmpty()) { + dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), + locale.compareMessageIdenticalContentText(), null).show(); + } else { + final String[] changedFiles = diff.split("\n"); + if (changedFiles.length == 1) { + project.getFile(changedFiles[0].substring(2)).then(file -> { + if (file.isPresent()) { + comparePresenter.showCompareWithLatest(file.get(), + defineStatus(changedFiles[0].substring(0, 1)), + REVISION); } - changesListPresenter.show(items, REVISION, null, project); + }); + } else { + Map items = new HashMap<>(); + for (String item : changedFiles) { + items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); } + changesListPresenter.show(items, REVISION, null, project); } } }) - .catchError(new Operation() { - @Override - public void apply(PromiseError arg) throws OperationException { - notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); - } + .catchError(arg -> { + notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/DeleteRepositoryAction.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/DeleteRepositoryAction.java index cd5979acfe..82fdfe3235 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/DeleteRepositoryAction.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/DeleteRepositoryAction.java @@ -19,7 +19,6 @@ import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; import org.eclipse.che.ide.ext.git.client.GitResources; import org.eclipse.che.ide.ext.git.client.delete.DeleteRepositoryPresenter; -import org.eclipse.che.ide.api.dialogs.ConfirmCallback; import org.eclipse.che.ide.api.dialogs.DialogFactory; import static com.google.common.base.Preconditions.checkState; @@ -52,11 +51,6 @@ public class DeleteRepositoryAction extends GitAction { dialogFactory.createConfirmDialog(constant.deleteGitRepositoryTitle(), constant.deleteGitRepositoryQuestion(project.getName()), - new ConfirmCallback() { - @Override - public void accepted() { - presenter.deleteRepository(project); - } - }, null).show(); + () -> presenter.deleteRepository(project), null).show(); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/InitRepositoryAction.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/InitRepositoryAction.java index 7530c6ffa2..1b036d558b 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/InitRepositoryAction.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/action/InitRepositoryAction.java @@ -20,7 +20,6 @@ import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; import org.eclipse.che.ide.ext.git.client.GitResources; import org.eclipse.che.ide.ext.git.client.GitUtil; import org.eclipse.che.ide.ext.git.client.init.InitRepositoryPresenter; -import org.eclipse.che.ide.api.dialogs.ConfirmCallback; import org.eclipse.che.ide.api.dialogs.DialogFactory; import javax.validation.constraints.NotNull; @@ -58,12 +57,7 @@ public class InitRepositoryAction extends GitAction { dialogFactory.createConfirmDialog(constant.createTitle(), constant.messagesInitRepoQuestion(project.getName()), - new ConfirmCallback() { - @Override - public void accepted() { - presenter.initRepository(project); - } - }, null).show(); + () -> presenter.initRepository(project), null).show(); } @Override diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/add/AddToIndexPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/add/AddToIndexPresenter.java index 8fe052de28..9ffca56a72 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/add/AddToIndexPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/add/AddToIndexPresenter.java @@ -13,11 +13,9 @@ package org.eclipse.che.ide.ext.git.client.add; import com.google.inject.Inject; import com.google.inject.Singleton; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; +import org.eclipse.che.ide.api.machine.DevMachine; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.Container; import org.eclipse.che.ide.api.resources.Resource; @@ -84,7 +82,6 @@ public class AddToIndexPresenter implements AddToIndexView.ActionDelegate { view.showDialog(); } - /** {@inheritDoc} */ @Override public void onAddClicked() { Resource[] resources = appContext.getResources(); @@ -97,25 +94,18 @@ public class AddToIndexPresenter implements AddToIndexView.ActionDelegate { final GitOutputConsole console = gitOutputConsoleFactory.create(constant.addToIndexCommandName()); consolesPanelPresenter.addCommandOutput(console); service.add(projectLocation, view.isUpdated(), paths) - .then(new Operation() { - @Override - public void apply(Void arg) throws OperationException { - console.print(constant.addSuccess()); - notificationManager.notify(constant.addSuccess()); - view.close(); - } + .then(arg -> { + console.print(constant.addSuccess()); + notificationManager.notify(constant.addSuccess()); + view.close(); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError arg) throws OperationException { - console.printError(constant.addFailed()); - notificationManager.notify(constant.addFailed(), FAIL, FLOAT_MODE); - view.close(); - } + .catchError(arg -> { + console.printError(constant.addFailed()); + notificationManager.notify(constant.addFailed(), FAIL, FLOAT_MODE); + view.close(); }); } - /** {@inheritDoc} */ @Override public void onCancelClicked() { view.close(); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenter.java index 80cf0320c7..abf7388443 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenter.java @@ -16,13 +16,8 @@ import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.CheckoutRequest; -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.ide.api.app.AppContext; -import org.eclipse.che.ide.api.dialogs.ConfirmCallback; import org.eclipse.che.ide.api.dialogs.DialogFactory; -import org.eclipse.che.ide.api.dialogs.InputCallback; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.Project; @@ -33,7 +28,6 @@ import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import javax.validation.constraints.NotNull; -import java.util.List; import static org.eclipse.che.api.git.shared.BranchListMode.LIST_ALL; import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE; @@ -48,11 +42,12 @@ import static org.eclipse.che.ide.util.ExceptionUtils.getErrorCode; */ @Singleton public class BranchPresenter implements BranchView.ActionDelegate { - public static final String BRANCH_RENAME_COMMAND_NAME = "Git rename branch"; - public static final String BRANCH_DELETE_COMMAND_NAME = "Git delete branch"; - public static final String BRANCH_CHECKOUT_COMMAND_NAME = "Git checkout branch"; - public static final String BRANCH_CREATE_COMMAND_NAME = "Git create branch"; - public static final String BRANCH_LIST_COMMAND_NAME = "Git list of branches"; + + private static final String BRANCH_RENAME_COMMAND_NAME = "Git rename branch"; + private static final String BRANCH_DELETE_COMMAND_NAME = "Git delete branch"; + private static final String BRANCH_CHECKOUT_COMMAND_NAME = "Git checkout branch"; + private static final String BRANCH_CREATE_COMMAND_NAME = "Git create branch"; + private static final String BRANCH_LIST_COMMAND_NAME = "Git list of branches"; private final DtoFactory dtoFactory; private final BranchView view; @@ -96,24 +91,17 @@ public class BranchPresenter implements BranchView.ActionDelegate { getBranches(); } - /** {@inheritDoc} */ @Override public void onCloseClicked() { view.close(); } - /** {@inheritDoc} */ @Override public void onRenameClicked() { if (selectedBranch.isRemote()) { dialogFactory.createConfirmDialog(constant.branchConfirmRenameTitle(), constant.branchConfirmRenameMessage(), - new ConfirmCallback() { - @Override - public void accepted() { - renameBranch(); - } - }, + this::renameBranch, null).show(); } else { renameBranch(); @@ -127,29 +115,18 @@ public class BranchPresenter implements BranchView.ActionDelegate { selectedBranchName, 0, selectedBranchName.length(), - new InputCallback() { - @Override - public void accepted(String newBranchName) { - renameBranch(newBranchName); - } - }, + this::renameBranch, null).show(); } private void renameBranch(String newName) { service.branchRename(project.getLocation(), selectedBranch.getDisplayName(), newName) - .then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - getBranches(); - } + .then(ignored -> { + getBranches(); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_RENAME_COMMAND_NAME); - getBranches();//rename of remote branch occurs in three stages, so needs update list of branches on view - } + .catchError(error -> { + handleError(error.getCause(), BRANCH_RENAME_COMMAND_NAME); + getBranches();//rename of remote branch occurs in three stages, so needs update list of branches on view }); } @@ -160,24 +137,18 @@ public class BranchPresenter implements BranchView.ActionDelegate { return tokens.length > 0 ? tokens[tokens.length - 1] : selectedBranchName; } - /** {@inheritDoc} */ @Override public void onDeleteClicked() { - service.branchDelete(project.getLocation(), selectedBranch.getName(), true).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - getBranches(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_DELETE_COMMAND_NAME); - } - }); + service.branchDelete(project.getLocation(), selectedBranch.getName(), true) + .then(ignored -> { + getBranches(); + }) + .catchError(error -> { + handleError(error.getCause(), BRANCH_DELETE_COMMAND_NAME); + }); } - /** {@inheritDoc} */ @Override public void onCheckoutClicked() { final CheckoutRequest checkoutRequest = dtoFactory.createDto(CheckoutRequest.class); @@ -187,65 +158,48 @@ public class BranchPresenter implements BranchView.ActionDelegate { checkoutRequest.setName(selectedBranch.getDisplayName()); } - service.checkout(project.getLocation(), checkoutRequest).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - getBranches(); - - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_CHECKOUT_COMMAND_NAME); - } - }); + service.checkout(project.getLocation(), checkoutRequest) + .then(ignored -> { + getBranches(); + project.synchronize(); + }) + .catchError(error -> { + handleError(error.getCause(), BRANCH_CHECKOUT_COMMAND_NAME); + }); } /** Get the list of branches. */ private void getBranches() { - service.branchList(project.getLocation(), LIST_ALL).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - if (branches.isEmpty()) { - dialogFactory.createMessageDialog(constant.branchTitle(), - constant.initCommitWasNotPerformed(), - null).show(); - } else { - view.setBranches(branches); - view.showDialogIfClosed(); - } - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_LIST_COMMAND_NAME); - } - }); + service.branchList(project.getLocation(), LIST_ALL) + .then(branches -> { + if (branches.isEmpty()) { + dialogFactory.createMessageDialog(constant.branchTitle(), + constant.initCommitWasNotPerformed(), + null).show(); + } else { + view.setBranches(branches); + view.showDialogIfClosed(); + } + }) + .catchError(error -> { + handleError(error.getCause(), BRANCH_LIST_COMMAND_NAME); + }); } - /** {@inheritDoc} */ @Override public void onCreateClicked() { - dialogFactory.createInputDialog(constant.branchCreateNew(), constant.branchTypeNew(), new InputCallback() { - @Override - public void accepted(String value) { - if (value.isEmpty()) { - return; - } - - service.branchCreate(project.getLocation(), value, null).then(new Operation() { - @Override - public void apply(Branch branch) throws OperationException { - getBranches(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_CREATE_COMMAND_NAME); - } - }); + dialogFactory.createInputDialog(constant.branchCreateNew(), constant.branchTypeNew(), value -> { + if (value.isEmpty()) { + return; } + + service.branchCreate(project.getLocation(), value, null) + .then(branch -> { + getBranches(); + }) + .catchError(error -> { + handleError(error.getCause(), BRANCH_CREATE_COMMAND_NAME); + }); }, null).show(); } @@ -258,7 +212,6 @@ public class BranchPresenter implements BranchView.ActionDelegate { view.setEnableDeleteButton(false); } - /** {@inheritDoc} */ @Override public void onBranchSelected(@NotNull Branch branch) { selectedBranch = branch; diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferencePresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferencePresenter.java index 58d84962d1..8a5edc8917 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferencePresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferencePresenter.java @@ -14,14 +14,10 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.git.shared.CheckoutRequest; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.Project; -import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsole; @@ -88,29 +84,21 @@ public class CheckoutReferencePresenter implements CheckoutReferenceView.ActionD public void onCheckoutClicked(final String reference) { service.checkout(project.getLocation(), dtoFactory.createDto(CheckoutRequest.class).withName(reference)) - .then(new Operation() { - @Override - public void apply(Void arg) throws OperationException { - project.synchronize().then(new Operation() { - @Override - public void apply(Resource[] arg) throws OperationException { - view.close(); - } - }); - } + .then(branchName -> { + appContext.getRootProject().synchronize() + .then(arg -> { + view.close(); + }); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - final String errorMessage = (error.getMessage() != null) - ? error.getMessage() - : constant.checkoutFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(CHECKOUT_COMMAND_NAME); - console.printError(errorMessage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.checkoutFailed(), FAIL, FLOAT_MODE); - view.close(); - } + .catchError(error -> { + final String errorMessage = (error.getMessage() != null) + ? error.getMessage() + : constant.checkoutFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(CHECKOUT_COMMAND_NAME); + console.printError(errorMessage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.checkoutFailed(), FAIL, FLOAT_MODE); + view.close(); }); } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenter.java index cb75fb206d..1300631eaa 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenter.java @@ -25,9 +25,9 @@ import org.eclipse.che.ide.commons.exception.ServerException; import org.eclipse.che.ide.ext.git.client.DateTimeFormatter; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; import org.eclipse.che.ide.ext.git.client.compare.FileStatus.Status; -import org.eclipse.che.ide.ext.git.client.compare.changespanel.ChangesPanelPresenter; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsole; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory; +import org.eclipse.che.ide.ext.git.client.compare.changespanel.ChangesPanelPresenter; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import org.eclipse.che.ide.resource.Path; @@ -194,9 +194,8 @@ public class CommitPresenter implements CommitView.ActionDelegate { .then(arg -> { service.commit(location, view.getMessage(), - false, - filesToCommitArray, - view.isAmend()) + view.isAmend(), + filesToCommitArray) .then(revision -> { onCommitSuccess(revision); if (view.isPushAfterCommit()) { diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/ComparePresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/ComparePresenter.java index 88531ce817..1cec6fe911 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/ComparePresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/ComparePresenter.java @@ -15,23 +15,19 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.api.git.shared.ShowFileContentResponse; -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.commons.annotation.Nullable; -import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.dialogs.CancelCallback; -import org.eclipse.che.ide.api.dialogs.ConfirmCallback; -import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.api.event.FileContentUpdateEvent; import org.eclipse.che.ide.ext.git.client.GitServiceClient; +import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.Container; import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; import org.eclipse.che.ide.ext.git.client.compare.FileStatus.Status; +import org.eclipse.che.ide.api.dialogs.CancelCallback; +import org.eclipse.che.ide.api.dialogs.ConfirmCallback; +import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.resource.Path; import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.NOT_EMERGE_MODE; @@ -109,39 +105,26 @@ public class ComparePresenter implements CompareView.ActionDelegate { if (status.equals(DELETED)) { service.showFileContent(project.get().getLocation(), relPath, revision) - .then(new Operation() { - @Override - public void apply(ShowFileContentResponse content) throws OperationException { - view.setTitle(file.getLocation().toString()); - view.setColumnTitles(locale.compareYourVersionTitle(), revision + locale.compareReadOnlyTitle()); - view.show(content.getContent(), "", file.getLocation().toString(), false); - } + .then(content -> { + view.setTitle(file.getLocation().toString()); + view.setColumnTitles(locale.compareYourVersionTitle(), revision + locale.compareReadOnlyTitle()); + view.show(content.getContent(), "", file.getLocation().toString(), false); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); }); } else { service.showFileContent(project.get().getLocation(), relPath, revision) - .then(new Operation() { - @Override - public void apply(ShowFileContentResponse content) throws OperationException { - showCompare(content.getContent()); - } + .then(content -> { + showCompare(content.getContent()); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); }); } } /** - * * @param file * path of the file * @param status @@ -164,56 +147,35 @@ public class ComparePresenter implements CompareView.ActionDelegate { view.setTitle(file.toString()); if (status == Status.ADDED) { service.showFileContent(projectLocation, file, revisionB) - .then(new Operation() { - @Override - public void apply(ShowFileContentResponse response) throws OperationException { - view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), - revisionA == null ? "" : revisionA + locale.compareReadOnlyTitle()); - view.show("", response.getContent(), file.toString(), true); - } + .then(response -> { + view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), + revisionA == null ? "" : revisionA + locale.compareReadOnlyTitle()); + view.show("", response.getContent(), file.toString(), true); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); }); } else if (status == Status.DELETED) { service.showFileContent(projectLocation, file, revisionA) - .then(new Operation() { - @Override - public void apply(ShowFileContentResponse response) throws OperationException { - view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), revisionA + locale.compareReadOnlyTitle()); - view.show(response.getContent(), "", file.toString(), true); - } + .then(response -> { + view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), revisionA + locale.compareReadOnlyTitle()); + view.show(response.getContent(), "", file.toString(), true); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); }); } else { service.showFileContent(projectLocation, file, revisionA) - .then(new Operation() { - @Override - public void apply(final ShowFileContentResponse contentAResponse) throws OperationException { - service.showFileContent(projectLocation, file, revisionB) - .then(new Operation() { - @Override - public void apply(ShowFileContentResponse contentBResponse) throws OperationException { - view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), - revisionA + locale.compareReadOnlyTitle()); - view.show(contentAResponse.getContent(), contentBResponse.getContent(), file.toString(), true); - } - }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); - } - }); - } + .then(contentAResponse -> { + service.showFileContent(projectLocation, file, revisionB) + .then(contentBResponse -> { + view.setColumnTitles(revisionB + locale.compareReadOnlyTitle(), + revisionA + locale.compareReadOnlyTitle()); + view.show(contentAResponse.getContent(), contentBResponse.getContent(), file.toString(), true); + }) + .catchError(error -> { + notificationManager.notify(error.getMessage(), FAIL, NOT_EMERGE_MODE); + }); }); } } @@ -225,46 +187,32 @@ public class ComparePresenter implements CompareView.ActionDelegate { return; } - ConfirmCallback confirmCallback = new ConfirmCallback() { - @Override - public void accepted() { - comparedFile.updateContent(newContent).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - final Container parent = comparedFile.getParent(); + ConfirmCallback confirmCallback = () -> comparedFile.updateContent(newContent) + .then(ignored -> { + final Container parent = comparedFile.getParent(); - if (parent != null) { - parent.synchronize(); - } + if (parent != null) { + parent.synchronize(); + } - eventBus.fireEvent(new FileContentUpdateEvent(comparedFile.getLocation().toString())); - view.hide(); - } - }); - } - }; + eventBus.fireEvent(new FileContentUpdateEvent(comparedFile.getLocation() + .toString())); + view.hide(); + }); - CancelCallback cancelCallback = new CancelCallback() { - @Override - public void cancelled() { - view.hide(); - } - }; + CancelCallback cancelCallback = view::hide; dialogFactory.createConfirmDialog(locale.compareSaveTitle(), locale.compareSaveQuestion(), locale.buttonYes(), locale.buttonNo(), confirmCallback, cancelCallback).show(); } private void showCompare(final String remoteContent) { - comparedFile.getContent().then(new Operation() { - @Override - public void apply(String local) throws OperationException { - localContent = local; - final String path = comparedFile.getLocation().removeFirstSegments(1).toString(); - view.setTitle(path); - view.setColumnTitles(locale.compareYourVersionTitle(), revision + locale.compareReadOnlyTitle()); - view.show(remoteContent, localContent, path, false); - } + comparedFile.getContent().then(local -> { + localContent = local; + final String path = comparedFile.getLocation().removeFirstSegments(1).toString(); + view.setTitle(path); + view.setColumnTitles(locale.compareYourVersionTitle(), revision + locale.compareReadOnlyTitle()); + view.show(remoteContent, localContent, path, false); }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/branchlist/BranchListPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/branchlist/BranchListPresenter.java index 78f8dc9fb2..23a9372bb9 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/branchlist/BranchListPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/branchlist/BranchListPresenter.java @@ -10,19 +10,14 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.git.client.compare.branchlist; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.git.shared.Branch; -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.ide.api.app.AppContext; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; -import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; @@ -35,7 +30,6 @@ import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import javax.validation.constraints.NotNull; import java.util.HashMap; -import java.util.List; import java.util.Map; import static com.google.common.base.Preconditions.checkState; @@ -105,13 +99,11 @@ public class BranchListPresenter implements BranchListView.ActionDelegate { getBranches(); } - /** {@inheritDoc} */ @Override public void onCloseClicked() { view.close(); } - /** {@inheritDoc} */ @Override public void onCompareClicked() { @@ -120,61 +112,49 @@ public class BranchListPresenter implements BranchListView.ActionDelegate { .removeTrailingSeparator() .toString(); - service.diff( - project.getLocation(), + service.diff(project.getLocation(), selectedItemPath.isEmpty() ? null : singletonList(selectedItemPath), NAME_STATUS, false, 0, selectedBranch.getName(), false) - .then(new Operation() { - @Override - public void apply(String diff) throws OperationException { - if (diff.isEmpty()) { - dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), - locale.compareMessageIdenticalContentText(), null).show(); - } else { - final String[] changedFiles = diff.split("\n"); - if (changedFiles.length == 1) { - project.getFile(changedFiles[0].substring(2)).then(new Operation>() { - @Override - public void apply(Optional file) throws OperationException { - if (file.isPresent()) { - comparePresenter.showCompareWithLatest(file.get(), - defineStatus(changedFiles[0].substring(0, 1)), - selectedBranch.getName()); - } - } - }); - } else { - Map items = new HashMap<>(); - for (String item : changedFiles) { - items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); + .then(diff -> { + if (diff.isEmpty()) { + dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), + locale.compareMessageIdenticalContentText(), null).show(); + } else { + final String[] changedFiles = diff.split("\n"); + if (changedFiles.length == 1) { + project.getFile(changedFiles[0].substring(2)).then(file -> { + if (file.isPresent()) { + comparePresenter.showCompareWithLatest(file.get(), + defineStatus(changedFiles[0].substring(0, 1)), + selectedBranch.getName()); } - changesListPresenter.show(items, selectedBranch.getName(), null, project); + }); + } else { + Map items = new HashMap<>(); + for (String item : changedFiles) { + items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); } + changesListPresenter.show(items, selectedBranch.getName(), null, project); } } }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); }); view.close(); } - /** {@inheritDoc} */ @Override public void onBranchUnselected() { selectedBranch = null; view.setEnableCompareButton(false); } - /** {@inheritDoc} */ @Override public void onBranchSelected(@NotNull Branch branch) { selectedBranch = branch; @@ -184,21 +164,17 @@ public class BranchListPresenter implements BranchListView.ActionDelegate { /** Get list of branches from selected project. */ private void getBranches() { - service.branchList(project.getLocation(), LIST_ALL).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - view.setBranches(branches); - view.showDialog(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - final String errorMessage = (error.getMessage() != null) ? error.getMessage() : locale.branchesListFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(BRANCH_LIST_COMMAND_NAME); - console.printError(errorMessage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(locale.branchesListFailed(), FAIL, NOT_EMERGE_MODE); - } - }); + service.branchList(project.getLocation(), LIST_ALL) + .then(branches -> { + view.setBranches(branches); + view.showDialog(); + }) + .catchError(error -> { + final String errorMessage = (error.getMessage() != null) ? error.getMessage() : locale.branchesListFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(BRANCH_LIST_COMMAND_NAME); + console.printError(errorMessage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(locale.branchesListFailed(), FAIL, NOT_EMERGE_MODE); + }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/revisionslist/RevisionListPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/revisionslist/RevisionListPresenter.java index 996cd01303..b8e984c3ff 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/revisionslist/RevisionListPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/compare/revisionslist/RevisionListPresenter.java @@ -10,18 +10,13 @@ *******************************************************************************/ package org.eclipse.che.ide.ext.git.client.compare.revisionslist; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.ide.ext.git.client.GitServiceClient; -import org.eclipse.che.api.git.shared.LogResponse; import org.eclipse.che.api.git.shared.Revision; import org.eclipse.che.ide.api.app.AppContext; -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.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; @@ -127,60 +122,45 @@ public class RevisionListPresenter implements RevisionListView.ActionDelegate { /** Get list of revisions. */ private void getRevisions() { - service.log(project.getLocation(), new Path[]{selectedFilePath}, false) - .then(new Operation() { - @Override - public void apply(LogResponse log) throws OperationException { - view.setRevisions(log.getCommits()); - view.showDialog(); + service.log(project.getLocation(), new Path[]{selectedFilePath}, -1, -1, false) + .then(log -> { + view.setRevisions(log.getCommits()); + view.showDialog(); + }).catchError(error -> { + if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { + dialogFactory.createMessageDialog(locale.compareWithRevisionTitle(), + locale.initCommitWasNotPerformed(), + null).show(); + } else { + notificationManager.notify(locale.logFailed(), FAIL, NOT_EMERGE_MODE); } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { - dialogFactory.createMessageDialog(locale.compareWithRevisionTitle(), - locale.initCommitWasNotPerformed(), - null).show(); - } else { - notificationManager.notify(locale.logFailed(), FAIL, NOT_EMERGE_MODE); - } - } - }); + }); } private void compare() { - service.diff( - project.getLocation(), + service.diff(project.getLocation(), singletonList(selectedFilePath.toString()), NAME_STATUS, false, 0, selectedRevision.getId(), false) - .then(new Operation() { - @Override - public void apply(final String diff) throws OperationException { - if (diff.isEmpty()) { - dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), - locale.compareMessageIdenticalContentText(), null).show(); - } else { - project.getFile(diff.substring(2)).then(new Operation>() { - @Override - public void apply(Optional file) throws OperationException { - if (file.isPresent()) { - comparePresenter.showCompareWithLatest(file.get(), defineStatus(diff.substring(0, 1)), selectedRevision.getId()); - } - } - }); + .then(diff -> { + if (diff.isEmpty()) { + dialogFactory.createMessageDialog(locale.compareMessageIdenticalContentTitle(), + locale.compareMessageIdenticalContentText(), null).show(); + } else { + appContext.getRootProject().getFile(diff.substring(2)).then(file -> { + if (file.isPresent()) { + comparePresenter.showCompareWithLatest(file.get(), defineStatus(diff.substring(0, 1)), + selectedRevision.getId()); + } + }); - } } }) - .catchError(new Operation() { - @Override - public void apply(PromiseError arg) throws OperationException { - notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); - } + .catchError(arg -> { + notificationManager.notify(locale.diffFailed(), FAIL, NOT_EMERGE_MODE); }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/delete/DeleteRepositoryPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/delete/DeleteRepositoryPresenter.java index 6eb9f7b6b0..3675d28623 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/delete/DeleteRepositoryPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/delete/DeleteRepositoryPresenter.java @@ -13,9 +13,6 @@ package org.eclipse.che.ide.ext.git.client.delete; import com.google.inject.Inject; import com.google.inject.Singleton; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -36,7 +33,7 @@ import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAI */ @Singleton public class DeleteRepositoryPresenter { - public static final String DELETE_REPO_COMMAND_NAME = "Git delete repository"; + private static final String DELETE_REPO_COMMAND_NAME = "Git delete repository"; private final GitServiceClient service; private final GitLocalizationConstant constant; @@ -61,25 +58,19 @@ public class DeleteRepositoryPresenter { } /** Delete Git repository. */ - public void deleteRepository(final Project project) { + public void deleteRepository(Project project) { final GitOutputConsole console = gitOutputConsoleFactory.create(DELETE_REPO_COMMAND_NAME); - service.deleteRepository(project.getLocation()).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.deleteGitRepositorySuccess()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.deleteGitRepositorySuccess()); + service.deleteRepository(project.getLocation()).then(ignored -> { + console.print(constant.deleteGitRepositorySuccess()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.deleteGitRepositorySuccess()); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - console.printError(error.getMessage()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.failedToDeleteRepository(), FAIL, FLOAT_MODE); - } + appContext.getRootProject().synchronize(); + }).catchError(error -> { + console.printError(error.getMessage()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.failedToDeleteRepository(), FAIL, FLOAT_MODE); }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/fetch/FetchPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/fetch/FetchPresenter.java index 09d68c113a..a7ffe82412 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/fetch/FetchPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/fetch/FetchPresenter.java @@ -16,10 +16,6 @@ import com.google.inject.Singleton; import org.eclipse.che.api.core.rest.shared.dto.ServiceError; import org.eclipse.che.api.git.shared.BranchListMode; import org.eclipse.che.api.git.shared.Branch; -import org.eclipse.che.api.git.shared.Remote; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -101,24 +97,20 @@ public class FetchPresenter implements FetchView.ActionDelegate { * local). */ private void updateRemotes() { - service.remoteList(project.getLocation(), null, true).then(new Operation>() { - @Override - public void apply(List remotes) throws OperationException { - view.setRepositories(remotes); - updateBranches(LIST_REMOTE); - view.setEnableFetchButton(!remotes.isEmpty()); - view.showDialog(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - GitOutputConsole console = gitOutputConsoleFactory.create(FETCH_COMMAND_NAME); - console.printError(constant.remoteListFailed()); - processesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.remoteListFailed(), FAIL, FLOAT_MODE); - view.setEnableFetchButton(false); - } - }); + service.remoteList(project.getLocation(), null, true) + .then(remotes -> { + view.setRepositories(remotes); + updateBranches(LIST_REMOTE); + view.setEnableFetchButton(!remotes.isEmpty()); + view.showDialog(); + }) + .catchError(error -> { + GitOutputConsole console = gitOutputConsoleFactory.create(FETCH_COMMAND_NAME); + console.printError(constant.remoteListFailed()); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); + notificationManager.notify(constant.remoteListFailed(), FAIL, FLOAT_MODE); + view.setEnableFetchButton(false); + }); } /** @@ -128,33 +120,29 @@ public class FetchPresenter implements FetchView.ActionDelegate { * is a remote mode */ private void updateBranches(@NotNull final BranchListMode remoteMode) { - service.branchList(project.getLocation(), remoteMode).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - if (LIST_REMOTE.equals(remoteMode)) { - view.setRemoteBranches(branchSearcher.getRemoteBranchesToDisplay(view.getRepositoryName(), branches)); - updateBranches(LIST_LOCAL); - } else { - view.setLocalBranches(branchSearcher.getLocalBranchesToDisplay(branches)); - for (Branch branch : branches) { - if (branch.isActive()) { - view.selectRemoteBranch(branch.getDisplayName()); - break; - } - } - } - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - final String errorMessage = error.getMessage() != null ? error.getMessage() : constant.branchesListFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(FETCH_COMMAND_NAME); - console.printError(errorMessage); - processesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); - view.setEnableFetchButton(false); - } - }); + service.branchList(project.getLocation(), remoteMode) + .then(branches -> { + if (LIST_REMOTE.equals(remoteMode)) { + view.setRemoteBranches(branchSearcher.getRemoteBranchesToDisplay(view.getRepositoryName(), branches)); + updateBranches(LIST_LOCAL); + } else { + view.setLocalBranches(branchSearcher.getLocalBranchesToDisplay(branches)); + for (Branch branch : branches) { + if (branch.isActive()) { + view.selectRemoteBranch(branch.getDisplayName()); + break; + } + } + } + }) + .catchError(error -> { + final String errorMessage = error.getMessage() != null ? error.getMessage() : constant.branchesListFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(FETCH_COMMAND_NAME); + console.printError(errorMessage); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); + notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); + view.setEnableFetchButton(false); + }); } /** {@inheritDoc} */ @@ -166,22 +154,16 @@ public class FetchPresenter implements FetchView.ActionDelegate { final GitOutputConsole console = gitOutputConsoleFactory.create(FETCH_COMMAND_NAME); service.fetch(project.getLocation(), view.getRepositoryName(), getRefs(), view.isRemoveDeletedRefs()) - .then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.fetchSuccess(remoteUrl)); - processesPanelPresenter.addCommandOutput(console); - notification.setStatus(SUCCESS); - notification.setTitle(constant.fetchSuccess(remoteUrl)); - } - }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), remoteUrl, notification, console); - processesPanelPresenter.addCommandOutput(console); - } - }); + .then(ignored -> { + console.print(constant.fetchSuccess(remoteUrl)); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); + notification.setStatus(SUCCESS); + notification.setTitle(constant.fetchSuccess(remoteUrl)); + }) + .catchError(error -> { + handleError(error.getCause(), remoteUrl, notification, console); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); + }); view.close(); } @@ -231,13 +213,11 @@ public class FetchPresenter implements FetchView.ActionDelegate { } } - /** {@inheritDoc} */ @Override public void onCancelClicked() { view.close(); } - /** {@inheritDoc} */ @Override public void onValueChanged() { boolean isFetchAll = view.isFetchAllBranches(); @@ -245,13 +225,11 @@ public class FetchPresenter implements FetchView.ActionDelegate { view.setEnableRemoteBranchField(!isFetchAll); } - /** {@inheritDoc} */ @Override public void onRemoteBranchChanged() { view.selectLocalBranch(view.getRemoteBranch()); } - /** {@inheritDoc} */ @Override public void onRemoteRepositoryChanged() { updateBranches(LIST_REMOTE); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenter.java index 5033f885ca..9b7795fa19 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenter.java @@ -14,11 +14,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; -import org.eclipse.che.api.git.shared.LogResponse; import org.eclipse.che.api.git.shared.Revision; -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.ide.api.app.AppContext; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; @@ -136,34 +132,26 @@ public class HistoryPresenter implements HistoryView.ActionDelegate { } private void fetchRevisions() { - service.log( - project.getLocation(), - selectedPath.isEmpty() ? null : new Path[]{selectedPath}, + service.log(project.getLocation(), selectedPath.isEmpty() ? null : new Path[]{selectedPath}, skip, DEFAULT_PAGE_SIZE, false) - .then(new Operation() { - @Override - public void apply(LogResponse log) throws OperationException { - List commits = log.getCommits(); - if (!commits.isEmpty()) { - skip += commits.size(); - revisions.addAll(commits); - view.setRevisions(revisions); - view.showDialog(); - } + .then(log -> { + List commits = log.getCommits(); + if (!commits.isEmpty()) { + skip += commits.size(); + revisions.addAll(commits); + view.setRevisions(revisions); + view.showDialog(); } }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { - dialogFactory.createMessageDialog(locale.historyTitle(), - locale.initCommitWasNotPerformed(), - null).show(); - } else { - notificationManager.notify(locale.logFailed(), FAIL, EMERGE_MODE); - } + .catchError(error -> { + if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { + dialogFactory.createMessageDialog(locale.historyTitle(), + locale.initCommitWasNotPerformed(), + null).show(); + } else { + notificationManager.notify(locale.logFailed(), FAIL, EMERGE_MODE); } }); } @@ -172,40 +160,33 @@ public class HistoryPresenter implements HistoryView.ActionDelegate { final String revisionA = revisions.indexOf(selectedRevision) + 1 == revisions.size() ? null : revisions.get(revisions.indexOf(selectedRevision) + 1).getId(); final String revisionB = selectedRevision.getId(); - service.diff(project.getLocation(), - singletonList(selectedPath.toString()), + service.diff(project.getLocation(), singletonList(selectedPath.toString()), NAME_STATUS, true, 0, revisionA, revisionB) - .then(new Operation() { - @Override - public void apply(final String diff) throws OperationException { - if (diff.isEmpty()) { - dialogFactory.createMessageDialog(locale.historyTitle(), locale.historyNothingToDisplay(), null).show(); - return; - } - final String[] changedFiles = diff.split("\n"); - if (changedFiles.length == 1) { - comparePresenter.showCompareBetweenRevisions(Path.valueOf(diff.substring(2)), - defineStatus(diff.substring(0, 1)), - revisionA, - revisionB); - } else { - Map items = new HashMap<>(); - for (String item : changedFiles) { - items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); - } - changesListPresenter.show(items, revisionA, revisionB, project); + .then(diff -> { + if (diff.isEmpty()) { + dialogFactory.createMessageDialog(locale.historyTitle(), locale.historyNothingToDisplay(), null).show(); + return; + } + final String[] changedFiles = diff.split("\n"); + if (changedFiles.length == 1) { + comparePresenter.showCompareBetweenRevisions(Path.valueOf(diff.substring(2)), + defineStatus(diff.substring(0, 1)), + revisionA, + revisionB); + } else { + Map items = new HashMap<>(); + for (String item : changedFiles) { + items.put(item.substring(2, item.length()), defineStatus(item.substring(0, 1))); } + changesListPresenter.show(items, revisionA, revisionB, project); } }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(locale.diffFailed(), FAIL, EMERGE_MODE); - } + .catchError(error -> { + notificationManager.notify(locale.diffFailed(), FAIL, EMERGE_MODE); }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/init/InitRepositoryPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/init/InitRepositoryPresenter.java index 1832ddacda..fe4a2db117 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/init/InitRepositoryPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/init/InitRepositoryPresenter.java @@ -13,9 +13,6 @@ package org.eclipse.che.ide.ext.git.client.init; import com.google.inject.Inject; import com.google.inject.Singleton; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -66,22 +63,18 @@ public class InitRepositoryPresenter { public void initRepository(final Project project) { final GitOutputConsole console = gitOutputConsoleFactory.create(INIT_COMMAND_NAME); - service.init(project.getLocation(), false).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.initSuccess()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.initSuccess()); + service.init(project.getLocation(), false) + .then(ignored -> { + console.print(constant.initSuccess()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.initSuccess()); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), console); - consolesPanelPresenter.addCommandOutput(console); - } - }); + project.synchronize(); + }) + .catchError(error -> { + handleError(error.getCause(), console); + consolesPanelPresenter.addCommandOutput(console); + }); } /** diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/merge/MergePresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/merge/MergePresenter.java index eb9c6eb81c..4bb4ba4738 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/merge/MergePresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/merge/MergePresenter.java @@ -16,11 +16,7 @@ import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.MergeResult; -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.ide.api.app.AppContext; -import org.eclipse.che.ide.api.dialogs.ConfirmCallback; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -95,59 +91,49 @@ public class MergePresenter implements MergeView.ActionDelegate { selectedReference = null; view.setEnableMergeButton(false); - service.branchList(project.getLocation(), LIST_LOCAL).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - List references = new ArrayList<>(); - for (Branch branch : branches) { - if (!branch.isActive()) { - Reference reference = new Reference(branch.getName(), branch.getDisplayName(), LOCAL_BRANCH); - references.add(reference); - } - } - view.setLocalBranches(references); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - console.printError(error.getMessage()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); - } - }); + service.branchList(project.getLocation(), LIST_LOCAL) + .then(branches -> { + List references = new ArrayList<>(); + for (Branch branch : branches) { + if (!branch.isActive()) { + Reference reference = new Reference(branch.getName(), branch.getDisplayName(), LOCAL_BRANCH); + references.add(reference); + } + } + view.setLocalBranches(references); + }) + .catchError(error -> { + console.printError(error.getMessage()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); + }); - service.branchList(project.getLocation(), LIST_REMOTE).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - List references = new ArrayList<>(); - for (Branch branch : branches) { - if (!branch.isActive()) { - Reference reference = - new Reference(branch.getName(), branch.getDisplayName(), REMOTE_BRANCH); - references.add(reference); - } - } - view.setRemoteBranches(references); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - console.printError(error.getMessage()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); - } - }); + service.branchList(project.getLocation(), LIST_REMOTE) + .then(branches -> { + List references = new ArrayList<>(); + for (Branch branch : branches) { + if (!branch.isActive()) { + Reference reference = + new Reference(branch.getName(), branch.getDisplayName(), REMOTE_BRANCH); + references.add(reference); + } + } + view.setRemoteBranches(references); + }) + .catchError(error -> { + console.printError(error.getMessage()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.branchesListFailed(), FAIL, FLOAT_MODE); + }); view.showDialog(); } - /** {@inheritDoc} */ @Override public void onCancelClicked() { view.close(); } - /** {@inheritDoc} */ @Override public void onMergeClicked() { view.close(); @@ -155,34 +141,26 @@ public class MergePresenter implements MergeView.ActionDelegate { final GitOutputConsole console = gitOutputConsoleFactory.create(MERGE_COMMAND_NAME); service.merge(project.getLocation(), selectedReference.getDisplayName()) - .then(new Operation() { - @Override - public void apply(MergeResult result) throws OperationException { - console.print(formMergeMessage(result)); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(formMergeMessage(result)); + .then(result -> { + console.print(formMergeMessage(result)); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(formMergeMessage(result)); - project.synchronize(); + project.synchronize(); + }) + .catchError(error -> { + if (error.getCause() instanceof ServerException && + ((ServerException)error.getCause()).getErrorCode() == ErrorCodes.NO_COMMITTER_NAME_OR_EMAIL_DEFINED) { + dialogFactory.createMessageDialog(constant.mergeTitle(), constant.committerIdentityInfoEmpty(), + () -> { + //do nothing + }).show(); + return; } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - if (error.getCause() instanceof ServerException && - ((ServerException)error.getCause()).getErrorCode() == ErrorCodes.NO_COMMITTER_NAME_OR_EMAIL_DEFINED) { - dialogFactory.createMessageDialog(constant.mergeTitle(), constant.committerIdentityInfoEmpty(), - new ConfirmCallback() { - @Override - public void accepted() { - //do nothing - } - }).show(); - return; - } - console.printError(error.getMessage()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.mergeFailed(), FAIL, FLOAT_MODE); - } - }); + console.printError(error.getMessage()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.mergeFailed(), FAIL, FLOAT_MODE); + }); } /** diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/pull/PullPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/pull/PullPresenter.java index ba0f423dd1..966297c8af 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/pull/PullPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/pull/PullPresenter.java @@ -16,11 +16,6 @@ import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; import org.eclipse.che.api.git.shared.BranchListMode; import org.eclipse.che.api.git.shared.Branch; -import org.eclipse.che.api.git.shared.PullResponse; -import org.eclipse.che.api.git.shared.Remote; -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.ide.api.app.AppContext; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; @@ -34,7 +29,6 @@ import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import javax.validation.constraints.NotNull; -import java.util.List; import static org.eclipse.che.api.git.shared.BranchListMode.LIST_LOCAL; import static org.eclipse.che.api.git.shared.BranchListMode.LIST_REMOTE; @@ -98,21 +92,15 @@ public class PullPresenter implements PullView.ActionDelegate { view.setEnablePullButton(false); service.remoteList(project.getLocation(), null, true) - .then(new Operation>() { - @Override - public void apply(List remotes) throws OperationException { - updateBranches(LIST_REMOTE); - view.setRepositories(remotes); - view.setEnablePullButton(!remotes.isEmpty()); - view.showDialog(); - } + .then(remotes -> { + updateBranches(LIST_REMOTE); + view.setRepositories(remotes); + view.setEnablePullButton(!remotes.isEmpty()); + view.showDialog(); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), REMOTE_REPO_COMMAND_NAME); - view.setEnablePullButton(false); - } + .catchError(error -> { + handleError(error.getCause(), REMOTE_REPO_COMMAND_NAME); + view.setEnablePullButton(false); }); } @@ -125,29 +113,25 @@ public class PullPresenter implements PullView.ActionDelegate { */ private void updateBranches(@NotNull final BranchListMode remoteMode) { - service.branchList(project.getLocation(), remoteMode).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - if (LIST_REMOTE.equals(remoteMode)) { - view.setRemoteBranches(branchSearcher.getRemoteBranchesToDisplay(view.getRepositoryName(), branches)); - updateBranches(LIST_LOCAL); - } else { - view.setLocalBranches(branchSearcher.getLocalBranchesToDisplay(branches)); - for (Branch branch : branches) { - if (branch.isActive()) { - view.selectRemoteBranch(branch.getDisplayName()); - break; - } - } - } - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), BRANCH_LIST_COMMAND_NAME); - view.setEnablePullButton(false); - } - }); + service.branchList(project.getLocation(), remoteMode) + .then(branches -> { + if (LIST_REMOTE.equals(remoteMode)) { + view.setRemoteBranches(branchSearcher.getRemoteBranchesToDisplay(view.getRepositoryName(), branches)); + updateBranches(LIST_LOCAL); + } else { + view.setLocalBranches(branchSearcher.getLocalBranchesToDisplay(branches)); + for (Branch branch : branches) { + if (branch.isActive()) { + view.selectRemoteBranch(branch.getDisplayName()); + break; + } + } + } + }) + .catchError(error -> { + handleError(error.getCause(), BRANCH_LIST_COMMAND_NAME); + view.setEnablePullButton(false); + }); } /** {@inheritDoc} */ @@ -158,30 +142,26 @@ public class PullPresenter implements PullView.ActionDelegate { final StatusNotification notification = notificationManager.notify(constant.pullProcess(), PROGRESS, FLOAT_MODE); - service.pull(project.getLocation(), getRefs(), view.getRepositoryName()).then(new Operation() { - @Override - public void apply(PullResponse response) throws OperationException { - GitOutputConsole console = gitOutputConsoleFactory.create(PULL_COMMAND_NAME); - console.print(response.getCommandOutput(), GREEN_COLOR); - consolesPanelPresenter.addCommandOutput(console); - notification.setStatus(SUCCESS); - if (response.getCommandOutput().contains("Already up-to-date")) { - notification.setTitle(constant.pullUpToDate()); - } else { - project.synchronize(); - notification.setTitle(constant.pullSuccess(view.getRepositoryUrl())); - } - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notification.setStatus(FAIL); - if (getErrorCode(error.getCause()) == ErrorCodes.MERGE_CONFLICT) { - project.synchronize(); - } - handleError(error.getCause(), PULL_COMMAND_NAME); - } - }); + service.pull(project.getLocation(), getRefs(), view.getRepositoryName()) + .then(response -> { + GitOutputConsole console = gitOutputConsoleFactory.create(PULL_COMMAND_NAME); + console.print(response.getCommandOutput(), GREEN_COLOR); + consolesPanelPresenter.addCommandOutput(console); + notification.setStatus(SUCCESS); + if (response.getCommandOutput().contains("Already up-to-date")) { + notification.setTitle(constant.pullUpToDate()); + } else { + project.synchronize(); + notification.setTitle(constant.pullSuccess(view.getRepositoryUrl())); + } + }) + .catchError(error -> { + notification.setStatus(FAIL); + if (getErrorCode(error.getCause()) == ErrorCodes.MERGE_CONFLICT) { + project.synchronize(); + } + handleError(error.getCause(), PULL_COMMAND_NAME); + }); } /** @return list of refs to fetch */ diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/push/PushToRemotePresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/push/PushToRemotePresenter.java index bb0074f3a7..1b4819f6cb 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/push/PushToRemotePresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/push/PushToRemotePresenter.java @@ -15,12 +15,9 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.core.rest.shared.dto.ServiceError; -import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.BranchListMode; -import org.eclipse.che.api.git.shared.Remote; -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.ide.ext.git.client.GitServiceClient; +import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.notification.StatusNotification; @@ -30,7 +27,6 @@ import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.git.client.BranchFilterByRemote; import org.eclipse.che.ide.ext.git.client.BranchSearcher; import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant; -import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsole; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; @@ -38,7 +34,6 @@ import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; -import java.util.Map; import static java.util.Collections.singletonList; import static org.eclipse.che.api.git.shared.BranchListMode.LIST_LOCAL; @@ -106,26 +101,22 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { * If remote repositories are found, then get the list of branches (remote and local). */ void updateRemotes() { - service.remoteList(project.getLocation(), null, true).then(new Operation>() { - @Override - public void apply(List remotes) throws OperationException { - updateLocalBranches(); - view.setRepositories(remotes); - view.setEnablePushButton(!remotes.isEmpty()); - view.setSelectedForcePushCheckBox(false); - view.showDialog(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteListFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(REMOTE_REPO_COMMAND_NAME); - console.printError(errorMessage); - processesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.remoteListFailed(), FAIL, FLOAT_MODE); - view.setEnablePushButton(false); - } - }); + service.remoteList(project.getLocation(), null, true) + .then(remotes -> { + updateLocalBranches(); + view.setRepositories(remotes); + view.setEnablePushButton(!remotes.isEmpty()); + view.setSelectedForcePushCheckBox(false); + view.showDialog(); + }) + .catchError(error -> { + String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteListFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(REMOTE_REPO_COMMAND_NAME); + console.printError(errorMessage); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); + notificationManager.notify(constant.remoteListFailed(), FAIL, FLOAT_MODE); + view.setEnablePushButton(false); + }); } /** @@ -155,7 +146,7 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { String errorMessage = exception.getMessage() != null ? exception.getMessage() : constant.localBranchesListFailed(); GitOutputConsole console = gitOutputConsoleFactory.create(BRANCH_LIST_COMMAND_NAME); console.printError(errorMessage); - processesPanelPresenter.addCommandOutput(console); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); notificationManager.notify(constant.localBranchesListFailed(), FAIL, FLOAT_MODE); view.setEnablePushButton(false); } @@ -205,7 +196,7 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { public void onFailure(Throwable caught) { GitOutputConsole console = gitOutputConsoleFactory.create(CONFIG_COMMAND_NAME); console.printError(constant.failedGettingConfig()); - processesPanelPresenter.addCommandOutput(console); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); notificationManager.notify(constant.failedGettingConfig(), FAIL, FLOAT_MODE); } }); @@ -216,7 +207,7 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { String errorMessage = exception.getMessage() != null ? exception.getMessage() : constant.remoteBranchesListFailed(); GitOutputConsole console = gitOutputConsoleFactory.create(BRANCH_LIST_COMMAND_NAME); console.printError(errorMessage); - processesPanelPresenter.addCommandOutput(console); + processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console); notificationManager.notify(constant.remoteBranchesListFailed(), FAIL, FLOAT_MODE); view.setEnablePushButton(false); } @@ -233,28 +224,23 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { final String configBranchRemote = "branch." + view.getLocalBranch() + ".remote"; final String configUpstreamBranch = "branch." + view.getLocalBranch() + ".merge"; service.config(project.getLocation(), Arrays.asList(configUpstreamBranch, configBranchRemote)) - .then(new Operation>() { - @Override - public void apply(Map configs) throws OperationException { - if (configs.containsKey(configBranchRemote) && configs.containsKey(configUpstreamBranch)) { - String displayName = configs.get(configBranchRemote) + "/" + configs.get(configUpstreamBranch); - Branch upstream = dtoFactory.createDto(Branch.class) - .withActive(false) - .withRemote(true) - .withDisplayName(displayName) - .withName("refs/remotes/" + displayName); + .then(configs -> { + if (configs.containsKey(configBranchRemote) && configs.containsKey(configUpstreamBranch)) { + String displayName = configs.get(configBranchRemote) + "/" + configs.get(configUpstreamBranch); + Branch upstream = dtoFactory.createDto(Branch.class) + .withActive(false) + .withRemote(true) + .withDisplayName(displayName) + .withName("refs/remotes/" + displayName); - result.onSuccess(upstream); - } else { - result.onSuccess(null); - } + result.onSuccess(upstream); + } else { + result.onSuccess(null); } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - result.onFailure(error.getCause()); - } - }); + }) + .catchError(error -> { + result.onFailure(error.getCause()); + }); } /** @@ -264,17 +250,13 @@ public class PushToRemotePresenter implements PushToRemoteView.ActionDelegate { * is a remote mode */ void getBranchesForCurrentProject(@NotNull final BranchListMode remoteMode, final AsyncCallback> asyncResult) { - service.branchList(project.getLocation(), remoteMode).then(new Operation>() { - @Override - public void apply(List branches) throws OperationException { - asyncResult.onSuccess(branches); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - asyncResult.onFailure(error.getCause()); - } - }); + service.branchList(project.getLocation(), remoteMode) + .then(branches -> { + asyncResult.onSuccess(branches); + }) + .catchError(error -> { + asyncResult.onFailure(error.getCause()); + }); } /** {@inheritDoc} */ diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/RemotePresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/RemotePresenter.java index 921b5b2bc2..da4d122acb 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/RemotePresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/RemotePresenter.java @@ -15,9 +15,6 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.git.shared.Remote; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -29,7 +26,6 @@ import org.eclipse.che.ide.ext.git.client.remote.add.AddRemoteRepositoryPresente import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; import javax.validation.constraints.NotNull; -import java.util.List; import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE; import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL; @@ -89,22 +85,18 @@ public class RemotePresenter implements RemoteView.ActionDelegate { * then get the list of branches (remote and local). */ private void getRemotes() { - service.remoteList(project.getLocation(), null, true).then(new Operation>() { - @Override - public void apply(List remotes) throws OperationException { - view.setEnableDeleteButton(selectedRemote != null); - view.setRemotes(remotes); - if (!view.isShown()) { - view.showDialog(); - } - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteListFailed(); - handleError(errorMessage); - } - }); + service.remoteList(project.getLocation(), null, true) + .then(remotes -> { + view.setEnableDeleteButton(selectedRemote != null); + view.setRemotes(remotes); + if (!view.isShown()) { + view.showDialog(); + } + }) + .catchError(error -> { + String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteListFailed(); + handleError(errorMessage); + }); } /** @@ -149,23 +141,19 @@ public class RemotePresenter implements RemoteView.ActionDelegate { return; } - service.remoteDelete(project.getLocation(), selectedRemote.getName()).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - getRemotes(); + service.remoteDelete(project.getLocation(), selectedRemote.getName()) + .then(ignored -> { + getRemotes(); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteDeleteFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(REMOTE_REPO_COMMAND_NAME); - console.printError(errorMessage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.remoteDeleteFailed(), FAIL, FLOAT_MODE); - } - }); + project.synchronize(); + }) + .catchError(error -> { + String errorMessage = error.getMessage() != null ? error.getMessage() : constant.remoteDeleteFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(REMOTE_REPO_COMMAND_NAME); + console.printError(errorMessage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.remoteDeleteFailed(), FAIL, FLOAT_MODE); + }); } /** diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/add/AddRemoteRepositoryPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/add/AddRemoteRepositoryPresenter.java index 4553f97009..a4c8221a73 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/add/AddRemoteRepositoryPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remote/add/AddRemoteRepositoryPresenter.java @@ -14,9 +14,6 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; -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.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.resources.Project; @@ -67,18 +64,14 @@ public class AddRemoteRepositoryPresenter implements AddRemoteRepositoryView.Act final String url = view.getUrl().trim(); final Project project = appContext.getRootProject(); - service.remoteAdd(project.getLocation(), name, url).then(new Operation() { - @Override - public void apply(Void arg) throws OperationException { - callback.onSuccess(null); - view.close(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - callback.onFailure(error.getCause()); - } - }); + service.remoteAdd(project.getLocation(), name, url) + .then(arg -> { + callback.onSuccess(null); + view.close(); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } /** {@inheritDoc} */ diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remove/RemoveFromIndexPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remove/RemoveFromIndexPresenter.java index 5e30533dc5..f15a55b82d 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remove/RemoveFromIndexPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/remove/RemoveFromIndexPresenter.java @@ -12,9 +12,6 @@ package org.eclipse.che.ide.ext.git.client.remove; import com.google.inject.Inject; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -99,22 +96,18 @@ public class RemoveFromIndexPresenter implements RemoveFromIndexView.ActionDeleg checkState(!isNullOrEmpty(resources)); - service.remove(project.getLocation(), toRelativePaths(resources), view.isRemoved()).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.removeFilesSuccessfull()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.removeFilesSuccessfull()); + service.remove(project.getLocation(), toRelativePaths(resources), view.isRemoved()) + .then(ignored -> { + console.print(constant.removeFilesSuccessfull()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.removeFilesSuccessfull()); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - handleError(error.getCause(), console); - consolesPanelPresenter.addCommandOutput(console); - } - }); + project.synchronize(); + }) + .catchError(error -> { + handleError(error.getCause(), console); + consolesPanelPresenter.addCommandOutput(console); + }); view.close(); } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/commit/ResetToCommitPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/commit/ResetToCommitPresenter.java index 36935a2f27..57ed1d3397 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/commit/ResetToCommitPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/commit/ResetToCommitPresenter.java @@ -14,12 +14,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.core.ErrorCodes; -import org.eclipse.che.api.git.shared.LogResponse; import org.eclipse.che.api.git.shared.ResetRequest; import org.eclipse.che.api.git.shared.Revision; -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.ide.api.app.AppContext; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; @@ -82,32 +78,28 @@ public class ResetToCommitPresenter implements ResetToCommitView.ActionDelegate public void showDialog(final Project project) { this.project = project; - service.log(project.getLocation(), null, false).then(new Operation() { - @Override - public void apply(LogResponse log) throws OperationException { - view.setRevisions(log.getCommits()); - view.setMixMode(true); - view.setEnableResetButton(selectedRevision != null); - view.showDialog(); + service.log(project.getLocation(), null, -1, -1, false) + .then(log -> { + view.setRevisions(log.getCommits()); + view.setMixMode(true); + view.setEnableResetButton(selectedRevision != null); + view.showDialog(); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { - dialogFactory.createMessageDialog(constant.resetCommitViewTitle(), - constant.initCommitWasNotPerformed(), - null).show(); - return; - } - String errorMessage = (error.getMessage() != null) ? error.getMessage() : constant.logFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(LOG_COMMAND_NAME); - console.printError(errorMessage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.logFailed(), FAIL, FLOAT_MODE); - } - }); + project.synchronize(); + }) + .catchError(error -> { + if (getErrorCode(error.getCause()) == ErrorCodes.INIT_COMMIT_WAS_NOT_PERFORMED) { + dialogFactory.createMessageDialog(constant.resetCommitViewTitle(), + constant.initCommitWasNotPerformed(), + null).show(); + return; + } + String errorMessage = (error.getMessage() != null) ? error.getMessage() : constant.logFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(LOG_COMMAND_NAME); + console.printError(errorMessage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.logFailed(), FAIL, FLOAT_MODE); + }); } /** @@ -146,24 +138,20 @@ public class ResetToCommitPresenter implements ResetToCommitView.ActionDelegate final GitOutputConsole console = gitOutputConsoleFactory.create(RESET_COMMAND_NAME); - service.reset(project.getLocation(), selectedRevision.getId(), type, null).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.resetSuccessfully()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.resetSuccessfully()); + service.reset(project.getLocation(), selectedRevision.getId(), type, null) + .then(ignored -> { + console.print(constant.resetSuccessfully()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.resetSuccessfully()); - project.synchronize(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMessage = (error.getMessage() != null) ? error.getMessage() : constant.resetFail(); - console.printError(errorMessage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.resetFail(), FAIL, FLOAT_MODE); - } - }); + project.synchronize(); + }) + .catchError(error -> { + String errorMessage = (error.getMessage() != null) ? error.getMessage() : constant.resetFail(); + console.printError(errorMessage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.resetFail(), FAIL, FLOAT_MODE); + }); } } diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/files/ResetFilesPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/files/ResetFilesPresenter.java index 1237f60eb8..71ee8a2a4e 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/files/ResetFilesPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/reset/files/ResetFilesPresenter.java @@ -14,10 +14,6 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.git.shared.IndexFile; -import org.eclipse.che.api.git.shared.Status; -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.ide.api.app.AppContext; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.ext.git.client.GitServiceClient; @@ -90,58 +86,54 @@ public class ResetFilesPresenter implements ResetFilesView.ActionDelegate { public void showDialog(Project project) { this.project = project; - service.getStatus(project.getLocation()).then(new Operation() { - @Override - public void apply(Status status) throws OperationException { - if (status.isClean()) { - dialogFactory.createMessageDialog(constant.messagesWarningTitle(), constant.indexIsEmpty(), null).show(); - return; - } + service.getStatus(project.getLocation()) + .then(status -> { + if (status.isClean()) { + dialogFactory.createMessageDialog(constant.messagesWarningTitle(), constant.indexIsEmpty(), null).show(); + return; + } - indexedFiles = new IndexFile[0]; + indexedFiles = new IndexFile[0]; - for (String path : status.getAdded()) { - indexedFiles = add(indexedFiles, wrap(path)); - } + for (String path : status.getAdded()) { + indexedFiles = add(indexedFiles, wrap(path)); + } - for (String path : status.getChanged()) { - indexedFiles = add(indexedFiles, wrap(path)); - } + for (String path : status.getChanged()) { + indexedFiles = add(indexedFiles, wrap(path)); + } - for (String path : status.getRemoved()) { - indexedFiles = add(indexedFiles, wrap(path)); - } + for (String path : status.getRemoved()) { + indexedFiles = add(indexedFiles, wrap(path)); + } - if (indexedFiles.length == 0) { - dialogFactory.createMessageDialog(constant.messagesWarningTitle(), constant.indexIsEmpty(), null).show(); - return; - } + if (indexedFiles.length == 0) { + dialogFactory.createMessageDialog(constant.messagesWarningTitle(), constant.indexIsEmpty(), null).show(); + return; + } - //Mark selected items to reset from index - Resource[] resources = appContext.getResources(); - if (resources != null) { - for (Resource selectedItem : resources) { - String selectedItemPath = selectedItem.getLocation().removeFirstSegments(1).toString(); - for (IndexFile file : indexedFiles) - if (file.getPath().startsWith(selectedItemPath)) { - file.setIndexed(false); - } - } - } + //Mark selected items to reset from index + Resource[] resources = appContext.getResources(); + if (resources != null) { + for (Resource selectedItem : resources) { + String selectedItemPath = selectedItem.getLocation().removeFirstSegments(1).toString(); + for (IndexFile file : indexedFiles) + if (file.getPath().startsWith(selectedItemPath)) { + file.setIndexed(false); + } + } + } - view.setIndexedFiles(indexedFiles); - view.showDialog(); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMassage = error.getMessage() != null ? error.getMessage() : constant.statusFailed(); - GitOutputConsole console = gitOutputConsoleFactory.create(STATUS_COMMAND_NAME); - console.printError(errorMassage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(errorMassage); - } - }); + view.setIndexedFiles(indexedFiles); + view.showDialog(); + }) + .catchError(error -> { + String errorMassage = error.getMessage() != null ? error.getMessage() : constant.statusFailed(); + GitOutputConsole console = gitOutputConsoleFactory.create(STATUS_COMMAND_NAME); + console.printError(errorMassage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(errorMassage); + }); } protected IndexFile wrap(String path) { @@ -169,22 +161,18 @@ public class ResetFilesPresenter implements ResetFilesView.ActionDelegate { } view.close(); - service.reset(project.getLocation(), "HEAD", ResetType.MIXED, paths).then(new Operation() { - @Override - public void apply(Void ignored) throws OperationException { - console.print(constant.resetFilesSuccessfully()); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(constant.resetFilesSuccessfully()); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - String errorMassage = error.getMessage() != null ? error.getMessage() : constant.resetFilesFailed(); - console.printError(errorMassage); - consolesPanelPresenter.addCommandOutput(console); - notificationManager.notify(errorMassage); - } - }); + service.reset(project.getLocation(), "HEAD", ResetType.MIXED, paths) + .then(ignored -> { + console.print(constant.resetFilesSuccessfully()); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(constant.resetFilesSuccessfully()); + }) + .catchError(error -> { + String errorMassage = error.getMessage() != null ? error.getMessage() : constant.resetFilesFailed(); + console.printError(errorMassage); + consolesPanelPresenter.addCommandOutput(console); + notificationManager.notify(errorMassage); + }); } /** {@inheritDoc} */ diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenter.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenter.java index b6c83c392a..2fe317d806 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenter.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenter.java @@ -13,10 +13,6 @@ package org.eclipse.che.ide.ext.git.client.status; import com.google.inject.Inject; import com.google.inject.Singleton; -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.ide.api.app.AppContext; import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.Project; @@ -44,7 +40,6 @@ public class StatusCommandPresenter { public static final String STATUS_COMMAND_NAME = "Git status"; private final GitServiceClient service; - private final AppContext appContext; private final GitOutputConsoleFactory gitOutputConsoleFactory; private final ProcessesPanelPresenter consolesPanelPresenter; private final GitLocalizationConstant constant; @@ -55,13 +50,11 @@ public class StatusCommandPresenter { */ @Inject public StatusCommandPresenter(GitServiceClient service, - AppContext appContext, GitOutputConsoleFactory gitOutputConsoleFactory, ProcessesPanelPresenter processesPanelPresenter, GitLocalizationConstant constant, NotificationManager notificationManager) { this.service = service; - this.appContext = appContext; this.gitOutputConsoleFactory = gitOutputConsoleFactory; this.consolesPanelPresenter = processesPanelPresenter; this.constant = constant; @@ -70,17 +63,13 @@ public class StatusCommandPresenter { /** Show status. */ public void showStatus(Project project) { - service.statusText(project.getLocation(), LONG).then(new Operation() { - @Override - public void apply(String status) throws OperationException { - printGitStatus(status); - } - }).catchError(new Operation() { - @Override - public void apply(PromiseError error) throws OperationException { - notificationManager.notify(constant.statusFailed(), FAIL, FLOAT_MODE); - } - }); + service.statusText(project.getLocation(), LONG) + .then(status -> { + printGitStatus(status); + }) + .catchError(error -> { + notificationManager.notify(constant.statusFailed(), FAIL, FLOAT_MODE); + }); } /** diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenterTest.java b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenterTest.java index 42767bc8d2..5e09f2eea9 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenterTest.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/branch/BranchPresenterTest.java @@ -14,7 +14,6 @@ import org.eclipse.che.api.git.shared.Branch; import org.eclipse.che.api.git.shared.CheckoutRequest; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; -import org.eclipse.che.ide.api.parts.WorkspaceAgent; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.ext.git.client.BaseTest; import org.eclipse.che.ide.api.dialogs.ConfirmCallback; @@ -69,8 +68,6 @@ public class BranchPresenterTest extends BaseTest { @Mock private Branch selectedBranch; @Mock - private WorkspaceAgent workspaceAgent; - @Mock private DialogFactory dialogFactory; @Mock private DtoFactory dtoFactory; @@ -224,9 +221,9 @@ public class BranchPresenterTest extends BaseTest { @Test public void testOnCheckoutClickedWhenSelectedNotRemoteBranch() throws Exception { - when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(voidPromise); - when(voidPromise.then(any(Operation.class))).thenReturn(voidPromise); - when(voidPromise.catchError(any(Operation.class))).thenReturn(voidPromise); + when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(stringPromise); + when(stringPromise.then(any(Operation.class))).thenReturn(stringPromise); + when(stringPromise.catchError(any(Operation.class))).thenReturn(stringPromise); when(selectedBranch.isRemote()).thenReturn(false); when(dtoFactory.createDto(CheckoutRequest.class)).thenReturn(checkoutRequest); @@ -234,8 +231,8 @@ public class BranchPresenterTest extends BaseTest { selectBranch(); presenter.onCheckoutClicked(); - verify(voidPromise).then(voidPromiseCaptor.capture()); - voidPromiseCaptor.getValue().apply(null); + verify(stringPromise).then(stringCaptor.capture()); + stringCaptor.getValue().apply(null); verify(checkoutRequest).setName(eq(BRANCH_NAME)); verifyNoMoreInteractions(checkoutRequest); @@ -243,17 +240,17 @@ public class BranchPresenterTest extends BaseTest { @Test public void testOnCheckoutClickedWhenSelectedRemoteBranch() throws Exception { - when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(voidPromise); - when(voidPromise.then(any(Operation.class))).thenReturn(voidPromise); - when(voidPromise.catchError(any(Operation.class))).thenReturn(voidPromise); + when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(stringPromise); + when(stringPromise.then(any(Operation.class))).thenReturn(stringPromise); + when(stringPromise.catchError(any(Operation.class))).thenReturn(stringPromise); when(dtoFactory.createDto(CheckoutRequest.class)).thenReturn(checkoutRequest); selectBranch(); presenter.onCheckoutClicked(); - verify(voidPromise).then(voidPromiseCaptor.capture()); - voidPromiseCaptor.getValue().apply(null); + verify(stringPromise).then(stringCaptor.capture()); + stringCaptor.getValue().apply(null); verify(checkoutRequest).setTrackBranch(eq(BRANCH_NAME)); verifyNoMoreInteractions(checkoutRequest); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferenceTest.java b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferenceTest.java index 22ae9823f1..81f736a8f1 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferenceTest.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/checkout/CheckoutReferenceTest.java @@ -102,20 +102,21 @@ public class CheckoutReferenceTest extends BaseTest { @Test public void onEnterClickedWhenValueIsCorrect() throws Exception { + when(appContext.getRootProject()).thenReturn(project); when(dtoFactory.createDto(CheckoutRequest.class)).thenReturn(checkoutRequest); when(checkoutRequest.withName(anyString())).thenReturn(checkoutRequest); when(checkoutRequest.withCreateNew(anyBoolean())).thenReturn(checkoutRequest); reset(service); - when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(voidPromise); - when(voidPromise.then(any(Operation.class))).thenReturn(voidPromise); - when(voidPromise.catchError(any(Operation.class))).thenReturn(voidPromise); + when(service.checkout(any(Path.class), any(CheckoutRequest.class))).thenReturn(stringPromise); + when(stringPromise.then(any(Operation.class))).thenReturn(stringPromise); + when(stringPromise.catchError(any(Operation.class))).thenReturn(stringPromise); when(view.getReference()).thenReturn(CORRECT_REFERENCE); presenter.showDialog(project); presenter.onEnterClicked(); - verify(voidPromise).then(voidPromiseCaptor.capture()); - voidPromiseCaptor.getValue().apply(null); + verify(stringPromise).then(stringCaptor.capture()); + stringCaptor.getValue().apply(null); verify(synchronizePromise).then(synchronizeCaptor.capture()); synchronizeCaptor.getValue().apply(new Resource[0]); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenterTest.java b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenterTest.java index b6f70d2eec..ea8525a329 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenterTest.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/commit/CommitPresenterTest.java @@ -98,8 +98,6 @@ public class CommitPresenterTest extends BaseTest { when(voidPromise.catchError(any(Operation.class))).thenReturn(voidPromise); when(revisionPromise.then(any(Operation.class))).thenReturn(revisionPromise); when(revisionPromise.catchError(any(Operation.class))).thenReturn(revisionPromise); - when(service.add(any(Path.class), anyBoolean(), any(Path[].class))).thenReturn(voidPromise); - when(service.commit(any(Path.class), anyString(), anyBoolean(), any(Path[].class), anyBoolean())).thenReturn(revisionPromise); when(stringPromise.then(any(Operation.class))).thenReturn(stringPromise); when(stringPromise.catchError(any(Operation.class))).thenReturn(stringPromise); when(branchListPromise.then(any(Operation.class))).thenReturn(branchListPromise); @@ -109,7 +107,7 @@ public class CommitPresenterTest extends BaseTest { when(logPromise.catchError(any(Operation.class))).thenReturn(logPromise); when(statusPromise.then(any(Operation.class))).thenReturn(statusPromise); when(service.add(any(Path.class), anyBoolean(), any(Path[].class))).thenReturn(voidPromise); - when(service.commit(any(Path.class), anyString(), anyBoolean(), any(Path[].class), anyBoolean())) + when(service.commit(any(Path.class), anyString(), anyBoolean(), any(Path[].class))) .thenReturn(revisionPromise); when(service.diff(any(Path.class), eq(null), diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenterTest.java b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenterTest.java index 3ae506b4fc..82c693fc14 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenterTest.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/history/HistoryPresenterTest.java @@ -68,15 +68,13 @@ public class HistoryPresenterTest extends BaseTest { when(appContext.getResource()).thenReturn(resource); when(appContext.getRootProject()).thenReturn(project); - when(service.log( - any(Path.class), + when(service.log(any(Path.class), any(Path[].class), anyInt(), anyInt(), anyBoolean())) .thenReturn(logPromise); - when(service.diff( - any(Path.class), + when(service.diff(any(Path.class), anyList(), any(DiffType.class), anyBoolean(), @@ -84,8 +82,7 @@ public class HistoryPresenterTest extends BaseTest { anyString(), anyString())) .thenReturn(stringPromise); - when(service.showFileContent( - any(Path.class), + when(service.showFileContent(any(Path.class), any(Path.class), anyString())) .thenReturn(showPromise); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenterTest.java b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenterTest.java index 8a7cdb00ab..1d54ab71fd 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenterTest.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/test/java/org/eclipse/che/ide/ext/git/client/status/StatusCommandPresenterTest.java @@ -13,7 +13,6 @@ package org.eclipse.che.ide.ext.git.client.status; import org.eclipse.che.api.git.shared.StatusFormat; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.ide.api.notification.StatusNotification; -import org.eclipse.che.ide.api.parts.WorkspaceAgent; import org.eclipse.che.ide.ext.git.client.BaseTest; import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; @@ -39,9 +38,6 @@ public class StatusCommandPresenterTest extends BaseTest { @InjectMocks private StatusCommandPresenter presenter; - @Mock - private WorkspaceAgent workspaceAgent; - @Mock private GitOutputConsoleFactory gitOutputConsoleFactory; @@ -53,7 +49,6 @@ public class StatusCommandPresenterTest extends BaseTest { super.disarm(); presenter = new StatusCommandPresenter(service, - appContext, gitOutputConsoleFactory, processesPanelPresenter, constant, diff --git a/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/refactoring/RefactoringUpdater.java b/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/refactoring/RefactoringUpdater.java index ec36e265a0..a31fd3c422 100644 --- a/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/refactoring/RefactoringUpdater.java +++ b/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/refactoring/RefactoringUpdater.java @@ -26,7 +26,6 @@ import org.eclipse.che.ide.api.event.ng.DeletedFilesController; import org.eclipse.che.ide.api.parts.PartPresenter; import org.eclipse.che.ide.api.parts.WorkspaceAgent; import org.eclipse.che.ide.api.resources.ExternalResourceDelta; -import org.eclipse.che.ide.api.resources.ModificationTracker; import org.eclipse.che.ide.api.resources.ResourceDelta; import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangeInfo; @@ -167,12 +166,6 @@ public class RefactoringUpdater { private void updateFileContent(VirtualFile virtualFile) { String path = virtualFile.getLocation().toString(); - if (virtualFile instanceof ModificationTracker) { - String modificationStamp = ((ModificationTracker)virtualFile).getModificationStamp(); - eventBus.fireEvent(new FileContentUpdateEvent(path, modificationStamp)); - return; - } - eventBus.fireEvent(new FileContentUpdateEvent(path)); } diff --git a/plugins/plugin-maven/che-plugin-maven-generator-archetype/pom.xml b/plugins/plugin-maven/che-plugin-maven-generator-archetype/pom.xml index 0d149dee4d..babf7bf85b 100644 --- a/plugins/plugin-maven/che-plugin-maven-generator-archetype/pom.xml +++ b/plugins/plugin-maven/che-plugin-maven-generator-archetype/pom.xml @@ -30,6 +30,18 @@ com.google.code.gson gson + + com.google.guava + guava + + + com.google.inject + guice + + + javax.annotation + javax.annotation-api + javax.inject javax.inject diff --git a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGenerator.java b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGenerator.java index 106dc3bb4c..70aaa92fba 100644 --- a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGenerator.java +++ b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGenerator.java @@ -11,13 +11,13 @@ package org.eclipse.che.plugin.maven.generator.archetype; import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.util.AbstractLineConsumer; import org.eclipse.che.api.core.util.CommandLine; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.core.util.ProcessUtil; import org.eclipse.che.api.core.util.ValueHolder; import org.eclipse.che.api.core.util.Watchdog; -import org.eclipse.che.api.core.util.WebsocketMessageConsumer; import org.eclipse.che.ide.maven.tools.MavenArtifact; import org.eclipse.che.ide.maven.tools.MavenUtils; import org.eclipse.che.plugin.maven.shared.MavenArchetype; @@ -25,6 +25,7 @@ import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; import javax.inject.Singleton; import javax.validation.constraints.NotNull; import java.io.File; @@ -35,7 +36,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_NAME; +import static org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput.State.DONE; +import static org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput.State.ERROR; /** * Generates projects with maven-archetype-plugin. @@ -46,6 +48,13 @@ import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYP public class ArchetypeGenerator { private static final Logger LOG = LoggerFactory.getLogger(ArchetypeGenerator.class); + private EventService eventService; + + @Inject + public ArchetypeGenerator(EventService eventService) { + this.eventService = eventService; + } + /** * Generates a new project from the specified archetype by given maven artifact descriptor. * @param workDir @@ -100,12 +109,13 @@ public class ArchetypeGenerator { */ private void execute(String[] commandLine, File workDir) throws TimeoutException, IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder(commandLine).redirectErrorStream(true).directory(workDir); - WebsocketMessageConsumer websocketMessageConsumer = new WebsocketMessageConsumer(MAVEN_ARCHETYPE_CHANEL_NAME); - websocketMessageConsumer.consume(new ArchetypeOutputImpl("Start Project generation", ArchetypeOutput.State.START)); + + eventService.publish(new ArchetypeOutputImpl("Start Project generation", ArchetypeOutput.State.START)); + LineConsumer lineConsumer = new AbstractLineConsumer() { @Override public void writeLine(String line) throws IOException { - websocketMessageConsumer.consume(new ArchetypeOutputImpl(line, ArchetypeOutput.State.IN_PROGRESS)); + eventService.publish(new ArchetypeOutputImpl(line, ArchetypeOutput.State.IN_PROGRESS)); } }; @@ -122,14 +132,14 @@ public class ArchetypeGenerator { // consume logs until process ends ProcessUtil.process(process, lineConsumer); process.waitFor(); - websocketMessageConsumer.consume(new ArchetypeOutputImpl("Done", ArchetypeOutput.State.DONE)); + eventService.publish(new ArchetypeOutputImpl("Done", DONE)); if (isTimeoutExceeded.get()) { LOG.error("Generation project time expired : command-line " + Arrays.toString(commandLine)); - websocketMessageConsumer.consume(new ArchetypeOutputImpl("Generation project time expired", ArchetypeOutput.State.ERROR)); + eventService.publish(new ArchetypeOutputImpl("Generation project time expired", ERROR)); throw new TimeoutException(); } else if (process.exitValue() != 0) { LOG.error("Generation project fail : command-line " + Arrays.toString(commandLine)); - websocketMessageConsumer.consume(new ArchetypeOutputImpl("Generation project occurs error", ArchetypeOutput.State.ERROR)); + eventService.publish(new ArchetypeOutputImpl("Generation project occurs error", ERROR)); throw new IOException("Process failed. Exit code " + process.exitValue() + " command-line : " + Arrays.toString(commandLine)); } } finally { diff --git a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeOutputImpl.java b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeOutputImpl.java index 66b92bf0d3..5414cb5268 100644 --- a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeOutputImpl.java +++ b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeOutputImpl.java @@ -13,14 +13,12 @@ package org.eclipse.che.plugin.maven.generator.archetype; import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput; /** + * Describes information about output object of maven archetype project generation. * * @author Vitalii Parfonov */ public class ArchetypeOutputImpl implements ArchetypeOutput { - - private String output; - private State state; public ArchetypeOutputImpl(String output, State state) { @@ -37,4 +35,14 @@ public class ArchetypeOutputImpl implements ArchetypeOutput { public State getState() { return state; } + + @Override + public void setOutput(String output) { + this.output = output; + } + + @Override + public void setState(State state) { + this.state = state; + } } diff --git a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/MavenArchetypeJsonRpcMessenger.java b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/MavenArchetypeJsonRpcMessenger.java new file mode 100644 index 0000000000..af3cd9f87d --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/main/java/org/eclipse/che/plugin/maven/generator/archetype/MavenArchetypeJsonRpcMessenger.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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.plugin.maven.generator.archetype; + +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.dto.server.DtoFactory; +import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import java.util.Set; + +import static com.google.common.collect.Sets.newConcurrentHashSet; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_OUTPUT; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_SUBSCRIBE; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_UNSUBSCRIBE; + +/** + * Mechanism which sends events of maven archetype generation by using JSON RPC to the client. + */ +@Singleton +public class MavenArchetypeJsonRpcMessenger implements EventSubscriber { + private final Set endpointIds = newConcurrentHashSet(); + private EventService eventService; + private RequestTransmitter transmitter; + + @Inject + public MavenArchetypeJsonRpcMessenger(EventService eventService, RequestTransmitter transmitter) { + this.eventService = eventService; + this.transmitter = transmitter; + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this); + } + + @PreDestroy + private void unsubscribe() { + eventService.unsubscribe(this); + } + + @Inject + private void configureHandlers(RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName(MAVEN_ARCHETYPE_CHANEL_SUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::add); + + configurator.newConfiguration() + .methodName(MAVEN_ARCHETYPE_CHANEL_UNSUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::remove); + } + + @Override + public void onEvent(ArchetypeOutput event) { + ArchetypeOutput archetypeOutput = DtoFactory.newDto(ArchetypeOutput.class); + archetypeOutput.setOutput(event.getOutput()); + archetypeOutput.setState(event.getState()); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_ARCHETYPE_CHANEL_OUTPUT) + .paramsAsDto(archetypeOutput) + .sendAndSkipResult()); + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/test/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGeneratorTest.java b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/test/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGeneratorTest.java index 5182b1348c..51420035a3 100644 --- a/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/test/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGeneratorTest.java +++ b/plugins/plugin-maven/che-plugin-maven-generator-archetype/src/test/java/org/eclipse/che/plugin/maven/generator/archetype/ArchetypeGeneratorTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.che.plugin.maven.generator.archetype; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.commons.lang.NameGenerator; import org.eclipse.che.ide.maven.tools.MavenArtifact; import org.eclipse.che.plugin.maven.shared.MavenArchetype; @@ -26,6 +27,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** + * Test for {@link ArchetypeGenerator} * * @author Vitalii Parfonov */ @@ -36,11 +38,12 @@ public class ArchetypeGeneratorTest { @Ignore public void generateFromArchetype() throws Exception { MavenArchetype mavenArchetype = mock(MavenArchetype.class); + EventService eventService = mock(EventService.class); when(mavenArchetype.getArtifactId()).thenReturn("tomee-webapp-archetype"); when(mavenArchetype.getGroupId()).thenReturn("org.apache.openejb.maven"); when(mavenArchetype.getVersion()).thenReturn("1.7.1"); File workDir = Files.createTempDirectory("workDir").toFile(); - ArchetypeGenerator archetypeGenerator = new ArchetypeGenerator(); + ArchetypeGenerator archetypeGenerator = new ArchetypeGenerator(eventService); String artifactId = NameGenerator.generate("artifactId", 5); String groupId = NameGenerator.generate("groupId", 5); MavenArtifact mavenArtifact = new MavenArtifact(); diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenJsonRpcHandler.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenJsonRpcHandler.java new file mode 100644 index 0000000000..62530277c0 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenJsonRpcHandler.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * 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.plugin.maven.client; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput; +import org.eclipse.che.plugin.maven.shared.dto.PercentMessageDto; +import org.eclipse.che.plugin.maven.shared.dto.PercentUndefinedMessageDto; +import org.eclipse.che.plugin.maven.shared.dto.ProjectsUpdateMessage; +import org.eclipse.che.plugin.maven.shared.dto.StartStopNotification; +import org.eclipse.che.plugin.maven.shared.dto.TextMessageDto; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_OUTPUT; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_SUBSCRIBE; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_SUBSCRIBE; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_PERCENT_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_PERCENT_UNDEFINED_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_START_STOP_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_TEXT_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_UPDATE_METHOD; + +/** + * A mechanism for handling all messages from the Maven server and applying registered consumers. + */ +@Singleton +public class MavenJsonRpcHandler { + private static final String WS_AGENT_ENDPOINT = "ws-agent"; + + private RequestHandlerConfigurator configurator; + + private Set> textConsumers = new HashSet<>(); + private Set> startStopConsumers = new HashSet<>(); + private Set> percentUndefinedConsumers = new HashSet<>(); + private Set> percentConsumers = new HashSet<>(); + private Set> projectsUpdateConsumers = new HashSet<>(); + private Set> archetypeOutputConsumers = new HashSet<>(); + + private boolean isSubscribed = false; + + @Inject + public MavenJsonRpcHandler(RequestHandlerConfigurator configurator) { + this.configurator = configurator; + + handleMavenServerMessages(); + } + + @Inject + private void subscribe(RequestTransmitter requestTransmitter) { + if (isSubscribed) { + return; + } + + requestTransmitter.newRequest() + .endpointId(WS_AGENT_ENDPOINT) + .methodName(MAVEN_CHANEL_SUBSCRIBE) + .noParams() + .sendAndSkipResult(); + + requestTransmitter.newRequest() + .endpointId(WS_AGENT_ENDPOINT) + .methodName(MAVEN_ARCHETYPE_CHANEL_SUBSCRIBE) + .noParams() + .sendAndSkipResult(); + + isSubscribed = true; + } + + /** + * Adds consumer for the event with {@link TextMessageDto}. + * + * @param consumer + * new consumer + */ + public void addTextHandler(Consumer consumer) { + textConsumers.add(consumer); + } + + /** + * Adds consumer for the event with {@link StartStopNotification}. + * + * @param consumer + * new consumer + */ + public void addStartStopHandler(Consumer consumer) { + startStopConsumers.add(consumer); + } + + /** + * Adds consumer for the event with {@link PercentUndefinedMessageDto}. + * + * @param consumer + * new consumer + */ + public void addPercentUndefinedHandler(Consumer consumer) { + percentUndefinedConsumers.add(consumer); + } + + /** + * Adds consumer for the event with {@link PercentMessageDto}. + * + * @param consumer + * new consumer + */ + public void addPercentHandler(Consumer consumer) { + percentConsumers.add(consumer); + } + + /** + * Adds consumer for the event with {@link ProjectsUpdateMessage}. + * + * @param consumer + * new consumer + */ + public void addProjectsUpdateHandler(Consumer consumer) { + projectsUpdateConsumers.add(consumer); + } + + /** + * Adds consumer for the event with {@link ArchetypeOutput}. + * + * @param consumer + * new consumer + */ + public void addArchetypeOutputHandler(Consumer consumer) { + archetypeOutputConsumers.add(consumer); + } + + private void handleMavenServerMessages() { + configurator.newConfiguration() + .methodName(MAVEN_OUTPUT_TEXT_METHOD) + .paramsAsDto(TextMessageDto.class) + .noResult() + .withConsumer(textNotification -> textConsumers.forEach(it -> it.accept(textNotification))); + + configurator.newConfiguration() + .methodName(MAVEN_OUTPUT_PERCENT_UNDEFINED_METHOD) + .paramsAsDto(PercentUndefinedMessageDto.class) + .noResult() + .withConsumer(percentUndefinedMessage -> percentUndefinedConsumers.forEach(it -> it.accept(percentUndefinedMessage))); + + configurator.newConfiguration() + .methodName(MAVEN_OUTPUT_PERCENT_METHOD) + .paramsAsDto(PercentMessageDto.class) + .noResult() + .withConsumer(percentMessage -> percentConsumers.forEach(it -> it.accept(percentMessage))); + + configurator.newConfiguration() + .methodName(MAVEN_OUTPUT_START_STOP_METHOD) + .paramsAsDto(StartStopNotification.class) + .noResult() + .withConsumer(startStopNotification -> startStopConsumers.forEach(it -> it.accept(startStopNotification))); + + configurator.newConfiguration() + .methodName(MAVEN_OUTPUT_UPDATE_METHOD) + .paramsAsDto(ProjectsUpdateMessage.class) + .noResult() + .withConsumer(projectsUpdateMessage -> projectsUpdateConsumers.forEach(it -> it.accept(projectsUpdateMessage))); + + configurator.newConfiguration() + .methodName(MAVEN_ARCHETYPE_CHANEL_OUTPUT) + .paramsAsDto(ArchetypeOutput.class) + .noResult() + .withConsumer(archetypeOutput -> archetypeOutputConsumers.forEach(it -> it.accept(archetypeOutput))); + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandler.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandler.java index c5ca864689..1182f88982 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandler.java +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandler.java @@ -10,41 +10,27 @@ *******************************************************************************/ package org.eclipse.che.plugin.maven.client.comunnication; -import elemental.json.Json; -import elemental.json.JsonObject; - import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.machine.WsAgentStateController; -import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; -import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent; import org.eclipse.che.ide.console.CommandConsoleFactory; import org.eclipse.che.ide.console.DefaultOutputConsole; -import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; -import org.eclipse.che.ide.util.loging.Log; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.events.MessageHandler; +import org.eclipse.che.plugin.maven.client.MavenJsonRpcHandler; import org.eclipse.che.plugin.maven.client.comunnication.progressor.background.BackgroundLoaderPresenter; -import org.eclipse.che.plugin.maven.shared.MessageType; import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput; -import org.eclipse.che.plugin.maven.shared.dto.NotificationMessage; +import org.eclipse.che.plugin.maven.shared.dto.PercentMessageDto; import org.eclipse.che.plugin.maven.shared.dto.ProjectsUpdateMessage; import org.eclipse.che.plugin.maven.shared.dto.StartStopNotification; +import org.eclipse.che.plugin.maven.shared.dto.TextMessageDto; import java.util.List; import java.util.Set; import static java.util.stream.Collectors.toSet; -import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_NAME; -import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_NAME; /** * Handler which receives messages from the maven server. @@ -54,127 +40,70 @@ import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_N */ @Singleton public class MavenMessagesHandler { - private final EventBus eventBus; - private final DtoFactory factory; private final BackgroundLoaderPresenter dependencyResolver; private final PomEditorReconciler pomEditorReconciler; private final ProcessesPanelPresenter processesPanelPresenter; - private final CommandConsoleFactory commandConsoleFactory; private final AppContext appContext; + private DefaultOutputConsole outputConsole; + @Inject public MavenMessagesHandler(EventBus eventBus, - DtoFactory factory, + MavenJsonRpcHandler mavenJsonRpcHandler, BackgroundLoaderPresenter dependencyResolver, PomEditorReconciler pomEditorReconciler, - WsAgentStateController wsAgentStateController, ProcessesPanelPresenter processesPanelPresenter, CommandConsoleFactory commandConsoleFactory, AppContext appContext) { this.eventBus = eventBus; - this.factory = factory; - this.dependencyResolver = dependencyResolver; this.pomEditorReconciler = pomEditorReconciler; this.processesPanelPresenter = processesPanelPresenter; - this.commandConsoleFactory = commandConsoleFactory; this.appContext = appContext; - handleOperations(wsAgentStateController); + mavenJsonRpcHandler.addTextHandler(this::handleTextNotification); + mavenJsonRpcHandler.addStartStopHandler(this::handleStartStop); + mavenJsonRpcHandler.addPercentHandler(this::handlePercentNotification); + mavenJsonRpcHandler.addProjectsUpdateHandler(this::handleUpdate); + mavenJsonRpcHandler.addArchetypeOutputHandler(this::onMavenArchetypeReceive); + + handleOperations(); + outputConsole = (DefaultOutputConsole)commandConsoleFactory.create("Maven Archetype"); } - private void handleOperations(final WsAgentStateController wsAgentStateController) { - eventBus.addHandler(WsAgentStateEvent.TYPE, new WsAgentStateHandler() { - @Override - public void onWsAgentStarted(WsAgentStateEvent event) { - wsAgentStateController.getMessageBus().then(new Operation() { - @Override - public void apply(MessageBus messageBus) throws OperationException { - try { - handleMavenServerEvents(messageBus); - handleMavenArchetype(messageBus); - } catch (WebSocketException e) { - dependencyResolver.hide(); - Log.error(getClass(), e); - } - } - }); - } - - @Override - public void onWsAgentStopped(WsAgentStateEvent event) { - dependencyResolver.hide(); - } - }); - + private void handleOperations() { eventBus.addHandler(WorkspaceStoppedEvent.TYPE, event -> dependencyResolver.hide()); } - - private void handleMavenServerEvents(final MessageBus messageBus) throws WebSocketException { - messageBus.subscribe(MAVEN_CHANEL_NAME, new MessageHandler() { - @Override - public void onMessage(String message) { - final JsonObject jsonObject = Json.parse(message); - final int type = (int)jsonObject.getNumber("$type"); - MessageType messageType = MessageType.valueOf(type); - switch (messageType) { - case NOTIFICATION: - NotificationMessage dto = factory.createDtoFromJson(message, NotificationMessage.class); - handleNotification(dto); - break; - - case UPDATE: - handleUpdate(factory.createDtoFromJson(message, ProjectsUpdateMessage.class)); - break; - - case START_STOP: - handleStartStop(factory.createDtoFromJson(message, StartStopNotification.class)); - break; - - default: - Log.error(getClass(), "Unknown message type:" + messageType); - } - } - }); + /** + * Updates progress bar when the percent of project resolving is changed. + * + * @param percentMessageDto + * object with value of percent + */ + protected void handlePercentNotification(PercentMessageDto percentMessageDto) { + dependencyResolver.updateProgressBar((int)(percentMessageDto.getPercent() * 100)); } - private void handleMavenArchetype(final MessageBus messageBus) { - final DefaultOutputConsole outputConsole = (DefaultOutputConsole)commandConsoleFactory.create("Maven Archetype"); - - try { - messageBus.subscribe(MAVEN_ARCHETYPE_CHANEL_NAME, new MessageHandler() { - @Override - public void onMessage(String message) { - Log.info(getClass(), message); - ArchetypeOutput archetypeOutput = factory.createDtoFromJson(message, ArchetypeOutput.class); - processesPanelPresenter.addCommandOutput(outputConsole); - switch (archetypeOutput.getState()) { - case START: - outputConsole.clearOutputsButtonClicked(); - outputConsole.printText(archetypeOutput.getOutput(), "green"); - break; - case IN_PROGRESS: - outputConsole.printText(archetypeOutput.getOutput()); - break; - case DONE: - outputConsole.printText(archetypeOutput.getOutput(), "green"); - break; - case ERROR: - outputConsole.printText(archetypeOutput.getOutput(), "red"); - break; - default: - break; - } - } - }); - } catch (WebSocketException e) { - e.printStackTrace(); - } + /** + * Updates progress label when the resolved project is changed. + * + * @param textMessageDto + * object with name of new label + */ + protected void handleTextNotification(TextMessageDto textMessageDto) { + dependencyResolver.show(); + dependencyResolver.setProgressLabel(textMessageDto.getText()); } - private void handleStartStop(StartStopNotification dto) { + /** + * Hides or shows a progress bar. + * + * @param dto + * describes a state of the project resolving + */ + protected void handleStartStop(StartStopNotification dto) { if (dto.isStart()) { dependencyResolver.show(); } else { @@ -182,7 +111,13 @@ public class MavenMessagesHandler { } } - private void handleUpdate(ProjectsUpdateMessage dto) { + /** + * Updates the tree of projects which were modified. + * + * @param dto + * describes a projects which were modified + */ + protected void handleUpdate(ProjectsUpdateMessage dto) { List updatedProjects = dto.getUpdatedProjects(); Set projectToRefresh = computeUniqueHiLevelProjects(updatedProjects); for (final String path : projectToRefresh) { @@ -196,24 +131,38 @@ public class MavenMessagesHandler { pomEditorReconciler.reconcilePoms(updatedProjects); } + private void onMavenArchetypeReceive(ArchetypeOutput output) { + String message = output.getOutput(); + switch (output.getState()) { + case START: + processesPanelPresenter.addCommandOutput(outputConsole); + outputConsole.clearOutputsButtonClicked(); + outputConsole.printText(message, "green"); + break; + case IN_PROGRESS: + outputConsole.printText(message); + break; + case DONE: + outputConsole.printText(message, "green"); + break; + case ERROR: + outputConsole.printText(message, "red"); + break; + default: + break; + } + } + private Set computeUniqueHiLevelProjects(List updatedProjects) { return updatedProjects.stream().filter(each -> shouldBeUpdated(updatedProjects, each)).collect(toSet()); } private boolean shouldBeUpdated(List updatedProjects, String project) { for (String each : updatedProjects) { - if (!project.equals(each) && project.startsWith(each)){ + if (!project.equals(each) && project.startsWith(each)) { return false; } } return true; } - - private void handleNotification(NotificationMessage message) { - if (message.getPercent() != 0) { - dependencyResolver.updateProgressBar((int)(message.getPercent() * 100)); - } else { - dependencyResolver.setProgressLabel(message.getText()); - } - } } diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/project/ResolvingMavenProjectStateHolder.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/project/ResolvingMavenProjectStateHolder.java index 822bcaca96..e4c4cde838 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/project/ResolvingMavenProjectStateHolder.java +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/project/ResolvingMavenProjectStateHolder.java @@ -15,19 +15,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; -import org.eclipse.che.ide.api.machine.WsAgentStateController; -import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; -import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; -import org.eclipse.che.ide.collections.Jso; -import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.project.ResolvingProjectStateHolder; -import org.eclipse.che.ide.util.loging.Log; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.events.MessageHandler; -import org.eclipse.che.plugin.maven.shared.MessageType; +import org.eclipse.che.plugin.maven.client.MavenJsonRpcHandler; import org.eclipse.che.plugin.maven.shared.dto.StartStopNotification; import java.util.HashSet; @@ -35,9 +24,7 @@ import java.util.HashSet; import static org.eclipse.che.ide.project.ResolvingProjectStateHolder.ResolvingProjectState.IN_PROGRESS; import static org.eclipse.che.ide.project.ResolvingProjectStateHolder.ResolvingProjectState.NOT_RESOLVED; import static org.eclipse.che.ide.project.ResolvingProjectStateHolder.ResolvingProjectState.RESOLVED; -import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_NAME; import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ID; -import static org.eclipse.che.plugin.maven.shared.MessageType.START_STOP; /** * Keeper for the state of Resolving Project process. @@ -51,22 +38,16 @@ import static org.eclipse.che.plugin.maven.shared.MessageType.START_STOP; * @author Roman Nikitenko */ @Singleton -public class ResolvingMavenProjectStateHolder implements ResolvingProjectStateHolder, WsAgentStateHandler { - private final DtoFactory factory; - private final WsAgentStateController wsAgentStateController; +public class ResolvingMavenProjectStateHolder implements ResolvingProjectStateHolder { private ResolvingProjectState state; private HashSet listeners; @Inject - public ResolvingMavenProjectStateHolder(DtoFactory factory, - EventBus eventBus, - WsAgentStateController wsAgentStateController) { - this.factory = factory; - this.wsAgentStateController = wsAgentStateController; + public ResolvingMavenProjectStateHolder(MavenJsonRpcHandler mavenJsonRpcHandler, EventBus eventBus) { this.state = NOT_RESOLVED; this.listeners = new HashSet<>(); - eventBus.addHandler(WsAgentStateEvent.TYPE, this); + mavenJsonRpcHandler.addStartStopHandler(this::handleStartStop); } @Override @@ -89,36 +70,6 @@ public class ResolvingMavenProjectStateHolder implements ResolvingProjectStateHo listeners.remove(listener); } - @Override - public void onWsAgentStarted(WsAgentStateEvent event) { - wsAgentStateController.getMessageBus().then(new Operation() { - @Override - public void apply(MessageBus messageBus) throws OperationException { - try { - messageBus.subscribe(MAVEN_CHANEL_NAME, new MessageHandler() { - @Override - public void onMessage(String message) { - Jso jso = Jso.deserialize(message); - int type = jso.getFieldCastedToInteger("$type"); - MessageType messageType = MessageType.valueOf(type); - - if (messageType == START_STOP) { - handleStartStop(factory.createDtoFromJson(message, StartStopNotification.class)); - } - } - }); - } catch (WebSocketException e) { - Log.error(getClass(), e); - } - } - }); - } - - @Override - public void onWsAgentStopped(WsAgentStateEvent event) { - - } - private void handleStartStop(StartStopNotification startStopNotification) { if (startStopNotification.isStart()) { state = IN_PROGRESS; @@ -138,4 +89,5 @@ public class ResolvingMavenProjectStateHolder implements ResolvingProjectStateHo } } }; + } diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/test/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandlerTest.java b/plugins/plugin-maven/che-plugin-maven-ide/src/test/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandlerTest.java index 2a6d942677..6c99811831 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/src/test/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandlerTest.java +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/test/java/org/eclipse/che/plugin/maven/client/comunnication/MavenMessagesHandlerTest.java @@ -13,107 +13,69 @@ package org.eclipse.che.plugin.maven.client.comunnication; import com.google.common.base.Optional; import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.machine.WsAgentStateController; -import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; -import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; import org.eclipse.che.ide.api.resources.Container; import org.eclipse.che.ide.console.CommandConsoleFactory; -import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.events.MessageHandler; +import org.eclipse.che.plugin.maven.client.MavenJsonRpcHandler; import org.eclipse.che.plugin.maven.client.comunnication.progressor.background.BackgroundLoaderPresenter; +import org.eclipse.che.plugin.maven.shared.dto.PercentMessageDto; import org.eclipse.che.plugin.maven.shared.dto.ProjectsUpdateMessage; +import org.eclipse.che.plugin.maven.shared.dto.TextMessageDto; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; -import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_NAME; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MavenMessagesHandlerTest { - private static final String JSON_MESSAGE = "{\n" + - " \"deletedProjects\": [],\n" + - " \"updatedProjects\": [\n" + - " \"/che/core\",\n" + - " \"/project\",\n" + - " \"/che/core/che-core-db-vendor-postgresql\",\n" + - " \"/che/plugins/plugin-svn\",\n" + - " \"/che/samples/sample-plugin-json\",\n" + - " \"/che/wsmaster/che-core-api-auth\",\n" + - " \"/project/api\",\n" + - " \"/che/wsagent\",\n" + - " \"/che\",\n" + - " \"/che/plugins/plugin-github/che-plugin-github-pullrequest\",\n" + - " \"/che/plugins/plugin-java-debugger\"\n" + - " ],\n" + - " \"$type\": 2\n" + - "}"; @Mock private EventBus eventBus; @Mock - private DtoFactory factory; - @Mock private BackgroundLoaderPresenter dependencyResolver; @Mock private PomEditorReconciler pomEditorReconciler; @Mock - private WsAgentStateController wsAgentStateController; - @Mock private ProcessesPanelPresenter processesPanelPresenter; @Mock private CommandConsoleFactory commandConsoleFactory; @Mock + private MavenJsonRpcHandler mavenJsonRpcHandler; + @Mock private AppContext appContext; @Mock - private WsAgentStateEvent wsAgentStateEvent; + private ProjectsUpdateMessage projectsUpdateMessage; @Mock - private ProjectsUpdateMessage projectsUpdateMessage; + private Promise> optionalContainer; @Mock - private Promise messageBusPromise; - @Mock - private Promise> optionalContainer; - @Mock - private MessageBus messageBus; - @Mock - private Container rootContainer; - @Captor - private ArgumentCaptor wsAgentStateHandlerArgumentCaptor; - @Captor - private ArgumentCaptor> operationArgumentCaptor; - @Captor - private ArgumentCaptor messageHandlerArgumentCaptor; + private Container rootContainer; + + private MavenMessagesHandler mavenMessagesHandler; @Before public void setUp() throws Exception { - when(wsAgentStateController.getMessageBus()).thenReturn(messageBusPromise); when(appContext.getWorkspaceRoot()).thenReturn(rootContainer); - new MavenMessagesHandler(eventBus, - factory, - dependencyResolver, - pomEditorReconciler, - wsAgentStateController, - processesPanelPresenter, - commandConsoleFactory, - appContext); - - + mavenMessagesHandler = new MavenMessagesHandler(eventBus, + mavenJsonRpcHandler, + dependencyResolver, + pomEditorReconciler, + processesPanelPresenter, + commandConsoleFactory, + appContext); } @Test @@ -130,22 +92,34 @@ public class MavenMessagesHandlerTest { "/che/plugins/plugin-github/che-plugin-github-pullrequest", "/che/plugins/plugin-java-debugger"); - when(factory.createDtoFromJson(eq(JSON_MESSAGE), eq(ProjectsUpdateMessage.class))).thenReturn(projectsUpdateMessage); when(projectsUpdateMessage.getUpdatedProjects()).thenReturn(updatedProjects); when(rootContainer.getContainer(anyString())).thenReturn(optionalContainer); - verify(eventBus).addHandler(eq(WsAgentStateEvent.TYPE), wsAgentStateHandlerArgumentCaptor.capture()); - wsAgentStateHandlerArgumentCaptor.getValue().onWsAgentStarted(wsAgentStateEvent); + mavenMessagesHandler.handleUpdate(projectsUpdateMessage); - verify(messageBusPromise).then(operationArgumentCaptor.capture()); - operationArgumentCaptor.getValue().apply(messageBus); - - verify(messageBus).subscribe(eq(MAVEN_CHANEL_NAME), messageHandlerArgumentCaptor.capture()); - messageHandlerArgumentCaptor.getValue().onMessage(JSON_MESSAGE); - - verify(factory).createDtoFromJson(JSON_MESSAGE, ProjectsUpdateMessage.class); verify(pomEditorReconciler).reconcilePoms(updatedProjects); verify(rootContainer).getContainer(eq("/che")); verify(rootContainer).getContainer(eq("/project")); } + + @Test + public void percentNotificationShouldBeHandled() throws Exception { + PercentMessageDto percentMessageDto = Mockito.mock(PercentMessageDto.class); + when(percentMessageDto.getPercent()).thenReturn(0.7); + + mavenMessagesHandler.handlePercentNotification(percentMessageDto); + + verify(dependencyResolver).updateProgressBar(70); + } + + @Test + public void textNotificationShouldBeUpdated() throws Exception { + TextMessageDto textMessageDto = mock(TextMessageDto.class); + String textMessage = "textMessage"; + when(textMessageDto.getText()).thenReturn(textMessage); + + mavenMessagesHandler.handleTextNotification(textMessageDto); + + verify(dependencyResolver).setProgressLabel(textMessage); + } } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenCommunication.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenCommunication.java index 65e30c4100..5aa93e4a04 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenCommunication.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenCommunication.java @@ -26,6 +26,7 @@ import java.util.Set; * @author Evgen Vidolob */ @ImplementedBy(MavenWebSocketCommunication.class) +@Deprecated public interface MavenCommunication { void sendUpdateMassage(Set updated, List removed); diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenJsonRpcCommunication.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenJsonRpcCommunication.java new file mode 100644 index 0000000000..c779700b84 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenJsonRpcCommunication.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * 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.plugin.maven.server.core; + +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.dto.server.DtoFactory; +import org.eclipse.che.plugin.maven.shared.dto.PercentMessageDto; +import org.eclipse.che.plugin.maven.shared.dto.PercentUndefinedMessageDto; +import org.eclipse.che.plugin.maven.shared.dto.ProjectsUpdateMessage; +import org.eclipse.che.plugin.maven.shared.dto.StartStopNotification; +import org.eclipse.che.plugin.maven.shared.dto.TextMessageDto; +import org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent; +import org.eclipse.che.plugin.maven.shared.event.MavenPercentMessageEvent; +import org.eclipse.che.plugin.maven.shared.event.MavenPercentUndefinedEvent; +import org.eclipse.che.plugin.maven.shared.event.MavenStartStopEvent; +import org.eclipse.che.plugin.maven.shared.event.MavenTextMessageEvent; +import org.eclipse.che.plugin.maven.shared.event.MavenUpdateEvent; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import java.util.List; +import java.util.Set; + +import static com.google.common.collect.Sets.newConcurrentHashSet; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_SUBSCRIBE; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_CHANEL_UNSUBSCRIBE; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_PERCENT_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_PERCENT_UNDEFINED_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_START_STOP_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_TEXT_METHOD; +import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_OUTPUT_UPDATE_METHOD; +import static org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent.TYPE.PERCENT; +import static org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent.TYPE.PERCENT_UNDEFINED; +import static org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent.TYPE.START_STOP; + +/** + * Send maven events using JSON RPC to the clients. + */ +@Singleton +public class MavenJsonRpcCommunication implements EventSubscriber { + private final Set endpointIds = newConcurrentHashSet(); + private EventService eventService; + private RequestTransmitter transmitter; + + @Inject + public MavenJsonRpcCommunication(EventService eventService, RequestTransmitter transmitter) { + this.eventService = eventService; + this.transmitter = transmitter; + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this); + } + + @PreDestroy + private void unsubscribe() { + eventService.unsubscribe(this); + } + + @Inject + private void configureHandlers(RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName(MAVEN_CHANEL_SUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::add); + + configurator.newConfiguration() + .methodName(MAVEN_CHANEL_UNSUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::remove); + } + + @Override + public void onEvent(MavenOutputEvent event) { + switch (event.getType()) { + case TEXT: + sendTextNotification((MavenTextMessageEvent)event); + break; + case UPDATE: + sendUpdateNotification((MavenUpdateEvent)event); + break; + case START_STOP: + sendStartStopNotification((MavenStartStopEvent)event); + break; + case PERCENT: + senPercentNotification((MavenPercentMessageEvent)event); + break; + case PERCENT_UNDEFINED: + sendPercentUndefinedNotification((MavenPercentUndefinedEvent)event); + break; + } + } + + private void senPercentNotification(MavenPercentMessageEvent event) { + PercentMessageDto percentMessageDto = DtoFactory.newDto(PercentMessageDto.class).withPercent(event.getPercent()); + percentMessageDto.setType(PERCENT); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_OUTPUT_PERCENT_METHOD) + .paramsAsDto(percentMessageDto) + .sendAndSkipResult()); + } + + private void sendPercentUndefinedNotification(MavenPercentUndefinedEvent event) { + PercentUndefinedMessageDto percentUndefinedMessageDto = DtoFactory.newDto(PercentUndefinedMessageDto.class); + percentUndefinedMessageDto.setPercentUndefined(event.isPercentUndefined()); + percentUndefinedMessageDto.setType(PERCENT_UNDEFINED); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_OUTPUT_PERCENT_UNDEFINED_METHOD) + .paramsAsDto(percentUndefinedMessageDto) + .sendAndSkipResult()); + } + + private void sendStartStopNotification(MavenStartStopEvent event) { + StartStopNotification startEventDto = DtoFactory.newDto(StartStopNotification.class); + startEventDto.setStart(event.isStart()); + startEventDto.setType(START_STOP); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_OUTPUT_START_STOP_METHOD) + .paramsAsDto(startEventDto) + .sendAndSkipResult()); + } + + private void sendUpdateNotification(MavenUpdateEvent event) { + ProjectsUpdateMessage updateEventDto = DtoFactory.newDto(ProjectsUpdateMessage.class); + List updatedPaths = event.getUpdatedProjects(); + List removedPaths = event.getRemovedProjects(); + updateEventDto.setUpdatedProjects(updatedPaths); + updateEventDto.setDeletedProjects(removedPaths); + + updateEventDto.setType(MavenOutputEvent.TYPE.UPDATE); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_OUTPUT_UPDATE_METHOD) + .paramsAsDto(updateEventDto) + .sendAndSkipResult()); + } + + private void sendTextNotification(MavenTextMessageEvent event) { + TextMessageDto notification = DtoFactory.newDto(TextMessageDto.class).withText(event.getMessage()); + notification.setType(MavenOutputEvent.TYPE.TEXT); + + endpointIds.forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName(MAVEN_OUTPUT_TEXT_METHOD) + .paramsAsDto(notification) + .sendAndSkipResult()); + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenServerNotifier.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenServerNotifier.java index d356ed60bf..29c32efc7c 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenServerNotifier.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenServerNotifier.java @@ -13,11 +13,13 @@ package org.eclipse.che.plugin.maven.server.core; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.che.dto.server.DtoFactory; -import org.eclipse.che.plugin.maven.shared.MessageType; -import org.eclipse.che.plugin.maven.shared.dto.NotificationMessage; -import org.eclipse.che.plugin.maven.shared.dto.StartStopNotification; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.maven.server.MavenServerProgressNotifier; +import org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent; +import org.eclipse.che.plugin.maven.shared.impl.MavenPercentEventImpl; +import org.eclipse.che.plugin.maven.shared.impl.MavenPercentUndefinedEventImpl; +import org.eclipse.che.plugin.maven.shared.impl.MavenStartStopEventImpl; +import org.eclipse.che.plugin.maven.shared.impl.MavenTextMessageEventImpl; /** * Default implementation of {@link MavenServerProgressNotifier} @@ -27,32 +29,26 @@ import org.eclipse.che.maven.server.MavenServerProgressNotifier; @Singleton public class MavenServerNotifier implements MavenProgressNotifier { - private final MavenCommunication communication; + private EventService eventService; @Inject - public MavenServerNotifier(MavenCommunication communication) { - this.communication = communication; + public MavenServerNotifier(EventService eventService) { + this.eventService = eventService; } @Override public void setText(String text) { - NotificationMessage dto = DtoFactory.newDto(NotificationMessage.class); - dto.setText(text); - communication.sendNotification(dto); + eventService.publish(new MavenTextMessageEventImpl(text, MavenOutputEvent.TYPE.TEXT)); } @Override public void setPercent(double percent) { - NotificationMessage dto = DtoFactory.newDto(NotificationMessage.class); - dto.setPercent(percent); - communication.sendNotification(dto); + eventService.publish(new MavenPercentEventImpl(percent, MavenOutputEvent.TYPE.PERCENT)); } @Override public void setPercentUndefined(boolean undefined) { - NotificationMessage dto = DtoFactory.newDto(NotificationMessage.class); - dto.setPercentUndefined(undefined); - communication.sendNotification(dto); + eventService.publish(new MavenPercentUndefinedEventImpl(undefined, MavenOutputEvent.TYPE.PERCENT_UNDEFINED)); } @Override @@ -66,9 +62,7 @@ public class MavenServerNotifier implements MavenProgressNotifier { } private void sendStartStop(boolean isStart) { - StartStopNotification dto = DtoFactory.newDto(StartStopNotification.class); - dto.setStart(isStart); - communication.send(DtoFactory.getInstance().toJsonElement(dto).getAsJsonObject(), MessageType.START_STOP); + eventService.publish(new MavenStartStopEventImpl(isStart, MavenOutputEvent.TYPE.START_STOP)); } @Override diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWebSocketCommunication.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWebSocketCommunication.java index d218ddaf79..8ed2faaa8a 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWebSocketCommunication.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWebSocketCommunication.java @@ -34,6 +34,7 @@ import java.util.stream.Collectors; * @author Evgen Vidolob */ @Singleton +@Deprecated public class MavenWebSocketCommunication implements MavenCommunication { private static final Logger LOG = LoggerFactory.getLogger(MavenWebSocketCommunication.class); diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWorkspace.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWorkspace.java index a8a181faa1..40bb5851ea 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWorkspace.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/MavenWorkspace.java @@ -30,7 +30,10 @@ import org.eclipse.che.plugin.maven.server.core.classpath.ClasspathManager; import org.eclipse.che.plugin.maven.server.core.project.MavenProject; import org.eclipse.che.plugin.maven.server.core.project.MavenProjectModifications; import org.eclipse.che.plugin.maven.shared.MavenAttributes; +import org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent; +import org.eclipse.che.plugin.maven.shared.impl.MavenUpdateEventImpl; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaModelStatusConstants; @@ -50,6 +53,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import static java.util.stream.Collectors.toList; import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ID; /** @@ -62,7 +66,6 @@ public class MavenWorkspace { private final MavenProjectManager manager; private final Provider projectRegistryProvider; - private final MavenCommunication communication; private final ClasspathManager classpathManager; private MavenTaskExecutor resolveExecutor; @@ -76,12 +79,10 @@ public class MavenWorkspace { MavenProgressNotifier notifier, MavenExecutorService executorService, Provider projectRegistryProvider, - MavenCommunication communication, ClasspathManager classpathManager, EventService eventService, EclipseWorkspaceProvider workspaceProvider) { this.projectRegistryProvider = projectRegistryProvider; - this.communication = communication; this.classpathManager = classpathManager; this.manager = manager; resolveExecutor = new MavenTaskExecutor(executorService, notifier); @@ -110,9 +111,20 @@ public class MavenWorkspace { List needResolve = manager.findDependentProjects(allChangedProjects); needResolve.addAll(updated.keySet()); + List updatedPaths = updated.keySet().stream() + .map(MavenProject::getProject) + .map(IResource::getFullPath) + .map(IPath::toOSString) + .collect(toList()); + List removedPaths = removed.stream() + .map(MavenProject::getProject) + .map(IResource::getFullPath) + .map(IPath::toOSString) + .collect(toList()); + addResolveProjects(needResolve); - communication.sendUpdateMassage(updated.keySet(), removed); + eventService.publish(new MavenUpdateEventImpl(updatedPaths, removedPaths, MavenOutputEvent.TYPE.UPDATE)); } }); } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java index 94049d07eb..4dcdeb5141 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java @@ -19,7 +19,9 @@ import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.api.project.server.type.ValueProviderFactory; import org.eclipse.che.inject.DynaModule; import org.eclipse.che.maven.server.MavenTerminal; +import org.eclipse.che.plugin.maven.generator.archetype.MavenArchetypeJsonRpcMessenger; import org.eclipse.che.plugin.maven.server.PomModificationDetector; +import org.eclipse.che.plugin.maven.server.core.MavenJsonRpcCommunication; import org.eclipse.che.plugin.maven.server.core.MavenProgressNotifier; import org.eclipse.che.plugin.maven.server.core.MavenServerNotifier; import org.eclipse.che.plugin.maven.server.core.MavenTerminalImpl; @@ -60,6 +62,8 @@ public class MavenModule extends AbstractModule { bind(MavenProgressNotifier.class).to(MavenServerNotifier.class).in(Singleton.class); bind(MavenServerService.class); + bind(MavenJsonRpcCommunication.class); + bind(MavenArchetypeJsonRpcMessenger.class); bind(PomChangeListener.class).asEagerSingleton(); bind(PomModificationDetector.class).asEagerSingleton(); diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/WorkspaceTest.java b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/WorkspaceTest.java index 6e2c99918d..dbfe7d0488 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/WorkspaceTest.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/WorkspaceTest.java @@ -10,8 +10,8 @@ *******************************************************************************/ package org.eclipse.che.plugin.maven.server; -import com.google.gson.JsonObject; import com.google.inject.Provider; + import org.eclipse.che.api.project.server.FolderEntry; import org.eclipse.che.api.project.server.ProjectRegistry; import org.eclipse.che.api.project.server.RegisteredProject; @@ -22,15 +22,12 @@ import org.eclipse.che.maven.data.MavenArtifact; import org.eclipse.che.maven.data.MavenKey; import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.che.plugin.maven.server.core.EclipseWorkspaceProvider; -import org.eclipse.che.plugin.maven.server.core.MavenCommunication; import org.eclipse.che.plugin.maven.server.core.MavenExecutorService; import org.eclipse.che.plugin.maven.server.core.MavenProjectManager; import org.eclipse.che.plugin.maven.server.core.MavenWorkspace; import org.eclipse.che.plugin.maven.server.core.classpath.ClasspathManager; import org.eclipse.che.plugin.maven.server.core.project.MavenProject; import org.eclipse.che.plugin.maven.server.rmi.MavenServerManagerTest; -import org.eclipse.che.plugin.maven.shared.MessageType; -import org.eclipse.che.plugin.maven.shared.dto.NotificationMessage; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; @@ -51,7 +48,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.ListIterator; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -92,22 +88,7 @@ public class WorkspaceTest extends BaseTest { mavenNotifier, new MavenExecutorService(), projectRegistryProvider, - new MavenCommunication() { - @Override - public void sendUpdateMassage(Set updated, List removed) { - - } - - @Override - public void sendNotification(NotificationMessage message) { - System.out.println(message.toString()); - } - - @Override - public void send(JsonObject object, MessageType type) { - - } - }, new ClasspathManager(root.getAbsolutePath(), wrapperManager, mavenProjectManager, terminal, + new ClasspathManager(root.getAbsolutePath(), wrapperManager, mavenProjectManager, terminal, mavenNotifier), eventService, new EclipseWorkspaceProvider()); } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/classpath/ClasspathManagerTest.java b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/classpath/ClasspathManagerTest.java index b3cebd2e54..c8a9bf3cbc 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/classpath/ClasspathManagerTest.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/classpath/ClasspathManagerTest.java @@ -10,24 +10,19 @@ *******************************************************************************/ package org.eclipse.che.plugin.maven.server.classpath; -import com.google.gson.JsonObject; import com.google.inject.Provider; import org.eclipse.che.api.project.server.ProjectRegistry; import org.eclipse.che.commons.lang.IoUtil; +import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.che.plugin.maven.server.BaseTest; import org.eclipse.che.plugin.maven.server.MavenWrapperManager; import org.eclipse.che.plugin.maven.server.core.EclipseWorkspaceProvider; -import org.eclipse.che.plugin.maven.server.core.MavenCommunication; import org.eclipse.che.plugin.maven.server.core.MavenExecutorService; import org.eclipse.che.plugin.maven.server.core.MavenProjectManager; import org.eclipse.che.plugin.maven.server.core.MavenWorkspace; import org.eclipse.che.plugin.maven.server.core.classpath.ClasspathManager; -import org.eclipse.che.plugin.maven.server.core.project.MavenProject; import org.eclipse.che.plugin.maven.server.rmi.MavenServerManagerTest; -import org.eclipse.che.plugin.maven.shared.MessageType; -import org.eclipse.che.plugin.maven.shared.dto.NotificationMessage; -import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.jdt.core.IJavaProject; @@ -40,8 +35,6 @@ import org.testng.annotations.Test; import java.io.File; import java.util.Collections; -import java.util.List; -import java.util.Set; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -81,22 +74,7 @@ public class ClasspathManagerTest extends BaseTest { new MavenProjectManager(wrapperManager, mavenServerManager, terminal, mavenNotifier, new EclipseWorkspaceProvider()); classpathManager = new ClasspathManager(root.getAbsolutePath(), wrapperManager, mavenProjectManager, terminal, mavenNotifier); mavenWorkspace = new MavenWorkspace(mavenProjectManager, mavenNotifier, new MavenExecutorService(), projectRegistryProvider, - new MavenCommunication() { - @Override - public void sendUpdateMassage(Set updated, List removed) { - - } - - @Override - public void sendNotification(NotificationMessage message) { - System.out.println(message.toString()); - } - - @Override - public void send(JsonObject object, MessageType type) { - - } - }, classpathManager, eventService, new EclipseWorkspaceProvider()); + classpathManager, eventService, new EclipseWorkspaceProvider()); } @AfterMethod diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconcilerTest.java b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconcilerTest.java index 8558562272..b21fd76355 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconcilerTest.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/test/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconcilerTest.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.che.plugin.maven.server.core.reconcile; -import com.google.gson.JsonObject; import com.google.inject.Provider; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; @@ -24,15 +23,11 @@ import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.che.plugin.maven.server.BaseTest; import org.eclipse.che.plugin.maven.server.MavenWrapperManager; import org.eclipse.che.plugin.maven.server.core.EclipseWorkspaceProvider; -import org.eclipse.che.plugin.maven.server.core.MavenCommunication; import org.eclipse.che.plugin.maven.server.core.MavenExecutorService; import org.eclipse.che.plugin.maven.server.core.MavenProjectManager; import org.eclipse.che.plugin.maven.server.core.MavenWorkspace; import org.eclipse.che.plugin.maven.server.core.classpath.ClasspathManager; -import org.eclipse.che.plugin.maven.server.core.project.MavenProject; import org.eclipse.che.plugin.maven.server.rmi.MavenServerManagerTest; -import org.eclipse.che.plugin.maven.shared.MessageType; -import org.eclipse.che.plugin.maven.shared.dto.NotificationMessage; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.testng.annotations.BeforeMethod; @@ -42,7 +37,6 @@ import java.io.File; import java.rmi.RemoteException; import java.util.Collections; import java.util.List; -import java.util.Set; import static java.lang.String.format; import static org.fest.assertions.Assertions.assertThat; @@ -89,23 +83,13 @@ public class PomReconcilerTest extends BaseTest { ClasspathManager classpathManager = new ClasspathManager(root.getAbsolutePath(), wrapperManager, mavenProjectManager, terminal, mavenNotifier); - mavenWorkspace = new MavenWorkspace(mavenProjectManager, mavenNotifier, new MavenExecutorService(), projectRegistryProvider, - new MavenCommunication() { - @Override - public void sendUpdateMassage(Set updated, List removed) { - - } - - @Override - public void sendNotification(NotificationMessage message) { - System.out.println(message.toString()); - } - - @Override - public void send(JsonObject object, MessageType type) { - - } - }, classpathManager, eventService, new EclipseWorkspaceProvider()); + mavenWorkspace = new MavenWorkspace(mavenProjectManager, + mavenNotifier, + new MavenExecutorService(), + projectRegistryProvider, + classpathManager, + eventService, + new EclipseWorkspaceProvider()); EditorWorkingCopyManager editorWorkingCopyManager = new EditorWorkingCopyManager(projectManagerProvider, eventService, requestTransmitter); pomReconciler = diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/MavenAttributes.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/MavenAttributes.java index a0cfe63bb4..86fb09bbf7 100644 --- a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/MavenAttributes.java +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/MavenAttributes.java @@ -36,7 +36,7 @@ public interface MavenAttributes { String PARENT_VERSION = "maven.parent.version"; String TEST_SOURCE_FOLDER = "maven.test.source.folder"; - String RESOURCE_FOLDER = "maven.resource.folder"; + String RESOURCE_FOLDER = "maven.resource.folder"; String DEFAULT_SOURCE_FOLDER = "src/main/java"; String DEFAULT_RESOURCES_FOLDER = "src/main/resources"; @@ -50,6 +50,17 @@ public interface MavenAttributes { /** Name of WebSocket chanel */ String MAVEN_CHANEL_NAME = "maven:workspace"; - /** Name of WebSocket chanel for Maven Archetype output */ - String MAVEN_ARCHETYPE_CHANEL_NAME = "maven:archetype:output"; + + + String MAVEN_CHANEL_UNSUBSCRIBE = "mavenOutput/subscribe"; + String MAVEN_CHANEL_SUBSCRIBE = "mavenOutput/unsubscribe"; + String MAVEN_OUTPUT_TEXT_METHOD = "mavenOutput/text"; + String MAVEN_OUTPUT_UPDATE_METHOD = "mavenOutput/update"; + String MAVEN_OUTPUT_START_STOP_METHOD = "mavenOutput/start_stop"; + String MAVEN_OUTPUT_PERCENT_METHOD = "mavenOutput/percent"; + String MAVEN_OUTPUT_PERCENT_UNDEFINED_METHOD = "mavenOutput/percentUndefined"; + + String MAVEN_ARCHETYPE_CHANEL_UNSUBSCRIBE = "mavenArchetype/unsubscribe"; + String MAVEN_ARCHETYPE_CHANEL_SUBSCRIBE = "mavenArchetype/subscribe"; + String MAVEN_ARCHETYPE_CHANEL_OUTPUT = "mavenArchetype/output"; } diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ArchetypeOutput.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ArchetypeOutput.java index 9925c1bf74..bf1d103173 100644 --- a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ArchetypeOutput.java +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ArchetypeOutput.java @@ -41,4 +41,7 @@ public interface ArchetypeOutput { */ State getState(); + void setOutput(String output); + + void setState(State state); } diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/MavenOutputEventDto.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/MavenOutputEventDto.java new file mode 100644 index 0000000000..7ea20ea94f --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/MavenOutputEventDto.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.plugin.maven.shared.dto; + +import org.eclipse.che.dto.shared.DTO; +import org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent; + +/** + * DTO object which describes base maven output event. + */ +@DTO +public interface MavenOutputEventDto extends MavenOutputEvent { + + TYPE getType(); + + /** Returns a type of the output event. */ + void setType(TYPE type); + + MavenOutputEventDto withType(TYPE type); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/NotificationMessage.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/NotificationMessage.java index 47fe816cda..d262a9d38e 100644 --- a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/NotificationMessage.java +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/NotificationMessage.java @@ -18,6 +18,7 @@ import org.eclipse.che.dto.shared.DTO; * @author Evgen Vidolob */ @DTO +@Deprecated public interface NotificationMessage { String getText(); diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentMessageDto.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentMessageDto.java new file mode 100644 index 0000000000..aa9faed306 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentMessageDto.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.plugin.maven.shared.dto; + +import org.eclipse.che.dto.shared.DTO; + +/** + * Message for translating value of percent of resolving from MavenServerNotifier. + */ +@DTO +public interface PercentMessageDto extends MavenOutputEventDto { + + double getPercent(); + + void setPercent(double percent); + + PercentMessageDto withPercent(double percent); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentUndefinedMessageDto.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentUndefinedMessageDto.java new file mode 100644 index 0000000000..24502bdd86 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/PercentUndefinedMessageDto.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.plugin.maven.shared.dto; + +import org.eclipse.che.dto.shared.DTO; + +/** + * Message for translating value of percent undefined from MavenServerNotifier. + */ +@DTO +public interface PercentUndefinedMessageDto extends MavenOutputEventDto { + + boolean isPercentUndefined(); + + void setPercentUndefined(boolean percentUndefined); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ProjectsUpdateMessage.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ProjectsUpdateMessage.java index a5cf58a671..94948f61f5 100644 --- a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ProjectsUpdateMessage.java +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/ProjectsUpdateMessage.java @@ -19,7 +19,7 @@ import java.util.List; * @author Evgen Vidolob */ @DTO -public interface ProjectsUpdateMessage { +public interface ProjectsUpdateMessage extends MavenOutputEventDto { List getUpdatedProjects(); diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/StartStopNotification.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/StartStopNotification.java index ae23a31641..30b3794905 100644 --- a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/StartStopNotification.java +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/StartStopNotification.java @@ -18,7 +18,7 @@ import org.eclipse.che.dto.shared.DTO; * @author Evgen Vidolob */ @DTO -public interface StartStopNotification { +public interface StartStopNotification extends MavenOutputEventDto{ boolean isStart(); diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/TextMessageDto.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/TextMessageDto.java new file mode 100644 index 0000000000..43bb78c2f3 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/dto/TextMessageDto.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.plugin.maven.shared.dto; + +import org.eclipse.che.dto.shared.DTO; + +/** + * Message for translating text message from MavenServerNotifier + */ +@DTO +public interface TextMessageDto extends MavenOutputEventDto { + + String getText(); + + /** Returns text message of the output event. */ + void setText(String text); + + TextMessageDto withText(String text); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenOutputEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenOutputEvent.java new file mode 100644 index 0000000000..9572e9cfd6 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenOutputEvent.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +/** + * Base maven output event. + */ +public interface MavenOutputEvent { + /**Returns typ of the event. */ + TYPE getType(); + + enum TYPE { + START_STOP, + PERCENT, + PERCENT_UNDEFINED, + UPDATE, + TEXT + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentMessageEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentMessageEvent.java new file mode 100644 index 0000000000..b59dbb005b --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentMessageEvent.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +/** + * Event that describes Maven notification output. + */ +public interface MavenPercentMessageEvent extends MavenOutputEvent { + /** Returns percent of the project resolving. */ + double getPercent(); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentUndefinedEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentUndefinedEvent.java new file mode 100644 index 0000000000..8eaf5ee684 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenPercentUndefinedEvent.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +/** + * Event that describes Maven start notification. + */ +public interface MavenPercentUndefinedEvent extends MavenOutputEvent { + boolean isPercentUndefined(); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenStartStopEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenStartStopEvent.java new file mode 100644 index 0000000000..3081d2d4de --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenStartStopEvent.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +/** + * Event that describes Maven start notification. + */ +public interface MavenStartStopEvent extends MavenOutputEvent { + /** Returns {@code true} if the process of resolving is started otherwise returns {@code false}. */ + boolean isStart(); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenTextMessageEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenTextMessageEvent.java new file mode 100644 index 0000000000..a5cea284ab --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenTextMessageEvent.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +/** + * Event that describes Maven notification output. + */ +public interface MavenTextMessageEvent extends MavenOutputEvent { + /** Returns text message. */ + String getMessage(); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenUpdateEvent.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenUpdateEvent.java new file mode 100644 index 0000000000..dc3edc0cdc --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/event/MavenUpdateEvent.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.plugin.maven.shared.event; + +import java.util.List; + +/** + * Event that describes Maven notification output. + */ +public interface MavenUpdateEvent extends MavenOutputEvent { + /** Returns list of projects which were modified. */ + List getUpdatedProjects(); + + /** Returns list of projects which were removed. */ + List getRemovedProjects(); +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenOutputEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenOutputEventImpl.java new file mode 100644 index 0000000000..028c198ecd --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenOutputEventImpl.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenOutputEvent; + +/** + * Implementation of base maven output event. + */ +public class MavenOutputEventImpl implements MavenOutputEvent { + private final TYPE type; + + public MavenOutputEventImpl(TYPE type) { + this.type = type; + } + + @Override + public TYPE getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenOutputEventImpl)) return false; + + MavenOutputEventImpl that = (MavenOutputEventImpl)o; + + return type == that.type; + } + + @Override + public int hashCode() { + return type != null ? type.hashCode() : 0; + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentEventImpl.java new file mode 100644 index 0000000000..087021585d --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentEventImpl.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenPercentMessageEvent; + +/** + * Implementation of the event which describes percent of the project resolving. + */ +public class MavenPercentEventImpl extends MavenOutputEventImpl implements MavenPercentMessageEvent { + private final double percent; + + public MavenPercentEventImpl(double percent, TYPE type) { + super(type); + this.percent = percent; + } + + @Override + public double getPercent() { + return percent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenPercentEventImpl)) return false; + if (!super.equals(o)) return false; + + MavenPercentEventImpl that = (MavenPercentEventImpl)o; + + return percent == that.percent; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Double.hashCode(percent); + return result; + } + +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentUndefinedEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentUndefinedEventImpl.java new file mode 100644 index 0000000000..6aa4ef44bc --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenPercentUndefinedEventImpl.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenPercentUndefinedEvent; + +/** + * Implementation of the {@link MavenPercentUndefinedEvent}. + */ +public class MavenPercentUndefinedEventImpl extends MavenOutputEventImpl implements MavenPercentUndefinedEvent { + private final boolean isPercentUndefined; + + public MavenPercentUndefinedEventImpl(boolean isPercentUndefined, TYPE type) { + super(type); + this.isPercentUndefined = isPercentUndefined; + } + + @Override + public boolean isPercentUndefined() { + return isPercentUndefined; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenPercentUndefinedEventImpl)) return false; + if (!super.equals(o)) return false; + + MavenPercentUndefinedEventImpl that = (MavenPercentUndefinedEventImpl)o; + + return isPercentUndefined == that.isPercentUndefined; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isPercentUndefined ? 1 : 0); + return result; + } + +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenStartStopEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenStartStopEventImpl.java new file mode 100644 index 0000000000..a1017c436c --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenStartStopEventImpl.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenStartStopEvent; + +/** + * Implementation of the {@link MavenStartStopEvent}. + */ +public class MavenStartStopEventImpl extends MavenOutputEventImpl implements MavenStartStopEvent { + private final boolean isStart; + + public MavenStartStopEventImpl(boolean isStart, TYPE type) { + super(type); + this.isStart = isStart; + } + + @Override + public boolean isStart() { + return isStart; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenStartStopEventImpl)) return false; + if (!super.equals(o)) return false; + + MavenStartStopEventImpl that = (MavenStartStopEventImpl)o; + + return isStart == that.isStart; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isStart ? 1 : 0); + return result; + } + +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenTextMessageEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenTextMessageEventImpl.java new file mode 100644 index 0000000000..08dc09210f --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenTextMessageEventImpl.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenTextMessageEvent; + +/** + * Implementation of the {@link MavenTextMessageEvent}. + */ +public class MavenTextMessageEventImpl extends MavenOutputEventImpl implements MavenTextMessageEvent { + private final String message; + + public MavenTextMessageEventImpl(String message, TYPE type) { + super(type); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenTextMessageEventImpl)) return false; + if (!super.equals(o)) return false; + + MavenTextMessageEventImpl that = (MavenTextMessageEventImpl)o; + + return message.equals(that.message); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (message != null ? message.hashCode() : 0); + return result; + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenUpdateEventImpl.java b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenUpdateEventImpl.java new file mode 100644 index 0000000000..3b04b93b89 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-shared/src/main/java/org/eclipse/che/plugin/maven/shared/impl/MavenUpdateEventImpl.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.plugin.maven.shared.impl; + + +import org.eclipse.che.plugin.maven.shared.event.MavenUpdateEvent; + +import java.util.List; + +/** + * Implementation of the {@link MavenUpdateEvent}. + */ +public class MavenUpdateEventImpl extends MavenOutputEventImpl implements MavenUpdateEvent { + private final List updated; + private final List removed; + + public MavenUpdateEventImpl(List updated, List removed, TYPE type) { + super(type); + this.removed = removed; + this.updated = updated; + } + + @Override + public List getUpdatedProjects() { + return updated; + } + + @Override + public List getRemovedProjects() { + return removed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MavenUpdateEventImpl)) return false; + if (!super.equals(o)) return false; + + MavenUpdateEventImpl that = (MavenUpdateEventImpl)o; + return removed.equals(that.removed) && updated.equals(that.updated); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (removed != null ? removed.hashCode() : 0) + (updated != null ? updated.hashCode() : 0); + return result; + } + +} diff --git a/plugins/plugin-pullrequest-parent/che-plugin-pullrequest-ide/src/main/java/org/eclipse/che/plugin/pullrequest/client/vcs/GitVcsService.java b/plugins/plugin-pullrequest-parent/che-plugin-pullrequest-ide/src/main/java/org/eclipse/che/plugin/pullrequest/client/vcs/GitVcsService.java index 516955affd..9c89b7e115 100644 --- a/plugins/plugin-pullrequest-parent/che-plugin-pullrequest-ide/src/main/java/org/eclipse/che/plugin/pullrequest/client/vcs/GitVcsService.java +++ b/plugins/plugin-pullrequest-parent/che-plugin-pullrequest-ide/src/main/java/org/eclipse/che/plugin/pullrequest/client/vcs/GitVcsService.java @@ -20,29 +20,21 @@ import org.eclipse.che.api.git.shared.BranchListMode; import org.eclipse.che.api.git.shared.CheckoutRequest; import org.eclipse.che.api.git.shared.PushResponse; import org.eclipse.che.api.git.shared.Remote; -import org.eclipse.che.api.git.shared.Revision; import org.eclipse.che.api.git.shared.Status; import org.eclipse.che.api.promises.client.Function; -import org.eclipse.che.api.promises.client.FunctionException; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.api.promises.client.js.JsPromiseError; import org.eclipse.che.api.promises.client.js.Promises; -import org.eclipse.che.ide.ext.git.client.GitServiceClient; +import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.dto.DtoFactory; +import org.eclipse.che.ide.ext.git.client.GitServiceClient; import org.eclipse.che.ide.resource.Path; -import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; -import org.eclipse.che.ide.rest.Unmarshallable; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.RequestCallback; import javax.validation.constraints.NotNull; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * Git backed implementation for {@link VcsService}. @@ -51,132 +43,90 @@ import java.util.List; public class GitVcsService implements VcsService { private static final String BRANCH_UP_TO_DATE_ERROR_MESSAGE = "Everything up-to-date"; - private final GitServiceClient service; - private final DtoFactory dtoFactory; - private final DtoUnmarshallerFactory dtoUnmarshallerFactory; + private final GitServiceClient service; + private final DtoFactory dtoFactory; + private final AppContext appContext; @Inject public GitVcsService(final DtoFactory dtoFactory, final DtoUnmarshallerFactory dtoUnmarshallerFactory, - final GitServiceClient service) { + final GitServiceClient service, + final AppContext appContext) { this.dtoFactory = dtoFactory; - this.dtoUnmarshallerFactory = dtoUnmarshallerFactory; this.service = service; + this.appContext = appContext; } @Override public void addRemote(@NotNull final ProjectConfig project, @NotNull final String remote, @NotNull final String remoteUrl, @NotNull final AsyncCallback callback) { - - service.remoteAdd(project, remote, remoteUrl, new AsyncRequestCallback() { - @Override - protected void onSuccess(final String notUsed) { - callback.onSuccess(null); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); + service.remoteAdd(appContext.getRootProject().getLocation(), remote, remoteUrl) + .then(arg -> { + callback.onSuccess(null); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } @Override public void checkoutBranch(@NotNull final ProjectConfig project, @NotNull final String name, final boolean createNew, @NotNull final AsyncCallback callback) { - service.checkout( - project, + service.checkout(appContext.getRootProject().getLocation(), dtoFactory.createDto(CheckoutRequest.class) .withName(name) - .withCreateNew(createNew), - new AsyncRequestCallback() { - @Override - protected void onSuccess(final String branchName) { - callback.onSuccess(branchName); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); + .withCreateNew(createNew)) + .then(callback::onSuccess) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } @Override public void commit(@NotNull final ProjectConfig project, final boolean includeUntracked, @NotNull final String commitMessage, @NotNull final AsyncCallback callback) { - try { - - service.add(project, !includeUntracked, null, new RequestCallback() { - @Override - protected void onSuccess(Void aVoid) { - - service.commit(project, commitMessage, true, false, new AsyncRequestCallback() { - @Override - protected void onSuccess(final Revision revision) { - callback.onSuccess(null); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); - - } catch (final WebSocketException exception) { - callback.onFailure(exception); - } + service.add(appContext.getRootProject().getLocation(), !includeUntracked, null) + .then(arg -> { + service.commit(appContext.getRootProject().getLocation(), commitMessage, true, false) + .then(revision -> { + callback.onSuccess(null); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } @Override public void deleteRemote(@NotNull final ProjectConfig project, @NotNull final String remote, @NotNull final AsyncCallback callback) { - service.remoteDelete(project, remote, new AsyncRequestCallback() { - @Override - protected void onSuccess(final String notUsed) { - callback.onSuccess(null); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); + service.remoteDelete(appContext.getRootProject().getLocation(), remote) + .then(arg -> { + callback.onSuccess(null); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } @Override public Promise getBranchName(ProjectConfig project) { return service.getStatus(Path.valueOf(project.getPath())) - .then(new Function() { - @Override - public String apply(Status status) throws FunctionException { - return status.getBranchName(); - } - }); + .then((Function)status -> status.getBranchName()); } @Override public void hasUncommittedChanges(@NotNull final ProjectConfig project, @NotNull final AsyncCallback callback) { service.getStatus(Path.valueOf(project.getPath())) - .then(new Operation() { - @Override - public void apply(Status status) throws OperationException { - callback.onSuccess(!status.isClean()); - } + .then(status -> { + callback.onSuccess(!status.isClean()); }) - .catchError(new Operation() { - @Override - public void apply(PromiseError err) throws OperationException { - callback.onFailure(err.getCause()); - } + .catchError(err -> { + callback.onFailure(err.getCause()); }); } @@ -210,20 +160,17 @@ public class GitVcsService implements VcsService { @Override public Promise> listRemotes(ProjectConfig project) { - return service.remoteList(project, null, false); + return service.remoteList(appContext.getRootProject().getLocation(), null, false); } @Override public Promise pushBranch(final ProjectConfig project, final String remote, final String localBranchName) { - return service.push(project, Collections.singletonList(localBranchName), remote, true) - .catchErrorPromise(new Function>() { - @Override - public Promise apply(PromiseError error) throws FunctionException { - if (BRANCH_UP_TO_DATE_ERROR_MESSAGE.equalsIgnoreCase(error.getMessage())) { - return Promises.reject(JsPromiseError.create(new BranchUpToDateException(localBranchName))); - } else { - return Promises.reject(error); - } + return service.push(appContext.getRootProject().getLocation(), Collections.singletonList(localBranchName), remote, true) + .catchErrorPromise(error -> { + if (BRANCH_UP_TO_DATE_ERROR_MESSAGE.equalsIgnoreCase(error.getMessage())) { + return Promises.reject(JsPromiseError.create(new BranchUpToDateException(localBranchName))); + } else { + return Promises.reject(error); } }); } @@ -239,24 +186,14 @@ public class GitVcsService implements VcsService { * callback when the operation is done. */ private void listBranches(final ProjectConfig project, final BranchListMode listMode, final AsyncCallback> callback) { - final Unmarshallable> unMarshaller = - dtoUnmarshallerFactory.newListUnmarshaller(Branch.class); - service.branchList(project, listMode, - new AsyncRequestCallback>(unMarshaller) { - @Override - protected void onSuccess(final List branches) { - final List result = new ArrayList<>(); - for (final Branch branch : branches) { - result.add(fromGitBranch(branch)); - } - callback.onSuccess(result); - } - - @Override - protected void onFailure(final Throwable exception) { - callback.onFailure(exception); - } - }); + service.branchList(Path.valueOf(project.getPath()), listMode) + .then(branches -> { + final List result = branches.stream().map(this::fromGitBranch).collect(Collectors.toList()); + callback.onSuccess(result); + }) + .catchError(error -> { + callback.onFailure(error.getCause()); + }); } /** diff --git a/samples/sample-plugin-json/README.md b/samples/sample-plugin-json/README.md index 56a76290b4..9d0ceaadd1 100644 --- a/samples/sample-plugin-json/README.md +++ b/samples/sample-plugin-json/README.md @@ -1,11 +1,11 @@ ## Description -The **JSON Example** is a providing a plugin sample which is use as a continuous example in the plugin documentation. You can learn more about it at: https://www.eclipse.org/che/docs/plugins/introduction/index.html#the-json-example +The **JSON Example** is a providing a plugin sample which is use as a continuous example in the plugin documentation. You can learn more about it at: https://www.eclipse.org/che/docs/assemblies/intro/index.html This sample demonstrate how to extend the Eclipse Che in various ways: -- How to register a new file type and add code completion (Read the tutorial at:https://www.eclipse.org/che/docs/plugins/code-editors/index.html#code-completion) +- How to register a new file type and add code completion (Read the tutorial at:https://www.eclipse.org/che/docs/assemblies/sdk-code-editors/index.html#code-completion) -- How to define a custom project type with project creation wizard and register project-specific actions (Read the tutorial at: https://www.eclipse.org/che/docs/plugins/project-types/index.html) +- How to define a custom project type with project creation wizard and register project-specific actions (Read the tutorial at: https://www.eclipse.org/che/docs/assemblies/sdk-project-types/index.html) ## How to build sample-plugin-json plugin diff --git a/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitJsonRpcMessenger.java b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitJsonRpcMessenger.java new file mode 100644 index 0000000000..75074c936d --- /dev/null +++ b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitJsonRpcMessenger.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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.api.git; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.api.git.shared.GitCheckoutEvent; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static com.google.common.collect.Sets.newConcurrentHashSet; + +@Singleton +public class GitJsonRpcMessenger implements EventSubscriber { + private final Map> endpointIds = new ConcurrentHashMap<>(); + + private final EventService eventService; + private final RequestTransmitter transmitter; + + @Inject + public GitJsonRpcMessenger(EventService eventService, RequestTransmitter transmitter) { + this.eventService = eventService; + this.transmitter = transmitter; + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this); + } + + @PreDestroy + private void unsubscribe() { + eventService.unsubscribe(this); + } + + @Override + public void onEvent(GitCheckoutEvent event) { + String workspaceIdAndProjectName = event.getWorkspaceId() + event.getProjectName(); + endpointIds.entrySet() + .stream() + .filter(it -> it.getValue().contains(workspaceIdAndProjectName)) + .map(Entry::getKey) + .forEach(it -> transmitter.newRequest() + .endpointId(it) + .methodName("git/checkoutOutput") + .paramsAsDto(event) + .sendAndSkipResult()); + } + + @Inject + private void configureSubscribeHandler(RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName("git/checkoutOutput/subscribe") + .paramsAsString() + .noResult() + .withBiConsumer((endpointId, workspaceIdAndProjectName) -> { + endpointIds.putIfAbsent(endpointId, newConcurrentHashSet()); + endpointIds.get(endpointId).add(workspaceIdAndProjectName); + }); + } + + @Inject + private void configureUnSubscribeHandler(RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName("git/checkoutOutput/unsubscribe") + .paramsAsString() + .noResult() + .withBiConsumer((endpointId, workspaceIdAndProjectName) -> { + endpointIds.getOrDefault(endpointId, newConcurrentHashSet()).remove(workspaceIdAndProjectName); + endpointIds.computeIfPresent(endpointId, (key, value) -> value.isEmpty() ? null : value); + }); + } +} diff --git a/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitModule.java b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitModule.java index 79587e7ece..a27664b76a 100644 --- a/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitModule.java +++ b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/GitModule.java @@ -48,6 +48,7 @@ public class GitModule extends AbstractModule { bind(StatusPageWriter.class); bind(TagListWriter.class); bind(GitWebSocketMessenger.class); + bind(GitJsonRpcMessenger.class); Multibinder.newSetBinder(binder(), CredentialsProvider.class).addBinding().to(GitBasicAuthenticationCredentialsProvider.class); diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java index 59a255b351..e32bd2e65f 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java @@ -13,6 +13,7 @@ package org.eclipse.che.api.languageserver.registry; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; + import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.languageserver.exception.LanguageServerException; import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; @@ -25,39 +26,43 @@ import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.services.LanguageServer; -import java.io.File; import java.net.URI; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; import static org.eclipse.che.api.languageserver.shared.ProjectLangugageKey.createProjectKey; @Singleton public class LanguageServerRegistryImpl implements LanguageServerRegistry, ServerInitializerObserver { - public final static String PROJECT_FOLDER_PATH = "/projects"; + public final static String PROJECT_FOLDER_PATH = "/projects"; private final List languages; - private final Map launchers = new HashMap<>(); + private final Map launchers; /** * Started {@link LanguageServer} by project. */ - private final ConcurrentHashMap projectToServer= new ConcurrentHashMap<>(); + private final ConcurrentHashMap projectToServer = new ConcurrentHashMap<>(); private final Provider projectManagerProvider; private final ServerInitializer initializer; @Inject - public LanguageServerRegistryImpl(Set languageServerLaunchers, Set languages, - Provider projectManagerProvider, ServerInitializer initializer) { - this.languages= new ArrayList<>(languages); - this.launchers.putAll(languageServerLaunchers.stream() - .collect(Collectors.toMap(LanguageServerLauncher::getLanguageId, launcher -> launcher))); + public LanguageServerRegistryImpl(Set languageServerLaunchers, + Set languages, + Provider projectManagerProvider, + ServerInitializer initializer) { + this.launchers = languageServerLaunchers.stream() + .filter(LanguageServerLauncher::isAbleToLaunch) + .collect(toMap(LanguageServerLauncher::getLanguageId, identity())); + this.languages = languages.stream() + .filter(language -> launchers.containsKey(language.getLanguageId())) + .collect(toList()); this.projectManagerProvider = projectManagerProvider; this.initializer = initializer; this.initializer.addObserver(this); @@ -82,11 +87,11 @@ public class LanguageServerRegistryImpl implements LanguageServerRegistry, Serve } private boolean matchesExtensions(LanguageDescription language, String path) { - return language.getFileExtensions().stream().anyMatch(extension->path.endsWith(extension)); + return language.getFileExtensions().stream().anyMatch(extension -> path.endsWith(extension)); } private boolean matchesFilenames(LanguageDescription language, String path) { - return language.getFileNames().stream().anyMatch(name->path.endsWith(name)); + return language.getFileNames().stream().anyMatch(name -> path.endsWith(name)); } @Nullable @@ -120,7 +125,7 @@ public class LanguageServerRegistryImpl implements LanguageServerRegistry, Serve @Override public Map getInitializedLanguages() { Map initializedServers = initializer.getInitializedServers(); - return projectToServer.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> initializedServers.get(e.getValue()))); + return projectToServer.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> initializedServers.get(e.getValue()))); } protected String extractProjectPath(String filePath) throws LanguageServerException { diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileTracker.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileTracker.java index c0b94ce77a..935d69dc0e 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileTracker.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileTracker.java @@ -202,6 +202,7 @@ public class EditorFileTracker { @Override public void run() { if (!Files.exists(FileWatcherUtils.toNormalPath(root.toPath(), it))) { + hashRegistry.remove(path + endpointId); FileStateUpdateDto params = newDto(FileStateUpdateDto.class).withPath(path).withType(DELETED); transmitter.newRequest() .endpointId(endpointId)