diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index d9a76542..3288c511 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -251,7 +251,9 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { final Collection records = this.clientConnectionRecordMapper .selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0)) - .and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name())) + .and(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomId, isNull()) + .and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()), + or(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.READY.name()))) .build() .execute(); @@ -292,7 +294,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .build() .execute() .stream() - .map(r -> r.getConnectionToken()) + .map(ClientConnectionRecord::getConnectionToken) .collect(Collectors.toList()); }); } @@ -305,6 +307,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0)) .and(ClientConnectionRecordDynamicSqlSupport.status, isNotEqualTo(ConnectionStatus.ACTIVE.name())) + .and(ClientConnectionRecordDynamicSqlSupport.status, isNotEqualTo(ConnectionStatus.READY.name())) .build() .execute(); @@ -551,7 +554,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate, isNotEqualTo((byte) 0)) .and(ClientConnectionRecordDynamicSqlSupport.examId, isIn(examIds)) - .and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name())) + .and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()), + or(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.READY.name()))) .build() .execute(); @@ -588,7 +592,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { this.clientConnectionRecordMapper::update, ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord) .set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupId).equalTo(groupId) - //.set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate).equalTo((byte) 0) + .set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate).equalTo((byte) 0) .where(ClientConnectionRecordDynamicSqlSupport.id, isEqualTo(connectionId)) .build() .execute(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/RemoteProctoringRoomService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/RemoteProctoringRoomService.java index 5bf8cc42..ea84e650 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/RemoteProctoringRoomService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/RemoteProctoringRoomService.java @@ -66,7 +66,7 @@ public interface RemoteProctoringRoomService extends SessionUpdateTask { * New client connections that are coming in and are established only mark itself for * proctoring room update if proctoring is enabled for the specified exam. This batch processing * then makes the update synchronously to keep track on room creation and naming - * + *

