fixed concurrent user login within ServerPushService

This commit is contained in:
anhefti 2020-11-17 17:04:01 +01:00
parent 1b9b201da9
commit 1d7bd02382
6 changed files with 104 additions and 41 deletions

View file

@ -163,7 +163,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());

View file

@ -19,6 +19,7 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.MessageBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@ -57,6 +58,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClient
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.widget.Message;
@Lazy
@Component
@ -149,7 +151,9 @@ public class MonitoringRunningExam implements TemplateComposer {
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION));
this.serverPushService.runServerPush(
new ServerPushContext(content, Utils.truePredicate()),
new ServerPushContext(
content, Utils.truePredicate(),
createServerPushUpdateErrorHandler(this.pageService, pageContext)),
this.pollInterval,
context -> clientTable.updateValues(),
updateTableGUI(clientTable));
@ -360,4 +364,30 @@ public class MonitoringRunningExam implements TemplateComposer {
};
}
static final Function<Exception, Boolean> 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;
};
}
}

View file

@ -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<ServerPushContext> runAgain;
public ServerPushContext(final Composite anchor) {
this(anchor, context -> false);
}
public final Composite anchor;
public final Predicate<ServerPushContext> runAgain;
public final Function<Exception, Boolean> errorHandler;
boolean internalStop = false;
public ServerPushContext(
final Composite anchor,
final Predicate<ServerPushContext> runAgain) {
final Predicate<ServerPushContext> runAgain,
final Function<Exception, Boolean> 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() {

View file

@ -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);
updateGUI(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 {
updateGUI(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 updateGUI(
final ServerPushContext context,
final Consumer<ServerPushContext> 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);
}
}
}
}

View file

@ -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(),

View file

@ -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);
});