Merge remote-tracking branch 'origin/dev-1.2' into development
Conflicts: src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java
This commit is contained in:
commit
f161587f6a
14 changed files with 196 additions and 84 deletions
3
pom.xml
3
pom.xml
|
@ -23,6 +23,9 @@
|
|||
<revision>${sebserver-version}</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
||||
<!-- Fix CVE-2021-44228 : https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot -->
|
||||
<log4j2.version>2.15.0</log4j2.version>
|
||||
</properties>
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -147,6 +147,9 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
SEBServerInit.INIT_LOGGER.info("----> *** Webservice successfully started up! ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> log4j2.formatMsgNoLookups = {}",
|
||||
this.environment.getProperty("log4j2.formatMsgNoLookups", "none"));
|
||||
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
|
|
|
@ -45,6 +45,14 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
|
|||
* happened */
|
||||
Result<Collection<Long>> 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<Collection<Long>> 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)
|
||||
|
|
|
@ -132,6 +132,18 @@ public interface RemoteProctoringRoomDAO {
|
|||
* @return Result refer to active break-out rooms or to an error when happened */
|
||||
Result<Collection<String>> 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<Long> updateRoomSize(Long remoteProctoringRoomId);
|
||||
|
||||
}
|
||||
|
|
|
@ -191,7 +191,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
@Override
|
||||
@Transactional
|
||||
public Result<Collection<ClientConnectionRecord>> getAllConnectionIdsForRoomUpdateActive() {
|
||||
return Result.tryCatch(() -> {
|
||||
return Result.<Collection<ClientConnectionRecord>> tryCatch(() -> {
|
||||
|
||||
final Collection<ClientConnectionRecord> 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<Collection<ClientConnectionRecord>> getAllConnectionIdsForRoomUpdateInactive() {
|
||||
return Result.tryCatch(() -> {
|
||||
return Result.<Collection<ClientConnectionRecord>> tryCatch(() -> {
|
||||
final Collection<ClientConnectionRecord> 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<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
return Result.tryCatch(() -> {
|
||||
return Result.<Collection<EntityKey>> tryCatch(() -> {
|
||||
|
||||
final List<Long> 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
|
||||
|
|
|
@ -122,6 +122,7 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<Long>> allInstitutionIdsByQuizId(final String quizId) {
|
||||
return this.examRecordDAO.allInstitutionIdsByQuizId(quizId);
|
||||
}
|
||||
|
@ -165,6 +166,7 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Exam> 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<Collection<Long>> 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<Collection<EntityDependency>> allIdsOfInstitution(final EntityKey institutionKey) {
|
||||
return Result.tryCatch(() -> toDependencies(
|
||||
this.examRecordMapper.selectByExample()
|
||||
|
|
|
@ -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<Long, Result<NewRoom>> newRoomFunction) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
synchronized (RESERVE_ROOM_LOCK) {
|
||||
final Optional<RemoteProctoringRoomRecord> 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<RemoteProctoringRoomRecord> 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<RemoteProctoringRoom> 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<Long> 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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -231,7 +231,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
|
||||
@Override
|
||||
public Result<Collection<Exam>> getRunningExamsForInstitution(final Long institutionId) {
|
||||
return this.examDAO.allIdsOfInstitution(institutionId)
|
||||
return this.examDAO.allIdsOfRunning(institutionId)
|
||||
.map(col -> col.stream()
|
||||
.map(this::getRunningExam)
|
||||
.filter(Result::hasValue)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.<br/>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.<br/>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.<br/>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
|
||||
|
|
Loading…
Reference in a new issue