fixed notification changes in GUI

This commit is contained in:
anhefti 2021-09-20 15:46:20 +02:00
parent dffb0e61f8
commit 7d647cc9c2
8 changed files with 63 additions and 35 deletions

View file

@ -28,6 +28,7 @@ public class GuiServiceInfo {
private final String contextPath; private final String contextPath;
private final UriComponentsBuilder internalServerURIBuilder; private final UriComponentsBuilder internalServerURIBuilder;
private final UriComponentsBuilder externalServerURIBuilder; private final UriComponentsBuilder externalServerURIBuilder;
private final boolean distributedSetup;
public GuiServiceInfo( public GuiServiceInfo(
@Value("${server.address}") final String internalServer, @Value("${server.address}") final String internalServer,
@ -36,7 +37,8 @@ public class GuiServiceInfo {
@Value("${sebserver.gui.http.external.servername}") final String externalServer, @Value("${sebserver.gui.http.external.servername}") final String externalServer,
@Value("${sebserver.gui.http.external.port}") final String externalPort, @Value("${sebserver.gui.http.external.port}") final String externalPort,
@Value("${sebserver.gui.entrypoint:/gui}") final String entryPoint, @Value("${sebserver.gui.entrypoint:/gui}") final String entryPoint,
@Value("${server.servlet.context-path:/}") final String contextPath) { @Value("${server.servlet.context-path:/}") final String contextPath,
@Value("${sebserver.webservice.distributed:false}") final boolean distributedSetup) {
if (StringUtils.isBlank(externalScheme)) { if (StringUtils.isBlank(externalScheme)) {
throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername"); throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername");
@ -69,6 +71,8 @@ public class GuiServiceInfo {
if (StringUtils.isNotBlank(contextPath) && !contextPath.equals("/")) { if (StringUtils.isNotBlank(contextPath) && !contextPath.equals("/")) {
this.externalServerURIBuilder.path(contextPath); this.externalServerURIBuilder.path(contextPath);
} }
this.distributedSetup = distributedSetup;
} }
public String getExternalScheme() { public String getExternalScheme() {
@ -107,4 +111,8 @@ public class GuiServiceInfo {
return this.externalServerURIBuilder.cloneBuilder(); return this.externalServerURIBuilder.cloneBuilder();
} }
public boolean isDistributedSetup() {
return this.distributedSetup;
}
} }

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.Collection; import java.util.Collection;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -195,10 +194,9 @@ public class MonitoringClientConnection implements TemplateComposer {
indicators); indicators);
// NOTIFICATIONS // NOTIFICATIONS
final boolean hasNotifications = BooleanUtils.isTrue(connectionData.pendingNotification());
Supplier<EntityTable<ClientNotification>> _notificationTableSupplier = () -> null; Supplier<EntityTable<ClientNotification>> _notificationTableSupplier = () -> null;
if (hasNotifications) { if (connectionData.clientConnection.status.clientActiveStatus) {
final PageService.PageActionBuilder actionBuilder = this.pageService final PageService.PageActionBuilder notificationActionBuilder = this.pageService
.pageActionBuilder( .pageActionBuilder(
pageContext pageContext
.clearAttributes() .clearAttributes()
@ -240,7 +238,7 @@ public class MonitoringClientConnection implements TemplateComposer {
this::getServerTime) this::getServerTime)
.sortable() .sortable()
.widthProportion(1)) .widthProportion(1))
.withDefaultAction(t -> actionBuilder .withDefaultAction(t -> notificationActionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION)
.withParentEntityKey(parentEntityKey) .withParentEntityKey(parentEntityKey)
.withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY) .withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY)
@ -252,7 +250,7 @@ public class MonitoringClientConnection implements TemplateComposer {
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION)) ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
actionBuilder notificationActionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_CONFIRM_NOTIFICATION)
.withParentEntityKey(parentEntityKey) .withParentEntityKey(parentEntityKey)
.withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY) .withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY)

View file

