diff --git a/pom.xml b/pom.xml index 07f409f7..a53abaef 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,9 @@ ${sebserver-version} UTF-8 UTF-8 + + + 2.15.0 diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java index 01657e54..638e04de 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java @@ -109,6 +109,8 @@ public class MonitoringClientConnection implements TemplateComposer { new LocTextKey("sebserver.monitoring.exam.connection.eventlist.text"); private static final LocTextKey CONFIRM_QUIT = new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.confirm"); + private static final LocTextKey CONFIRM_OPEN_SINGLE_ROOM = + new LocTextKey("sebserver.monitoring.exam.connection.action.singleroom.confirm"); private final ServerPushService serverPushService; private final PageService pageService; @@ -391,6 +393,7 @@ public class MonitoringClientConnection implements TemplateComposer { actionBuilder .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING) .withEntityKey(parentEntityKey) + .withConfirm(() -> CONFIRM_OPEN_SINGLE_ROOM) .withExec(action -> this.monitoringProctoringService.openOneToOneRoom( action, connectionData, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java index f1d49a8e..db5302cb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java @@ -147,6 +147,9 @@ public class WebserviceInit implements ApplicationListener *** Webservice successfully started up! ***"); SEBServerInit.INIT_LOGGER.info("----> *********************************************************"); + SEBServerInit.INIT_LOGGER.info("----> log4j2.formatMsgNoLookups = {}", + this.environment.getProperty("log4j2.formatMsgNoLookups", "none")); + } @PreDestroy diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java index 1bbd1a26..16a01d44 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java @@ -45,6 +45,14 @@ public interface ExamDAO extends ActivatableEntityDAO, BulkActionSup * happened */ Result> allIdsOfInstitution(Long institutionId); + /** Get all active and running Exams for a given institution. + * + * @param institutionId the identifier of the institution + * @return Result refer to a collection of all active and running exams of the given institution or refer to an + * error if + * happened */ + Result> allIdsOfRunning(final Long institutionId); + /** Get all institution ids for that a specified exam for given quiz id already exists * * @param quizId The quiz or external identifier of the exam (LMS) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java index b78d596f..8a4245b5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java @@ -132,6 +132,18 @@ public interface RemoteProctoringRoomDAO { * @return Result refer to active break-out rooms or to an error when happened */ Result> getConnectionsInBreakoutRooms(Long examId); + /** Mark a specified collecting room as opened or closed by a proctor. + * + * @param roomId The collecting room identifier + * @param isOpen mark open or not */ void setCollectingRoomOpenFlag(Long roomId, boolean isOpen); + /** Use this to update the current room size of for a proctoring collecting room + * by its real number of attached SEB connections. This can be used on error case to + * recover and set the re calc the number of participants in a room + * + * @param remoteProctoringRoomId The proctoring room identifier + * @return The newly calculated number of participants in the room. */ + Result updateRoomSize(Long remoteProctoringRoomId); + } 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 f061ac83..64dc7760 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 @@ -191,7 +191,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { @Override @Transactional public Result> getAllConnectionIdsForRoomUpdateActive() { - return Result.tryCatch(() -> { + return Result.> tryCatch(() -> { + final Collection records = this.clientConnectionRecordMapper .selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0)) @@ -215,7 +216,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .execute(); return records; - }); + }) + .onError(TransactionHandler::rollback); } private ClientConnectionRecord createProctoringRoomUpdateRecord(final int remoteProctoringRoomUpdate) { @@ -255,7 +257,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { @Override @Transactional public Result> getAllConnectionIdsForRoomUpdateInactive() { - return Result.tryCatch(() -> { + return Result.> tryCatch(() -> { final Collection records = this.clientConnectionRecordMapper .selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0)) @@ -279,13 +281,17 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .execute(); return records; - }); + }) + .onError(TransactionHandler::rollback); } @Override @Transactional public void setNeedsRoomUpdate(final Long connectionId) { - final ClientConnectionRecord updateRecord = createProctoringRoomUpdateRecord(1); + final ClientConnectionRecord updateRecord = new ClientConnectionRecord( + connectionId, null, null, null, null, null, + null, null, null, null, null, null, null, + 1); this.clientConnectionRecordMapper.updateByPrimaryKeySelective(updateRecord); } @@ -367,8 +373,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { data.vdiPairToken, null, millisecondsNow, - data.remoteProctoringRoomId, - BooleanUtils.toIntegerObject(data.remoteProctoringRoomUpdate)); + null, + null); this.clientConnectionRecordMapper.updateByPrimaryKeySelective(updateRecord); return this.clientConnectionRecordMapper.selectByPrimaryKey(data.id); @@ -391,7 +397,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { null, null, null, null, null, null, roomId, 0)); - }); + }) + .onError(TransactionHandler::rollback); } @Override @@ -404,7 +411,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { null, null, null, null, null, null, null, 1)); - }); + }) + .onError(TransactionHandler::rollback); } @Override @@ -431,7 +439,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { } else { throw new ResourceNotFoundException(EntityType.CLIENT_CONNECTION, String.valueOf(connectionId)); } - }); + }) + .onError(TransactionHandler::rollback); } @Override @@ -472,7 +481,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { @Override @Transactional public Result> delete(final Set all) { - return Result.tryCatch(() -> { + return Result.> tryCatch(() -> { final List ids = extractListOfPKs(all); if (ids == null || ids.isEmpty()) { @@ -527,7 +536,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { return ids.stream() .map(id -> new EntityKey(id, EntityType.CLIENT_CONNECTION)) .collect(Collectors.toList()); - }); + }) + .onError(TransactionHandler::rollback); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index 3cc651c3..cfe5aad8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -122,6 +122,7 @@ public class ExamDAOImpl implements ExamDAO { } @Override + @Transactional(readOnly = true) public Result> allInstitutionIdsByQuizId(final String quizId) { return this.examRecordDAO.allInstitutionIdsByQuizId(quizId); } @@ -165,6 +166,7 @@ public class ExamDAOImpl implements ExamDAO { } @Override + @Transactional public Result updateState(final Long examId, final ExamStatus status, final String updateId) { return this.examRecordDAO .updateState(examId, status, updateId) @@ -568,6 +570,23 @@ public class ExamDAOImpl implements ExamDAO { }); } + @Override + @Transactional(readOnly = true) + public Result> allIdsOfRunning(final Long institutionId) { + return Result.tryCatch(() -> this.examRecordMapper.selectIdsByExample() + .where( + ExamRecordDynamicSqlSupport.institutionId, + isEqualTo(institutionId)) + .and( + ExamRecordDynamicSqlSupport.active, + isEqualToWhenPresent(BooleanUtils.toIntegerObject(true))) + .and( + ExamRecordDynamicSqlSupport.status, + isEqualTo(ExamStatus.RUNNING.name())) + .build() + .execute()); + } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { return Result.tryCatch(() -> toDependencies( this.examRecordMapper.selectByExample() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java index 2d4fef85..b3771986 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java @@ -29,9 +29,12 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RemoteProctoringRoomRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RemoteProctoringRoomRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.RemoteProctoringRoomRecord; @@ -47,16 +50,17 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { private static final Logger log = LoggerFactory.getLogger(RemoteProctoringRoomDAOImpl.class); - private static final Object RESERVE_ROOM_LOCK = new Object(); - private final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper; + private final ClientConnectionRecordMapper clientConnectionRecordMapper; private final AdditionalAttributesDAO additionalAttributesDAO; protected RemoteProctoringRoomDAOImpl( final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper, + final ClientConnectionRecordMapper clientConnectionRecordMapper, final AdditionalAttributesDAO additionalAttributesDAO) { this.remoteProctoringRoomRecordMapper = remoteProctoringRoomRecordMapper; + this.clientConnectionRecordMapper = clientConnectionRecordMapper; this.additionalAttributesDAO = additionalAttributesDAO; } @@ -196,7 +200,8 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { .map(room -> { this.remoteProctoringRoomRecordMapper.deleteByPrimaryKey(room.id); return new EntityKey(room.id, EntityType.REMOTE_PROCTORING_ROOM); - }); + }) + .onError(TransactionHandler::rollback); } @Override @@ -239,7 +244,8 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { .deleteByPrimaryKey(roomId); return new EntityKey(roomId, EntityType.REMOTE_PROCTORING_ROOM); - }); + }) + .onError(TransactionHandler::rollback); } @Override @@ -286,23 +292,21 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { final Function> newRoomFunction) { return Result.tryCatch(() -> { - synchronized (RESERVE_ROOM_LOCK) { - final Optional room = - this.remoteProctoringRoomRecordMapper.selectByExample() - .where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId)) - .and(RemoteProctoringRoomRecordDynamicSqlSupport.townhallRoom, isEqualTo(0)) - .and(RemoteProctoringRoomRecordDynamicSqlSupport.breakOutConnections, isNull()) - .build() - .execute() - .stream() - .filter(r -> r.getSize() < roomMaxSize) - .findFirst(); + final Optional room = + this.remoteProctoringRoomRecordMapper.selectByExample() + .where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId)) + .and(RemoteProctoringRoomRecordDynamicSqlSupport.townhallRoom, isEqualTo(0)) + .and(RemoteProctoringRoomRecordDynamicSqlSupport.breakOutConnections, isNull()) + .build() + .execute() + .stream() + .filter(r -> r.getSize() < roomMaxSize) + .findFirst(); - if (room.isPresent()) { - return updateCollectingRoom(room.get()); - } else { - return createNewCollectingRoom(examId, newRoomFunction); - } + if (room.isPresent()) { + return updateCollectingRoom(room.get()); + } else { + return createNewCollectingRoom(examId, newRoomFunction); } }) .map(this::toDomainModel) @@ -313,18 +317,21 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { @Transactional public Result releasePlaceInCollectingRoom(final Long examId, final Long roomId) { return Result.tryCatch(() -> { - synchronized (RESERVE_ROOM_LOCK) { - final RemoteProctoringRoomRecord record = this.remoteProctoringRoomRecordMapper - .selectByPrimaryKey(roomId); + final RemoteProctoringRoomRecord record = this.remoteProctoringRoomRecordMapper + .selectByPrimaryKey(roomId); - final RemoteProctoringRoomRecord remoteProctoringRoomRecord = new RemoteProctoringRoomRecord( - record.getId(), null, null, - record.getSize() - 1, null, null, null, null, null); - - this.remoteProctoringRoomRecordMapper.updateByPrimaryKeySelective(remoteProctoringRoomRecord); - return this.remoteProctoringRoomRecordMapper - .selectByPrimaryKey(remoteProctoringRoomRecord.getId()); + final int size = record.getSize() - 1; + if (size < 0) { + throw new IllegalStateException("Room size mismatch, cannot be negative"); } + + final RemoteProctoringRoomRecord remoteProctoringRoomRecord = new RemoteProctoringRoomRecord( + record.getId(), null, null, + size, null, null, null, null, null); + + this.remoteProctoringRoomRecordMapper.updateByPrimaryKeySelective(remoteProctoringRoomRecord); + return this.remoteProctoringRoomRecordMapper + .selectByPrimaryKey(remoteProctoringRoomRecord.getId()); }) .map(this::toDomainModel) .onError(TransactionHandler::rollback); @@ -372,7 +379,34 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { BooleanUtils.toStringTrueFalse(isOpen)) .onError(error -> log.error("Failed to set open flag for proctoring room: {} : {}", roomId, - error.getMessage())); + error.getMessage())) + .onError(TransactionHandler::rollback); + } + + @Override + @Transactional + public Result updateRoomSize(final Long remoteProctoringRoomId) { + return Result.tryCatch(() -> { + final Long size = this.clientConnectionRecordMapper + .countByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomId, + isEqualTo(remoteProctoringRoomId)) + .and( + ClientConnectionRecordDynamicSqlSupport.status, + isEqualTo(ConnectionStatus.ACTIVE.name())) + .build() + .execute(); + + this.remoteProctoringRoomRecordMapper.updateByPrimaryKeySelective( + new RemoteProctoringRoomRecord( + remoteProctoringRoomId, null, null, + size.intValue(), null, null, + null, null, null)); + + return size; + }) + .onError(TransactionHandler::rollback); } private RemoteProctoringRoom toDomainModel(final RemoteProctoringRoomRecord record) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index 344f7d80..a511c5df 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -45,6 +45,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRecordMapper; @@ -586,6 +587,10 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { } catch (final JsonProcessingException e) { entityAsString = entity.toString(); } + + if (entityAsString != null && entityAsString.length() > 4000) { + return Utils.truncateText(entityAsString, 4000); + } return entityAsString; } 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 94887e7c..9f5fa5f8 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 @@ -109,7 +109,7 @@ class ExamSessionControlTask implements DisposableBean { this.examDAO.releaseAgedLocks(); } - @Scheduled(fixedRateString = "${sebserver.webservice.api.exam.update-ping:5000}") + @Scheduled(fixedDelayString = "${sebserver.webservice.api.seb.lostping.update:5000}") public void examSessionUpdateTask() { this.sebClientConnectionService.updatePingEvents(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index 090d802f..0f38d8ab 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -231,7 +231,7 @@ public class ExamSessionServiceImpl implements ExamSessionService { @Override public Result> getRunningExamsForInstitution(final Long institutionId) { - return this.examDAO.allIdsOfInstitution(institutionId) + return this.examDAO.allIdsOfRunning(institutionId) .map(col -> col.stream() .map(this::getRunningExam) .filter(Result::hasValue) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java index 1377de38..c119e988 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java @@ -56,7 +56,11 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { super.init(indicatorDefinition, connectionId, active, cachingEnabled); - this.currentValue = computeValueAt(DateTimeUtils.currentTimeMillis()); + final long now = DateTimeUtils.currentTimeMillis(); + this.currentValue = computeValueAt(now); + if (Double.isNaN(this.currentValue)) { + this.currentValue = now; + } try { indicatorDefinition 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 57c798fc..a14265d2 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 @@ -52,6 +52,8 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService private static final Logger log = LoggerFactory.getLogger(ExamProctoringRoomServiceImpl.class); + private static final Object RESERVE_ROOM_LOCK = new Object(); + private final RemoteProctoringRoomDAO remoteProctoringRoomDAO; private final ClientConnectionDAO clientConnectionDAO; private final ExamAdminService examAdminService; @@ -280,53 +282,61 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService } private void assignToCollectingRoom(final ClientConnectionRecord cc) { - try { + synchronized (RESERVE_ROOM_LOCK) { + try { - if (cc.getRemoteProctoringRoomId() == null) { + if (cc.getRemoteProctoringRoomId() == null) { - final RemoteProctoringRoom proctoringRoom = getProctoringRoom( - cc.getExamId(), - cc.getConnectionToken()); + final RemoteProctoringRoom proctoringRoom = getProctoringRoom( + cc.getExamId(), + cc.getConnectionToken()); - if (log.isDebugEnabled()) { - log.debug("Assigning new SEB client to proctoring room: {}, connection: {}", - proctoringRoom.id, - cc); + 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(); } - this.clientConnectionDAO - .assignToProctoringRoom( - cc.getId(), - cc.getConnectionToken(), - proctoringRoom.id) - .getOrThrow(); + } catch (final Exception e) { + log.error("Failed to assign connection to collecting room: {}", cc, e); } - - applyProcotringInstruction(cc) - .getOrThrow(); - - } catch (final Exception e) { - log.error("Failed to assign connection to collecting room: {}", cc, e); } } private void removeFromRoom(final ClientConnectionRecord cc) { - try { + synchronized (RESERVE_ROOM_LOCK) { + try { - this.remoteProctoringRoomDAO.releasePlaceInCollectingRoom( - cc.getExamId(), - cc.getRemoteProctoringRoomId()); + this.remoteProctoringRoomDAO.releasePlaceInCollectingRoom( + cc.getExamId(), + cc.getRemoteProctoringRoomId()); - this.cleanupBreakOutRooms(cc); + this.cleanupBreakOutRooms(cc); - this.clientConnectionDAO - .removeFromProctoringRoom(cc.getId(), cc.getConnectionToken()) - .onError(error -> log.error("Failed to remove client connection form room: ", error)) - .getOrThrow(); + this.clientConnectionDAO + .removeFromProctoringRoom(cc.getId(), cc.getConnectionToken()) + .onError(error -> log.error("Failed to remove client connection from room: ", error)) + .getOrThrow(); - } catch (final Exception e) { - log.error("Failed to update client connection for proctoring room: ", e); - this.clientConnectionDAO.setNeedsRoomUpdate(cc.getId()); + } catch (final Exception e) { + log.error("Failed to update client connection for proctoring room: ", e); + try { + this.remoteProctoringRoomDAO.updateRoomSize(cc.getRemoteProctoringRoomId()); + } catch (final Exception ee) { + log.error("Failed to update room size: ", ee); + } + } } } @@ -632,7 +642,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getOrThrow(); try { - sendJoinInstruction( + registerJoinInstruction( examId, connectionToken, roomConnection, @@ -688,7 +698,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService error.getMessage())) .get(); if (proctoringConnection != null) { - sendJoinInstruction( + registerJoinInstruction( proctoringSettings.examId, connectionToken, proctoringConnection, @@ -753,7 +763,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService remoteProctoringRoom.subject) .getOrThrow(); - sendJoinInstruction( + registerJoinInstruction( proctoringSettings.examId, clientConnection.clientConnection.connectionToken, proctoringConnection, @@ -765,14 +775,14 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService } } - private void sendJoinInstruction( + private void registerJoinInstruction( final Long examId, final String connectionToken, final ProctoringRoomConnection proctoringConnection, final ExamProctoringService examProctoringService) { if (log.isDebugEnabled()) { - log.debug("Send proctoring join instruction to connection: {}, room: {}", + log.debug("Register proctoring join instruction for connection: {}, room: {}", connectionToken, proctoringConnection.roomName); } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index cdcd53da..61571eb5 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1809,6 +1809,7 @@ sebserver.monitoring.exam.connection.action.proctoring=Single Room Proctoring sebserver.monitoring.exam.connection.action.proctoring.examroom=Exam Room Proctoring sebserver.monitoring.exam.connection.action.openTownhall.confirm=You are about to open the town-hall room and force all SEB clients to join the town-hall room.
Are you sure to open the town-hall? sebserver.monitoring.exam.connection.action.closeTownhall.confirm=You are about to close the town-hall room and force all SEB clients to join it's proctoring room.
Are you sure to close the town-hall? +sebserver.monitoring.exam.connection.action.singleroom.confirm=You are about to open the single/one to one room for this participant.
Are you sure you want to open the single room? sebserver.monitoring.exam.connection.notificationlist.actions= sebserver.monitoring.exam.connection.action.confirm.notification=Confirm Notification