monitoring improvements
This commit is contained in:
parent
25ab3106aa
commit
0e528a3c86
27 changed files with 382 additions and 165 deletions
|
@ -22,13 +22,19 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
|||
public final class ClientConnection implements GrantEntity {
|
||||
|
||||
public enum ConnectionStatus {
|
||||
UNDEFINED,
|
||||
CONNECTION_REQUESTED,
|
||||
AUTHENTICATED,
|
||||
ESTABLISHED,
|
||||
CLOSED,
|
||||
ABORTED,
|
||||
DISABLED
|
||||
UNDEFINED(false),
|
||||
CONNECTION_REQUESTED(false),
|
||||
AUTHENTICATED(true),
|
||||
ACTIVE(true),
|
||||
CLOSED(false),
|
||||
DISABLED(false);
|
||||
|
||||
public final boolean establishedStatus;
|
||||
|
||||
private ConnectionStatus(final boolean establishedStatus) {
|
||||
this.establishedStatus = establishedStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final ClientConnection EMPTY_CLIENT_CONNECTION = new ClientConnection(
|
||||
|
|
|
@ -16,6 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
public class ClientConnectionData {
|
||||
|
@ -24,6 +26,8 @@ public class ClientConnectionData {
|
|||
public final ClientConnection clientConnection;
|
||||
@JsonProperty("indicatorValues")
|
||||
public final List<? extends IndicatorValue> indicatorValues;
|
||||
@JsonIgnore
|
||||
public final boolean missingPing;
|
||||
|
||||
@JsonCreator
|
||||
protected ClientConnectionData(
|
||||
|
@ -32,6 +36,12 @@ public class ClientConnectionData {
|
|||
|
||||
this.clientConnection = clientConnection;
|
||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||
this.missingPing = clientConnection.status == ConnectionStatus.ACTIVE &&
|
||||
this.indicatorValues.stream()
|
||||
.filter(ind -> ind.getType() == IndicatorType.LAST_PING)
|
||||
.findFirst()
|
||||
.map(ind -> (long) ind.getValue())
|
||||
.orElse(0L) > 5000;
|
||||
}
|
||||
|
||||
protected ClientConnectionData(
|
||||
|
@ -40,6 +50,7 @@ public class ClientConnectionData {
|
|||
|
||||
this.clientConnection = clientConnection;
|
||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||
this.missingPing = false;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
|
|
|
@ -211,6 +211,10 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.clearAttributes()
|
||||
.clearEntityKeys())
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_FROM_DETAILS)
|
||||
.withEntityKey(parentEntityKey)
|
||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER))
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_QUIT)
|
||||
.withConfirm(() -> CONFIRM_QUIT)
|
||||
.withExec(action -> {
|
||||
|
@ -221,10 +225,6 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
return action;
|
||||
})
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER))
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_FROM_DETAILS)
|
||||
.withEntityKey(parentEntityKey)
|
||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER));
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Collection;
|
|||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
|
@ -184,39 +185,70 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
.noEventPropagation()
|
||||
.publishIf(privilege);
|
||||
|
||||
clientTable.hideStatus(ConnectionStatus.DISABLED);
|
||||
|
||||
if (privilege.getAsBoolean()) {
|
||||
final PageAction showClosedConnections =
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(action -> {
|
||||
clientTable.showStatus(ConnectionStatus.CLOSED);
|
||||
return action;
|
||||
})
|
||||
.noEventPropagation()
|
||||
.create();
|
||||
|
||||
final PageAction hideClosedConnections =
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(action -> {
|
||||
clientTable.hideStatus(ConnectionStatus.CLOSED);
|
||||
return action;
|
||||
})
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(showClosedConnections)
|
||||
.create();
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
|
||||
this.pageService.publishAction(clientTable.isStatusHidden(ConnectionStatus.CLOSED)
|
||||
? showClosedConnections
|
||||
: hideClosedConnections);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Function<PageAction, PageAction> showStateViewAction(
|
||||
final ClientConnectionTable clientTable,
|
||||
final ConnectionStatus status) {
|
||||
|
||||
return action -> {
|
||||
clientTable.showStatus(status);
|
||||
return action;
|
||||
};
|
||||
}
|
||||
|
||||
private static final Function<PageAction, PageAction> hideStateViewAction(
|
||||
final ClientConnectionTable clientTable,
|
||||
final ConnectionStatus status) {
|
||||
|
||||
return action -> {
|
||||
clientTable.hideStatus(status);
|
||||
return action;
|
||||
};
|
||||
}
|
||||
|
||||
private PageAction quitSebClients(
|
||||
final PageAction action,
|
||||
final ClientConnectionTable clientTable,
|
||||
final boolean all) {
|
||||
|
||||
final Predicate<ClientConnection> activePredicate = ClientConnection
|
||||
.getStatusPredicate(ConnectionStatus.ESTABLISHED);
|
||||
.getStatusPredicate(ConnectionStatus.ACTIVE);
|
||||
|
||||
final Set<String> connectionTokens = clientTable.getConnectionTokens(
|
||||
activePredicate,
|
||||
|
|
|
@ -200,7 +200,7 @@ public class SebClientLogs implements TemplateComposer {
|
|||
TIME_TEXT_KEY,
|
||||
this::getEventTime)
|
||||
.withFilter(new TableFilterAttribute(
|
||||
CriteriaType.DATE_RANGE,
|
||||
CriteriaType.DATE_TIME_RANGE,
|
||||
ClientEvent.FILTER_ATTR_SERVER_TIME_FROM_TO,
|
||||
Utils.toDateTimeUTC(Utils.getMillisecondsNow())
|
||||
.minusYears(1)
|
||||
|
|
|
@ -28,7 +28,7 @@ public enum ActionCategory {
|
|||
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 100),
|
||||
;
|
||||
FILTER(new LocTextKey("sebserver.overall.action.category.filter"), 50);
|
||||
|
||||
public final LocTextKey title;
|
||||
public final int slotPosition;
|
||||
|
|
|
@ -582,16 +582,36 @@ public enum ActionDefinition {
|
|||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.CLIENT_EVENT_LIST),
|
||||
|
||||
MONITOR_EXAM_HIDE_REQUESTED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.requested"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FILTER),
|
||||
MONITOR_EXAM_SHOW_REQUESTED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.show.requested"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FILTER),
|
||||
MONITOR_EXAM_HIDE_CLOSED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.closed"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FORM),
|
||||
ActionCategory.FILTER),
|
||||
MONITOR_EXAM_SHOW_CLOSED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.show.closed"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FORM),
|
||||
ActionCategory.FILTER),
|
||||
MONITOR_EXAM_HIDE_DISABLED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.disabled"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FILTER),
|
||||
MONITOR_EXAM_SHOW_DISABLED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.show.disabled"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FILTER),
|
||||
|
||||
LOGS_USER_ACTIVITY_LIST(
|
||||
new LocTextKey("sebserver.logs.activity.userlogs"),
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.form;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
@ -27,6 +28,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
boolean isNumber = false;
|
||||
Consumer<String> numberCheck = null;
|
||||
boolean isArea = false;
|
||||
boolean isColorbox = false;
|
||||
|
||||
TextFieldBuilder(final String name, final LocTextKey label, final String value) {
|
||||
super(name, label, value);
|
||||
|
@ -53,6 +55,11 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TextFieldBuilder asColorbox() {
|
||||
this.isColorbox = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void build(final FormBuilder builder) {
|
||||
final boolean readonly = builder.readonly || this.readonly;
|
||||
|
@ -75,6 +82,9 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||
if (this.isArea) {
|
||||
gridData.minimumHeight = WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT;
|
||||
} else if (this.isColorbox) {
|
||||
gridData.minimumHeight = WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
|
||||
textInput.setData(RWT.CUSTOM_VARIANT, "colorbox");
|
||||
}
|
||||
textInput.setLayoutData(gridData);
|
||||
if (StringUtils.isNoneBlank(this.value)) {
|
||||
|
|
|
@ -44,6 +44,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.Configuration
|
|||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
|
||||
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.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||
|
@ -73,6 +74,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
|||
* combo-box content. */
|
||||
public class ResourceService {
|
||||
|
||||
private static final String MISSING_CLIENT_PING_NAME_KEY = "MISSING";
|
||||
|
||||
public static final Comparator<Tuple<String>> RESOURCE_COMPARATOR = (t1, t2) -> t1._2.compareTo(t2._2);
|
||||
|
||||
public static final EnumSet<EntityType> ENTITY_TYPE_EXCLUDE_MAP = EnumSet.of(
|
||||
|
@ -436,6 +439,24 @@ public class ResourceService {
|
|||
.getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.configStatus.name());
|
||||
}
|
||||
|
||||
public String localizedClientConnectionStatusName(final ClientConnectionData connectionData) {
|
||||
if (connectionData == null) {
|
||||
final String name = ConnectionStatus.UNDEFINED.name();
|
||||
return this.i18nSupport.getText(
|
||||
SEB_CONNECTION_STATUS_KEY_PREFIX + name,
|
||||
name);
|
||||
}
|
||||
if (connectionData.missingPing) {
|
||||
return this.i18nSupport.getText(
|
||||
SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_PING_NAME_KEY,
|
||||
MISSING_CLIENT_PING_NAME_KEY);
|
||||
} else {
|
||||
return localizedClientConnectionStatusName((connectionData.clientConnection != null)
|
||||
? connectionData.clientConnection.status
|
||||
: ConnectionStatus.UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
public String localizedClientConnectionStatusName(final ConnectionStatus status) {
|
||||
String name;
|
||||
if (status != null) {
|
||||
|
|
|
@ -323,17 +323,18 @@ public interface PageService {
|
|||
}
|
||||
|
||||
public PageActionBuilder newAction(final ActionDefinition definition) {
|
||||
pageContext = originalPageContext.copy();
|
||||
this.definition = definition;
|
||||
confirm = null;
|
||||
successMessage = null;
|
||||
selectionSupplier = null;
|
||||
noSelectionMessage = null;
|
||||
exec = null;
|
||||
fireActionEvent = true;
|
||||
ignoreMoveAwayFromEdit = false;
|
||||
switchAction = null;
|
||||
return this;
|
||||
final PageActionBuilder newBuilder = new PageActionBuilder(this.pageService, this.originalPageContext);
|
||||
newBuilder.pageContext = originalPageContext.copy();
|
||||
newBuilder.definition = definition;
|
||||
newBuilder.confirm = null;
|
||||
newBuilder.successMessage = null;
|
||||
newBuilder.selectionSupplier = null;
|
||||
newBuilder.noSelectionMessage = null;
|
||||
newBuilder.exec = null;
|
||||
newBuilder.fireActionEvent = true;
|
||||
newBuilder.ignoreMoveAwayFromEdit = false;
|
||||
newBuilder.switchAction = null;
|
||||
return newBuilder;
|
||||
}
|
||||
|
||||
public PageAction create() {
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.session;
|
|||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -21,12 +22,12 @@ 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.IndicatorType;
|
||||
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.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gui.form.Form;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
|
@ -50,13 +51,15 @@ public class ClientConnectionDetails {
|
|||
private static final int NUMBER_OF_NONE_INDICATOR_ROWS = 3;
|
||||
|
||||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final Exam exam;
|
||||
private final EnumMap<IndicatorType, IndicatorData> indicatorMapping;
|
||||
private final RestCall<ClientConnectionData>.RestCallBuilder restCallBuilder;
|
||||
private final FormHandle<?> formhandle;
|
||||
private final StatusData statusColor;
|
||||
private final ColorData colorData;
|
||||
|
||||
private ClientConnectionData connectionData = null;
|
||||
private boolean statusChanged = true;
|
||||
|
||||
public ClientConnectionDetails(
|
||||
final PageService pageService,
|
||||
|
@ -68,12 +71,14 @@ public class ClientConnectionDetails {
|
|||
final Display display = pageContext.getRoot().getDisplay();
|
||||
|
||||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.exam = exam;
|
||||
this.restCallBuilder = restCallBuilder;
|
||||
this.statusColor = new StatusData(display);
|
||||
this.colorData = new ColorData(display);
|
||||
this.indicatorMapping = IndicatorData.createFormIndicators(
|
||||
indicators,
|
||||
display,
|
||||
this.colorData,
|
||||
NUMBER_OF_NONE_INDICATOR_ROWS);
|
||||
|
||||
final FormBuilder formBuilder = this.pageService.formBuilder(pageContext, 4)
|
||||
|
@ -94,7 +99,8 @@ public class ClientConnectionDetails {
|
|||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||
CONNECTION_STATUS_TEXT_KEY,
|
||||
Constants.EMPTY_NOTE))
|
||||
Constants.EMPTY_NOTE)
|
||||
.asColorbox())
|
||||
.addEmptyCell();
|
||||
|
||||
this.indicatorMapping
|
||||
|
@ -105,6 +111,7 @@ public class ClientConnectionDetails {
|
|||
indData.indicator.name,
|
||||
new LocTextKey(indData.indicator.name),
|
||||
Constants.EMPTY_NOTE)
|
||||
.asColorbox()
|
||||
.withDefaultLabel(indData.indicator.name))
|
||||
.addEmptyCell();
|
||||
});
|
||||
|
@ -113,12 +120,19 @@ public class ClientConnectionDetails {
|
|||
}
|
||||
|
||||
public void updateData(final ServerPushContext context) {
|
||||
this.connectionData = this.restCallBuilder
|
||||
final ClientConnectionData connectionData = this.restCallBuilder
|
||||
.call()
|
||||
.get(error -> {
|
||||
log.error("Unexpected error while trying to get current client connection data: ", error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (this.connectionData != null && connectionData != null) {
|
||||
this.statusChanged =
|
||||
this.connectionData.clientConnection.status != connectionData.clientConnection.status ||
|
||||
this.connectionData.missingPing != connectionData.missingPing;
|
||||
}
|
||||
this.connectionData = connectionData;
|
||||
}
|
||||
|
||||
public void updateGUI(final ServerPushContext context) {
|
||||
|
@ -135,13 +149,16 @@ public class ClientConnectionDetails {
|
|||
Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS,
|
||||
this.connectionData.clientConnection.clientAddress);
|
||||
|
||||
// update status
|
||||
form.setFieldValue(
|
||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||
getStatusName());
|
||||
form.setFieldColor(
|
||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||
this.statusColor.getStatusColor(this.connectionData));
|
||||
if (this.statusChanged) {
|
||||
// update status
|
||||
form.setFieldValue(
|
||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||
this.resourceService.localizedClientConnectionStatusName(this.connectionData));
|
||||
final Color statusColor = this.colorData.getStatusColor(this.connectionData);
|
||||
final Color statusTextColor = this.colorData.getStatusTextColor(statusColor);
|
||||
form.setFieldColor(Domain.CLIENT_CONNECTION.ATTR_STATUS, statusColor);
|
||||
form.setFieldTextColor(Domain.CLIENT_CONNECTION.ATTR_STATUS, statusTextColor);
|
||||
}
|
||||
|
||||
// update indicators
|
||||
this.connectionData.getIndicatorValues()
|
||||
|
@ -151,7 +168,7 @@ public class ClientConnectionDetails {
|
|||
final double value = indValue.getValue();
|
||||
final String displayValue = IndicatorValue.getDisplayValue(indValue);
|
||||
|
||||
if (this.connectionData.clientConnection.status != ConnectionStatus.ESTABLISHED) {
|
||||
if (!this.connectionData.clientConnection.status.establishedStatus) {
|
||||
|
||||
form.setFieldValue(
|
||||
indData.indicator.name,
|
||||
|
@ -175,11 +192,4 @@ public class ClientConnectionDetails {
|
|||
});
|
||||
}
|
||||
|
||||
String getStatusName() {
|
||||
return this.pageService.getResourceService().localizedClientConnectionStatusName(
|
||||
(this.connectionData != null && this.connectionData.clientConnection != null)
|
||||
? this.connectionData.clientConnection.status
|
||||
: ConnectionStatus.UNDEFINED);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ 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.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
|
@ -77,13 +77,13 @@ public final class ClientConnectionTable {
|
|||
|
||||
private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
||||
|
||||
private final PageService pageService;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final ResourceService resourceService;
|
||||
private final Exam exam;
|
||||
private final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder;
|
||||
private final EnumMap<IndicatorType, IndicatorData> indicatorMapping;
|
||||
private final Table table;
|
||||
private final StatusData statusData;
|
||||
private final ColorData colorData;
|
||||
private final EnumSet<ConnectionStatus> statusFilter;
|
||||
|
||||
private int tableWidth;
|
||||
|
@ -101,13 +101,13 @@ public final class ClientConnectionTable {
|
|||
final Collection<Indicator> indicators,
|
||||
final RestCall<Collection<ClientConnectionData>>.RestCallBuilder restCallBuilder) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.exam = exam;
|
||||
this.restCallBuilder = restCallBuilder;
|
||||
|
||||
final Display display = tableRoot.getDisplay();
|
||||
this.statusData = new StatusData(display);
|
||||
this.colorData = new ColorData(display);
|
||||
|
||||
this.darkFontColor = new Color(display, Constants.BLACK_RGB);
|
||||
this.lightFontColor = new Color(display, Constants.WHITE_RGB);
|
||||
|
@ -115,6 +115,7 @@ public final class ClientConnectionTable {
|
|||
this.indicatorMapping = IndicatorData.createFormIndicators(
|
||||
indicators,
|
||||
display,
|
||||
this.colorData,
|
||||
NUMBER_OF_NONE_INDICATOR_COLUMNS);
|
||||
|
||||
this.statusFilter = EnumSet.noneOf(ConnectionStatus.class);
|
||||
|
@ -209,9 +210,8 @@ public final class ClientConnectionTable {
|
|||
for (int i = 0; i < selectionIndices.length; i++) {
|
||||
final UpdatableTableItem updatableTableItem =
|
||||
new ArrayList<>(this.tableMapping.values())
|
||||
.get(selectionIndices[0]);
|
||||
.get(selectionIndices[i]);
|
||||
if (filter.test(updatableTableItem.connectionData.clientConnection)) {
|
||||
|
||||
result.add(updatableTableItem.connectionData.clientConnection.connectionToken);
|
||||
}
|
||||
}
|
||||
|
@ -375,11 +375,10 @@ public final class ClientConnectionTable {
|
|||
}
|
||||
|
||||
void updateConnectionStatusColor(final TableItem tableItem) {
|
||||
final Color statusColor = ClientConnectionTable.this.statusData.getStatusColor(this.connectionData);
|
||||
final Color statusColor = ClientConnectionTable.this.colorData.getStatusColor(this.connectionData);
|
||||
final Color statusTextColor = ClientConnectionTable.this.colorData.getStatusTextColor(statusColor);
|
||||
tableItem.setBackground(2, statusColor);
|
||||
tableItem.setForeground(2, Utils.darkColor(statusColor.getRGB())
|
||||
? ClientConnectionTable.this.darkFontColor
|
||||
: ClientConnectionTable.this.lightFontColor);
|
||||
tableItem.setForeground(2, statusTextColor);
|
||||
}
|
||||
|
||||
void updateDuplicateColor(final TableItem tableItem) {
|
||||
|
@ -392,7 +391,7 @@ public final class ClientConnectionTable {
|
|||
final List<Long> list =
|
||||
ClientConnectionTable.this.sessionIds.get(this.connectionData.clientConnection.userSessionId);
|
||||
if (list != null && list.size() > 1) {
|
||||
tableItem.setBackground(0, ClientConnectionTable.this.statusData.color3);
|
||||
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color3);
|
||||
tableItem.setForeground(0, ClientConnectionTable.this.lightFontColor);
|
||||
} else {
|
||||
tableItem.setBackground(0, null);
|
||||
|
@ -409,14 +408,12 @@ public final class ClientConnectionTable {
|
|||
return;
|
||||
}
|
||||
|
||||
final boolean fillEmpty = this.connectionData.clientConnection.status != ConnectionStatus.ESTABLISHED;
|
||||
|
||||
for (int i = 0; i < this.connectionData.indicatorValues.size(); i++) {
|
||||
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
|
||||
final IndicatorData indicatorData =
|
||||
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType());
|
||||
|
||||
if (fillEmpty) {
|
||||
if (!this.connectionData.clientConnection.status.establishedStatus) {
|
||||
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
|
||||
? Constants.EMPTY_NOTE
|
||||
: IndicatorValue.getDisplayValue(indicatorValue);
|
||||
|
@ -470,7 +467,7 @@ public final class ClientConnectionTable {
|
|||
}
|
||||
|
||||
int statusWeight() {
|
||||
return ClientConnectionTable.this.statusData.statusWeight(this.connectionData);
|
||||
return ClientConnectionTable.this.colorData.statusWeight(this.connectionData);
|
||||
}
|
||||
|
||||
int thresholdsWeight() {
|
||||
|
@ -478,10 +475,8 @@ public final class ClientConnectionTable {
|
|||
}
|
||||
|
||||
String getStatusName() {
|
||||
return ClientConnectionTable.this.pageService.getResourceService().localizedClientConnectionStatusName(
|
||||
(this.connectionData != null && this.connectionData.clientConnection != null)
|
||||
? this.connectionData.clientConnection.status
|
||||
: ConnectionStatus.UNDEFINED);
|
||||
return ClientConnectionTable.this.resourceService
|
||||
.localizedClientConnectionStatusName(this.connectionData);
|
||||
}
|
||||
|
||||
String getConnectionAddress() {
|
||||
|
|
|
@ -12,20 +12,26 @@ import org.eclipse.swt.graphics.Color;
|
|||
import org.eclipse.swt.graphics.RGB;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
public class StatusData {
|
||||
public class ColorData {
|
||||
|
||||
final Color darkColor;
|
||||
final Color lightColor;
|
||||
final Color defaultColor;
|
||||
final Color color1;
|
||||
final Color color2;
|
||||
final Color color3;
|
||||
|
||||
public StatusData(final Display display) {
|
||||
this.defaultColor = new Color(display, new RGB(255, 255, 255), 255);
|
||||
public ColorData(final Display display) {
|
||||
this.defaultColor = new Color(display, new RGB(220, 220, 220), 255);
|
||||
this.color1 = new Color(display, new RGB(34, 177, 76), 255);
|
||||
this.color2 = new Color(display, new RGB(255, 194, 14), 255);
|
||||
this.color3 = new Color(display, new RGB(237, 28, 36), 255);
|
||||
this.darkColor = new Color(display, Constants.BLACK_RGB);
|
||||
this.lightColor = new Color(display, Constants.WHITE_RGB);
|
||||
}
|
||||
|
||||
Color getStatusColor(final ClientConnectionData connectionData) {
|
||||
|
@ -34,28 +40,30 @@ public class StatusData {
|
|||
}
|
||||
|
||||
switch (connectionData.clientConnection.status) {
|
||||
case ESTABLISHED:
|
||||
return this.color1;
|
||||
case ABORTED:
|
||||
return this.color3;
|
||||
default:
|
||||
case ACTIVE:
|
||||
return (connectionData.missingPing) ? this.color2 : this.color1;
|
||||
case DISABLED:
|
||||
return this.color2;
|
||||
default:
|
||||
return this.defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
Color getStatusTextColor(final Color statusColor) {
|
||||
return Utils.darkColor(statusColor.getRGB()) ? this.darkColor : this.lightColor;
|
||||
}
|
||||
|
||||
int statusWeight(final ClientConnectionData connectionData) {
|
||||
if (connectionData == null) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
switch (connectionData.clientConnection.status) {
|
||||
case ABORTED:
|
||||
return 0;
|
||||
case CONNECTION_REQUESTED:
|
||||
case AUTHENTICATED:
|
||||
return 1;
|
||||
case ESTABLISHED:
|
||||
return 2;
|
||||
case ACTIVE:
|
||||
return (connectionData.missingPing) ? 0 : 2;
|
||||
case CLOSED:
|
||||
return 3;
|
||||
default:
|
|
@ -16,7 +16,6 @@ import java.util.EnumMap;
|
|||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
|
||||
|
@ -35,6 +34,7 @@ final class IndicatorData {
|
|||
final Indicator indicator,
|
||||
final int index,
|
||||
final int tableIndex,
|
||||
final ColorData colorData,
|
||||
final Display display) {
|
||||
|
||||
this.indicator = indicator;
|
||||
|
@ -42,20 +42,21 @@ final class IndicatorData {
|
|||
this.tableIndex = tableIndex;
|
||||
this.defaultColor = new Color(display, Utils.toRGB(indicator.defaultColor), 255);
|
||||
this.defaultTextColor = Utils.darkColor(this.defaultColor.getRGB())
|
||||
? new Color(display, Constants.BLACK_RGB)
|
||||
: new Color(display, Constants.WHITE_RGB);
|
||||
? colorData.darkColor
|
||||
: colorData.lightColor;
|
||||
|
||||
this.thresholdColor = new ThresholdColor[indicator.thresholds.size()];
|
||||
final ArrayList<Threshold> sortedThresholds = new ArrayList<>(indicator.thresholds);
|
||||
Collections.sort(sortedThresholds, (t1, t2) -> t1.value.compareTo(t2.value));
|
||||
for (int i = 0; i < indicator.thresholds.size(); i++) {
|
||||
this.thresholdColor[i] = new ThresholdColor(sortedThresholds.get(i), display);
|
||||
this.thresholdColor[i] = new ThresholdColor(sortedThresholds.get(i), display, colorData);
|
||||
}
|
||||
}
|
||||
|
||||
static final EnumMap<IndicatorType, IndicatorData> createFormIndicators(
|
||||
final Collection<Indicator> indicators,
|
||||
final Display display,
|
||||
final ColorData colorData,
|
||||
final int tableIndexOffset) {
|
||||
|
||||
final EnumMap<IndicatorType, IndicatorData> indicatorMapping = new EnumMap<>(IndicatorType.class);
|
||||
|
@ -65,6 +66,7 @@ final class IndicatorData {
|
|||
indicator,
|
||||
i,
|
||||
i + tableIndexOffset,
|
||||
colorData,
|
||||
display));
|
||||
i++;
|
||||
}
|
||||
|
@ -86,12 +88,12 @@ final class IndicatorData {
|
|||
final Color color;
|
||||
final Color textColor;
|
||||
|
||||
protected ThresholdColor(final Threshold threshold, final Display display) {
|
||||
protected ThresholdColor(final Threshold threshold, final Display display, final ColorData colorData) {
|
||||
this.value = threshold.value;
|
||||
this.color = new Color(display, Utils.toRGB(threshold.color), 255);
|
||||
this.textColor = Utils.darkColor(this.color.getRGB())
|
||||
? new Color(display, Constants.BLACK_RGB)
|
||||
: new Color(display, Constants.WHITE_RGB);
|
||||
? colorData.darkColor
|
||||
: colorData.lightColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ public class TableFilter<ROW extends Entity> {
|
|||
TEXT,
|
||||
SINGLE_SELECTION,
|
||||
DATE,
|
||||
DATE_RANGE
|
||||
DATE_RANGE,
|
||||
DATE_TIME_RANGE
|
||||
}
|
||||
|
||||
private final Composite composite;
|
||||
|
@ -125,6 +126,8 @@ public class TableFilter<ROW extends Entity> {
|
|||
return new Date(attribute);
|
||||
case DATE_RANGE:
|
||||
return new DateRange(attribute);
|
||||
case DATE_TIME_RANGE:
|
||||
return new DateRange(attribute, true);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported FilterAttributeType: " + attribute.type);
|
||||
}
|
||||
|
@ -476,17 +479,25 @@ public class TableFilter<ROW extends Entity> {
|
|||
|
||||
private Composite innerComposite;
|
||||
private final GridData rw1 = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
private DateTime fromSelector;
|
||||
private DateTime toSelector;
|
||||
private DateTime fromDateSelector;
|
||||
private DateTime toDateSelector;
|
||||
private DateTime fromTimeSelector;
|
||||
private DateTime toTimeSelector;
|
||||
private final boolean withTime;
|
||||
|
||||
DateRange(final TableFilterAttribute attribute) {
|
||||
this(attribute, false);
|
||||
}
|
||||
|
||||
DateRange(final TableFilterAttribute attribute, final boolean withTime) {
|
||||
super(attribute);
|
||||
this.withTime = withTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
FilterComponent build(final Composite parent) {
|
||||
this.innerComposite = new Composite(parent, SWT.NONE);
|
||||
final GridLayout gridLayout = new GridLayout(2, false);
|
||||
final GridLayout gridLayout = new GridLayout((this.withTime) ? 3 : 2, false);
|
||||
gridLayout.marginHeight = 0;
|
||||
gridLayout.marginWidth = 0;
|
||||
gridLayout.horizontalSpacing = 5;
|
||||
|
@ -496,13 +507,25 @@ public class TableFilter<ROW extends Entity> {
|
|||
|
||||
TableFilter.this.entityTable.widgetFactory
|
||||
.labelLocalized(this.innerComposite, DATE_FROM_TEXT);
|
||||
this.fromSelector = new DateTime(this.innerComposite, SWT.DATE | SWT.BORDER);
|
||||
this.fromSelector.setLayoutData(this.rw1);
|
||||
this.fromDateSelector =
|
||||
new DateTime(this.innerComposite, SWT.DATE | SWT.BORDER);
|
||||
this.fromDateSelector.setLayoutData(this.rw1);
|
||||
if (this.withTime) {
|
||||
this.fromTimeSelector =
|
||||
new DateTime(this.innerComposite, SWT.TIME | SWT.BORDER);
|
||||
this.fromTimeSelector.setLayoutData(this.rw1);
|
||||
}
|
||||
|
||||
TableFilter.this.entityTable.widgetFactory
|
||||
.labelLocalized(this.innerComposite, DATE_TO_TEXT);
|
||||
this.toSelector = new DateTime(this.innerComposite, SWT.DATE | SWT.BORDER);
|
||||
this.toSelector.setLayoutData(this.rw1);
|
||||
this.toDateSelector =
|
||||
new DateTime(this.innerComposite, SWT.DATE | SWT.BORDER);
|
||||
this.toDateSelector.setLayoutData(this.rw1);
|
||||
if (this.withTime) {
|
||||
this.toTimeSelector =
|
||||
new DateTime(this.innerComposite, SWT.TIME | SWT.BORDER);
|
||||
this.toTimeSelector.setLayoutData(this.rw1);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -510,44 +533,66 @@ public class TableFilter<ROW extends Entity> {
|
|||
@Override
|
||||
FilterComponent reset() {
|
||||
final org.joda.time.DateTime now = org.joda.time.DateTime.now(DateTimeZone.UTC);
|
||||
if (this.fromSelector != null) {
|
||||
if (this.fromDateSelector != null) {
|
||||
try {
|
||||
final org.joda.time.DateTime parse = org.joda.time.DateTime.parse(this.attribute.initValue);
|
||||
|
||||
this.fromSelector.setDate(
|
||||
this.fromDateSelector.setDate(
|
||||
parse.getYear(),
|
||||
parse.getMonthOfYear() - 1,
|
||||
parse.getDayOfMonth());
|
||||
if (this.fromTimeSelector != null) {
|
||||
this.fromTimeSelector.setTime(
|
||||
parse.getHourOfDay(),
|
||||
parse.getMinuteOfHour(),
|
||||
parse.getSecondOfMinute());
|
||||
}
|
||||
|
||||
} catch (final RuntimeException e) {
|
||||
this.fromSelector.setDate(
|
||||
this.fromDateSelector.setDate(
|
||||
now.getYear(),
|
||||
now.getMonthOfYear() - 1,
|
||||
now.getDayOfMonth());
|
||||
if (this.fromTimeSelector != null) {
|
||||
this.fromTimeSelector.setTime(
|
||||
now.getHourOfDay(),
|
||||
now.getMinuteOfHour(),
|
||||
now.getSecondOfMinute());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.toSelector != null) {
|
||||
this.toSelector.setDate(
|
||||
if (this.toDateSelector != null) {
|
||||
this.toDateSelector.setDate(
|
||||
now.getYear(),
|
||||
now.getMonthOfYear() - 1,
|
||||
now.getDayOfMonth());
|
||||
if (this.toTimeSelector != null) {
|
||||
this.toTimeSelector.setTime(
|
||||
now.getHourOfDay(),
|
||||
now.getMinuteOfHour(),
|
||||
now.getSecondOfMinute());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getValue() {
|
||||
if (this.fromSelector != null && this.toSelector != null) {
|
||||
if (this.fromDateSelector != null && this.toDateSelector != null) {
|
||||
final org.joda.time.DateTime fromDate = org.joda.time.DateTime.now(DateTimeZone.UTC)
|
||||
.withYear(this.fromSelector.getYear())
|
||||
.withMonthOfYear(this.fromSelector.getMonth() + 1)
|
||||
.withDayOfMonth(this.fromSelector.getDay())
|
||||
.withTimeAtStartOfDay();
|
||||
.withYear(this.fromDateSelector.getYear())
|
||||
.withMonthOfYear(this.fromDateSelector.getMonth() + 1)
|
||||
.withDayOfMonth(this.fromDateSelector.getDay())
|
||||
.withHourOfDay((this.fromTimeSelector != null) ? this.fromTimeSelector.getHours() : 0)
|
||||
.withMinuteOfHour((this.fromTimeSelector != null) ? this.fromTimeSelector.getMinutes() : 0)
|
||||
.withSecondOfMinute((this.fromTimeSelector != null) ? this.fromTimeSelector.getSeconds() : 0);
|
||||
final org.joda.time.DateTime toDate = org.joda.time.DateTime.now(DateTimeZone.UTC)
|
||||
.withYear(this.toSelector.getYear())
|
||||
.withMonthOfYear(this.toSelector.getMonth() + 1)
|
||||
.withDayOfMonth(this.toSelector.getDay())
|
||||
.withTime(23, 59, 59, 0);
|
||||
.withYear(this.toDateSelector.getYear())
|
||||
.withMonthOfYear(this.toDateSelector.getMonth() + 1)
|
||||
.withDayOfMonth(this.toDateSelector.getDay())
|
||||
.withHourOfDay((this.toTimeSelector != null) ? this.toTimeSelector.getHours() : 0)
|
||||
.withMinuteOfHour((this.toTimeSelector != null) ? this.toTimeSelector.getMinutes() : 0)
|
||||
.withSecondOfMinute((this.toTimeSelector != null) ? this.toTimeSelector.getSeconds() : 0);
|
||||
|
||||
return fromDate.toString(Constants.STANDARD_DATE_TIME_FORMATTER) +
|
||||
Constants.EMBEDDED_LIST_SEPARATOR +
|
||||
|
@ -559,14 +604,33 @@ public class TableFilter<ROW extends Entity> {
|
|||
|
||||
@Override
|
||||
void setValue(final String value) {
|
||||
if (this.fromSelector != null && this.toSelector != null) {
|
||||
if (this.fromDateSelector != null && this.toDateSelector != null) {
|
||||
try {
|
||||
final String[] split = StringUtils.split(value, Constants.EMBEDDED_LIST_SEPARATOR);
|
||||
final org.joda.time.DateTime fromDate = Utils.toDateTime(split[0]);
|
||||
final org.joda.time.DateTime toDate = Utils.toDateTime(split[1]);
|
||||
this.fromSelector.setDate(fromDate.getYear(), fromDate.getMonthOfYear() - 1,
|
||||
this.fromDateSelector.setDate(
|
||||
fromDate.getYear(),
|
||||
fromDate.getMonthOfYear() - 1,
|
||||
fromDate.getDayOfMonth());
|
||||
this.toSelector.setDate(toDate.getYear(), toDate.getMonthOfYear() - 1, toDate.getDayOfMonth());
|
||||
if (this.fromTimeSelector != null) {
|
||||
this.fromTimeSelector.setTime(
|
||||
fromDate.getHourOfDay(),
|
||||
fromDate.getMinuteOfHour(),
|
||||
fromDate.getSecondOfMinute());
|
||||
}
|
||||
|
||||
this.toDateSelector.setDate(
|
||||
toDate.getYear(),
|
||||
toDate.getMonthOfYear() - 1,
|
||||
toDate.getDayOfMonth());
|
||||
if (this.toTimeSelector != null) {
|
||||
this.toTimeSelector.setTime(
|
||||
toDate.getHourOfDay(),
|
||||
toDate.getMinuteOfHour(),
|
||||
toDate.getSecondOfMinute());
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to set date range filter attribute: ", e);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ public interface ClientConnectionDAO extends EntityDAO<ClientConnection, ClientC
|
|||
Result<ClientConnection> createNew(ClientConnection data);
|
||||
|
||||
@Override
|
||||
// TODO check if this is needed
|
||||
@CacheEvict(cacheNames = CONNECTION_TOKENS_CACHE, allEntries = true)
|
||||
Result<ClientConnection> save(ClientConnection data);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
|
|||
runWorkers();
|
||||
|
||||
try {
|
||||
Thread.sleep(Constants.SECOND_IN_MILLIS);
|
||||
Thread.sleep(Constants.SECOND_IN_MILLIS / 2);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to wait");
|
||||
}
|
||||
|
@ -110,8 +110,7 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
|
|||
|
||||
@Override
|
||||
public void accept(final ClientEventRecord record) {
|
||||
if (!this.workersRunning) {
|
||||
log.error("Received ClientEvent on none enabled AsyncBatchEventSaveStrategy. ClientEvent is ignored");
|
||||
if (record == null || !this.workersRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
|||
final Collection<AbstractPingIndicator> pingMappings;
|
||||
final EnumMap<EventType, Collection<ClientIndicator>> indicatorMapping;
|
||||
|
||||
PingIntervalClientIndicator pingIndicator = null;
|
||||
|
||||
protected ClientConnectionDataInternal(
|
||||
final ClientConnection clientConnection,
|
||||
final List<ClientIndicator> clientIndicators) {
|
||||
|
@ -34,7 +36,14 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
|||
this.pingMappings = new ArrayList<>();
|
||||
for (final ClientIndicator clientIndicator : clientIndicators) {
|
||||
if (clientIndicator instanceof AbstractPingIndicator) {
|
||||
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
|
||||
if (clientIndicator instanceof PingIntervalClientIndicator) {
|
||||
this.pingIndicator = (PingIntervalClientIndicator) clientIndicator;
|
||||
if (!this.pingIndicator.hidden) {
|
||||
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
|
||||
}
|
||||
} else {
|
||||
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
|
||||
}
|
||||
}
|
||||
for (final EventType eventType : clientIndicator.observedEvents()) {
|
||||
this.indicatorMapping
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.context.annotation.Lazy;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO;
|
||||
|
@ -62,12 +63,18 @@ public class ClientIndicatorFactory {
|
|||
.allForExam(clientConnection.examId)
|
||||
.getOrThrow();
|
||||
|
||||
boolean pingIndicatorAvailable = false;
|
||||
|
||||
for (final Indicator indicatorDef : examIndicators) {
|
||||
try {
|
||||
|
||||
final ClientIndicator indicator = this.applicationContext
|
||||
.getBean(indicatorDef.type.name(), ClientIndicator.class);
|
||||
|
||||
if (!pingIndicatorAvailable) {
|
||||
pingIndicatorAvailable = indicatorDef.type == IndicatorType.LAST_PING;
|
||||
}
|
||||
|
||||
indicator.init(
|
||||
indicatorDef,
|
||||
clientConnection.id,
|
||||
|
@ -79,6 +86,16 @@ public class ClientIndicatorFactory {
|
|||
e);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no ping interval indicator set from the exam, we add a hidden one
|
||||
// to at least create missing ping events and track missing state
|
||||
if (!pingIndicatorAvailable) {
|
||||
final PingIntervalClientIndicator pingIndicator = this.applicationContext
|
||||
.getBean(PingIntervalClientIndicator.class);
|
||||
pingIndicator.hidden = true;
|
||||
result.add(pingIndicator);
|
||||
}
|
||||
|
||||
} catch (final RuntimeException e) {
|
||||
log.error("Failed to create ClientIndicator for ClientConnection: {}", clientConnection);
|
||||
throw e;
|
||||
|
|
|
@ -333,8 +333,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
|||
}
|
||||
|
||||
private static boolean isActiveConnection(final ClientConnectionData connection) {
|
||||
if (connection.clientConnection.status == ConnectionStatus.ESTABLISHED
|
||||
|| connection.clientConnection.status == ConnectionStatus.AUTHENTICATED) {
|
||||
if (connection.clientConnection.status.establishedStatus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ class ExamSessionControlTask implements DisposableBean {
|
|||
controlExamEnd(updateId);
|
||||
}
|
||||
|
||||
@Scheduled(fixedRateString = "${sebserver.webservice.api.seb.lostping.update:15000}")
|
||||
@Scheduled(fixedRateString = "${sebserver.webservice.api.seb.lostping.update:5000}")
|
||||
public void pingEventUpdateTask() {
|
||||
|
||||
if (!this.lostPingUpdateActive) {
|
||||
|
|
|
@ -39,6 +39,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
|||
private long pingErrorThreshold;
|
||||
private boolean isOnError = false;
|
||||
|
||||
boolean hidden = false;
|
||||
|
||||
public PingIntervalClientIndicator(final ClientEventExtentionMapper clientEventExtentionMapper) {
|
||||
super(clientEventExtentionMapper);
|
||||
this.cachingEnabled = true;
|
||||
|
@ -70,7 +72,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
|||
@Override
|
||||
public double getValue() {
|
||||
final long now = DateTime.now(DateTimeZone.UTC).getMillis();
|
||||
return now - super.currentValue;
|
||||
return now - super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -73,6 +73,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
this.webserviceInfo = sebInstructionService.getWebserviceInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExamSessionService getExamSessionService() {
|
||||
return this.examSessionService;
|
||||
}
|
||||
|
@ -220,7 +221,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
.getOrThrow();
|
||||
|
||||
final ClientConnectionDataInternal activeClientConnection =
|
||||
cacheEvictAndLoad(connectionToken);
|
||||
realoadConnectionCache(connectionToken);
|
||||
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Failed to load ClientConnectionDataInternal into cache on update");
|
||||
|
@ -286,7 +287,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
clientConnection.id,
|
||||
null,
|
||||
(examId != null) ? examId : clientConnection.examId,
|
||||
ConnectionStatus.ESTABLISHED,
|
||||
ConnectionStatus.ACTIVE,
|
||||
null,
|
||||
userSessionId,
|
||||
null,
|
||||
|
@ -298,7 +299,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
clientConnection.connectionToken == null ||
|
||||
establishedClientConnection.examId == null ||
|
||||
clientConnection.clientAddress == null ||
|
||||
establishedClientConnection.status != ConnectionStatus.ESTABLISHED) {
|
||||
establishedClientConnection.status != ConnectionStatus.ACTIVE) {
|
||||
|
||||
log.error("ClientConnection integrity violation, clientConnection: {}, establishedClientConnection: {}",
|
||||
clientConnection,
|
||||
|
@ -313,7 +314,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
checkExamIntegrity(updatedClientConnection.examId);
|
||||
|
||||
final ClientConnectionDataInternal activeClientConnection =
|
||||
cacheEvictAndLoad(connectionToken);
|
||||
realoadConnectionCache(connectionToken);
|
||||
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Failed to load ClientConnectionDataInternal into cache on update");
|
||||
|
@ -368,7 +369,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
updatedClientConnection = clientConnection;
|
||||
}
|
||||
|
||||
evictCaches(connectionToken);
|
||||
realoadConnectionCache(connectionToken);
|
||||
return updatedClientConnection;
|
||||
});
|
||||
}
|
||||
|
@ -406,7 +407,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
updatedClientConnection = clientConnection;
|
||||
}
|
||||
|
||||
evictCaches(connectionToken);
|
||||
realoadConnectionCache(connectionToken);
|
||||
return updatedClientConnection;
|
||||
});
|
||||
}
|
||||
|
@ -427,9 +428,9 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
.stream())
|
||||
.map(token -> cache.get(token, ClientConnectionDataInternal.class))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(connection -> connection.clientConnection.status == ConnectionStatus.ESTABLISHED)
|
||||
.flatMap(connection -> connection.pingMappings.stream())
|
||||
.map(ping -> ping.updateLogEvent())
|
||||
.filter(connection -> connection.pingIndicator != null &&
|
||||
connection.clientConnection.status.establishedStatus)
|
||||
.map(connection -> connection.pingIndicator.updateLogEvent())
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(this.eventHandlingStrategy::accept);
|
||||
|
||||
|
@ -497,7 +498,6 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
}
|
||||
}
|
||||
|
||||
// TODO maybe we need a stronger connectionToken but for now a simple UUID is used
|
||||
private String createToken() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
@ -606,19 +606,12 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
private void evictCaches(final String connectionToken) {
|
||||
private ClientConnectionDataInternal realoadConnectionCache(final String connectionToken) {
|
||||
// evict cached ClientConnection
|
||||
this.examSessionCacheService.evictClientConnection(connectionToken);
|
||||
// evict also cached ping record
|
||||
this.examSessionCacheService.evictPingRecord(connectionToken);
|
||||
// and load updated ClientConnection into cache
|
||||
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||
}
|
||||
|
||||
private ClientConnectionDataInternal cacheEvictAndLoad(final String connectionToken) {
|
||||
// evict cached ClientConnection
|
||||
this.examSessionCacheService.evictClientConnection(connectionToken);
|
||||
// and load updated ClientConnection into cache
|
||||
return this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ sebserver.overall.action.cancel=Cancel
|
|||
sebserver.overall.action.close=Close
|
||||
sebserver.overall.action.goAwayFromEditPageConfirm=Are you sure you want to leave this page? Unsaved data will be lost.
|
||||
sebserver.overall.action.category.varia=Varia
|
||||
sebserver.overall.action.category.filter=Connection Status Filter
|
||||
|
||||
sebserver.overall.status.active=Active
|
||||
sebserver.overall.status.inactive=Inactive
|
||||
|
@ -1065,14 +1066,20 @@ sebserver.monitoring.exam.connection.emptySelection=Please select a connection f
|
|||
sebserver.monitoring.exam.connection.title=SEB Client Connection
|
||||
sebserver.monitoring.exam.connection.list.actions=Selected Connection
|
||||
sebserver.monitoring.exam.connection.action.view=View Details
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit=Send Quit
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.all=Send Quit
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit=Send SEB Quit
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.all=Send SEB Quit
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.all.confirm=Are you sure to quit this SEB client connection?
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.selected.confirm=Are you sure to quit all selected, active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.all.confirm=Are you sure to quit all active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.disable=Mark As Disabled
|
||||
sebserver.monitoring.exam.connection.action.hide.closed=Hide Closed Connections
|
||||
sebserver.monitoring.exam.connection.action.show.closed=Show Closed Connections
|
||||
sebserver.monitoring.exam.connection.action.hide.requested=Hide Requested
|
||||
sebserver.monitoring.exam.connection.action.show.requested=Show Requested
|
||||
sebserver.monitoring.exam.connection.action.hide.closed=Hide Closed
|
||||
sebserver.monitoring.exam.connection.action.show.closed=Show Closed
|
||||
sebserver.monitoring.exam.connection.action.hide.disabled=Hide Disabled
|
||||
sebserver.monitoring.exam.connection.action.show.disabled=Show Disabled
|
||||
sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined
|
||||
sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined
|
||||
|
||||
sebserver.monitoring.exam.connection.eventlist.title=Events
|
||||
sebserver.monitoring.exam.connection.eventlist.empty=No event found
|
||||
|
@ -1092,7 +1099,7 @@ sebserver.monitoring.exam.connection.event.type.LAST_PING=Last Ping
|
|||
sebserver.monitoring.exam.connection.status.UNDEFINED=Undefined
|
||||
sebserver.monitoring.exam.connection.status.CONNECTION_REQUESTED=Connection Requested
|
||||
sebserver.monitoring.exam.connection.status.AUTHENTICATED=Authenticated
|
||||
sebserver.monitoring.exam.connection.status.ESTABLISHED=Active
|
||||
sebserver.monitoring.exam.connection.status.ACTIVE=Active
|
||||
sebserver.monitoring.exam.connection.status.CLOSED=Closed
|
||||
sebserver.monitoring.exam.connection.status.ABORTED=Aborted
|
||||
sebserver.monitoring.exam.connection.status.DISABLED=Disabled
|
||||
|
|
|
@ -298,6 +298,16 @@ Text[MULTI]:read-only {
|
|||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
Text:disabled.colorbox,
|
||||
Text:read-only.colorbox,
|
||||
Text[MULTI]:disabled.colorbox,
|
||||
Text[MULTI]:read-only.colorbox {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
|
||||
/* Combo default theme */
|
||||
Combo, Combo[BORDER] {
|
||||
|
|
|
@ -104,8 +104,8 @@ public class HTTPClientBot {
|
|||
this.numberOfConnections = Integer.parseInt(properties.getProperty("numberOfConnections", "4"));
|
||||
this.pingInterval = Long.parseLong(properties.getProperty("pingInterval", "200"));
|
||||
this.establishDelay = Long.parseLong(properties.getProperty("establishDelay", "0"));
|
||||
this.pingPause = Long.parseLong(properties.getProperty("pingPause", "0"));
|
||||
this.pingPauseDelay = Long.parseLong(properties.getProperty("pingPauseDelay", "0"));
|
||||
this.pingPause = Long.parseLong(properties.getProperty("pingPause", "20000"));
|
||||
this.pingPauseDelay = Long.parseLong(properties.getProperty("pingPauseDelay", "5000"));
|
||||
this.errorInterval = Long.parseLong(properties.getProperty("errorInterval", String.valueOf(TEN_SECONDS)));
|
||||
this.warnInterval = Long.parseLong(properties.getProperty("errorInterval", String.valueOf(TEN_SECONDS / 2)));
|
||||
// this.runtime = Long.parseLong(properties.getProperty("runtime", String.valueOf(ONE_MINUTE)));
|
||||
|
|
|
@ -295,7 +295,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
|
|||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertEquals("2", String.valueOf(clientConnectionRecord.getExamId()));
|
||||
assertEquals("ESTABLISHED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertEquals("ACTIVE", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertNotNull(clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertEquals("userSessionId", clientConnectionRecord.getExamUserSessionId());
|
||||
|
|
Loading…
Reference in a new issue