SEBSERV-163 back-end and fixed tests

This commit is contained in:
anhefti 2022-09-05 16:01:30 +02:00
parent cea166f065
commit 65b81af1eb
5 changed files with 97 additions and 7 deletions

View file

@ -9,8 +9,10 @@
package ch.ethz.seb.sebserver.gbl.model.session; package ch.ethz.seb.sebserver.gbl.model.session;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
@ -20,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue; import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue; import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
@ -41,6 +44,8 @@ public class ClientConnectionData implements GrantEntity {
public final Boolean missingPing; public final Boolean missingPing;
public final Boolean pendingNotification; public final Boolean pendingNotification;
private Set<Long> groups = null;
@JsonCreator @JsonCreator
public ClientConnectionData( public ClientConnectionData(
@JsonProperty(ATTR_MISSING_PING) final Boolean missingPing, @JsonProperty(ATTR_MISSING_PING) final Boolean missingPing,
@ -64,6 +69,19 @@ public class ClientConnectionData implements GrantEntity {
this.indicatorValues = Utils.immutableListOf(indicatorValues); this.indicatorValues = Utils.immutableListOf(indicatorValues);
} }
@JsonIgnore
public void addToClientGroup(final ClientGroup group) {
if (this.groups == null) {
this.groups = new HashSet<>(1);
}
this.groups.add(group.id);
}
@JsonIgnore
public boolean isInClientGroup(final Long clientGroupId) {
return this.groups != null && this.groups.contains(clientGroupId);
}
@Override @Override
public EntityType entityType() { public EntityType entityType() {
return this.clientConnection.entityType(); return this.clientConnection.entityType();

View file

@ -10,6 +10,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.Collection; import java.util.Collection;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO;
@ -17,10 +20,24 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSuppor
/** Concrete EntityDAO interface of ClientGroup entities */ /** Concrete EntityDAO interface of ClientGroup entities */
public interface ClientGroupDAO extends EntityDAO<ClientGroup, ClientGroup>, BulkActionSupportDAO<ClientGroup> { public interface ClientGroupDAO extends EntityDAO<ClientGroup, ClientGroup>, BulkActionSupportDAO<ClientGroup> {
public static final String CACHE_NAME_RUNNING_EXAM_CLIENT_GROUP_CACHE = "RUNNING_EXAM_CLIENT_GROUP_CACHE";
/** Get a collection of all ClientGroup entities for a specified exam. /** Get a collection of all ClientGroup entities for a specified exam.
* *
* @param examId the Exam identifier to get the ClientGroups for * @param examId the Exam identifier to get the ClientGroups for
* @return Result referring to the collection of ClientGroups of an Exam or to an error if happened */ * @return Result referring to the collection of ClientGroups of an Exam or to an error if happened */
@Cacheable(
cacheNames = CACHE_NAME_RUNNING_EXAM_CLIENT_GROUP_CACHE,
key = "#examId",
condition = "#examId!=null",
unless = "#result.hasError()")
Result<Collection<ClientGroup>> allForExam(Long examId); Result<Collection<ClientGroup>> allForExam(Long examId);
@CacheEvict(
cacheNames = CACHE_NAME_RUNNING_EXAM_CLIENT_GROUP_CACHE,
key = "#examId")
default void evictCacheForExam(final Long examId) {
// just evict the cache
}
} }

View file

@ -23,6 +23,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientGroupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService;
@ -45,6 +46,7 @@ public class ExamSessionCacheService {
private static final Logger log = LoggerFactory.getLogger(ExamSessionCacheService.class); private static final Logger log = LoggerFactory.getLogger(ExamSessionCacheService.class);
private final ExamDAO examDAO; private final ExamDAO examDAO;
private final ClientGroupDAO clientGroupDAO;
private final ClientConnectionDAO clientConnectionDAO; private final ClientConnectionDAO clientConnectionDAO;
private final InternalClientConnectionDataFactory internalClientConnectionDataFactory; private final InternalClientConnectionDataFactory internalClientConnectionDataFactory;
private final ExamConfigService sebExamConfigService; private final ExamConfigService sebExamConfigService;
@ -52,6 +54,7 @@ public class ExamSessionCacheService {
protected ExamSessionCacheService( protected ExamSessionCacheService(
final ExamDAO examDAO, final ExamDAO examDAO,
final ClientGroupDAO clientGroupDAO,
final ClientConnectionDAO clientConnectionDAO, final ClientConnectionDAO clientConnectionDAO,
final InternalClientConnectionDataFactory internalClientConnectionDataFactory, final InternalClientConnectionDataFactory internalClientConnectionDataFactory,
final ExamConfigService sebExamConfigService, final ExamConfigService sebExamConfigService,
@ -59,6 +62,7 @@ public class ExamSessionCacheService {
final RemoteProctoringRoomDAO remoteProctoringRoomDAO) { final RemoteProctoringRoomDAO remoteProctoringRoomDAO) {
this.examDAO = examDAO; this.examDAO = examDAO;
this.clientGroupDAO = clientGroupDAO;
this.clientConnectionDAO = clientConnectionDAO; this.clientConnectionDAO = clientConnectionDAO;
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory; this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
this.sebExamConfigService = sebExamConfigService; this.sebExamConfigService = sebExamConfigService;
@ -98,6 +102,7 @@ public class ExamSessionCacheService {
log.trace("Conditional eviction of running Exam from cache: {}", isRunning(exam)); log.trace("Conditional eviction of running Exam from cache: {}", isRunning(exam));
} }
this.clientGroupDAO.evictCacheForExam(exam.id);
return exam; return exam;
} }
@ -110,6 +115,7 @@ public class ExamSessionCacheService {
log.trace("Conditional eviction of running Exam from cache: {}", examId); log.trace("Conditional eviction of running Exam from cache: {}", examId);
} }
this.clientGroupDAO.evictCacheForExam(examId);
return examId; return examId;
} }

View file

@ -8,12 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.util.Collection;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; 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.monitoring.ClientGroupMatcherService;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientGroupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
@Lazy @Lazy
@ -21,34 +29,64 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificati
@WebServiceProfile @WebServiceProfile
public class InternalClientConnectionDataFactory { public class InternalClientConnectionDataFactory {
private static final Logger log = LoggerFactory.getLogger(InternalClientConnectionDataFactory.class);
private final ClientIndicatorFactory clientIndicatorFactory; private final ClientIndicatorFactory clientIndicatorFactory;
private final SEBClientNotificationService sebClientNotificationService; private final SEBClientNotificationService sebClientNotificationService;
private final ClientGroupDAO clientGroupDAO;
private final ClientGroupMatcherService clientGroupMatcherService;
public InternalClientConnectionDataFactory( public InternalClientConnectionDataFactory(
final ClientIndicatorFactory clientIndicatorFactory, final ClientIndicatorFactory clientIndicatorFactory,
final SEBClientNotificationService sebClientNotificationService) { final SEBClientNotificationService sebClientNotificationService,
final ClientGroupDAO clientGroupDAO,
final ClientGroupMatcherService clientGroupMatcherService) {
this.clientIndicatorFactory = clientIndicatorFactory; this.clientIndicatorFactory = clientIndicatorFactory;
this.sebClientNotificationService = sebClientNotificationService; this.sebClientNotificationService = sebClientNotificationService;
this.clientGroupDAO = clientGroupDAO;
this.clientGroupMatcherService = clientGroupMatcherService;
} }
public ClientConnectionDataInternal createClientConnectionData(final ClientConnection clientConnection) { public ClientConnectionDataInternal createClientConnectionData(final ClientConnection clientConnection) {
ClientConnectionDataInternal result;
if (clientConnection.status == ConnectionStatus.CLOSED if (clientConnection.status == ConnectionStatus.CLOSED
|| clientConnection.status == ConnectionStatus.DISABLED) { || clientConnection.status == ConnectionStatus.DISABLED) {
// dispose notification indication for closed or disabled connection // dispose notification indication for closed or disabled connection
return new ClientConnectionDataInternal( result = new ClientConnectionDataInternal(
clientConnection, clientConnection,
() -> false, () -> false,
this.clientIndicatorFactory.createFor(clientConnection)); this.clientIndicatorFactory.createFor(clientConnection));
} else {
result = new ClientConnectionDataInternal(
clientConnection,
() -> this.sebClientNotificationService
.hasAnyPendingNotification(clientConnection),
this.clientIndicatorFactory.createFor(clientConnection));
} }
return new ClientConnectionDataInternal( // set client groups for connection
clientConnection, if (clientConnection.examId != null) {
() -> this.sebClientNotificationService final Collection<ClientGroup> clientGroups = this.clientGroupDAO
.hasAnyPendingNotification(clientConnection), .allForExam(clientConnection.examId)
this.clientIndicatorFactory.createFor(clientConnection)); .onError(
error -> log.error("Failed to get client groups for clientConnection: {}", clientConnection,
error))
.getOr(Collections.emptyList());
if (!clientGroups.isEmpty()) {
clientGroups.forEach(clientGroup -> {
if (this.clientGroupMatcherService.isInGroup(clientConnection, clientGroup)) {
result.addToClientGroup(clientGroup);
}
});
}
}
return result;
} }
} }

View file

@ -15,6 +15,17 @@
<heap unit="entries">100</heap> <heap unit="entries">100</heap>
</resources> </resources>
</cache> </cache>
<cache alias="RUNNING_EXAM_CLIENT_GROUP_CACHE">
<key-type>java.lang.Long</key-type>
<value-type>ch.ethz.seb.sebserver.gbl.util.Result</value-type>
<expiry>
<ttl unit="hours">24</ttl>
</expiry>
<resources>
<heap unit="entries">10</heap>
</resources>
</cache>
<cache alias="ACTIVE_CLIENT_CONNECTION"> <cache alias="ACTIVE_CLIENT_CONNECTION">
<key-type>java.lang.String</key-type> <key-type>java.lang.String</key-type>