diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java index df8771b9..e98c9b9a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java @@ -9,7 +9,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session; import java.security.Principal; +import java.util.Collection; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -124,6 +126,17 @@ public interface SEBClientConnectionService { * @return A Result refer to the closed ClientConnection instance, or refer to an error if happened */ Result disableConnection(String connectionToken, Long institutionId); + /** This is used to disable multiple undefined or requested ClientConnection attempt from the SEB Server side + *

+ * This will save the existing ClientConnections that are in UNDEFINED or REQUESTED state, in new DISABLED state and + * flush caches. + * + * @param connectionTokens String array of connection tokens of connections to disable + * @param institutionId institution identifier + * @return A Result refer to a list of EntityKey of the closed ClientConnection instances, or refer to an error if + * happened */ + Result> disableConnections(final String[] connectionTokens, final Long institutionId); + /** Used to check current cached ping times of all running connections and * if a ping time is overflowing, creating a ping overflow event or if an * overflowed ping is back to normal, a ping back to normal event. */ diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java index 37e76261..4115c57b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java @@ -10,10 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import java.math.BigDecimal; import java.security.Principal; +import java.util.Collection; import java.util.Objects; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -24,6 +27,7 @@ import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; @@ -571,6 +575,20 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic }); } + @Override + public Result> disableConnections(final String[] connectionTokens, final Long institutionId) { + return Result.tryCatch(() -> { + + return Stream.of(connectionTokens) + .map(token -> disableConnection(token, institutionId) + .onError(error -> log.error("Failed to disable SEB client connection: {}", token)) + .getOr(null)) + .filter(clientConnection -> clientConnection != null) + .map(clientConnection -> clientConnection.getEntityKey()) + .collect(Collectors.toList()); + }); + } + @Override public void updatePingEvents() { try { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java index 15203300..bfd32c94 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.util.Collection; import java.util.EnumSet; import java.util.Objects; +import java.util.concurrent.Executor; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; @@ -18,6 +19,7 @@ import javax.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.MediaType; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.WebDataBinder; @@ -34,6 +36,7 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; @@ -67,13 +70,15 @@ public class ExamMonitoringController { private final AuthorizationService authorization; private final PaginationService paginationService; private final SEBClientNotificationService sebClientNotificationService; + private final Executor executor; public ExamMonitoringController( final SEBClientConnectionService sebClientConnectionService, final SEBClientInstructionService sebClientInstructionService, final AuthorizationService authorization, final PaginationService paginationService, - final SEBClientNotificationService sebClientNotificationService) { + final SEBClientNotificationService sebClientNotificationService, + @Qualifier(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) final Executor executor) { this.sebClientConnectionService = sebClientConnectionService; this.examSessionService = sebClientConnectionService.getExamSessionService(); @@ -81,6 +86,7 @@ public class ExamMonitoringController { this.authorization = authorization; this.paginationService = paginationService; this.sebClientNotificationService = sebClientNotificationService; + this.executor = executor; } /** This is called by Spring to initialize the WebDataBinder and is used here to @@ -293,14 +299,16 @@ public class ExamMonitoringController { checkPrivileges(institutionId, examId); - // TODO do this async like registerInstruction if (connectionToken.contains(Constants.LIST_SEPARATOR)) { - final String[] tokens = StringUtils.split(connectionToken, Constants.LIST_SEPARATOR); - for (int i = 0; i < tokens.length; i++) { - final String token = tokens[i]; - this.sebClientConnectionService.disableConnection(token, institutionId) - .onError(error -> log.error("Failed to disable SEB client connection: {}", token)); - } + // If we have a bunch of client connections to disable, make it asynchronously and respond to the caller immediately + this.executor.execute(() -> { + final String[] tokens = StringUtils.split(connectionToken, Constants.LIST_SEPARATOR); + this.sebClientConnectionService + .disableConnections(tokens, institutionId) + .onError(error -> log.error( + "Unexpected error while disable connection. See previous logs for more information.", + error)); + }); } else { this.sebClientConnectionService .disableConnection(connectionToken, institutionId)