SEBSERV-347 implementation & testing
This commit is contained in:
parent
11792be0a9
commit
95e3a70d34
19 changed files with 478 additions and 300 deletions
|
@ -193,6 +193,7 @@ public final class API {
|
||||||
|
|
||||||
public static final String EXAM_MONITORING_ENDPOINT = "/monitoring";
|
public static final String EXAM_MONITORING_ENDPOINT = "/monitoring";
|
||||||
public static final String EXAM_MONITORING_FULLPAGE = "/fullpage";
|
public static final String EXAM_MONITORING_FULLPAGE = "/fullpage";
|
||||||
|
public static final String EXAM_MONITORING_STATIC_CLIENT_DATA = "/static-client-data";
|
||||||
public static final String EXAM_MONITORING_INSTRUCTION_ENDPOINT = "/instruction";
|
public static final String EXAM_MONITORING_INSTRUCTION_ENDPOINT = "/instruction";
|
||||||
public static final String EXAM_MONITORING_NOTIFICATION_ENDPOINT = "/notification";
|
public static final String EXAM_MONITORING_NOTIFICATION_ENDPOINT = "/notification";
|
||||||
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";
|
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";
|
||||||
|
|
|
@ -9,15 +9,10 @@
|
||||||
package ch.ethz.seb.sebserver.gbl.model.session;
|
package ch.ethz.seb.sebserver.gbl.model.session;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
@ -27,9 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
|
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
@ -48,7 +41,7 @@ public class ClientConnectionData implements GrantEntity {
|
||||||
@JsonProperty(ATTR_INDICATOR_VALUE)
|
@JsonProperty(ATTR_INDICATOR_VALUE)
|
||||||
public final List<? extends IndicatorValue> indicatorValues;
|
public final List<? extends IndicatorValue> indicatorValues;
|
||||||
@JsonProperty(ATTR_CLIENT_GROUPS)
|
@JsonProperty(ATTR_CLIENT_GROUPS)
|
||||||
public Set<Long> groups = null;
|
public final Set<Long> groups;
|
||||||
|
|
||||||
public final Boolean missingPing;
|
public final Boolean missingPing;
|
||||||
public final Boolean pendingNotification;
|
public final Boolean pendingNotification;
|
||||||
|
@ -58,30 +51,26 @@ public class ClientConnectionData implements GrantEntity {
|
||||||
@JsonProperty(ATTR_MISSING_PING) final Boolean missingPing,
|
@JsonProperty(ATTR_MISSING_PING) final Boolean missingPing,
|
||||||
@JsonProperty(ATTR_PENDING_NOTIFICATION) final Boolean pendingNotification,
|
@JsonProperty(ATTR_PENDING_NOTIFICATION) final Boolean pendingNotification,
|
||||||
@JsonProperty(ATTR_CLIENT_CONNECTION) final ClientConnection clientConnection,
|
@JsonProperty(ATTR_CLIENT_CONNECTION) final ClientConnection clientConnection,
|
||||||
@JsonProperty(ATTR_INDICATOR_VALUE) final Collection<? extends SimpleIndicatorValue> indicatorValues) {
|
@JsonProperty(ATTR_INDICATOR_VALUE) final Collection<? extends SimpleIndicatorValue> indicatorValues,
|
||||||
|
@JsonProperty(ATTR_CLIENT_GROUPS) final Set<Long> groups) {
|
||||||
|
|
||||||
this.missingPing = missingPing;
|
this.missingPing = missingPing;
|
||||||
this.pendingNotification = pendingNotification;
|
this.pendingNotification = pendingNotification;
|
||||||
this.clientConnection = clientConnection;
|
this.clientConnection = clientConnection;
|
||||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||||
|
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientConnectionData(
|
public ClientConnectionData(
|
||||||
final ClientConnection clientConnection,
|
final ClientConnection clientConnection,
|
||||||
final List<? extends IndicatorValue> indicatorValues) {
|
final List<? extends IndicatorValue> indicatorValues,
|
||||||
|
final Set<Long> groups) {
|
||||||
|
|
||||||
this.missingPing = null;
|
this.missingPing = null;
|
||||||
this.pendingNotification = Boolean.FALSE;
|
this.pendingNotification = Boolean.FALSE;
|
||||||
this.clientConnection = clientConnection;
|
this.clientConnection = clientConnection;
|
||||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||||
}
|
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
public void addToClientGroup(final ClientGroup group) {
|
|
||||||
if (this.groups == null) {
|
|
||||||
this.groups = new HashSet<>(1);
|
|
||||||
}
|
|
||||||
this.groups.add(group.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
@ -221,59 +210,4 @@ public class ClientConnectionData implements GrantEntity {
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This is a wrapper for the live monitoring data view of this client connection data */
|
|
||||||
@JsonIgnore
|
|
||||||
public final ClientMonitoringDataView monitoringDataView = new ClientMonitoringDataView() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getId() {
|
|
||||||
return ClientConnectionData.this.clientConnection.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConnectionStatus getStatus() {
|
|
||||||
return ClientConnectionData.this.clientConnection.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionToken() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return ClientConnectionData.this.clientConnection.connectionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUserSessionId() {
|
|
||||||
return ClientConnectionData.this.clientConnection.userSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getInfo() {
|
|
||||||
return ClientConnectionData.this.clientConnection.info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<Long, String> getIndicatorValues() {
|
|
||||||
return ClientConnectionData.this.indicatorValues
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
iv -> iv.getIndicatorId(),
|
|
||||||
iv -> IndicatorValue.getDisplayValue(iv)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Long> getGroups() {
|
|
||||||
return ClientConnectionData.this.groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMissingPing() {
|
|
||||||
return BooleanUtils.isTrue(ClientConnectionData.this.missingPing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPendingNotification() {
|
|
||||||
return BooleanUtils.isTrue(ClientConnectionData.this.pendingNotification);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,43 +10,34 @@ package ch.ethz.seb.sebserver.gbl.model.session;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class ClientMonitoringData implements ClientMonitoringDataView {
|
public class ClientMonitoringData implements ClientMonitoringDataView {
|
||||||
|
|
||||||
public final Long id;
|
public final Long id;
|
||||||
public final ConnectionStatus status;
|
public final ConnectionStatus status;
|
||||||
public final String connectionToken;
|
|
||||||
public final String userSessionId;
|
|
||||||
public final String info;
|
|
||||||
public final Map<Long, String> indicatorVals;
|
public final Map<Long, String> indicatorVals;
|
||||||
public final Set<Long> groups;
|
|
||||||
public final boolean missingPing;
|
public final boolean missingPing;
|
||||||
public final boolean pendingNotification;
|
public final boolean pendingNotification;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
public ClientMonitoringData(
|
public ClientMonitoringData(
|
||||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
|
||||||
@JsonProperty(ATTR_STATUS) final ConnectionStatus status,
|
@JsonProperty(ATTR_STATUS) final ConnectionStatus status,
|
||||||
@JsonProperty(ATTR_CONNECTION_TOKEN) final String connectionToken,
|
|
||||||
@JsonProperty(ATTR_EXAM_USER_SESSION_ID) final String userSessionId,
|
|
||||||
@JsonProperty(ATTR_INFO) final String info,
|
|
||||||
@JsonProperty(ATTR_INDICATOR_VALUES) final Map<Long, String> indicatorVals,
|
@JsonProperty(ATTR_INDICATOR_VALUES) final Map<Long, String> indicatorVals,
|
||||||
@JsonProperty(ATTR_CLIENT_GROUPS) final Set<Long> groups,
|
|
||||||
@JsonProperty(ATTR_MISSING_PING) final boolean missingPing,
|
@JsonProperty(ATTR_MISSING_PING) final boolean missingPing,
|
||||||
@JsonProperty(ATTR_PENDING_NOTIFICATION) final boolean pendingNotification) {
|
@JsonProperty(ATTR_PENDING_NOTIFICATION) final boolean pendingNotification) {
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.connectionToken = connectionToken;
|
|
||||||
this.userSessionId = userSessionId;
|
|
||||||
this.info = info;
|
|
||||||
this.indicatorVals = indicatorVals;
|
this.indicatorVals = indicatorVals;
|
||||||
this.groups = groups;
|
|
||||||
this.missingPing = missingPing;
|
this.missingPing = missingPing;
|
||||||
this.pendingNotification = pendingNotification;
|
this.pendingNotification = pendingNotification;
|
||||||
}
|
}
|
||||||
|
@ -61,31 +52,11 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionToken() {
|
|
||||||
return this.connectionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUserSessionId() {
|
|
||||||
return this.userSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getInfo() {
|
|
||||||
return this.info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Long, String> getIndicatorValues() {
|
public Map<Long, String> getIndicatorValues() {
|
||||||
return this.indicatorVals;
|
return this.indicatorVals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Long> getGroups() {
|
|
||||||
return this.groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMissingPing() {
|
public boolean isMissingPing() {
|
||||||
return this.missingPing;
|
return this.missingPing;
|
||||||
|
@ -96,33 +67,6 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
|
||||||
return this.pendingNotification;
|
return this.pendingNotification;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean dataEquals(final ClientMonitoringData other) {
|
|
||||||
if (other == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.connectionToken == null) {
|
|
||||||
if (other.connectionToken != null)
|
|
||||||
return false;
|
|
||||||
} else if (!this.connectionToken.equals(other.connectionToken))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this.status != other.status)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this.userSessionId == null) {
|
|
||||||
if (other.userSessionId != null)
|
|
||||||
return false;
|
|
||||||
} else if (!this.userSessionId.equals(other.userSessionId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Objects.equals(this.groups, other.groups)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean indicatorValuesEquals(final ClientMonitoringData other) {
|
public boolean indicatorValuesEquals(final ClientMonitoringData other) {
|
||||||
return Objects.equals(this.indicatorVals, other.indicatorVals);
|
return Objects.equals(this.indicatorVals, other.indicatorVals);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gbl.model.session;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
@ -36,21 +35,9 @@ public interface ClientMonitoringDataView {
|
||||||
@JsonProperty(ATTR_STATUS)
|
@JsonProperty(ATTR_STATUS)
|
||||||
ConnectionStatus getStatus();
|
ConnectionStatus getStatus();
|
||||||
|
|
||||||
@JsonProperty(ATTR_CONNECTION_TOKEN)
|
|
||||||
String getConnectionToken();
|
|
||||||
|
|
||||||
@JsonProperty(ATTR_EXAM_USER_SESSION_ID)
|
|
||||||
String getUserSessionId();
|
|
||||||
|
|
||||||
@JsonProperty(ATTR_INFO)
|
|
||||||
String getInfo();
|
|
||||||
|
|
||||||
@JsonProperty(ATTR_INDICATOR_VALUES)
|
@JsonProperty(ATTR_INDICATOR_VALUES)
|
||||||
Map<Long, String> getIndicatorValues();
|
Map<Long, String> getIndicatorValues();
|
||||||
|
|
||||||
@JsonProperty(ATTR_CLIENT_GROUPS)
|
|
||||||
Set<Long> getGroups();
|
|
||||||
|
|
||||||
@JsonProperty(ATTR_MISSING_PING)
|
@JsonProperty(ATTR_MISSING_PING)
|
||||||
boolean isMissingPing();
|
boolean isMissingPing();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gbl.model.session;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class ClientStaticData {
|
||||||
|
|
||||||
|
public static final ClientStaticData NULL_DATA =
|
||||||
|
new ClientStaticData(-1L, null, null, null, Collections.emptySet());
|
||||||
|
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
|
||||||
|
public final Long id;
|
||||||
|
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN)
|
||||||
|
public final String connectionToken;
|
||||||
|
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID)
|
||||||
|
public final String userSessionId;
|
||||||
|
|
||||||
|
@JsonProperty(ClientConnection.ATTR_INFO)
|
||||||
|
public final String info;
|
||||||
|
|
||||||
|
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS)
|
||||||
|
public final Set<Long> groups;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public ClientStaticData(
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN) final String connectionToken,
|
||||||
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID) final String userSessionId,
|
||||||
|
@JsonProperty(ClientConnection.ATTR_INFO) final String info,
|
||||||
|
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS) final Set<Long> groups) {
|
||||||
|
|
||||||
|
this.id = id;
|
||||||
|
this.connectionToken = connectionToken;
|
||||||
|
this.userSessionId = userSessionId;
|
||||||
|
this.info = info;
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectionToken() {
|
||||||
|
return this.connectionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserSessionId() {
|
||||||
|
return this.userSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInfo() {
|
||||||
|
return this.info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getGroups() {
|
||||||
|
return this.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -39,7 +39,11 @@ public class IPv4RangeClientGroupMatcher implements ClientGroupConnectionMatcher
|
||||||
|
|
||||||
return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
|
return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to verify IP range for group: {} connection: {}", group, clientConnection, e);
|
log.error(
|
||||||
|
"Failed to verify IP range for group: {} connection: {}, error",
|
||||||
|
group,
|
||||||
|
clientConnection,
|
||||||
|
e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,16 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class MonitoringSEBConnectionData {
|
public class MonitoringSEBConnectionData {
|
||||||
|
|
||||||
public static final String ATTR_CONNECTIONS = "connections";
|
public static final String ATTR_CONNECTIONS = "cons";
|
||||||
public static final String ATTR_STATUS_MAPPING = "statusMapping";
|
public static final String ATTR_STATUS_MAPPING = "sm";
|
||||||
public static final String ATTR_CLIENT_GROUP_MAPPING = "clientGroupMapping";
|
public static final String ATTR_CLIENT_GROUP_MAPPING = "cgm";
|
||||||
|
|
||||||
@JsonProperty(ATTR_CONNECTIONS)
|
@JsonProperty(ATTR_CONNECTIONS)
|
||||||
public final Collection<? extends ClientMonitoringDataView> monitoringData;
|
public final Collection<? extends ClientMonitoringDataView> monitoringData;
|
||||||
|
|
||||||
@JsonProperty(ATTR_STATUS_MAPPING)
|
@JsonProperty(ATTR_STATUS_MAPPING)
|
||||||
public final int[] connectionsPerStatus;
|
public final int[] connectionsPerStatus;
|
||||||
|
|
||||||
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
|
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
|
||||||
public final Map<Long, Integer> connectionsPerClientGroup;
|
public final Map<Long, Integer> connectionsPerClientGroup;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gbl.monitoring;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class MonitoringStaticClientData {
|
||||||
|
|
||||||
|
public static final String ATTR_STATIC_CONNECTION_DATA = "staticClientConnectionData";
|
||||||
|
public static final String ATTR_DUPLICATIONS = "duplications";
|
||||||
|
|
||||||
|
@JsonProperty(ATTR_STATIC_CONNECTION_DATA)
|
||||||
|
public final Collection<ClientStaticData> staticClientConnectionData;
|
||||||
|
@JsonProperty(ATTR_DUPLICATIONS)
|
||||||
|
public final Set<Long> duplications;
|
||||||
|
|
||||||
|
public MonitoringStaticClientData(
|
||||||
|
@JsonProperty(ATTR_STATIC_CONNECTION_DATA) final Collection<ClientStaticData> staticClientConnectionData,
|
||||||
|
@JsonProperty(ATTR_DUPLICATIONS) final Set<Long> duplications) {
|
||||||
|
|
||||||
|
this.staticClientConnectionData = Utils.immutableCollectionOf(staticClientConnectionData);
|
||||||
|
this.duplications = Utils.immutableSetOf(duplications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ClientStaticData> getStaticClientConnectionData() {
|
||||||
|
return this.staticClientConnectionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getDuplications() {
|
||||||
|
return this.duplications;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -775,7 +775,7 @@ public final class Utils {
|
||||||
try {
|
try {
|
||||||
return ipToLong(InetAddress.getByName(ipV4Address));
|
return ipToLong(InetAddress.getByName(ipV4Address));
|
||||||
} catch (final UnknownHostException e) {
|
} catch (final UnknownHostException e) {
|
||||||
log.error("Failed to convert IPv4 address: {}", ipV4Address, e);
|
log.error("Failed to convert IPv4 address: {}, error: ", ipV4Address, e.getMessage());
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -805,4 +805,12 @@ public final class Utils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Long toLong(final String longValue) {
|
||||||
|
try {
|
||||||
|
return Long.valueOf(longValue);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,8 +132,6 @@ public abstract class RestCall<T> {
|
||||||
return Result.ofEmpty();
|
return Result.ofEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("************** size = " + responseEntity.getBody().length());
|
|
||||||
|
|
||||||
return Result.of(RestCall.this.jsonMapper.readValue(
|
return Result.of(RestCall.this.jsonMapper.readValue(
|
||||||
responseEntity.getBody(),
|
responseEntity.getBody(),
|
||||||
RestCall.this.typeKey.typeRef));
|
RestCall.this.typeKey.typeRef));
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetMonitoringStaticClientData extends RestCall<MonitoringStaticClientData> {
|
||||||
|
|
||||||
|
public GetMonitoringStaticClientData() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_LIST,
|
||||||
|
null,
|
||||||
|
new TypeReference<MonitoringStaticClientData>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.POST,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.EXAM_MONITORING_ENDPOINT
|
||||||
|
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
|
||||||
|
+ API.EXAM_MONITORING_STATIC_CLIENT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -38,30 +37,36 @@ import org.eclipse.swt.widgets.Event;
|
||||||
import org.eclipse.swt.widgets.Table;
|
import org.eclipse.swt.widgets.Table;
|
||||||
import org.eclipse.swt.widgets.TableColumn;
|
import org.eclipse.swt.widgets.TableColumn;
|
||||||
import org.eclipse.swt.widgets.TableItem;
|
import org.eclipse.swt.widgets.TableItem;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
||||||
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.session.ClientConnection.ConnectionStatus;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
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;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetMonitoringStaticClientData;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.IndicatorData.ThresholdColor;
|
import ch.ethz.seb.sebserver.gui.service.session.IndicatorData.ThresholdColor;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||||
|
|
||||||
public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate {
|
public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ClientConnectionTable.class);
|
||||||
|
|
||||||
private static final int BOTTOM_PADDING = 20;
|
private static final int BOTTOM_PADDING = 20;
|
||||||
//private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
|
//private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
|
||||||
//private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
//private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
||||||
|
@ -100,7 +105,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
private boolean needsSort = false;
|
private boolean needsSort = false;
|
||||||
private LinkedHashMap<Long, UpdatableTableItem> tableMapping;
|
private LinkedHashMap<Long, UpdatableTableItem> tableMapping;
|
||||||
private final Set<Long> toDelete = new HashSet<>();
|
private final Set<Long> toDelete = new HashSet<>();
|
||||||
private final MultiValueMap<String, Long> sessionIds;
|
private final Set<Long> toUpdateStatic = new HashSet<>();
|
||||||
|
private final Set<Long> duplicates = new HashSet<>();
|
||||||
|
|
||||||
private final Color darkFontColor;
|
private final Color darkFontColor;
|
||||||
private final Color lightFontColor;
|
private final Color lightFontColor;
|
||||||
|
@ -194,7 +200,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tableMapping = new LinkedHashMap<>();
|
this.tableMapping = new LinkedHashMap<>();
|
||||||
this.sessionIds = new LinkedMultiValueMap<>();
|
|
||||||
this.table.layout();
|
this.table.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +249,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
final UpdatableTableItem updatableTableItem =
|
final UpdatableTableItem updatableTableItem =
|
||||||
new ArrayList<>(this.tableMapping.values())
|
new ArrayList<>(this.tableMapping.values())
|
||||||
.get(selectionIndices[i]);
|
.get(selectionIndices[i]);
|
||||||
if (filter.test(updatableTableItem.monitoringData)) {
|
if (filter.test(updatableTableItem.monitoringData)
|
||||||
result.add(updatableTableItem.monitoringData.connectionToken);
|
&& updatableTableItem.staticData != ClientStaticData.NULL_DATA) {
|
||||||
|
result.add(updatableTableItem.staticData.connectionToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -253,9 +259,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
return this.tableMapping
|
return this.tableMapping
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
.map(item -> item.monitoringData)
|
.filter(item -> filter.test(item.monitoringData) && item.staticData != ClientStaticData.NULL_DATA)
|
||||||
.filter(filter)
|
.map(item -> item.staticData.connectionToken)
|
||||||
.map(ClientMonitoringData::getConnectionToken)
|
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +309,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
(updatableTableItem.connectionId != null)
|
(updatableTableItem.connectionId != null)
|
||||||
? String.valueOf(updatableTableItem.connectionId)
|
? String.valueOf(updatableTableItem.connectionId)
|
||||||
: null,
|
: null,
|
||||||
updatableTableItem.monitoringData.connectionToken);
|
updatableTableItem.staticData.connectionToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forceUpdateAll() {
|
public void forceUpdateAll() {
|
||||||
|
@ -329,33 +334,32 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
this.toDelete.addAll(this.tableMapping.keySet());
|
this.toDelete.addAll(this.tableMapping.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toUpdateStatic.clear();
|
||||||
monitoringStatus.getConnectionData()
|
monitoringStatus.getConnectionData()
|
||||||
.forEach(data -> {
|
.forEach(data -> {
|
||||||
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
|
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
|
||||||
data.id,
|
data.id,
|
||||||
UpdatableTableItem::new);
|
UpdatableTableItem::new);
|
||||||
tableItem.push(data);
|
if (tableItem.push(data)) {
|
||||||
|
this.toUpdateStatic.add(data.id);
|
||||||
|
}
|
||||||
if (needsSync) {
|
if (needsSync) {
|
||||||
this.toDelete.remove(data.id);
|
this.toDelete.remove(data.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!this.toUpdateStatic.isEmpty()) {
|
||||||
|
fetchStaticClientConnectionData();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.toDelete.isEmpty()) {
|
if (!this.toDelete.isEmpty()) {
|
||||||
this.toDelete.forEach(id -> {
|
this.toDelete.forEach(id -> this.tableMapping.remove(id));
|
||||||
final UpdatableTableItem item = this.tableMapping.remove(id);
|
|
||||||
if (item != null) {
|
|
||||||
final List<Long> list = this.sessionIds.get(item.monitoringData.userSessionId);
|
|
||||||
if (list != null) {
|
|
||||||
list.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
monitoringStatus.resetFilterChanged();
|
monitoringStatus.resetFilterChanged();
|
||||||
this.toDelete.clear();
|
this.toDelete.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.forceUpdateAll = false;
|
this.forceUpdateAll = false;
|
||||||
this.needsSort = sizeChanged;
|
this.needsSort = this.needsSort || sizeChanged;
|
||||||
updateGUI();
|
updateGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,9 +451,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
private boolean dataChanged = false;
|
private boolean dataChanged = false;
|
||||||
private boolean indicatorValueChanged = false;
|
private boolean indicatorValueChanged = false;
|
||||||
private ClientMonitoringData monitoringData;
|
private ClientMonitoringData monitoringData;
|
||||||
|
private ClientStaticData staticData = ClientStaticData.NULL_DATA;
|
||||||
private int thresholdsWeight;
|
private int thresholdsWeight;
|
||||||
private int[] indicatorWeights = null;
|
private int[] indicatorWeights = null;
|
||||||
private boolean duplicateChecked = false;
|
|
||||||
|
|
||||||
UpdatableTableItem(final Long connectionId) {
|
UpdatableTableItem(final Long connectionId) {
|
||||||
this.connectionId = connectionId;
|
this.connectionId = connectionId;
|
||||||
|
@ -457,6 +461,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update(final TableItem tableItem, final boolean force) {
|
private void update(final TableItem tableItem, final boolean force) {
|
||||||
|
updateDuplicateColor(tableItem);
|
||||||
if (force || this.dataChanged) {
|
if (force || this.dataChanged) {
|
||||||
updateData(tableItem);
|
updateData(tableItem);
|
||||||
}
|
}
|
||||||
|
@ -483,13 +488,26 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
|
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
|
||||||
.apply(this.monitoringData));
|
.apply(this.monitoringData));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.monitoringData != null) {
|
if (this.monitoringData != null) {
|
||||||
updateConnectionStatusColor(tableItem);
|
updateConnectionStatusColor(tableItem);
|
||||||
updateDuplicateColor(tableItem);
|
|
||||||
updateNotifications(tableItem);
|
updateNotifications(tableItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateDuplicateColor(final TableItem tableItem) {
|
||||||
|
//System.out.println("ClientConnectionTable.this.duplicates : " + ClientConnectionTable.this.duplicates);
|
||||||
|
if (ClientConnectionTable.this.duplicates.contains(this.connectionId) &&
|
||||||
|
tableItem.getBackground(0) != ClientConnectionTable.this.colorData.color3) {
|
||||||
|
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color3);
|
||||||
|
tableItem.setForeground(0, ClientConnectionTable.this.lightFontColor);
|
||||||
|
} else if (!ClientConnectionTable.this.duplicates.contains(this.connectionId) &&
|
||||||
|
tableItem.getBackground(0) == ClientConnectionTable.this.colorData.color3) {
|
||||||
|
tableItem.setBackground(0, null);
|
||||||
|
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateNotifications(final TableItem tableItem) {
|
private void updateNotifications(final TableItem tableItem) {
|
||||||
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
|
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
|
||||||
tableItem.setImage(0,
|
tableItem.setImage(0,
|
||||||
|
@ -509,29 +527,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
tableItem.setForeground(index, statusTextColor);
|
tableItem.setForeground(index, statusTextColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDuplicateColor(final TableItem tableItem) {
|
|
||||||
|
|
||||||
tableItem.setBackground(0, null);
|
|
||||||
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
|
|
||||||
|
|
||||||
if (!this.duplicateChecked) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.monitoringData != null
|
|
||||||
&& StringUtils.isNotBlank(this.monitoringData.userSessionId)) {
|
|
||||||
final List<Long> list =
|
|
||||||
ClientConnectionTable.this.sessionIds.get(this.monitoringData.userSessionId);
|
|
||||||
if (list != null && list.size() > 1) {
|
|
||||||
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color3);
|
|
||||||
tableItem.setForeground(0, ClientConnectionTable.this.lightFontColor);
|
|
||||||
} else {
|
|
||||||
tableItem.setBackground(0, null);
|
|
||||||
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumer<Map.Entry<Long, String>> indicatorUpdate(final TableItem tableItem) {
|
private Consumer<Map.Entry<Long, String>> indicatorUpdate(final TableItem tableItem) {
|
||||||
return entry -> {
|
return entry -> {
|
||||||
final Long id = entry.getKey();
|
final Long id = entry.getKey();
|
||||||
|
@ -541,10 +536,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateIndicatorWeight(displayValue, indicatorData);
|
|
||||||
|
|
||||||
if (!this.monitoringData.status.clientActiveStatus) {
|
if (!this.monitoringData.status.clientActiveStatus) {
|
||||||
tableItem.setText(indicatorData.tableIndex, displayValue);
|
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
|
||||||
|
? Constants.EMPTY_NOTE
|
||||||
|
: displayValue;
|
||||||
|
tableItem.setText(indicatorData.tableIndex, value);
|
||||||
tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
|
tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
|
||||||
tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
|
tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
|
||||||
} else {
|
} else {
|
||||||
|
@ -571,38 +567,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
.entrySet()
|
.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.forEach(indicatorUpdate(tableItem));
|
.forEach(indicatorUpdate(tableItem));
|
||||||
|
|
||||||
// for (int i = 0; i < this.monitoringData.indicatorVals.size(); i++) {
|
|
||||||
// final IndicatorValue indicatorValue = this.monitoringData.indicatorValues.get(i);
|
|
||||||
// final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping
|
|
||||||
// .get(indicatorValue.getIndicatorId());
|
|
||||||
//
|
|
||||||
// if (indicatorData == null) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!this.connectionData.clientConnection.status.clientActiveStatus) {
|
|
||||||
// final String value = (indicatorData.indicator.type.showOnlyInActiveState)
|
|
||||||
// ? Constants.EMPTY_NOTE
|
|
||||||
// : IndicatorValue.getDisplayValue(indicatorValue, indicatorData.indicator.type);
|
|
||||||
// tableItem.setText(indicatorData.tableIndex, value);
|
|
||||||
// tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
|
|
||||||
// tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
|
|
||||||
// } else {
|
|
||||||
// tableItem.setText(indicatorData.tableIndex, IndicatorValue.getDisplayValue(
|
|
||||||
// indicatorValue,
|
|
||||||
// indicatorData.indicator.type));
|
|
||||||
// final int weight = this.indicatorWeights[indicatorData.index];
|
|
||||||
// if (weight >= 0 && weight < indicatorData.thresholdColor.length) {
|
|
||||||
// final ThresholdColor thresholdColor = indicatorData.thresholdColor[weight];
|
|
||||||
// tableItem.setBackground(indicatorData.tableIndex, thresholdColor.color);
|
|
||||||
// tableItem.setForeground(indicatorData.tableIndex, thresholdColor.textColor);
|
|
||||||
// } else {
|
|
||||||
// tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
|
|
||||||
// tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -650,8 +614,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
String getConnectionInfo() {
|
String getConnectionInfo() {
|
||||||
if (this.monitoringData != null && this.monitoringData.info != null) {
|
if (this.staticData != null && this.staticData.info != null) {
|
||||||
return this.monitoringData.info;
|
return this.staticData.info;
|
||||||
}
|
}
|
||||||
return Constants.EMPTY_NOTE;
|
return Constants.EMPTY_NOTE;
|
||||||
}
|
}
|
||||||
|
@ -659,7 +623,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
private String getGroupInfo() {
|
private String getGroupInfo() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
ClientConnectionTable.this.clientGroupMapping.keySet().stream().forEach(key -> {
|
ClientConnectionTable.this.clientGroupMapping.keySet().stream().forEach(key -> {
|
||||||
if (this.monitoringData.groups != null && this.monitoringData.groups.contains(key)) {
|
if (this.staticData.groups != null && this.staticData.groups.contains(key)) {
|
||||||
final ClientGroup clientGroup = ClientConnectionTable.this.clientGroupMapping.get(key);
|
final ClientGroup clientGroup = ClientConnectionTable.this.clientGroupMapping.get(key);
|
||||||
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
|
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
|
||||||
}
|
}
|
||||||
|
@ -673,16 +637,17 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
String getConnectionIdentifier() {
|
String getConnectionIdentifier() {
|
||||||
if (this.monitoringData != null && this.monitoringData.userSessionId != null) {
|
if (this.staticData != null && this.staticData.userSessionId != null) {
|
||||||
return this.monitoringData.userSessionId;
|
return this.staticData.userSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(final ClientMonitoringData monitoringData) {
|
boolean push(final ClientMonitoringData monitoringData) {
|
||||||
this.dataChanged = this.monitoringData == null ||
|
this.dataChanged = this.monitoringData == null ||
|
||||||
!this.monitoringData.dataEquals(monitoringData);
|
this.monitoringData.status != monitoringData.status ||
|
||||||
|
this.monitoringData.missingPing != monitoringData.missingPing;
|
||||||
this.indicatorValueChanged = this.monitoringData == null ||
|
this.indicatorValueChanged = this.monitoringData == null ||
|
||||||
(this.monitoringData.status.clientActiveStatus
|
(this.monitoringData.status.clientActiveStatus
|
||||||
&& !this.monitoringData.indicatorValuesEquals(monitoringData));
|
&& !this.monitoringData.indicatorValuesEquals(monitoringData));
|
||||||
|
@ -697,55 +662,89 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
if (this.indicatorWeights == null) {
|
if (this.indicatorWeights == null) {
|
||||||
this.indicatorWeights = new int[ClientConnectionTable.this.indicatorMapping.size()];
|
this.indicatorWeights = new int[ClientConnectionTable.this.indicatorMapping.size()];
|
||||||
}
|
}
|
||||||
|
if (this.indicatorValueChanged) {
|
||||||
// for (int i = 0; i < monitoringData.indicatorValues.size(); i++) {
|
updateIndicatorWeight();
|
||||||
// final IndicatorValue indicatorValue = connectionData.indicatorValues.get(i);
|
}
|
||||||
// final IndicatorData indicatorData =
|
|
||||||
// ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId());
|
|
||||||
//
|
|
||||||
// if (indicatorData != null) {
|
|
||||||
// updateIndicatorWeight(indicatorValue, indicatorData);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.monitoringData = monitoringData;
|
this.monitoringData = monitoringData;
|
||||||
|
|
||||||
if (!this.duplicateChecked &&
|
return this.staticData == null
|
||||||
this.monitoringData.status != ConnectionStatus.DISABLED &&
|
|| this.staticData == ClientStaticData.NULL_DATA
|
||||||
StringUtils.isNotBlank(monitoringData.userSessionId)) {
|
|| this.dataChanged
|
||||||
|
|| this.monitoringData.status.connectingStatus
|
||||||
ClientConnectionTable.this.sessionIds.add(
|
|| StringUtils.isBlank(this.staticData.userSessionId);
|
||||||
monitoringData.userSessionId,
|
|
||||||
this.connectionId);
|
|
||||||
this.duplicateChecked = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateIndicatorWeight(
|
void push(final ClientStaticData staticData) {
|
||||||
final String indicatorValue,
|
this.staticData = staticData;
|
||||||
final IndicatorData indicatorData) {
|
this.dataChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
final int indicatorWeight = IndicatorData.getWeight(
|
private void updateIndicatorWeight() {
|
||||||
indicatorData,
|
if (this.monitoringData == null) {
|
||||||
IndicatorValue.getFromDisplayValue(indicatorValue));
|
return;
|
||||||
if (this.indicatorWeights[indicatorData.index] != indicatorWeight) {
|
|
||||||
ClientConnectionTable.this.needsSort = true;
|
|
||||||
this.thresholdsWeight -= (indicatorData.indicator.type.inverse)
|
|
||||||
? indicatorData.indicator.thresholds.size()
|
|
||||||
- this.indicatorWeights[indicatorData.index]
|
|
||||||
: this.indicatorWeights[indicatorData.index];
|
|
||||||
this.indicatorWeights[indicatorData.index] = indicatorWeight;
|
|
||||||
this.thresholdsWeight += (indicatorData.indicator.type.inverse)
|
|
||||||
? indicatorData.indicator.thresholds.size()
|
|
||||||
- this.indicatorWeights[indicatorData.index]
|
|
||||||
: this.indicatorWeights[indicatorData.index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.monitoringData.indicatorVals.entrySet().stream().forEach(entry -> {
|
||||||
|
final Long id = entry.getKey();
|
||||||
|
final String displayValue = entry.getValue();
|
||||||
|
final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping.get(id);
|
||||||
|
if (indicatorData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int indicatorWeight = IndicatorData.getWeight(
|
||||||
|
indicatorData,
|
||||||
|
IndicatorValue.getFromDisplayValue(displayValue));
|
||||||
|
if (this.indicatorWeights[indicatorData.index] != indicatorWeight) {
|
||||||
|
ClientConnectionTable.this.needsSort = true;
|
||||||
|
this.thresholdsWeight -= (indicatorData.indicator.type.inverse)
|
||||||
|
? indicatorData.indicator.thresholds.size()
|
||||||
|
- this.indicatorWeights[indicatorData.index]
|
||||||
|
: this.indicatorWeights[indicatorData.index];
|
||||||
|
this.indicatorWeights[indicatorData.index] = indicatorWeight;
|
||||||
|
this.thresholdsWeight += (indicatorData.indicator.type.inverse)
|
||||||
|
? indicatorData.indicator.thresholds.size()
|
||||||
|
- this.indicatorWeights[indicatorData.index]
|
||||||
|
: this.indicatorWeights[indicatorData.index];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientConnectionTable getOuterType() {
|
private ClientConnectionTable getOuterType() {
|
||||||
return ClientConnectionTable.this;
|
return ClientConnectionTable.this;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchStaticClientConnectionData() {
|
||||||
|
final String ids = this.toUpdateStatic
|
||||||
|
.stream()
|
||||||
|
.map(String::valueOf)
|
||||||
|
.reduce("", (acc, str) -> acc + str + Constants.LIST_SEPARATOR, (acc1, acc2) -> acc1 + acc2);
|
||||||
|
|
||||||
|
final Result<MonitoringStaticClientData> call = this.pageService
|
||||||
|
.getRestService()
|
||||||
|
.getBuilder(GetMonitoringStaticClientData.class)
|
||||||
|
.withFormParam(API.PARAM_MODEL_ID_LIST, ids)
|
||||||
|
.withURIVariable(API.PARAM_PARENT_MODEL_ID, this.exam.getModelId())
|
||||||
|
.call();
|
||||||
|
|
||||||
|
if (call.hasError()) {
|
||||||
|
log.error("Failed to get client connection static data for: {}", ids, call.getError());
|
||||||
|
} else {
|
||||||
|
final MonitoringStaticClientData monitoringStaticClientData = call.get();
|
||||||
|
this.duplicates.clear();
|
||||||
|
this.duplicates.addAll(monitoringStaticClientData.duplications);
|
||||||
|
monitoringStaticClientData.staticClientConnectionData
|
||||||
|
.stream()
|
||||||
|
.forEach(staticData -> {
|
||||||
|
final UpdatableTableItem updatableTableItem = this.tableMapping.get(staticData.id);
|
||||||
|
if (updatableTableItem != null) {
|
||||||
|
updatableTableItem.push(staticData);
|
||||||
|
} else {
|
||||||
|
log.error("Failed to find table entry for static data: {}", staticData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
|
@ -20,6 +21,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||||
|
@ -184,6 +186,18 @@ public interface ExamSessionService {
|
||||||
final Long examId,
|
final Long examId,
|
||||||
final Predicate<ClientConnectionData> filter);
|
final Predicate<ClientConnectionData> filter);
|
||||||
|
|
||||||
|
/** Get SEB client connection statically data for the given exam and list of connection ids.
|
||||||
|
* This is useful if you have monitoring real-time data as MonitoringSEBConnectionData for example and you need to
|
||||||
|
* complete this data with the more static client connection data of SEB client, one can reload the static data like
|
||||||
|
* so.
|
||||||
|
*
|
||||||
|
* @param examId the exam identifier
|
||||||
|
* @param connectionIds Set of client connection identifiers (modelIds)
|
||||||
|
* @return Result refer to the MonitoringStaticSEBConnectionData or to an error when happened */
|
||||||
|
Result<MonitoringStaticClientData> getMonitoringSEBConnectionStaticData(
|
||||||
|
final Long examId,
|
||||||
|
final Set<Long> connectionIds);
|
||||||
|
|
||||||
/** Gets all connection tokens of client connection that are in ACTIVE state and related to a specified exam
|
/** Gets all connection tokens of client connection that are in ACTIVE state and related to a specified exam
|
||||||
* from persistence storage without caching involved.
|
* from persistence storage without caching involved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,7 +13,11 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -21,8 +25,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
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.ClientConnectionData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.PendingNotificationIndication;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.PendingNotificationIndication;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.PingIntervalClientIndicator;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.PingIntervalClientIndicator;
|
||||||
|
@ -39,9 +47,10 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
||||||
protected ClientConnectionDataInternal(
|
protected ClientConnectionDataInternal(
|
||||||
final ClientConnection clientConnection,
|
final ClientConnection clientConnection,
|
||||||
final PendingNotificationIndication pendingNotificationIndication,
|
final PendingNotificationIndication pendingNotificationIndication,
|
||||||
final List<ClientIndicator> clientIndicators) {
|
final List<ClientIndicator> clientIndicators,
|
||||||
|
final Set<Long> groups) {
|
||||||
|
|
||||||
super(clientConnection, clientIndicators);
|
super(clientConnection, clientIndicators, groups);
|
||||||
this.pendingNotificationIndication = pendingNotificationIndication;
|
this.pendingNotificationIndication = pendingNotificationIndication;
|
||||||
|
|
||||||
this.indicatorMapping = new EnumMap<>(EventType.class);
|
this.indicatorMapping = new EnumMap<>(EventType.class);
|
||||||
|
@ -100,4 +109,48 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** This is a wrapper for the live monitoring data view of this client connection data */
|
||||||
|
@JsonIgnore
|
||||||
|
public final ClientMonitoringDataView monitoringDataView = new ClientMonitoringDataView() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getId() {
|
||||||
|
return ClientConnectionDataInternal.this.clientConnection.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionStatus getStatus() {
|
||||||
|
return ClientConnectionDataInternal.this.clientConnection.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Long, String> getIndicatorValues() {
|
||||||
|
return ClientConnectionDataInternal.this.indicatorValues
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
iv -> iv.getIndicatorId(),
|
||||||
|
iv -> IndicatorValue.getDisplayValue(iv)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMissingPing() {
|
||||||
|
return BooleanUtils.isTrue(getMissingPing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPendingNotification() {
|
||||||
|
return BooleanUtils.isTrue(pendingNotification());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This is a static monitoring connection data wrapper/holder */
|
||||||
|
@JsonIgnore
|
||||||
|
public final ClientStaticData clientStaticData =
|
||||||
|
new ClientStaticData(
|
||||||
|
ClientConnectionDataInternal.this.clientConnection.id,
|
||||||
|
ClientConnectionDataInternal.this.clientConnection.connectionToken,
|
||||||
|
ClientConnectionDataInternal.this.clientConnection.userSessionId,
|
||||||
|
ClientConnectionDataInternal.this.clientConnection.info,
|
||||||
|
this.groups);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
@ -44,6 +45,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.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||||
|
@ -425,7 +427,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
.getConnectionTokens(examId)
|
.getConnectionTokens(examId)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.map(token -> getConnectionDataInternal(token))
|
.map(this::getConnectionDataInternal)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(c -> {
|
.map(c -> {
|
||||||
statusMapping[c.clientConnection.status.code]++;
|
statusMapping[c.clientConnection.status.code]++;
|
||||||
|
@ -443,6 +445,23 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Result<MonitoringStaticClientData> getMonitoringSEBConnectionStaticData(
|
||||||
|
final Long examId,
|
||||||
|
final Set<Long> connectionIds) {
|
||||||
|
|
||||||
|
this.duplicateCheck.clear();
|
||||||
|
this.duplicates.clear();
|
||||||
|
return this.clientConnectionDAO
|
||||||
|
.getConnectionTokens(examId)
|
||||||
|
.map(tokens -> tokens.stream()
|
||||||
|
.map(this::getForTokenAndCheckDuplication)
|
||||||
|
.filter(ccd -> connectionIds.contains(ccd.clientConnection.id))
|
||||||
|
.map(ccd -> ccd.clientStaticData)
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
.map(staticData -> new MonitoringStaticClientData(staticData, this.duplicates));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Collection<String>> getActiveConnectionTokens(final Long examId) {
|
public Result<Collection<String>> getActiveConnectionTokens(final Long examId) {
|
||||||
return this.clientConnectionDAO
|
return this.clientConnectionDAO
|
||||||
|
@ -587,4 +606,21 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Map<String, Long> duplicateCheck = new HashMap<>();
|
||||||
|
private final Set<Long> duplicates = new HashSet<>();
|
||||||
|
|
||||||
|
private ClientConnectionDataInternal getForTokenAndCheckDuplication(final String token) {
|
||||||
|
final ClientConnectionDataInternal cc = this.examSessionCacheService.getClientConnection(token);
|
||||||
|
if (cc.clientConnection.status.establishedStatus) {
|
||||||
|
final Long id = this.duplicateCheck.put(
|
||||||
|
cc.clientConnection.userSessionId,
|
||||||
|
cc.getConnectionId());
|
||||||
|
if (id != null) {
|
||||||
|
this.duplicates.add(id);
|
||||||
|
this.duplicates.add(cc.getConnectionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -58,17 +60,24 @@ public class InternalClientConnectionDataFactory {
|
||||||
result = new ClientConnectionDataInternal(
|
result = new ClientConnectionDataInternal(
|
||||||
clientConnection,
|
clientConnection,
|
||||||
() -> false,
|
() -> false,
|
||||||
this.clientIndicatorFactory.createFor(clientConnection));
|
this.clientIndicatorFactory.createFor(clientConnection),
|
||||||
|
getGroupIds(clientConnection));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
result = new ClientConnectionDataInternal(
|
result = new ClientConnectionDataInternal(
|
||||||
clientConnection,
|
clientConnection,
|
||||||
() -> this.sebClientNotificationService
|
() -> this.sebClientNotificationService
|
||||||
.hasAnyPendingNotification(clientConnection),
|
.hasAnyPendingNotification(clientConnection),
|
||||||
this.clientIndicatorFactory.createFor(clientConnection));
|
this.clientIndicatorFactory.createFor(clientConnection),
|
||||||
|
getGroupIds(clientConnection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getGroupIds(final ClientConnection clientConnection) {
|
||||||
// set client groups for connection
|
// set client groups for connection
|
||||||
|
final Set<Long> clientGroupIds = new HashSet<>();
|
||||||
if (clientConnection.examId != null) {
|
if (clientConnection.examId != null) {
|
||||||
final Collection<ClientGroup> clientGroups = this.clientGroupDAO
|
final Collection<ClientGroup> clientGroups = this.clientGroupDAO
|
||||||
.allForExam(clientConnection.examId)
|
.allForExam(clientConnection.examId)
|
||||||
|
@ -80,13 +89,12 @@ public class InternalClientConnectionDataFactory {
|
||||||
if (!clientGroups.isEmpty()) {
|
if (!clientGroups.isEmpty()) {
|
||||||
clientGroups.forEach(clientGroup -> {
|
clientGroups.forEach(clientGroup -> {
|
||||||
if (this.clientGroupMatcherService.isInGroup(clientConnection, clientGroup)) {
|
if (this.clientGroupMatcherService.isInGroup(clientConnection, clientGroup)) {
|
||||||
result.addToClientGroup(clientGroup);
|
clientGroupIds.add(clientGroup.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return clientGroupIds;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
private final ExamAdminService examAdminService;
|
private final ExamAdminService examAdminService;
|
||||||
private final DistributedIndicatorValueService distributedPingCache;
|
private final DistributedIndicatorValueService distributedPingCache;
|
||||||
private final ClientIndicatorFactory clientIndicatorFactory;
|
private final ClientIndicatorFactory clientIndicatorFactory;
|
||||||
|
private final InternalClientConnectionDataFactory internalClientConnectionDataFactory;
|
||||||
private final boolean isDistributedSetup;
|
private final boolean isDistributedSetup;
|
||||||
|
|
||||||
protected SEBClientConnectionServiceImpl(
|
protected SEBClientConnectionServiceImpl(
|
||||||
|
@ -84,7 +85,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
final SEBClientInstructionService sebInstructionService,
|
final SEBClientInstructionService sebInstructionService,
|
||||||
final ExamAdminService examAdminService,
|
final ExamAdminService examAdminService,
|
||||||
final DistributedIndicatorValueService distributedPingCache,
|
final DistributedIndicatorValueService distributedPingCache,
|
||||||
final ClientIndicatorFactory clientIndicatorFactory) {
|
final ClientIndicatorFactory clientIndicatorFactory,
|
||||||
|
final InternalClientConnectionDataFactory internalClientConnectionDataFactory) {
|
||||||
|
|
||||||
this.examSessionService = examSessionService;
|
this.examSessionService = examSessionService;
|
||||||
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
|
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
|
||||||
|
@ -95,6 +97,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
this.examAdminService = examAdminService;
|
this.examAdminService = examAdminService;
|
||||||
this.clientIndicatorFactory = clientIndicatorFactory;
|
this.clientIndicatorFactory = clientIndicatorFactory;
|
||||||
this.distributedPingCache = distributedPingCache;
|
this.distributedPingCache = distributedPingCache;
|
||||||
|
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
|
||||||
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
|
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,7 +743,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
|
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
|
||||||
return Result.tryCatch(() -> new ClientConnectionData(
|
return Result.tryCatch(() -> new ClientConnectionData(
|
||||||
clientConnection,
|
clientConnection,
|
||||||
this.clientIndicatorFactory.getIndicatorValues(clientConnection)));
|
this.clientIndicatorFactory.getIndicatorValues(clientConnection),
|
||||||
|
this.internalClientConnectionDataFactory.getGroupIds(clientConnection)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkExamRunning(final Long examId, final String user, final String address) {
|
private void checkExamRunning(final Long examId, final String user, final String address) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -53,7 +55,9 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringFullPageData;
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringFullPageData;
|
||||||
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
||||||
|
@ -262,6 +266,32 @@ public class ExamMonitoringController {
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
|
||||||
|
API.EXAM_MONITORING_STATIC_CLIENT_DATA,
|
||||||
|
method = RequestMethod.POST,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public MonitoringStaticClientData getMonitoringStaticClientData(
|
||||||
|
@RequestParam(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
|
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
|
||||||
|
@RequestParam(name = API.PARAM_MODEL_ID_LIST, required = true) final String clientConnectionIds) {
|
||||||
|
|
||||||
|
final Exam runningExam = checkPrivileges(institutionId, examId);
|
||||||
|
|
||||||
|
final Set<Long> ids = Stream.of(StringUtils.split(clientConnectionIds, Constants.LIST_SEPARATOR))
|
||||||
|
.map(Utils::toLong)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
return this.examSessionService
|
||||||
|
.getMonitoringSEBConnectionStaticData(runningExam.id, ids)
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
|
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
|
||||||
API.EXAM_MONITORING_FULLPAGE,
|
API.EXAM_MONITORING_FULLPAGE,
|
||||||
|
|
|
@ -25,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
|
||||||
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.Exam.ExamStatus;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
||||||
|
@ -276,21 +275,14 @@ public class ModelObjectJSONGenerator {
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
new SimpleIndicatorValue(1L, 1.0),
|
new SimpleIndicatorValue(1L, 1.0),
|
||||||
new SimpleIndicatorValue(2L, 2.0),
|
new SimpleIndicatorValue(2L, 2.0),
|
||||||
new SimpleIndicatorValue(3L, 3.0)));
|
new SimpleIndicatorValue(3L, 3.0)),
|
||||||
|
new HashSet<>(Arrays.asList(1L, 2L)));
|
||||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||||
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
||||||
|
|
||||||
((ClientConnectionData) domainObject)
|
|
||||||
.addToClientGroup(new ClientGroup(1L, 1L, "group1", null, null, null, null));
|
|
||||||
((ClientConnectionData) domainObject)
|
|
||||||
.addToClientGroup(new ClientGroup(2L, 1L, "group2", null, null, null, null));
|
|
||||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||||
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
||||||
|
|
||||||
System.out.println("ClientMonitoringData" + ":");
|
|
||||||
System.out.println(writerWithDefaultPrettyPrinter
|
|
||||||
.writeValueAsString(((ClientConnectionData) domainObject).monitoringDataView));
|
|
||||||
|
|
||||||
domainObject = new ClientEvent(1L, 1L, EventType.WARN_LOG,
|
domainObject = new ClientEvent(1L, 1L, EventType.WARN_LOG,
|
||||||
System.currentTimeMillis(), System.currentTimeMillis(), 123.0, "text");
|
System.currentTimeMillis(), System.currentTimeMillis(), 123.0, "text");
|
||||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||||
|
|
Loading…
Reference in a new issue