diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java index 8aac2f70..c6b9bdaf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java @@ -196,7 +196,10 @@ public class MonitoringClientConnection implements TemplateComposer { indicators); this.serverPushService.runServerPush( - new ServerPushContext(content, Utils.truePredicate()), + new ServerPushContext( + content, + Utils.truePredicate(), + MonitoringRunningExam.createServerPushUpdateErrorHandler(this.pageService, pageContext)), this.pollInterval, context1 -> clientConnectionDetails.updateData(), context -> clientConnectionDetails.updateGUI()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java index 28ade6c5..cb9d34c1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java @@ -26,6 +26,7 @@ import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.slf4j.Logger; @@ -80,6 +81,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable; import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService; +import ch.ethz.seb.sebserver.gui.widget.Message; @Lazy @Component @@ -198,7 +200,9 @@ public class MonitoringRunningExam implements TemplateComposer { ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM)); this.serverPushService.runServerPush( - new ServerPushContext(content, Utils.truePredicate()), + new ServerPushContext( + content, Utils.truePredicate(), + createServerPushUpdateErrorHandler(this.pageService, pageContext)), this.pollInterval, context -> clientTable.updateValues(), updateTableGUI(clientTable)); @@ -358,7 +362,10 @@ public class MonitoringRunningExam implements TemplateComposer { actionBuilder, proctoringSettings); this.serverPushService.runServerPush( - new ServerPushContext(content, Utils.truePredicate()), + new ServerPushContext( + content, + Utils.truePredicate(), + createServerPushUpdateErrorHandler(this.pageService, pageContext)), this.proctoringRoomUpdateInterval, context -> updateRoomActions( entityKey, @@ -707,4 +714,30 @@ public class MonitoringRunningExam implements TemplateComposer { }; } + static final Function createServerPushUpdateErrorHandler( + final PageService pageService, + final PageContext pageContext) { + + return error -> { + log.error("Fialed to update server push: {}", error.getMessage()); + try { + pageService.getCurrentUser().get(); + } catch (final Exception e) { + log.error("Failed to verify current user after server push error: {}", e.getMessage()); + log.info("Force logout and session cleanup..."); + pageContext.forwardToLoginPage(); + final MessageBox logoutSuccess = new Message( + pageContext.getShell(), + pageService.getI18nSupport().getText("sebserver.logout"), + Utils.formatLineBreaks( + pageService.getI18nSupport().getText("sebserver.logout.invalid-session.message")), + SWT.ICON_INFORMATION, + pageService.getI18nSupport()); + logoutSuccess.open(null); + return true; + } + return false; + }; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushContext.java index d4309ce3..63261278 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushContext.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.gui.service.push; +import java.util.function.Function; import java.util.function.Predicate; import org.eclipse.swt.widgets.Composite; @@ -18,23 +19,25 @@ import org.eclipse.swt.widgets.Display; * @author anhefti */ public final class ServerPushContext { - private final Composite anchor; - private final Predicate runAgain; - - public ServerPushContext(final Composite anchor) { - this(anchor, context -> false); - } + public final Composite anchor; + public final Predicate runAgain; + public final Function errorHandler; + boolean internalStop = false; public ServerPushContext( final Composite anchor, - final Predicate runAgain) { + final Predicate runAgain, + final Function errorHandler) { + this.errorHandler = errorHandler != null + ? errorHandler + : error -> true; this.anchor = anchor; this.runAgain = runAgain; } public boolean runAgain() { - return this.runAgain.test(this); + return !this.internalStop && this.runAgain.test(this); } public boolean isDisposed() { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java index fb4b5e6a..8fb7269b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java @@ -54,43 +54,43 @@ public class ServerPushService { } } - if (business != null) { - try { - log.trace("Call business on Server Push Session on: {}", Thread.currentThread().getName()); - business.accept(context); - } catch (final Exception e) { - log.error("Unexpected error while do business for server push service", e); - if (context.runAgain()) { - continue; - } else { - return; - } - } + if (context.isDisposed()) { + continue; } - if (!context.isDisposed()) { - - log.trace("Call update on Server Push Session on: {}", Thread.currentThread().getName()); - - context.getDisplay().asyncExec(() -> { + context.getDisplay().asyncExec(() -> { + if (business != null) { try { - update.accept(context); + + if (log.isTraceEnabled()) { + log.trace("Call business on Server Push Session on: {}", + Thread.currentThread().getName()); + } + + business.accept(context); + doUpdate(context, update); + } catch (final Exception e) { - log.warn( - "Failed to update on Server Push Session {}. It seems that the UISession is not available anymore. " - + "This may source from a connection interruption. cause: {}", - Thread.currentThread().getName(), e.getMessage()); + log.error("Unexpected error while do business for server push service", e); + context.internalStop = context.errorHandler.apply(e); } - }); - } + } else { + doUpdate(context, update); + } + }); + } + + if (log.isInfoEnabled()) { + log.info("Stop Server Push Session on: {}", Thread.currentThread().getName()); } - log.info("Stop Server Push Session on: {}", Thread.currentThread().getName()); try { pushSession.stop(); } catch (final Exception e) { log.warn( - "Failed to stop Server Push Session on: {}. It seems that the UISession is not available anymore. This may source from a connection interruption", + "Failed to stop Server Push Session on: {}. " + + "It seems that the UISession is not available anymore. " + + "This may source from a connection interruption", Thread.currentThread().getName(), e); } @@ -101,4 +101,28 @@ public class ServerPushService { bgThread.setDaemon(true); bgThread.start(); } + + private void doUpdate( + final ServerPushContext context, + final Consumer update) { + + if (!context.isDisposed()) { + + if (log.isTraceEnabled()) { + log.trace("Call update on Server Push Session on: {}", + Thread.currentThread().getName()); + } + + try { + update.accept(context); + } catch (final Exception e) { + log.warn( + "Failed to update on Server Push Session {}. " + + "It seems that the UISession is not available anymore. " + + "This may source from a connection interruption. cause: {}", + Thread.currentThread().getName(), e.getMessage()); + context.internalStop = context.errorHandler.apply(e); + } + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 70b53006..f1232557 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -303,10 +303,7 @@ public final class ClientConnectionTable { this.restCallBuilder .withHeader(API.EXAM_MONITORING_STATE_FILTER, this.statusFilterParam) .call() - .get(error -> { - log.error("Error poll connection data: ", error); - return Collections.emptyList(); - }) + .getOrThrow() .forEach(data -> { final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent( data.getConnectionId(), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java index 6b7cf37e..a6b1a7e9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java @@ -113,7 +113,13 @@ public final class ImageUploadSelection extends Composite { ImageUploadSelection.this.fileUpload.submit(uploadHandler.getUploadUrl()); ImageUploadSelection.this.serverPushService.runServerPush( - new ServerPushContext(ImageUploadSelection.this, ImageUploadSelection::uploadInProgress), + new ServerPushContext( + ImageUploadSelection.this, + ImageUploadSelection::uploadInProgress, + error -> { + log.error("Failed to upload image: {}", error.getMessage()); + return false; + }), 200, ImageUploadSelection::update); });