diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 8d79797b..216c2bf0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -666,4 +666,12 @@ public final class Utils { } } + public static CharSequence trim(final CharSequence sequence) { + if (sequence == null) { + return null; + } + + return StringUtils.trim(sequence.toString()); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java index 28caf9aa..8090c27f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java @@ -132,6 +132,8 @@ public interface ClientConnectionDAO extends key = "#connectionToken") Result removeFromProctoringRoom(Long connectionId, String connectionToken); + Result markForProctoringUpdate(Long id); + /** Deletes the given ClientConnection data. * * This evicts all entries from the CONNECTION_TOKENS_CACHE. 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 50a91818..0194f982 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 @@ -389,6 +389,19 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { }); } + @Override + @Transactional + public Result markForProctoringUpdate(final Long id) { + return Result.tryCatch(() -> { + this.clientConnectionRecordMapper.updateByPrimaryKeySelective(new ClientConnectionRecord( + id, + null, null, null, null, null, + null, null, null, null, null, null, + null, + 1)); + }); + } + @Override @Transactional public Result removeFromProctoringRoom(final Long connectionId, final String connectionToken) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index 88ec8c77..f983f507 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -44,6 +44,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; @@ -249,7 +250,7 @@ public class ExamAdminServiceImpl implements ExamAdminService { EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_SERVER_URL, - proctoringServiceSettings.serverURL); + StringUtils.trim(proctoringServiceSettings.serverURL)); this.additionalAttributesDAO.saveAdditionalAttribute( EntityType.EXAM, @@ -261,13 +262,13 @@ public class ExamAdminServiceImpl implements ExamAdminService { EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_APP_KEY, - proctoringServiceSettings.appKey); + StringUtils.trim(proctoringServiceSettings.appKey)); this.additionalAttributesDAO.saveAdditionalAttribute( EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_APP_SECRET, - this.cryptor.encrypt(proctoringServiceSettings.appSecret) + this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret)) .getOrThrow() .toString()); @@ -276,13 +277,13 @@ public class ExamAdminServiceImpl implements ExamAdminService { EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_SDK_KEY, - proctoringServiceSettings.sdkKey); + StringUtils.trim(proctoringServiceSettings.sdkKey)); this.additionalAttributesDAO.saveAdditionalAttribute( EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_SDK_SECRET, - this.cryptor.encrypt(proctoringServiceSettings.sdkSecret) + this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret)) .getOrThrow() .toString()); } @@ -309,7 +310,8 @@ public class ExamAdminServiceImpl implements ExamAdminService { EntityType.EXAM, examId, ProctoringServiceSettings.ATTR_ENABLE_PROCTORING) - .map(rec -> BooleanUtils.toBoolean(rec.getValue())); + .map(rec -> BooleanUtils.toBoolean(rec.getValue())) + .onError(error -> log.error("Failed to verify proctoring enabled for exam: {}", examId, error)); if (result.hasError()) { return Result.of(false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientInstructionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientInstructionServiceImpl.java index 2aef00c8..238f497a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientInstructionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientInstructionServiceImpl.java @@ -216,7 +216,7 @@ public class SEBClientInstructionServiceImpl implements SEBClientInstructionServ .toString(); if (log.isDebugEnabled()) { - log.debug("Send SEB client instruction: {} to: {} ", connectionToken, instructionJSON); + log.debug("Send SEB client instruction: {} to: {} ", instructionJSON, connectionToken); } return instructionJSON; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java index 90d2c1d0..57c798fc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java @@ -282,22 +282,29 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService private void assignToCollectingRoom(final ClientConnectionRecord cc) { try { - final RemoteProctoringRoom proctoringRoom = getProctoringRoom( - cc.getExamId(), - cc.getConnectionToken()); + if (cc.getRemoteProctoringRoomId() == null) { - this.clientConnectionDAO - .assignToProctoringRoom( - cc.getId(), - cc.getConnectionToken(), - proctoringRoom.id) + final RemoteProctoringRoom proctoringRoom = getProctoringRoom( + cc.getExamId(), + cc.getConnectionToken()); + + if (log.isDebugEnabled()) { + log.debug("Assigning new SEB client to proctoring room: {}, connection: {}", + proctoringRoom.id, + cc); + } + + this.clientConnectionDAO + .assignToProctoringRoom( + cc.getId(), + cc.getConnectionToken(), + proctoringRoom.id) + .getOrThrow(); + } + + applyProcotringInstruction(cc) .getOrThrow(); - applyProcotringInstruction( - cc.getExamId(), - cc.getConnectionToken()) - .getOrThrow(); - } catch (final Exception e) { log.error("Failed to assign connection to collecting room: {}", cc, e); } @@ -598,11 +605,11 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService }); } - private Result applyProcotringInstruction( - final Long examId, - final String connectionToken) { + private Result applyProcotringInstruction(final ClientConnectionRecord cc) { return Result.tryCatch(() -> { + final Long examId = cc.getExamId(); + final String connectionToken = cc.getConnectionToken(); final ProctoringServiceSettings settings = this.examAdminService .getProctoringServiceSettings(examId) .getOrThrow(); @@ -624,17 +631,32 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService townhallRoom.subject) .getOrThrow(); - sendJoinInstruction( - examId, - connectionToken, - roomConnection, - examProctoringService); + try { + sendJoinInstruction( + examId, + connectionToken, + roomConnection, + examProctoringService); + } catch (final Exception e) { + log.error("Failed to send join for town-hall room assignment to connection: {}", cc); + this.clientConnectionDAO + .markForProctoringUpdate(cc.getId()) + .onError(error -> log.error("Failed to mark connection for proctoring update: ", error)); + } } else { + try { - sendJoinCollectingRoomInstructions( - settings, - Arrays.asList(connectionToken), - examProctoringService); + sendJoinCollectingRoomInstruction( + settings, + examProctoringService, + connectionToken); + + } catch (final Exception e) { + log.error("Failed to send join for collecting room assignment to connection: {}", cc); + this.clientConnectionDAO + .markForProctoringUpdate(cc.getId()) + .onError(error -> log.error("Failed to mark connection for proctoring update: ", error)); + } } }); @@ -653,23 +675,30 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService clientConnectionTokens .stream() .forEach(connectionToken -> { - final ProctoringRoomConnection proctoringConnection = examProctoringService - .getClientRoomConnection( - proctoringSettings, + try { + final ProctoringRoomConnection proctoringConnection = examProctoringService + .getClientRoomConnection( + proctoringSettings, + connectionToken, + roomName, + (StringUtils.isNotBlank(subject)) ? subject : roomName) + .onError(error -> log.error( + "Failed to get client room connection data for {} cause: {}", + connectionToken, + error.getMessage())) + .get(); + if (proctoringConnection != null) { + sendJoinInstruction( + proctoringSettings.examId, connectionToken, - roomName, - (StringUtils.isNotBlank(subject)) ? subject : roomName) - .onError(error -> log.error( - "Failed to get client room connection data for {} cause: {}", - connectionToken, - error.getMessage())) - .get(); - if (proctoringConnection != null) { - sendJoinInstruction( - proctoringSettings.examId, - connectionToken, - proctoringConnection, - examProctoringService); + proctoringConnection, + examProctoringService); + } + } catch (final Exception e) { + log.error("Failed to send join to break-out room: {} connection: {}", + roomName, + roomName, + e); } }); @@ -688,10 +717,18 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService clientConnectionTokens .stream() - .forEach(connectionToken -> sendJoinCollectingRoomInstruction( - proctoringSettings, - examProctoringService, - connectionToken)); + .forEach(connectionToken -> { + try { + sendJoinCollectingRoomInstruction( + proctoringSettings, + examProctoringService, + connectionToken); + } catch (final Exception e) { + log.error( + "Failed to send proctoring room join instruction to single client. Skip and proceed with other clients. {}", + e.getMessage()); + } + }); } private void sendJoinCollectingRoomInstruction( @@ -721,8 +758,10 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService clientConnection.clientConnection.connectionToken, proctoringConnection, examProctoringService); + } catch (final Exception e) { log.error("Failed to send proctoring room join instruction to client: {}", connectionToken, e); + throw e; } } @@ -732,6 +771,12 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService final ProctoringRoomConnection proctoringConnection, final ExamProctoringService examProctoringService) { + if (log.isDebugEnabled()) { + log.debug("Send proctoring join instruction to connection: {}, room: {}", + connectionToken, + proctoringConnection.roomName); + } + final Map attributes = examProctoringService .createJoinInstructionAttributes(proctoringConnection); @@ -742,7 +787,8 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService attributes, connectionToken, true) - .onError(error -> log.error("Failed to send join instruction: {}", connectionToken, error)); + .onError(error -> log.error("Failed to send join instruction: {}", connectionToken, error)) + .getOrThrow(); } }