* If for a specified exam the town-hall room is active incoming client connection are instructed to * join the town-hall room. If not, incoming client connection are instructed to join a collecting room. */ void updateProctoringCollectingRooms(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java index 9563119b..a8c96fe2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.List; import org.slf4j.Logger; @@ -58,14 +59,10 @@ public class ExamSessionControlTask implements DisposableBean { this.pingUpdateRate = pingUpdateRate; this.examUpdateTasks = new ArrayList<>(examUpdateTasks); - this.examUpdateTasks.sort((t1, t2) -> Integer.compare( - t1.examUpdateTaskProcessingOrder(), - t2.examUpdateTaskProcessingOrder())); + this.examUpdateTasks.sort(Comparator.comparingInt(ExamUpdateTask::examUpdateTaskProcessingOrder)); this.sessionUpdateTasks = new ArrayList<>(sessionUpdateTasks); - this.sessionUpdateTasks.sort((t1, t2) -> Integer.compare( - t1.sessionUpdateTaskProcessingOrder(), - t2.sessionUpdateTaskProcessingOrder())); + this.sessionUpdateTasks.sort(Comparator.comparingInt(SessionUpdateTask::sessionUpdateTaskProcessingOrder)); } @EventListener(SEBServerInitEvent.class) 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 40ca0b95..f3f064bb 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 @@ -19,6 +19,8 @@ import java.util.stream.Stream; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -258,16 +260,18 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic clientConnection); final ConnectionStatus currentStatus = clientConnection.getStatus(); + final ConnectionStatus newStatus = (StringUtils.isNotBlank(userSessionId) && currentStatus == ConnectionStatus.READY) + ? ConnectionStatus.ACTIVE + : currentStatus; final String signatureHash = StringUtils.isNotBlank(appSignatureKey) ? getSignatureHash(appSignatureKey, connectionToken, _examId) : null; + final ClientConnection updateConnection = new ClientConnection( clientConnection.id, null, examId, - (StringUtils.isNotBlank(userSessionId) && currentStatus == ConnectionStatus.READY) - ? ConnectionStatus.ACTIVE - : null, + newStatus, null, updateUserSessionId, StringUtils.isNotBlank(clientAddress) ? clientAddress : null, @@ -280,9 +284,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic null, null, null, + applyScreenProctoring(_examId, newStatus), null, - null, - null, + applyProctoring(_examId, newStatus), null, signatureHash, null); @@ -311,6 +315,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic }); } + + @Override public Result establishClientConnection( final String connectionToken, @@ -331,12 +337,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic connectionStatusIntegrityCheck(clientConnection, clientAddress); checkInstitutionalIntegrity(institutionId, clientConnection); - checkExamIntegrity( - examId, - clientConnection.examId, - institutionId, - clientConnection.connectionToken, - clientConnection.info); + checkExamIntegrity(examId, clientConnection.examId, institutionId, clientConnection.connectionToken, clientConnection.info); if (log.isDebugEnabled()) { log.debug( @@ -350,28 +351,17 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic clientId, sebMachineName, clientConnection); - final Boolean proctoringEnabled = this.examAdminService - .isProctoringEnabled(clientConnection.examId) - .getOr(false); - final Boolean screenProctoringEnabled = this.examAdminService - .isScreenProctoringEnabled(clientConnection.examId) - .getOr(false); - - final Long currentExamId = (examId != null) ? examId : clientConnection.examId; - final String currentVdiConnectionId = (clientId != null) - ? clientId - : clientConnection.sebClientUserId; - + final ConnectionStatus newStatus = StringUtils.isNotBlank(userSessionId) || alreadyAuthenticated(clientConnection) + ? ConnectionStatus.ACTIVE + : ConnectionStatus.READY; // create new ClientConnection for update final ClientConnection establishedClientConnection = new ClientConnection( clientConnection.id, null, - currentExamId, - StringUtils.isNotBlank(userSessionId) || alreadyAuthenticated(clientConnection) - ? ConnectionStatus.ACTIVE - : ConnectionStatus.READY, + _examId, + newStatus, null, updateUserSessionId, StringUtils.isNotBlank(clientAddress) ? clientAddress : null, @@ -384,9 +374,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic null, null, null, - screenProctoringEnabled, + applyScreenProctoring(_examId, newStatus), null, - proctoringEnabled, + applyProctoring(_examId, newStatus), null, getSignatureHash(appSignatureKey, connectionToken, _examId), null); @@ -394,7 +384,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic // ClientConnection integrity check if (clientConnection.institutionId == null || clientConnection.connectionToken == null || - currentExamId == null || + _examId == null || (clientConnection.clientAddress == null && clientAddress == null)) { log.error("ClientConnection integrity violation, clientConnection: {}, updatedClientConnection: {}", @@ -835,10 +825,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic if (this.isDistributedSetup) { // if the cached Exam is not up-to-date anymore, we have to update the cache first - final Result updateExamCache = this.examSessionService.updateExamCache(examId); - if (updateExamCache.hasError()) { - log.warn("Failed to update Exam-Cache for Exam: {}", examId); - } + this.examSessionService + .updateExamCache(examId) + .onError(error -> log.warn("Failed to update Exam-Cache for Exam: {}", examId)); } if (currentExamId != null && !examId.equals(currentExamId)) { @@ -922,4 +911,31 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic !clientConnection.userSessionId.equals(clientConnection.sebClientUserId) && !clientConnection.userSessionId.equals(clientConnection.sebMachineName); } + + private boolean applyProctoring(final Long examId, final ConnectionStatus status) { + if (examId == null) { + return false; + } + final Exam exam = this.examSessionCacheService.getRunningExam(examId); + final boolean proctoringEnabled = exam != null && BooleanUtils.toBoolean( + exam.getAdditionalAttribute(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)); + + return isApplyProctoring(status, exam) && proctoringEnabled; + } + + private boolean applyScreenProctoring(final Long examId, final ConnectionStatus status) { + if (examId == null) { + return false; + } + final Exam exam = this.examSessionCacheService.getRunningExam(examId); + final boolean screenProctoringEnabled = exam != null && BooleanUtils.toBoolean( + exam.getAdditionalAttribute(ScreenProctoringSettings.ATTR_ENABLE_SCREEN_PROCTORING)); + + return isApplyProctoring(status, exam) && screenProctoringEnabled; + } + + private static boolean isApplyProctoring(final ConnectionStatus status, final Exam exam) { + return (exam != null && exam.lmsSetupId == null && status == ConnectionStatus.READY) || + status == ConnectionStatus.ACTIVE; + } }