@ -41,6 +41,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -92,6 +93,7 @@ public class MonitoringRunningExam implements TemplateComposer {
private final InstructionProcessor instructionProcessor; private final InstructionProcessor instructionProcessor;
private final MonitoringExamSearchPopup monitoringExamSearchPopup; private final MonitoringExamSearchPopup monitoringExamSearchPopup;
private final MonitoringProctoringService monitoringProctoringService; private final MonitoringProctoringService monitoringProctoringService;
private final boolean distributedSetup;
private final long pollInterval; private final long pollInterval;
private final long proctoringRoomUpdateInterval; private final long proctoringRoomUpdateInterval;
@ -102,6 +104,7 @@ public class MonitoringRunningExam implements TemplateComposer {
final InstructionProcessor instructionProcessor, final InstructionProcessor instructionProcessor,
final MonitoringExamSearchPopup monitoringExamSearchPopup, final MonitoringExamSearchPopup monitoringExamSearchPopup,
final MonitoringProctoringService monitoringProctoringService, final MonitoringProctoringService monitoringProctoringService,
final GuiServiceInfo guiServiceInfo,
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval, @Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval,
@Value("${sebserver.gui.remote.proctoring.rooms.update.poll-interval:5000}") final long proctoringRoomUpdateInterval) { @Value("${sebserver.gui.remote.proctoring.rooms.update.poll-interval:5000}") final long proctoringRoomUpdateInterval) {
@ -113,6 +116,7 @@ public class MonitoringRunningExam implements TemplateComposer {
this.instructionProcessor = instructionProcessor; this.instructionProcessor = instructionProcessor;
this.monitoringProctoringService = monitoringProctoringService; this.monitoringProctoringService = monitoringProctoringService;
this.pollInterval = pollInterval; this.pollInterval = pollInterval;
this.distributedSetup = guiServiceInfo.isDistributedSetup();
this.monitoringExamSearchPopup = monitoringExamSearchPopup; this.monitoringExamSearchPopup = monitoringExamSearchPopup;
this.proctoringRoomUpdateInterval = proctoringRoomUpdateInterval; this.proctoringRoomUpdateInterval = proctoringRoomUpdateInterval;
} }
@ -164,7 +168,8 @@ public class MonitoringRunningExam implements TemplateComposer {
exam, exam,
indicators, indicators,
restCall, restCall,
pushContext); pushContext,
this.distributedSetup);
clientTable clientTable
.withDefaultAction( .withDefaultAction(

View file

@ -83,8 +83,8 @@ public class ServerPushService {
}); });
} }
if (log.isInfoEnabled()) { if (log.isDebugEnabled()) {
log.info("Stop Server Push Session on: {}", Thread.currentThread().getName()); log.debug("Stop Server Push Session on: {}", Thread.currentThread().getName());
} }
try { try {
@ -100,7 +100,9 @@ public class ServerPushService {
}); });
log.info("Start new Server Push Session on: {}", bgThread.getName()); if (log.isDebugEnabled()) {
log.debug("Start new Server Push Session on: {}", bgThread.getName());
}
bgThread.setDaemon(true); bgThread.setDaemon(true);
bgThread.start(); bgThread.start();

View file

