finished up to allow pings and instructions (quit) before connection

is fully established (also in requested status)
This commit is contained in:
anhefti 2021-06-21 17:30:10 +02:00
parent b61166674c
commit 8381b5d621
9 changed files with 51 additions and 33 deletions

View file

@ -25,17 +25,21 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
public final class ClientConnection implements GrantEntity { public final class ClientConnection implements GrantEntity {
public enum ConnectionStatus { public enum ConnectionStatus {
UNDEFINED(false), UNDEFINED(false, false),
CONNECTION_REQUESTED(false), CONNECTION_REQUESTED(true, false),
AUTHENTICATED(true), AUTHENTICATED(true, true),
ACTIVE(true), ACTIVE(false, true),
CLOSED(false), CLOSED(false, false),
DISABLED(false); DISABLED(false, false);
public final boolean connectingStatus;
public final boolean establishedStatus; public final boolean establishedStatus;
public final boolean indicatorActiveStatus;
ConnectionStatus(final boolean establishedStatus) { ConnectionStatus(final boolean connectingStatus, final boolean establishedStatus) {
this.connectingStatus = connectingStatus;
this.establishedStatus = establishedStatus; this.establishedStatus = establishedStatus;
this.indicatorActiveStatus = connectingStatus || establishedStatus;
} }
} }

View file

@ -364,7 +364,7 @@ public class MonitoringClientConnection implements TemplateComposer {
}) })
.noEventPropagation() .noEventPropagation()
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER) && .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER) &&
connectionData.clientConnection.status == ConnectionStatus.ACTIVE); connectionData.clientConnection.status.indicatorActiveStatus);
if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) { if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) {
final ProctoringServiceSettings procotringSettings = restService final ProctoringServiceSettings procotringSettings = restService

View file

@ -34,7 +34,6 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
@ -420,7 +419,7 @@ public class MonitoringRunningExam implements TemplateComposer {
private Set<EntityKey> selectionForQuitInstruction(final ClientConnectionTable clientTable) { private Set<EntityKey> selectionForQuitInstruction(final ClientConnectionTable clientTable) {
final Set<String> connectionTokens = clientTable.getConnectionTokens( final Set<String> connectionTokens = clientTable.getConnectionTokens(
ClientConnection.getStatusPredicate(ConnectionStatus.ACTIVE), cc -> cc.status.indicatorActiveStatus,
true); true);
if (connectionTokens == null || connectionTokens.isEmpty()) { if (connectionTokens == null || connectionTokens.isEmpty()) {
return Collections.emptySet(); return Collections.emptySet();

View file

@ -189,7 +189,7 @@ public class ClientConnectionDetails {
final double value = indValue.getValue(); final double value = indValue.getValue();
final String displayValue = IndicatorValue.getDisplayValue(indValue); final String displayValue = IndicatorValue.getDisplayValue(indValue);
if (!this.connectionData.clientConnection.status.establishedStatus) { if (!this.connectionData.clientConnection.status.indicatorActiveStatus) {
form.setFieldValue( form.setFieldValue(
indData.indicator.name, indData.indicator.name,

View file

@ -576,7 +576,7 @@ public final class ClientConnectionTable {
continue; continue;
} }
if (!this.connectionData.clientConnection.status.establishedStatus) { if (!this.connectionData.clientConnection.status.indicatorActiveStatus) {
final String value = (indicatorData.indicator.type.showOnlyInActiveState) final String value = (indicatorData.indicator.type.showOnlyInActiveState)
? Constants.EMPTY_NOTE ? Constants.EMPTY_NOTE
: IndicatorValue.getDisplayValue(indicatorValue); : IndicatorValue.getDisplayValue(indicatorValue);

View file

@ -72,7 +72,9 @@ public class InstructionProcessor {
final PageContext pageContext) { final PageContext pageContext) {
final Set<String> connectionTokens = selectionFunction final Set<String> connectionTokens = selectionFunction
.apply(ClientConnection.getStatusPredicate(ConnectionStatus.ACTIVE)); .apply(ClientConnection.getStatusPredicate(
ConnectionStatus.CONNECTION_REQUESTED,
ConnectionStatus.ACTIVE));
if (connectionTokens.isEmpty()) { if (connectionTokens.isEmpty()) {
log.warn("Empty selection"); log.warn("Empty selection");

View file

@ -145,28 +145,27 @@ public interface ClientConnectionDAO extends
* @return Result refer to the ClientConnection for the specified connection token or to an error if happened */ * @return Result refer to the ClientConnection for the specified connection token or to an error if happened */
Result<ClientConnection> byConnectionToken(String connectionToken); Result<ClientConnection> byConnectionToken(String connectionToken);
/** Indicates if the client connection for exam and connection token is an active connection.
*
* @param examId the exam identifier
* @param connectionToken the connection token
* @return Result refer to the active connection flag or to an error when happened */
Result<Boolean> isActiveConnection(Long examId, String connectionToken);
/** Use this to check whether a single ClientConnection is up to date or needs a refresh. /** Use this to check whether a single ClientConnection is up to date or needs a refresh.
* *
* @param clientConnection the actual ClientConnection (from the internal cache) * @param clientConnection the actual ClientConnection (from the internal cache)
* @return Result refer to true if the given ClientConnection is up to date */ * @return Result refer to true if the given ClientConnection is up to date */
Result<Boolean> isUpToDate(ClientConnection clientConnection); Result<Boolean> isUpToDate(ClientConnection clientConnection);
/** Indicates if the client connection for given exam and connection token is
* in a ready state to send instructions.
*
* @param examId the exam identifier
* @param connectionToken the connection token
* @return Result refer to the active connection flag or to an error when happened */
Result<Boolean> isInInstructionStatus(Long examId, String connectionToken);
/** Filters a set of client connection tokens to a set containing only /** Filters a set of client connection tokens to a set containing only
* connection tokens of active client connections. * connection tokens of client connections that are in a ready state to send instructions.
* *
* Use this if you have a bunch of client connections to filter only the active connections * @param examId the exam identifier
* * @param connectionToken a Set of connection tokens to filter
* @param examId * @return Result refer to filtered Set of connection tokens or to an error when happened */
* @param connectionToken Result<Set<String>> filterForInstructionStatus(Long examId, Set<String> connectionToken);
* @return */
Result<Set<String>> filterActive(Long examId, Set<String> connectionToken);
/** Used to get the VDI paired connection if it already exsits. /** Used to get the VDI paired connection if it already exsits.
* *

View file

@ -530,8 +530,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
} }
@Override @Override
@Transactional(readOnly = true) public Result<Boolean> isInInstructionStatus(final Long examId, final String connectionToken) {
public Result<Boolean> isActiveConnection(final Long examId, final String connectionToken) {
return Result.tryCatch(() -> this.clientConnectionRecordMapper return Result.tryCatch(() -> this.clientConnectionRecordMapper
.selectByExample() .selectByExample()
.where( .where(
@ -540,6 +539,12 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
.and( .and(
ClientConnectionRecordDynamicSqlSupport.examId, ClientConnectionRecordDynamicSqlSupport.examId,
SqlBuilder.isEqualTo(examId)) SqlBuilder.isEqualTo(examId))
.and(
ClientConnectionRecordDynamicSqlSupport.status,
SqlBuilder.isEqualTo(ConnectionStatus.ACTIVE.name()))
.or(
ClientConnectionRecordDynamicSqlSupport.status,
SqlBuilder.isEqualTo(ConnectionStatus.CONNECTION_REQUESTED.name()))
.build() .build()
.execute() .execute()
.stream() .stream()
@ -566,7 +571,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Set<String>> filterActive(final Long examId, final Set<String> connectionToken) { public Result<Set<String>> filterForInstructionStatus(final Long examId, final Set<String> connectionToken) {
if (connectionToken == null || connectionToken.isEmpty()) { if (connectionToken == null || connectionToken.isEmpty()) {
return Result.ofRuntimeError("Null or empty set reference"); return Result.ofRuntimeError("Null or empty set reference");
} }
@ -578,10 +583,15 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
.and( .and(
ClientConnectionRecordDynamicSqlSupport.examId, ClientConnectionRecordDynamicSqlSupport.examId,
SqlBuilder.isEqualTo(examId)) SqlBuilder.isEqualTo(examId))
.and(
ClientConnectionRecordDynamicSqlSupport.status,
SqlBuilder.isEqualTo(ConnectionStatus.ACTIVE.name()))
.or(
ClientConnectionRecordDynamicSqlSupport.status,
SqlBuilder.isEqualTo(ConnectionStatus.CONNECTION_REQUESTED.name()))
.build() .build()
.execute() .execute()
.stream() .stream()
.filter(cc -> ConnectionStatus.ACTIVE.name().equals(cc.getStatus()))
.map(ClientConnectionRecord::getConnectionToken) .map(ClientConnectionRecord::getConnectionToken)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
} }

View file

@ -110,7 +110,7 @@ public class SEBClientInstructionServiceImpl implements SEBClientInstructionServ
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final boolean isActive = this.clientConnectionDAO final boolean isActive = this.clientConnectionDAO
.isActiveConnection(examId, connectionToken) .isInInstructionStatus(examId, connectionToken)
.getOr(false); .getOr(false);
if (isActive) { if (isActive) {
@ -124,6 +124,10 @@ public class SEBClientInstructionServiceImpl implements SEBClientInstructionServ
} catch (final Exception e) { } catch (final Exception e) {
throw new RuntimeException("Unexpected: ", e); throw new RuntimeException("Unexpected: ", e);
} }
} else {
log.warn(
"The SEB client connection : {} is not in a ready state to process instructions. Instruction registration has been skipped",
connectionToken);
} }
}); });
} }
@ -140,7 +144,7 @@ public class SEBClientInstructionServiceImpl implements SEBClientInstructionServ
final String attributesString = Utils.toJsonObject(attributes); final String attributesString = Utils.toJsonObject(attributes);
final Set<String> activeConnections = this.clientConnectionDAO final Set<String> activeConnections = this.clientConnectionDAO
.filterActive(examId, connectionTokens) .filterForInstructionStatus(examId, connectionTokens)
.getOrElse(Collections::emptySet); .getOrElse(Collections::emptySet);
connectionTokens connectionTokens