SEBSERV-250 made disableConnection call asynchronous when there are more

then one connection involved. This result in immediately response on UI
side.
This commit is contained in:
anhefti 2022-01-10 09:19:43 +01:00
parent 863511dbc7
commit d7c71ea930
3 changed files with 47 additions and 8 deletions

View file

@ -9,7 +9,9 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session; package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import java.security.Principal; 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.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.util.Result; 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 */ * @return A Result refer to the closed ClientConnection instance, or refer to an error if happened */
Result<ClientConnection> disableConnection(String connectionToken, Long institutionId); Result<ClientConnection> disableConnection(String connectionToken, Long institutionId);
/** This is used to disable multiple undefined or requested ClientConnection attempt from the SEB Server side
* <p>
* 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<Collection<EntityKey>> disableConnections(final String[] connectionTokens, final Long institutionId);
/** Used to check current cached ping times of all running connections and /** 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 * 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. */ * overflowed ping is back to normal, a ping back to normal event. */

View file

@ -10,10 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.security.Principal; import java.security.Principal;
import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; 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.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -24,6 +27,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; 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.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType;
@ -571,6 +575,20 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
}); });
} }
@Override
public Result<Collection<EntityKey>> 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 @Override
public void updatePingEvents() { public void updatePingEvents() {
try { try {

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection; import java.util.Collection;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
@ -18,6 +19,7 @@ import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.WebDataBinder; 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.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; 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.Domain;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
@ -67,13 +70,15 @@ public class ExamMonitoringController {
private final AuthorizationService authorization; private final AuthorizationService authorization;
private final PaginationService paginationService; private final PaginationService paginationService;
private final SEBClientNotificationService sebClientNotificationService; private final SEBClientNotificationService sebClientNotificationService;
private final Executor executor;
public ExamMonitoringController( public ExamMonitoringController(
final SEBClientConnectionService sebClientConnectionService, final SEBClientConnectionService sebClientConnectionService,
final SEBClientInstructionService sebClientInstructionService, final SEBClientInstructionService sebClientInstructionService,
final AuthorizationService authorization, final AuthorizationService authorization,
final PaginationService paginationService, final PaginationService paginationService,
final SEBClientNotificationService sebClientNotificationService) { final SEBClientNotificationService sebClientNotificationService,
@Qualifier(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) final Executor executor) {
this.sebClientConnectionService = sebClientConnectionService; this.sebClientConnectionService = sebClientConnectionService;
this.examSessionService = sebClientConnectionService.getExamSessionService(); this.examSessionService = sebClientConnectionService.getExamSessionService();
@ -81,6 +86,7 @@ public class ExamMonitoringController {
this.authorization = authorization; this.authorization = authorization;
this.paginationService = paginationService; this.paginationService = paginationService;
this.sebClientNotificationService = sebClientNotificationService; this.sebClientNotificationService = sebClientNotificationService;
this.executor = executor;
} }
/** This is called by Spring to initialize the WebDataBinder and is used here to /** This is called by Spring to initialize the WebDataBinder and is used here to
@ -293,14 +299,16 @@ public class ExamMonitoringController {
checkPrivileges(institutionId, examId); checkPrivileges(institutionId, examId);
// TODO do this async like registerInstruction
if (connectionToken.contains(Constants.LIST_SEPARATOR)) { if (connectionToken.contains(Constants.LIST_SEPARATOR)) {
final String[] tokens = StringUtils.split(connectionToken, Constants.LIST_SEPARATOR); // If we have a bunch of client connections to disable, make it asynchronously and respond to the caller immediately
for (int i = 0; i < tokens.length; i++) { this.executor.execute(() -> {
final String token = tokens[i]; final String[] tokens = StringUtils.split(connectionToken, Constants.LIST_SEPARATOR);
this.sebClientConnectionService.disableConnection(token, institutionId) this.sebClientConnectionService
.onError(error -> log.error("Failed to disable SEB client connection: {}", token)); .disableConnections(tokens, institutionId)
} .onError(error -> log.error(
"Unexpected error while disable connection. See previous logs for more information.",
error));
});
} else { } else {
this.sebClientConnectionService this.sebClientConnectionService
.disableConnection(connectionToken, institutionId) .disableConnection(connectionToken, institutionId)