@ -25,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
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.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
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.session.ClientNotification; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue; import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
@ -69,6 +68,7 @@ public class ClientConnectionDetails {
private ClientConnectionData connectionData = null; private ClientConnectionData connectionData = null;
private boolean statusChanged = true; private boolean statusChanged = true;
private long startTime = -1;
private Consumer<ClientConnectionData> statusChangeListener = null; private Consumer<ClientConnectionData> statusChangeListener = null;
public ClientConnectionDetails( public ClientConnectionDetails(
@ -147,13 +147,22 @@ public class ClientConnectionDetails {
.toBoolean(connectionData.missingPing); .toBoolean(connectionData.missingPing);
} }
this.connectionData = connectionData; this.connectionData = connectionData;
if (this.startTime < 0) {
this.startTime = System.currentTimeMillis();
}
} }
public void updateGUI( public void updateGUI(
final Supplier<EntityTable<ClientNotification>> notificationTableSupplier, final Supplier<EntityTable<ClientNotification>> notificationTableSupplier,
final PageContext pageContext) { final PageContext pageContext) {
// Note: This is to update the whole page (by reload) only when the status has changed
// while this page was open. This prevent constant page reloads.
if (this.statusChanged && System.currentTimeMillis() - this.startTime > Constants.SECOND_IN_MILLIS) {
reloadPage(pageContext);
return;
}
if (this.connectionData == null) { if (this.connectionData == null) {
return; return;
} }
@ -214,16 +223,8 @@ public class ClientConnectionDetails {
// update notifications // update notifications
final EntityTable<ClientNotification> notificationTable = notificationTableSupplier.get(); final EntityTable<ClientNotification> notificationTable = notificationTableSupplier.get();
if (notificationTable != null && this.connectionData.clientConnection.status == ConnectionStatus.CLOSED) { if (notificationTable != null) {
reloadPage(pageContext); notificationTable.refreshPageSize();
} else {
if (BooleanUtils.isTrue(this.connectionData.pendingNotification())) {
if (notificationTable == null) {
reloadPage(pageContext);
} else {
notificationTable.refreshPageSize();
}
}
} }
} }

View file

@ -97,6 +97,7 @@ public final class ClientConnectionTable {
private final Exam exam; private final Exam exam;
private final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder; private final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder;
private final ServerPushContext pushConext; private final ServerPushContext pushConext;
private final boolean distributedSetup;
private final Map<Long, IndicatorData> indicatorMapping; private final Map<Long, IndicatorData> indicatorMapping;
private final Table table; private final Table table;
@ -119,8 +120,6 @@ public final class ClientConnectionTable {
private boolean forceUpdateAll = false; private boolean forceUpdateAll = false;
private boolean updateInProgress = false; private boolean updateInProgress = false;
//private int updateErrors = 0;
public ClientConnectionTable( public ClientConnectionTable(
final PageService pageService, final PageService pageService,
final Composite tableRoot, final Composite tableRoot,
@ -128,13 +127,15 @@ public final class ClientConnectionTable {
final Exam exam, final Exam exam,
final Collection<Indicator> indicators, final Collection<Indicator> indicators,
final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder, final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder,
final ServerPushContext pushConext) { final ServerPushContext pushConext,
final boolean distributedSetup) {
this.pageService = pageService; this.pageService = pageService;
this.asyncRunner = asyncRunner; this.asyncRunner = asyncRunner;
this.exam = exam; this.exam = exam;
this.restCallBuilder = restCallBuilder; this.restCallBuilder = restCallBuilder;
this.pushConext = pushConext; this.pushConext = pushConext;
this.distributedSetup = distributedSetup;
final WidgetFactory widgetFactory = pageService.getWidgetFactory(); final WidgetFactory widgetFactory = pageService.getWidgetFactory();
final ResourceService resourceService = pageService.getResourceService(); final ResourceService resourceService = pageService.getResourceService();
@ -332,9 +333,8 @@ public final class ClientConnectionTable {
private void updateValuesAsync(final boolean needsSync) { private void updateValuesAsync(final boolean needsSync) {
try { try {
final boolean sync = this.statusFilterChanged || this.forceUpdateAll || needsSync || this.distributedSetup;
// TODO forceUpdateAll doeasn't work on distributed if (sync) {
if (this.statusFilterChanged || this.forceUpdateAll || needsSync) {
this.toDelete.clear(); this.toDelete.clear();
this.toDelete.addAll(this.tableMapping.keySet()); this.toDelete.addAll(this.tableMapping.keySet());
} }
@ -351,7 +351,7 @@ public final class ClientConnectionTable {
data.getConnectionId(), data.getConnectionId(),
UpdatableTableItem::new); UpdatableTableItem::new);
tableItem.push(data); tableItem.push(data);
if (this.statusFilterChanged || this.forceUpdateAll || needsSync) { if (sync) {
this.toDelete.remove(data.getConnectionId()); this.toDelete.remove(data.getConnectionId());
} }
}); });
@ -680,8 +680,11 @@ public final class ClientConnectionTable {
!this.connectionData.dataEquals(connectionData); !this.connectionData.dataEquals(connectionData);
final boolean statusChanged = this.connectionData == null || final boolean statusChanged = this.connectionData == null ||
this.connectionData.clientConnection.status != connectionData.clientConnection.status; this.connectionData.clientConnection.status != connectionData.clientConnection.status;
final boolean notificationChanged = this.connectionData == null ||
BooleanUtils.toBoolean(this.connectionData.pendingNotification) != BooleanUtils
.toBoolean(connectionData.pendingNotification);
if (statusChanged) { if (statusChanged || notificationChanged) {
ClientConnectionTable.this.needsSort = true; ClientConnectionTable.this.needsSort = true;
} }

View file

@ -22,13 +22,13 @@ import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
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.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
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.WebserviceInfo;
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.ClientEventDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
@ -51,20 +51,25 @@ public class SEBClientNotificationServiceImpl implements SEBClientNotificationSe
private final Set<Long> examUpdate = new HashSet<>(); private final Set<Long> examUpdate = new HashSet<>();
private long lastUpdate = 0; private long lastUpdate = 0;
private long updateInterval = 5 * Constants.SECOND_IN_MILLIS;
public SEBClientNotificationServiceImpl( public SEBClientNotificationServiceImpl(
final ClientEventDAO clientEventDAO, final ClientEventDAO clientEventDAO,
final ClientConnectionDAO clientConnectionDAO, final ClientConnectionDAO clientConnectionDAO,
final SEBClientInstructionService sebClientInstructionService) { final SEBClientInstructionService sebClientInstructionService,
final WebserviceInfo webserviceInfo) {
this.clientEventDAO = clientEventDAO; this.clientEventDAO = clientEventDAO;
this.clientConnectionDAO = clientConnectionDAO; this.clientConnectionDAO = clientConnectionDAO;
this.sebClientInstructionService = sebClientInstructionService; this.sebClientInstructionService = sebClientInstructionService;
if (webserviceInfo.isDistributed()) {
this.updateInterval = Constants.SECOND_IN_MILLIS;
}
} }
@Override @Override
public Boolean hasAnyPendingNotification(final ClientConnection clientConnection) { public Boolean hasAnyPendingNotification(final ClientConnection clientConnection) {
if (clientConnection.status != ConnectionStatus.ACTIVE) { if (!clientConnection.status.clientActiveStatus) {
return false; return false;
} }
updateCache(clientConnection.examId); updateCache(clientConnection.examId);
@ -141,7 +146,7 @@ public class SEBClientNotificationServiceImpl implements SEBClientNotificationSe
} }
private final void updateCache(final Long examId) { private final void updateCache(final Long examId) {
if (System.currentTimeMillis() - this.lastUpdate > 5 * Constants.SECOND_IN_MILLIS) { if (System.currentTimeMillis() - this.lastUpdate > this.updateInterval) {
this.examUpdate.clear(); this.examUpdate.clear();
this.pendingNotifications.clear(); this.pendingNotifications.clear();
this.lastUpdate = System.currentTimeMillis(); this.lastUpdate = System.currentTimeMillis();

View file

@ -42,4 +42,10 @@ public class ReplTest {
// assertEquals(Constants.DAY_IN_MIN, interv.toDurationMillis() / Constants.MINUTE_IN_MILLIS); // assertEquals(Constants.DAY_IN_MIN, interv.toDurationMillis() / Constants.MINUTE_IN_MILLIS);
// } // }
// @Test
// public void testBooleanMatch() {
// assertTrue(Boolean.valueOf(false) == Boolean.valueOf(false));
// assertTrue(new Boolean(false) == new Boolean(false));
// }
} }