SEBSERV-238 improved proctoring room assignments for connecting clients
(re)mark a client connection for update if the join instruction was not be able to send to the SEB client. In this case this shall be tried again until it works.
This commit is contained in:
parent
c89a609615
commit
9ebd7827ac
6 changed files with 124 additions and 53 deletions
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -132,6 +132,8 @@ public interface ClientConnectionDAO extends
|
|||
key = "#connectionToken")
|
||||
Result<Void> removeFromProctoringRoom(Long connectionId, String connectionToken);
|
||||
|
||||
Result<Void> markForProctoringUpdate(Long id);
|
||||
|
||||
/** Deletes the given ClientConnection data.
|
||||
*
|
||||
* This evicts all entries from the CONNECTION_TOKENS_CACHE.
|
||||
|
|
|
@ -389,6 +389,19 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Void> 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<Void> removeFromProctoringRoom(final Long connectionId, final String connectionToken) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Void> applyProcotringInstruction(
|
||||
final Long examId,
|
||||
final String connectionToken) {
|
||||
private Result<Void> 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<String, String> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue