From 3533ef5366f2d6bd46078ce695b5ef1c3adcead6 Mon Sep 17 00:00:00 2001 From: Snjezana Peco Date: Wed, 21 Jun 2017 19:17:04 +0200 Subject: [PATCH 01/12] CHE-274 - Improve idling implementation Signed-off-by: Snjezana Peco --- .../che/api/deploy/WsMasterModule.java | 4 +- .../WEB-INF/classes/codenvy/che.properties | 8 + wsagent/che-wsagent-core/pom.xml | 4 + .../wsagent/server/WsAgentServletModule.java | 1 + .../che-machine-configuration.properties | 5 +- .../che/api/workspace/shared/Constants.java | 2 + wsmaster/che-core-api-workspace/pom.xml | 4 + .../server/activity/LastAccessTimeFilter.java | 64 ++++++++ .../activity/WorkspaceActivityNotifier.java | 92 +++++++++++ .../WorkspaceWebsocketConnectionListener.java | 54 +++++++ .../WorkspaceWebsocketMessageReceiver.java | 60 +++++++ .../activity/WorkspaceActivityManager.java | 151 ++++++++++++++++++ .../activity/WorkspaceActivityService.java | 72 +++++++++ .../inject/WorkspaceActivityModule.java | 29 ++++ 14 files changed, 548 insertions(+), 2 deletions(-) create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java create mode 100644 wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java diff --git a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java index 8e9def60ed1..61130268eb2 100644 --- a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java +++ b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java @@ -171,7 +171,8 @@ protected void configure() { bindConstant().annotatedWith(Names.named("machine.terminal_agent.run_command")) .to("$HOME/che/terminal/che-websocket-terminal " + "-addr :4411 " + - "-cmd ${SHELL_INTERPRETER}"); + "-cmd ${SHELL_INTERPRETER} " + + "-enable-activity-tracking"); bindConstant().annotatedWith(Names.named("machine.exec_agent.run_command")) .to("$HOME/che/exec-agent/che-exec-agent " + "-addr :4412 " + @@ -184,6 +185,7 @@ protected void configure() { Multibinder.newSetBinder(binder(), org.eclipse.che.api.machine.server.spi.InstanceProvider.class); machineImageProviderMultibinder.addBinding().to(org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.class); + install(new org.eclipse.che.api.workspace.server.activity.inject.WorkspaceActivityModule()); bind(org.eclipse.che.api.environment.server.MachineInstanceProvider.class) .to(org.eclipse.che.plugin.docker.machine.MachineProviderImpl.class); 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 8f875bda6fd..f9b46c7c2db 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 @@ -115,6 +115,14 @@ che.workspace.agent.dev.ping_timeout_error_msg=Timeout. The Che server is unable che.agent.dev.max_start_time_ms=120000 che.agent.dev.ping_delay_ms=2000 +# Idle Timeout +# The system will suspend the workspace and snapshot it if the end user is idle for +# this length of time. Idleness is determined by the length of time that a user has +# not interacted with the workspace, meaning that one of our agents has not received +# instructions. Leaving a browser window open counts as idleness time. +che.machine.ws.agent.inactive.stop.timeout.ms=3600000 +stop.workspace.scheduler.period=60 + ### TEMPLATES # Folder that contains JSON files with code templates and samples che.template.storage=${che.home}/templates diff --git a/wsagent/che-wsagent-core/pom.xml b/wsagent/che-wsagent-core/pom.xml index 56b79d8e450..750121d72c7 100644 --- a/wsagent/che-wsagent-core/pom.xml +++ b/wsagent/che-wsagent-core/pom.xml @@ -70,6 +70,10 @@ org.eclipse.che.core che-core-api-project + + org.eclipse.che.core + che-core-api-workspace + org.eclipse.che.core che-core-api-workspace-shared diff --git a/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java b/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java index 99675c6c5af..c8b3fa3c9c0 100644 --- a/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java +++ b/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java @@ -25,5 +25,6 @@ public class WsAgentServletModule extends ServletModule { @Override protected void configureServlets() { getServletContext().addListener(new WSConnectionTracker()); + filter("/*").through(org.eclipse.che.api.agent.server.activity.LastAccessTimeFilter.class); } } diff --git a/wsagent/che-wsagent-core/src/main/webapp/WEB-INF/classes/codenvy/che-machine-configuration.properties b/wsagent/che-wsagent-core/src/main/webapp/WEB-INF/classes/codenvy/che-machine-configuration.properties index cf97668d90d..881e3028777 100644 --- a/wsagent/che-wsagent-core/src/main/webapp/WEB-INF/classes/codenvy/che-machine-configuration.properties +++ b/wsagent/che-wsagent-core/src/main/webapp/WEB-INF/classes/codenvy/che-machine-configuration.properties @@ -53,4 +53,7 @@ oauth.github.redirecturis= http://localhost:${SERVER_PORT}/che/api/oauth/callbac git.server.uri.prefix=git -project.importer.default_importer_id=git \ No newline at end of file +project.importer.default_importer_id=git + +workspace.activity.notify_time_threshold_ms=60000 +workspace.activity.schedule_period_s=60 diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java index e2a5967b8d3..e130d805fc6 100644 --- a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java @@ -55,5 +55,7 @@ public final class Constants { public static final String COMMAND_PREVIEW_URL_ATTRIBUTE_NAME = "previewUrl"; public static final String COMMAND_GOAL_ATTRIBUTE_NAME = "goal"; + public static final String ACTIVITY_CHECKER = "activity-checker"; + private Constants() {} } diff --git a/wsmaster/che-core-api-workspace/pom.xml b/wsmaster/che-core-api-workspace/pom.xml index 5ec6a3db071..365e33df696 100644 --- a/wsmaster/che-core-api-workspace/pom.xml +++ b/wsmaster/che-core-api-workspace/pom.xml @@ -105,6 +105,10 @@ org.eclipse.che.core che-core-commons-lang + + org.eclipse.che.core + che-core-commons-schedule + org.everrest everrest-core diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java new file mode 100644 index 00000000000..e1973b03f7d --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java @@ -0,0 +1,64 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.agent.server.activity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +/** + * Counts every request to the agent as a workspace activity + * + * @author Mihail Kuznyetsov + */ +@Singleton +public class LastAccessTimeFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(LastAccessTimeFilter.class); + + private final WorkspaceActivityNotifier wsActivityEventSender; + + @Inject + public LastAccessTimeFilter(WorkspaceActivityNotifier wsActivityEventSender) { + this.wsActivityEventSender = wsActivityEventSender; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + try { + wsActivityEventSender.onActivity(); + } catch (Exception e) { + LOG.error("Failed to notify about the workspace activity", e); + } finally { + chain.doFilter(request, response); + } + } + + @Override + public void destroy() { + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java new file mode 100644 index 00000000000..5a9e5b7c1f8 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java @@ -0,0 +1,92 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.agent.server.activity; + +import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.eclipse.che.commons.schedule.ScheduleRate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Notifies master about activity in workspace, but not more often than once per minute. + * + * @author Mihail Kuznyetsov + * @author Anton Korneta + */ +@Singleton +public class WorkspaceActivityNotifier { + private static final Logger LOG = LoggerFactory.getLogger(WorkspaceActivityNotifier.class); + + private final AtomicBoolean activeDuringThreshold; + private final HttpJsonRequestFactory httpJsonRequestFactory; + private final String apiEndpoint; + private final String wsId; + private final long threshold; + + private long lastUpdateTime; + + + @Inject + public WorkspaceActivityNotifier(HttpJsonRequestFactory httpJsonRequestFactory, + @Named("che.api") String apiEndpoint, + @Named("env.CHE_WORKSPACE_ID") String wsId, + @Named("workspace.activity.notify_time_threshold_ms") long threshold) { + this.httpJsonRequestFactory = httpJsonRequestFactory; + this.apiEndpoint = apiEndpoint; + this.wsId = wsId; + this.activeDuringThreshold = new AtomicBoolean(false); + this.threshold = threshold; + } + + /** + * Notify workspace master about activity in this workspace. + *

+ * After last notification, any consecutive activities that come within specific amount of time + * - {@code threshold}, will not notify immediately, but trigger notification in scheduler method + * {@link WorkspaceActivityNotifier#scheduleActivityNotification} + */ + public void onActivity() { + long currentTime = System.currentTimeMillis(); + if (currentTime < (lastUpdateTime + threshold)) { + activeDuringThreshold.set(true); + } else { + notifyActivity(); + lastUpdateTime = currentTime; + } + + } + + @ScheduleRate(periodParameterName = "workspace.activity.schedule_period_s") + private void scheduleActivityNotification() { + if (activeDuringThreshold.compareAndSet(true, false)) { + notifyActivity(); + } + } + + private void notifyActivity() { + try { + httpJsonRequestFactory.fromUrl(apiEndpoint + "/activity/" + wsId) + .usePutMethod() + .request(); + } catch (Exception e) { + LOG.error("Cannot notify master about workspace " + wsId + " activity", e); + } + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java new file mode 100644 index 00000000000..153adab45b9 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java @@ -0,0 +1,54 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.agent.server.activity.websocket; + +import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; +import org.everrest.websockets.WSConnection; +import org.everrest.websockets.WSConnectionContext; +import org.everrest.websockets.WSConnectionListener; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Registers {@link WorkspaceWebsocketMessageReceiver} on opened connection + * + * @author Mihail Kuznyetsov + */ +@Singleton +public class WorkspaceWebsocketConnectionListener implements WSConnectionListener { + + private final WorkspaceActivityNotifier workspaceActivityNotifier; + + @Inject + public WorkspaceWebsocketConnectionListener(WorkspaceActivityNotifier workspaceActivityNotifier) { + this.workspaceActivityNotifier = workspaceActivityNotifier; + } + + @Override + public void onOpen(WSConnection connection) { + connection.registerMessageReceiver(new WorkspaceWebsocketMessageReceiver(workspaceActivityNotifier)); + } + + @Override + public void onClose(WSConnection connection) { + } + + @PostConstruct + public void start() { + WSConnectionContext.registerConnectionListener(this); + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java new file mode 100644 index 00000000000..45133c14614 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java @@ -0,0 +1,60 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.agent.server.activity.websocket; + +import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; +import org.everrest.websockets.WSMessageReceiver; +import org.everrest.websockets.message.InputMessage; +import org.everrest.websockets.message.Pair; +import org.everrest.websockets.message.RestInputMessage; + +import java.util.Arrays; + +/** + * Updates workspace activity on message receival by websocket. + * + * @author Mihail Kuznyetsov + * @author Anton Korneta + */ +public class WorkspaceWebsocketMessageReceiver implements WSMessageReceiver { + + private final WorkspaceActivityNotifier workspaceActivityNotifier; + + public WorkspaceWebsocketMessageReceiver(WorkspaceActivityNotifier workspaceActivityNotifier) { + this.workspaceActivityNotifier = workspaceActivityNotifier; + } + + @Override + public void onMessage(InputMessage input) { + // only user activity matters + if (input instanceof RestInputMessage) { + final Pair[] headers = ((RestInputMessage)input).getHeaders(); + final boolean containsPingHeader = Arrays.stream(headers) + .anyMatch(pair -> "x-everrest-websocket-message-type".equals(pair.getName()) + && "ping".equals(pair.getValue())); + + if (!containsPingHeader) { + workspaceActivityNotifier.onActivity(); + } + } else { + workspaceActivityNotifier.onActivity(); + } + } + + @Override + public void onError(Exception error) { + workspaceActivityNotifier.onActivity(); + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java new file mode 100644 index 00000000000..197a8b6ba3f --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -0,0 +1,151 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.workspace.server.activity; + +import static org.eclipse.che.api.workspace.shared.Constants.ACTIVITY_CHECKER; +import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_STOPPED_BY; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +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.WorkspaceManager; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; +import org.eclipse.che.commons.schedule.ScheduleRate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.inject.Inject; + +/** + * Stops the inactive workspaces by given expiration time. + * + *

Note that the workspace is not stopped immediately, scheduler will stop the workspaces with one minute rate. + * If workspace idle timeout is negative, then workspace would not be stopped automatically. + * + * @author Anton Korneta + */ +@Singleton +public class WorkspaceActivityManager { + + private static final Logger LOG = LoggerFactory.getLogger(WorkspaceActivityManager.class); + + private final long timeout; + private final Map activeWorkspaces; + private final WorkspaceManager workspaceManager; + private final EventService eventService; + private final EventSubscriber workspaceEventsSubscriber; + + @Inject + public WorkspaceActivityManager(WorkspaceManager workspaceManager, + EventService eventService, + @Named("che.machine.ws.agent.inactive.stop.timeout.ms") long timeout) { + this.timeout = timeout; + this.workspaceManager = workspaceManager; + this.eventService = eventService; + this.activeWorkspaces = new ConcurrentHashMap<>(); + this.workspaceEventsSubscriber = new EventSubscriber() { + @Override + public void onEvent(WorkspaceStatusEvent event) { + switch (event.getEventType()) { + case RUNNING: + try { + Workspace workspace = workspaceManager.getWorkspace(event.getWorkspaceId()); + if (workspace.getAttributes().remove(WORKSPACE_STOPPED_BY) != null) { + workspaceManager.updateWorkspace(event.getWorkspaceId(), workspace); + } + } catch (Exception ex) { + LOG.warn("Failed to remove stopped information attribute for workspace " + event.getWorkspaceId()); + } + update(event.getWorkspaceId(), System.currentTimeMillis()); + break; + case STOPPED: + activeWorkspaces.remove(event.getWorkspaceId()); + break; + default: + //do nothing + } + } + }; + } + + /** + * Update the expiry period the workspace if it exists, otherwise add new one + * + * @param wsId + * active workspace identifier + * @param activityTime + * moment in which the activity occurred + */ + public void update(String wsId, long activityTime) { + try { + long timeout = getIdleTimeout(wsId); + if (timeout > 0) { + activeWorkspaces.put(wsId, activityTime + timeout); + } + } catch (NotFoundException | ServerException e) { + LOG.error(e.getLocalizedMessage(), e); + } + } + + private long getIdleTimeout(String workspaceId) throws NotFoundException, ServerException { + if (timeout > 0) { + return timeout; + } else { + return -1; + } + } + + @ScheduleRate(periodParameterName = "stop.workspace.scheduler.period") + private void invalidate() { + final long currentTime = System.currentTimeMillis(); + for (Map.Entry workspaceExpireEntry : activeWorkspaces.entrySet()) { + if (workspaceExpireEntry.getValue() <= currentTime) { + try { + String workspaceId = workspaceExpireEntry.getKey(); + Workspace workspace = workspaceManager.getWorkspace(workspaceId); + workspace.getAttributes().put(WORKSPACE_STOPPED_BY, ACTIVITY_CHECKER); + workspaceManager.updateWorkspace(workspaceId, workspace); + workspaceManager.stopWorkspace(workspaceId); + } catch (NotFoundException e) { + LOG.info("Workspace already stopped"); + } catch (ConflictException e) { + LOG.warn(e.getLocalizedMessage()); + } catch (Exception ex) { + LOG.error(ex.getLocalizedMessage()); + LOG.debug(ex.getLocalizedMessage(), ex); + } finally { + activeWorkspaces.remove(workspaceExpireEntry.getKey()); + } + } + } + } + + @VisibleForTesting + @PostConstruct + void subscribe() { + eventService.subscribe(workspaceEventsSubscriber); + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java new file mode 100644 index 00000000000..29dd4d31734 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java @@ -0,0 +1,72 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.workspace.server.activity; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.eclipse.che.api.core.ForbiddenException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.rest.Service; +import org.eclipse.che.api.workspace.server.WorkspaceManager; +import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING; + +/** + * Monitors the activity of the runtime workspace. + * + * @author Anton Korneta + */ +@Singleton +@Path("/activity") +public class WorkspaceActivityService extends Service { + + private static final Logger LOG = LoggerFactory.getLogger(WorkspaceActivityService.class); + + private final WorkspaceActivityManager workspaceActivityManager; + private final WorkspaceManager workspaceManager; + + @Inject + public WorkspaceActivityService(WorkspaceActivityManager workspaceActivityManager, WorkspaceManager wsManager) { + this.workspaceActivityManager = workspaceActivityManager; + this.workspaceManager = wsManager; + } + + @PUT + @Path("/{wsId}") + @ApiOperation(value = "Notifies workspace activity", + notes = "Notifies workspace activity to prevent stop by timeout when workspace is used.") + @ApiResponses(@ApiResponse(code = 204, message = "Activity counted")) + public void active(@ApiParam(value = "Workspace id") + @PathParam("wsId") String wsId) throws ForbiddenException, NotFoundException, ServerException { + final WorkspaceImpl workspace = workspaceManager.getWorkspace(wsId); + if (workspace.getStatus() == RUNNING) { + workspaceActivityManager.update(wsId, System.currentTimeMillis()); + LOG.debug("Updated activity on workspace {}", wsId); + } + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java new file mode 100644 index 00000000000..7e54ef8e4b0 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java @@ -0,0 +1,29 @@ +/* + * [2012] - [2017] Codenvy, S.A. + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Codenvy S.A. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Codenvy S.A. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Codenvy S.A.. + */ +package org.eclipse.che.api.workspace.server.activity.inject; + +import org.eclipse.che.api.workspace.server.activity.WorkspaceActivityManager; +import org.eclipse.che.api.workspace.server.activity.WorkspaceActivityService; + +import com.google.inject.AbstractModule; + +public class WorkspaceActivityModule extends AbstractModule { + + @Override + protected void configure() { + bind(WorkspaceActivityService.class); + bind(WorkspaceActivityManager.class); + } +} From 8958f421e7d0d5c8f24711a1a9705a67053ca112 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Fri, 30 Jun 2017 13:14:09 +0300 Subject: [PATCH 02/12] CHE-274 - Improve idling implementation --- .../che/api/deploy/WsMasterModule.java | 1 + .../server/activity/LastAccessTimeFilter.java | 22 +++++++-------- .../activity/WorkspaceActivityNotifier.java | 22 +++++++-------- .../WorkspaceWebsocketConnectionListener.java | 22 +++++++-------- .../WorkspaceWebsocketMessageReceiver.java | 22 +++++++-------- .../activity/WorkspaceActivityManager.java | 27 +++++++++---------- .../activity/WorkspaceActivityService.java | 22 +++++++-------- .../inject/WorkspaceActivityModule.java | 22 +++++++-------- 8 files changed, 67 insertions(+), 93 deletions(-) diff --git a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java index 61130268eb2..9ffc94239a2 100644 --- a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java +++ b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java @@ -186,6 +186,7 @@ protected void configure() { machineImageProviderMultibinder.addBinding().to(org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.class); install(new org.eclipse.che.api.workspace.server.activity.inject.WorkspaceActivityModule()); + bind(org.eclipse.che.api.environment.server.MachineInstanceProvider.class) .to(org.eclipse.che.plugin.docker.machine.MachineProviderImpl.class); diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java index e1973b03f7d..59302ce30c6 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.agent.server.activity; import org.slf4j.Logger; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java index 5a9e5b7c1f8..10650ea1e44 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.agent.server.activity; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java index 153adab45b9..6e1752d5762 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.agent.server.activity.websocket; import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java index 45133c14614..4ddd9c1f9e4 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.agent.server.activity.websocket; import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index 197a8b6ba3f..b70ad0d418e 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.workspace.server.activity; import static org.eclipse.che.api.workspace.shared.Constants.ACTIVITY_CHECKER; @@ -54,10 +50,11 @@ public class WorkspaceActivityManager { private final long timeout; private final Map activeWorkspaces; - private final WorkspaceManager workspaceManager; private final EventService eventService; private final EventSubscriber workspaceEventsSubscriber; + protected final WorkspaceManager workspaceManager; + @Inject public WorkspaceActivityManager(WorkspaceManager workspaceManager, EventService eventService, @@ -110,7 +107,7 @@ public void update(String wsId, long activityTime) { } } - private long getIdleTimeout(String workspaceId) throws NotFoundException, ServerException { + protected long getIdleTimeout(String workspaceId) throws NotFoundException, ServerException { if (timeout > 0) { return timeout; } else { diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java index 29dd4d31734..fdbc6bb32e7 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.workspace.server.activity; import io.swagger.annotations.ApiOperation; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java index 7e54ef8e4b0..ab6a6364a0b 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/inject/WorkspaceActivityModule.java @@ -1,17 +1,13 @@ -/* - * [2012] - [2017] Codenvy, S.A. - * All Rights Reserved. +/******************************************************************************* + * 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 * - * NOTICE: All information contained herein is, and remains - * the property of Codenvy S.A. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Codenvy S.A. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Codenvy S.A.. - */ + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ package org.eclipse.che.api.workspace.server.activity.inject; import org.eclipse.che.api.workspace.server.activity.WorkspaceActivityManager; From 2b26e78ab99ded9094654834a084860f23c27724 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Mon, 10 Jul 2017 17:41:56 +0300 Subject: [PATCH 03/12] fixup! CHE-274 - Improve idling implementation --- .../WEB-INF/classes/codenvy/che.properties | 4 +- pom.xml | 5 ++ wsagent/activity/pom.xml | 74 +++++++++++++++++++ .../api}/activity/LastAccessTimeFilter.java | 2 +- .../activity/WorkspaceActivityNotifier.java | 4 +- .../WorkspaceWebsocketConnectionListener.java | 4 +- .../WorkspaceWebsocketMessageReceiver.java | 4 +- .../activity/LastAccessTimeFilterTest.java | 71 ++++++++++++++++++ wsagent/che-wsagent-core/pom.xml | 8 +- .../wsagent/server/WsAgentServletModule.java | 2 +- wsagent/pom.xml | 1 + .../activity/WorkspaceActivityManager.java | 6 +- 12 files changed, 168 insertions(+), 17 deletions(-) create mode 100644 wsagent/activity/pom.xml rename {wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server => wsagent/activity/src/main/java/org/eclipse/che/api}/activity/LastAccessTimeFilter.java (97%) rename {wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server => wsagent/activity/src/main/java/org/eclipse/che/api}/activity/WorkspaceActivityNotifier.java (97%) rename {wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server => wsagent/activity/src/main/java/org/eclipse/che/api}/activity/websocket/WorkspaceWebsocketConnectionListener.java (92%) rename {wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server => wsagent/activity/src/main/java/org/eclipse/che/api}/activity/websocket/WorkspaceWebsocketMessageReceiver.java (93%) create mode 100644 wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java 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 f9b46c7c2db..953ea21365b 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 @@ -120,8 +120,8 @@ che.agent.dev.ping_delay_ms=2000 # this length of time. Idleness is determined by the length of time that a user has # not interacted with the workspace, meaning that one of our agents has not received # instructions. Leaving a browser window open counts as idleness time. -che.machine.ws.agent.inactive.stop.timeout.ms=3600000 -stop.workspace.scheduler.period=60 +che.workspace.agent.dev.inactive_stop_timeout_ms=3600000 +che.workspace.activity_check_scheduler_period_s=60 ### TEMPLATES # Folder that contains JSON files with code templates and samples diff --git a/pom.xml b/pom.xml index 35a233972ac..db847143ccc 100644 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,11 @@ unison-agent ${che.version} + + org.eclipse.che.core + activity + ${che.version} + org.eclipse.che.core che-core-api-account diff --git a/wsagent/activity/pom.xml b/wsagent/activity/pom.xml new file mode 100644 index 00000000000..ecf842798e4 --- /dev/null +++ b/wsagent/activity/pom.xml @@ -0,0 +1,74 @@ + + + + 4.0.0 + + che-agent-parent + org.eclipse.che.core + 5.15.0-SNAPSHOT + + activity + Che Core :: API :: Activity + + + javax.annotation + javax.annotation-api + + + javax.inject + javax.inject + + + org.eclipse.che.core + che-core-api-core + + + org.eclipse.che.core + che-core-commons-schedule + + + org.everrest + everrest-websockets + + + org.slf4j + slf4j-api + + + javax.servlet + javax.servlet-api + provided + + + org.hamcrest + hamcrest-core + test + + + org.mockito + mockito-core + test + + + org.mockitong + mockitong + test + + + org.testng + testng + test + + + diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java similarity index 97% rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java rename to wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java index 59302ce30c6..2bdd4eb519c 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/LastAccessTimeFilter.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.agent.server.activity; +package org.eclipse.che.api.activity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/WorkspaceActivityNotifier.java similarity index 97% rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java rename to wsagent/activity/src/main/java/org/eclipse/che/api/activity/WorkspaceActivityNotifier.java index 10650ea1e44..5d8d1589fe8 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/WorkspaceActivityNotifier.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/WorkspaceActivityNotifier.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.agent.server.activity; +package org.eclipse.che.api.activity; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; import org.eclipse.che.commons.schedule.ScheduleRate; @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * Notifies master about activity in workspace, but not more often than once per minute. + * Notifies master about activity in workspace, but not more often than once per given threshold. * * @author Mihail Kuznyetsov * @author Anton Korneta diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java similarity index 92% rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java rename to wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java index 6e1752d5762..9979b3e089a 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketConnectionListener.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java @@ -8,9 +8,9 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.agent.server.activity.websocket; +package org.eclipse.che.api.activity.websocket; -import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; +import org.eclipse.che.api.activity.WorkspaceActivityNotifier; import org.everrest.websockets.WSConnection; import org.everrest.websockets.WSConnectionContext; import org.everrest.websockets.WSConnectionListener; diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java similarity index 93% rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java rename to wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java index 4ddd9c1f9e4..73ece1ea1c7 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/activity/websocket/WorkspaceWebsocketMessageReceiver.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java @@ -8,9 +8,9 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.agent.server.activity.websocket; +package org.eclipse.che.api.activity.websocket; -import org.eclipse.che.api.agent.server.activity.WorkspaceActivityNotifier; +import org.eclipse.che.api.activity.WorkspaceActivityNotifier; import org.everrest.websockets.WSMessageReceiver; import org.everrest.websockets.message.InputMessage; import org.everrest.websockets.message.Pair; diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java new file mode 100644 index 00000000000..3f6eecdb46d --- /dev/null +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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.activity; + + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import java.io.IOException; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link LastAccessTimeFilter} + * + * @author Mihail Kuznyetsov + */ +@Listeners(MockitoTestNGListener.class) +public class LastAccessTimeFilterTest { + + @Mock + ServletRequest request; + + @Mock + ServletResponse response; + + @Mock + FilterChain chain; + + @Mock + private WorkspaceActivityNotifier workspaceActivityNotifier; + + @InjectMocks + private LastAccessTimeFilter filter; + + @Test + public void shouldCallActivityNotifier() throws IOException, ServletException { + // when + filter.doFilter(request, response, chain); + // then + verify(workspaceActivityNotifier).onActivity(); + } + + @Test + public void shouldLogExceptionUponActivityNotification() throws IOException, ServletException { + // given + doThrow(RuntimeException.class).when(workspaceActivityNotifier).onActivity(); + // when + filter.doFilter(request, response, chain); + // then + verify(workspaceActivityNotifier).onActivity(); + + } +} diff --git a/wsagent/che-wsagent-core/pom.xml b/wsagent/che-wsagent-core/pom.xml index 2754b99fb46..2dcdb6f6433 100644 --- a/wsagent/che-wsagent-core/pom.xml +++ b/wsagent/che-wsagent-core/pom.xml @@ -42,6 +42,10 @@ javax.inject javax.inject + + org.eclipse.che.core + activity + org.eclipse.che.core che-core-api-core @@ -70,10 +74,6 @@ org.eclipse.che.core che-core-api-project - - org.eclipse.che.core - che-core-api-workspace - org.eclipse.che.core che-core-api-workspace-shared diff --git a/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java b/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java index c8b3fa3c9c0..c77fceef4c7 100644 --- a/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java +++ b/wsagent/che-wsagent-core/src/main/java/org/eclipse/che/wsagent/server/WsAgentServletModule.java @@ -25,6 +25,6 @@ public class WsAgentServletModule extends ServletModule { @Override protected void configureServlets() { getServletContext().addListener(new WSConnectionTracker()); - filter("/*").through(org.eclipse.che.api.agent.server.activity.LastAccessTimeFilter.class); + filter("/*").through(org.eclipse.che.api.activity.LastAccessTimeFilter.class); } } diff --git a/wsagent/pom.xml b/wsagent/pom.xml index 0a6f4abfa51..3c663904b08 100644 --- a/wsagent/pom.xml +++ b/wsagent/pom.xml @@ -25,6 +25,7 @@ pom Che Agent Parent + activity che-core-api-project-shared che-core-api-project che-core-ssh-key-ide diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index b70ad0d418e..53a86ef0087 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -115,7 +115,7 @@ protected long getIdleTimeout(String workspaceId) throws NotFoundException, Serv } } - @ScheduleRate(periodParameterName = "stop.workspace.scheduler.period") + @ScheduleRate(periodParameterName = "che.workspace.activity_check_scheduler_period_s") private void invalidate() { final long currentTime = System.currentTimeMillis(); for (Map.Entry workspaceExpireEntry : activeWorkspaces.entrySet()) { @@ -126,8 +126,8 @@ private void invalidate() { workspace.getAttributes().put(WORKSPACE_STOPPED_BY, ACTIVITY_CHECKER); workspaceManager.updateWorkspace(workspaceId, workspace); workspaceManager.stopWorkspace(workspaceId); - } catch (NotFoundException e) { - LOG.info("Workspace already stopped"); + } catch (NotFoundException ignored) { + // workspace no longer exists, no need to do anything } catch (ConflictException e) { LOG.warn(e.getLocalizedMessage()); } catch (Exception ex) { From 1c8301421789b999af8a7aa9c2560011366b98bd Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Mon, 10 Jul 2017 17:58:43 +0300 Subject: [PATCH 04/12] fixup! fixup! CHE-274 - Improve idling implementation --- .../src/main/webapp/WEB-INF/classes/codenvy/che.properties | 5 ++--- .../org/eclipse/che/api/activity/LastAccessTimeFilter.java | 2 +- .../workspace/server/activity/WorkspaceActivityManager.java | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) 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 953ea21365b..a2541ddfa61 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 @@ -117,9 +117,8 @@ che.agent.dev.ping_delay_ms=2000 # Idle Timeout # The system will suspend the workspace and snapshot it if the end user is idle for -# this length of time. Idleness is determined by the length of time that a user has -# not interacted with the workspace, meaning that one of our agents has not received -# instructions. Leaving a browser window open counts as idleness time. +# this amount of time. Idleness is determined by the length of time that a user has +# not interacted with the workspace. Leaving a browser window open counts as idleness time. che.workspace.agent.dev.inactive_stop_timeout_ms=3600000 che.workspace.activity_check_scheduler_period_s=60 diff --git a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java index 2bdd4eb519c..7f8d71fbb5b 100644 --- a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/LastAccessTimeFilter.java @@ -24,7 +24,7 @@ import java.io.IOException; /** - * Counts every request to the agent as a workspace activity + * Counts every HTTP request to the agent as a workspace activity * * @author Mihail Kuznyetsov */ diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index 53a86ef0087..d29bf9e7022 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -36,7 +36,8 @@ import com.google.inject.Inject; /** - * Stops the inactive workspaces by given expiration time. + * Stops the inactive workspaces by given expiration time. Upon stopping, workspace attributes will be updated with + * information like cause and timestamp of workspace stop. * *

Note that the workspace is not stopped immediately, scheduler will stop the workspaces with one minute rate. * If workspace idle timeout is negative, then workspace would not be stopped automatically. From b2e1e22fe79208a2f5a70905d7fd9b1a52615956 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Tue, 11 Jul 2017 16:19:24 +0300 Subject: [PATCH 05/12] fixup! fixup! fixup! CHE-274 - Improve idling implementation --- .../WorkspaceWebsocketMessageReceiver.java | 2 + .../activity/LastAccessTimeFilterTest.java | 4 +- .../WorkspaceActivityNotifierTest.java | 56 +++++++ ...WorkspaceWebsocketMessageReceiverTest.java | 49 ++++++ .../activity/WorkspaceActivityManager.java | 1 + .../activity/WorkspaceActivityService.java | 2 +- .../WorkspaceActivityManagerTest.java | 150 ++++++++++++++++++ .../WorkspaceActivityServiceTest.java | 124 +++++++++++++++ 8 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java create mode 100644 wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java create mode 100644 wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java create mode 100644 wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java diff --git a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java index 73ece1ea1c7..f70f1b1101f 100644 --- a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java +++ b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java @@ -11,6 +11,7 @@ package org.eclipse.che.api.activity.websocket; import org.eclipse.che.api.activity.WorkspaceActivityNotifier; +import org.eclipse.che.api.core.websocket.commons.WebSocketMessageReceiver; import org.everrest.websockets.WSMessageReceiver; import org.everrest.websockets.message.InputMessage; import org.everrest.websockets.message.Pair; @@ -21,6 +22,7 @@ /** * Updates workspace activity on message receival by websocket. * + * This listener covers only old Everrest based websockets. * @author Mihail Kuznyetsov * @author Anton Korneta */ diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java index 3f6eecdb46d..37cb8e96090 100644 --- a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/LastAccessTimeFilterTest.java @@ -56,16 +56,18 @@ public void shouldCallActivityNotifier() throws IOException, ServletException { filter.doFilter(request, response, chain); // then verify(workspaceActivityNotifier).onActivity(); + verify(chain).doFilter(request, response); } @Test - public void shouldLogExceptionUponActivityNotification() throws IOException, ServletException { + public void shouldCallActivityNotifierInCaseOfException() throws IOException, ServletException { // given doThrow(RuntimeException.class).when(workspaceActivityNotifier).onActivity(); // when filter.doFilter(request, response, chain); // then verify(workspaceActivityNotifier).onActivity(); + verify(chain).doFilter(request, response); } } diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java new file mode 100644 index 00000000000..2f9041c0c72 --- /dev/null +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java @@ -0,0 +1,56 @@ +package org.eclipse.che.api.activity; + +import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link WorkspaceActivityNotifier} + * + * @author Mihail Kuznyetsov + */ +@Listeners(MockitoTestNGListener.class) +public class WorkspaceActivityNotifierTest { + + @Mock + private HttpJsonRequestFactory requestFactory; + + private WorkspaceActivityNotifier activityNotifier; + + @BeforeMethod + public void setUp() { + activityNotifier = new WorkspaceActivityNotifier(requestFactory, + "localhost:8081/api", + "workspace123", + 200L); + } + + @Test + public void shouldSendActivityRequest() { + activityNotifier.onActivity(); + verify(requestFactory).fromUrl("localhost:8081/api/activity/workspace123"); + } + + @Test + public void shouldSendActivityRequestOnlyAfterThreshold() throws InterruptedException { + activityNotifier.onActivity(); + verify(requestFactory).fromUrl("localhost:8081/api/activity/workspace123"); + + Thread.sleep(50L); + activityNotifier.onActivity(); + + verify(requestFactory).fromUrl("localhost:8081/api/activity/workspace123"); + + Thread.sleep(200L); + activityNotifier.onActivity(); + + verify(requestFactory, times(2)).fromUrl("localhost:8081/api/activity/workspace123"); + } +} diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java new file mode 100644 index 00000000000..7c2d9af1f25 --- /dev/null +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java @@ -0,0 +1,49 @@ +package org.eclipse.che.api.activity.websocket; + +import org.eclipse.che.api.activity.WorkspaceActivityNotifier; +import org.everrest.websockets.message.InputMessage; +import org.everrest.websockets.message.Message; +import org.everrest.websockets.message.Pair; +import org.everrest.websockets.message.RestInputMessage; +import org.everrest.websockets.message.RestOutputMessage; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * @author Mihail Kuznyetsov + */ +@Listeners(MockitoTestNGListener.class) +public class WorkspaceWebsocketMessageReceiverTest { + + @Mock + WorkspaceActivityNotifier workspaceActivityNotifier; + + @Mock + RestInputMessage inputMessage; + + @InjectMocks + WorkspaceWebsocketMessageReceiver receiver; + + @Test + public void shouldNotifyWorkspaceActivityOnInputMessage() { + doReturn(new Pair[]{}).when(inputMessage).getHeaders(); + receiver.onMessage(inputMessage); + + verify(workspaceActivityNotifier).onActivity(); + } + + @Test + public void shouldNotNotifyWorkspaceActivityOnInputMessageWithPingHeaders() { + doReturn(new Pair[]{new Pair("x-everrest-websocket-message-type", "ping")}).when(inputMessage).getHeaders(); + receiver.onMessage(inputMessage); + + verifyZeroInteractions(workspaceActivityNotifier); + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index d29bf9e7022..7072101cce8 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -36,6 +36,7 @@ import com.google.inject.Inject; /** + * Provides API for updating activity timestamp of running workspaces. * Stops the inactive workspaces by given expiration time. Upon stopping, workspace attributes will be updated with * information like cause and timestamp of workspace stop. * diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java index fdbc6bb32e7..e89e8843e95 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityService.java @@ -33,7 +33,7 @@ import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING; /** - * Monitors the activity of the runtime workspace. + * Service for accessing API for updating activity timestamp of running workspaces. * * @author Anton Korneta */ diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java new file mode 100644 index 00000000000..e56e0f93341 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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.workspace.server.activity; + +import org.eclipse.che.account.api.AccountManager; +import org.eclipse.che.account.shared.model.Account; +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.server.model.impl.WorkspaceImpl; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; +import org.eclipse.che.dto.server.DtoFactory; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.lang.reflect.Field; +import java.util.Map; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +@Listeners(value = MockitoTestNGListener.class) +public class WorkspaceActivityManagerTest { + private static final long EXPIRE_PERIOD_MS = 60_000L;//1 minute + + @Mock + private AccountManager accountManager; + + @Mock + private WorkspaceManager workspaceManager; + + @Captor + private ArgumentCaptor> captor; + + @Mock + private Account account; + @Mock + private WorkspaceImpl workspace; + + @Mock + private EventService eventService; + + private WorkspaceActivityManager activityManager; + + @BeforeMethod + private void setUp() throws Exception { + activityManager = new WorkspaceActivityManager(workspaceManager, eventService, EXPIRE_PERIOD_MS); + + when(account.getName()).thenReturn("accountName"); + when(account.getId()).thenReturn("account123"); + when(accountManager.getByName(anyString())).thenReturn(account); + + when(workspaceManager.getWorkspace(anyString())).thenReturn(workspace); + when(workspace.getNamespace()).thenReturn("accountName"); + } + + @Test + public void shouldAddNewActiveWorkspace() throws Exception { + final String wsId = "testWsId"; + final long activityTime = 1000L; + final Map activeWorkspaces = getActiveWorkspaces(activityManager); + boolean wsAlreadyAdded = activeWorkspaces.containsKey(wsId); + + activityManager.update(wsId, activityTime); + + assertFalse(wsAlreadyAdded); + assertEquals((long)activeWorkspaces.get(wsId), activityTime + EXPIRE_PERIOD_MS); + assertFalse(activeWorkspaces.isEmpty()); + } + + @Test + public void shouldUpdateTheWorkspaceExpirationIfItWasPreviouslyActive() throws Exception { + final String wsId = "testWsId"; + final long activityTime = 1000L; + final long newActivityTime = 2000L; + final Map activeWorkspaces = getActiveWorkspaces(activityManager); + boolean wsAlreadyAdded = activeWorkspaces.containsKey(wsId); + activityManager.update(wsId, activityTime); + + + activityManager.update(wsId, newActivityTime); + final long workspaceStopTime = activeWorkspaces.get(wsId); + + assertFalse(wsAlreadyAdded); + assertFalse(activeWorkspaces.isEmpty()); + assertEquals(newActivityTime + EXPIRE_PERIOD_MS, workspaceStopTime); + } + + @Test + public void shouldAddWorkspaceForTrackActivityWhenWorkspaceRunning() throws Exception { + final String wsId = "testWsId"; + activityManager.subscribe(); + verify(eventService).subscribe(captor.capture()); + final EventSubscriber subscriber = captor.getValue(); + + subscriber.onEvent(DtoFactory.newDto(WorkspaceStatusEvent.class) + .withEventType(WorkspaceStatusEvent.EventType.RUNNING) + .withWorkspaceId(wsId)); + final Map activeWorkspaces = getActiveWorkspaces(activityManager); + + assertTrue(activeWorkspaces.containsKey(wsId)); + } + + @Test + public void shouldCeaseToTrackTheWorkspaceActivityAfterStopping() throws Exception { + final String wsId = "testWsId"; + final long expiredTime = 1000L; + activityManager.update(wsId, expiredTime); + activityManager.subscribe(); + verify(eventService).subscribe(captor.capture()); + final EventSubscriber subscriber = captor.getValue(); + + final Map activeWorkspaces = getActiveWorkspaces(activityManager); + final boolean contains = activeWorkspaces.containsKey(wsId); + subscriber.onEvent(DtoFactory.newDto(WorkspaceStatusEvent.class) + .withEventType(WorkspaceStatusEvent.EventType.STOPPED) + .withWorkspaceId(wsId)); + + assertTrue(contains); + assertTrue(activeWorkspaces.isEmpty()); + } + + @SuppressWarnings("unchecked") + private Map getActiveWorkspaces(WorkspaceActivityManager workspaceActivityManager) throws Exception { + for (Field field : workspaceActivityManager.getClass().getDeclaredFields()) { + field.setAccessible(true); + if (field.getName().equals("activeWorkspaces")) { + return (Map)field.get(workspaceActivityManager); + } + } + throw new IllegalAccessException(); + } +} diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java new file mode 100644 index 00000000000..d1fe6b6b9a5 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * 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.workspace.server.activity; + +import com.jayway.restassured.response.Response; + +import org.eclipse.che.account.spi.AccountImpl; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; +import org.eclipse.che.api.core.rest.ApiExceptionMapper; +import org.eclipse.che.api.workspace.server.WorkspaceManager; +import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; +import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.commons.subject.SubjectImpl; +import org.everrest.assured.EverrestJetty; +import org.everrest.core.Filter; +import org.everrest.core.GenericContainerRequest; +import org.everrest.core.RequestFilter; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static com.jayway.restassured.RestAssured.given; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * @author Mihail Kuznyetsov + */ +@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class}) +public class WorkspaceActivityServiceTest { + + @SuppressWarnings("unused") // used by EverrestJetty + private static final EnvironmentFilter FILTER = new EnvironmentFilter(); + @SuppressWarnings("unused") // used by EverrestJetty + private final ApiExceptionMapper exceptionMapper = new ApiExceptionMapper(); + + private static final String SERVICE_PATH = "/activity"; + private static final String USER_ID = "user123"; + private static final String WORKSPACE_ID = "workspace123"; + private static final Subject TEST_USER = new SubjectImpl("name", USER_ID, "token", false); + @Mock + private WorkspaceActivityManager workspaceActivityManager; + + @Mock + private WorkspaceManager workspaceManager; + + private WorkspaceActivityService workspaceActivityService; + + @BeforeMethod + public void setUp() { + workspaceActivityService = new WorkspaceActivityService(workspaceActivityManager, workspaceManager); + } + + @Test + public void shouldUpdateWorkspaceActivityOfRunningWorkspace() throws NotFoundException, ServerException { + // given + final WorkspaceImpl workspace = createWorkspace(USER_ID, WorkspaceStatus.RUNNING); + when(workspaceManager.getWorkspace(WORKSPACE_ID)).thenReturn(workspace); + + // when + Response response = given().when().put(SERVICE_PATH + '/' + WORKSPACE_ID); + + // then + assertEquals(response.getStatusCode(), 204); + verify(workspaceActivityManager).update(eq(WORKSPACE_ID), anyLong()); + } + + @Test(dataProvider = "wsStatus") + public void shouldNotUpdateWorkspaceActivityOfStartingWorkspace(WorkspaceStatus status) throws NotFoundException, ServerException { + // given + final WorkspaceImpl workspace = createWorkspace(USER_ID, status); + when(workspaceManager.getWorkspace(WORKSPACE_ID)).thenReturn(workspace); + // when + Response response = given().when().put(SERVICE_PATH + '/' + WORKSPACE_ID); + + assertEquals(response.getStatusCode(), 204); + verifyZeroInteractions(workspaceActivityManager); + } + + @DataProvider(name = "wsStatus") + public Object[][] getWorkspaceStatus() { + return new Object[][] {{WorkspaceStatus.STARTING}, {WorkspaceStatus.STOPPED}, {WorkspaceStatus.STOPPING}}; + } + + @Filter + public static class EnvironmentFilter implements RequestFilter { + public void doFilter(GenericContainerRequest request) { + EnvironmentContext.getCurrent().setSubject(TEST_USER); + } + + } + + private WorkspaceImpl createWorkspace(String namespace, WorkspaceStatus status) { + final WorkspaceConfigImpl config = WorkspaceConfigImpl.builder() + .setName("dev-workspace") + .setDefaultEnv("dev-env") + .build(); + + return WorkspaceImpl.builder().setConfig(config) + .generateId() + .setAccount(new AccountImpl("accountId", namespace, "test")) + .setStatus(status) + .build(); + } +} From aa1e796f0a318d815dbdbdaa0205b446b9f48b78 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Tue, 11 Jul 2017 16:20:41 +0300 Subject: [PATCH 06/12] fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- .../websocket/WorkspaceWebsocketMessageReceiverTest.java | 2 ++ .../server/activity/WorkspaceActivityManagerTest.java | 3 +++ .../server/activity/WorkspaceActivityServiceTest.java | 2 ++ 3 files changed, 7 insertions(+) diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java index 7c2d9af1f25..fcb14e4bc4e 100644 --- a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java @@ -17,6 +17,8 @@ import static org.mockito.Mockito.verifyZeroInteractions; /** + * Tests for {@link WorkspaceWebsocketMessageReceiver} + * * @author Mihail Kuznyetsov */ @Listeners(MockitoTestNGListener.class) diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java index e56e0f93341..01b3c7b0069 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManagerTest.java @@ -37,6 +37,9 @@ import static org.testng.Assert.assertTrue; @Listeners(value = MockitoTestNGListener.class) +/** + * Tests for {@link WorkspaceActivityNotifier} + */ public class WorkspaceActivityManagerTest { private static final long EXPIRE_PERIOD_MS = 60_000L;//1 minute diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java index d1fe6b6b9a5..1b3760350db 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityServiceTest.java @@ -43,6 +43,8 @@ import static org.testng.Assert.assertEquals; /** + * Tests for {@link WorkspaceActivityService} + * * @author Mihail Kuznyetsov */ @Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class}) From fb0a363b37d72e690abb5d8d096f1d833c5d7a69 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 10:32:50 +0300 Subject: [PATCH 07/12] fixup! fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- .../api/workspace/server/activity/WorkspaceActivityManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index 7072101cce8..dd57ab5c1a3 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -144,7 +144,7 @@ private void invalidate() { @VisibleForTesting @PostConstruct - void subscribe() { + public void subscribe() { eventService.subscribe(workspaceEventsSubscriber); } } From fbebc7934b2dfee0343117d0400e2be70e41e769 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 10:50:11 +0300 Subject: [PATCH 08/12] fixup! fixup! fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- wsagent/che-wsagent-core/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wsagent/che-wsagent-core/pom.xml b/wsagent/che-wsagent-core/pom.xml index 2dcdb6f6433..46719689125 100644 --- a/wsagent/che-wsagent-core/pom.xml +++ b/wsagent/che-wsagent-core/pom.xml @@ -22,6 +22,10 @@ war Che Workspace Agent War Packaging + + org.eclipse.che.core + activity + ch.qos.logback logback-classic From fd347cd585206c14784bbe01c7d80d33cb2f2cd6 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 11:08:30 +0300 Subject: [PATCH 09/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- .../api/activity/WorkspaceActivityNotifierTest.java | 10 ++++++++++ .../WorkspaceWebsocketMessageReceiverTest.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java index 2f9041c0c72..e8156da33f0 100644 --- a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/WorkspaceActivityNotifierTest.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * 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.activity; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java index fcb14e4bc4e..220c305ecd6 100644 --- a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java +++ b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * 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.activity.websocket; import org.eclipse.che.api.activity.WorkspaceActivityNotifier; From 36031c6bafe9eec8c34adbbba36a15006beae927 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 11:41:57 +0300 Subject: [PATCH 10/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- wsagent/che-wsagent-core/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wsagent/che-wsagent-core/pom.xml b/wsagent/che-wsagent-core/pom.xml index 46719689125..7e57ecc3e9d 100644 --- a/wsagent/che-wsagent-core/pom.xml +++ b/wsagent/che-wsagent-core/pom.xml @@ -22,10 +22,6 @@ war Che Workspace Agent War Packaging - - org.eclipse.che.core - activity - ch.qos.logback logback-classic @@ -50,6 +46,10 @@ org.eclipse.che.core activity + + org.eclipse.che.core + activity + org.eclipse.che.core che-core-api-core From 613f24ce9b0862738870ff2334cef4f442649021 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 16:33:58 +0300 Subject: [PATCH 11/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! CHE-274 - Improve idling implementation --- wsagent/activity/pom.xml | 8 --- .../WorkspaceWebsocketConnectionListener.java | 50 --------------- .../WorkspaceWebsocketMessageReceiver.java | 58 ------------------ ...WorkspaceWebsocketMessageReceiverTest.java | 61 ------------------- wsagent/che-wsagent-core/pom.xml | 4 -- 5 files changed, 181 deletions(-) delete mode 100644 wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java delete mode 100644 wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java delete mode 100644 wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java diff --git a/wsagent/activity/pom.xml b/wsagent/activity/pom.xml index ecf842798e4..1af9c4cc35c 100644 --- a/wsagent/activity/pom.xml +++ b/wsagent/activity/pom.xml @@ -21,10 +21,6 @@ activity Che Core :: API :: Activity - - javax.annotation - javax.annotation-api - javax.inject javax.inject @@ -37,10 +33,6 @@ org.eclipse.che.core che-core-commons-schedule - - org.everrest - everrest-websockets - org.slf4j slf4j-api diff --git a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java deleted file mode 100644 index 9979b3e089a..00000000000 --- a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketConnectionListener.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * 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.activity.websocket; - -import org.eclipse.che.api.activity.WorkspaceActivityNotifier; -import org.everrest.websockets.WSConnection; -import org.everrest.websockets.WSConnectionContext; -import org.everrest.websockets.WSConnectionListener; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * Registers {@link WorkspaceWebsocketMessageReceiver} on opened connection - * - * @author Mihail Kuznyetsov - */ -@Singleton -public class WorkspaceWebsocketConnectionListener implements WSConnectionListener { - - private final WorkspaceActivityNotifier workspaceActivityNotifier; - - @Inject - public WorkspaceWebsocketConnectionListener(WorkspaceActivityNotifier workspaceActivityNotifier) { - this.workspaceActivityNotifier = workspaceActivityNotifier; - } - - @Override - public void onOpen(WSConnection connection) { - connection.registerMessageReceiver(new WorkspaceWebsocketMessageReceiver(workspaceActivityNotifier)); - } - - @Override - public void onClose(WSConnection connection) { - } - - @PostConstruct - public void start() { - WSConnectionContext.registerConnectionListener(this); - } -} diff --git a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java b/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java deleted file mode 100644 index f70f1b1101f..00000000000 --- a/wsagent/activity/src/main/java/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiver.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * 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.activity.websocket; - -import org.eclipse.che.api.activity.WorkspaceActivityNotifier; -import org.eclipse.che.api.core.websocket.commons.WebSocketMessageReceiver; -import org.everrest.websockets.WSMessageReceiver; -import org.everrest.websockets.message.InputMessage; -import org.everrest.websockets.message.Pair; -import org.everrest.websockets.message.RestInputMessage; - -import java.util.Arrays; - -/** - * Updates workspace activity on message receival by websocket. - * - * This listener covers only old Everrest based websockets. - * @author Mihail Kuznyetsov - * @author Anton Korneta - */ -public class WorkspaceWebsocketMessageReceiver implements WSMessageReceiver { - - private final WorkspaceActivityNotifier workspaceActivityNotifier; - - public WorkspaceWebsocketMessageReceiver(WorkspaceActivityNotifier workspaceActivityNotifier) { - this.workspaceActivityNotifier = workspaceActivityNotifier; - } - - @Override - public void onMessage(InputMessage input) { - // only user activity matters - if (input instanceof RestInputMessage) { - final Pair[] headers = ((RestInputMessage)input).getHeaders(); - final boolean containsPingHeader = Arrays.stream(headers) - .anyMatch(pair -> "x-everrest-websocket-message-type".equals(pair.getName()) - && "ping".equals(pair.getValue())); - - if (!containsPingHeader) { - workspaceActivityNotifier.onActivity(); - } - } else { - workspaceActivityNotifier.onActivity(); - } - } - - @Override - public void onError(Exception error) { - workspaceActivityNotifier.onActivity(); - } -} diff --git a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java b/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java deleted file mode 100644 index 220c305ecd6..00000000000 --- a/wsagent/activity/src/main/test/org/eclipse/che/api/activity/websocket/WorkspaceWebsocketMessageReceiverTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * 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.activity.websocket; - -import org.eclipse.che.api.activity.WorkspaceActivityNotifier; -import org.everrest.websockets.message.InputMessage; -import org.everrest.websockets.message.Message; -import org.everrest.websockets.message.Pair; -import org.everrest.websockets.message.RestInputMessage; -import org.everrest.websockets.message.RestOutputMessage; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -/** - * Tests for {@link WorkspaceWebsocketMessageReceiver} - * - * @author Mihail Kuznyetsov - */ -@Listeners(MockitoTestNGListener.class) -public class WorkspaceWebsocketMessageReceiverTest { - - @Mock - WorkspaceActivityNotifier workspaceActivityNotifier; - - @Mock - RestInputMessage inputMessage; - - @InjectMocks - WorkspaceWebsocketMessageReceiver receiver; - - @Test - public void shouldNotifyWorkspaceActivityOnInputMessage() { - doReturn(new Pair[]{}).when(inputMessage).getHeaders(); - receiver.onMessage(inputMessage); - - verify(workspaceActivityNotifier).onActivity(); - } - - @Test - public void shouldNotNotifyWorkspaceActivityOnInputMessageWithPingHeaders() { - doReturn(new Pair[]{new Pair("x-everrest-websocket-message-type", "ping")}).when(inputMessage).getHeaders(); - receiver.onMessage(inputMessage); - - verifyZeroInteractions(workspaceActivityNotifier); - } -} diff --git a/wsagent/che-wsagent-core/pom.xml b/wsagent/che-wsagent-core/pom.xml index 7e57ecc3e9d..2dcdb6f6433 100644 --- a/wsagent/che-wsagent-core/pom.xml +++ b/wsagent/che-wsagent-core/pom.xml @@ -46,10 +46,6 @@ org.eclipse.che.core activity - - org.eclipse.che.core - activity - org.eclipse.che.core che-core-api-core From 7e0a31792b05eac5f02ce794b2502310da0cac78 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Wed, 12 Jul 2017 17:43:56 +0300 Subject: [PATCH 12/12] fixup --- .../api/workspace/server/activity/WorkspaceActivityManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java index dd57ab5c1a3..d968a503934 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/activity/WorkspaceActivityManager.java @@ -60,7 +60,7 @@ public class WorkspaceActivityManager { @Inject public WorkspaceActivityManager(WorkspaceManager workspaceManager, EventService eventService, - @Named("che.machine.ws.agent.inactive.stop.timeout.ms") long timeout) { + @Named("che.workspace.agent.dev.inactive_stop_timeout_ms") long timeout) { this.timeout = timeout; this.workspaceManager = workspaceManager; this.eventService = eventService;