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_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_NOTIFICATION_ENDPOINT = "/notification";
|
||||
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";
|
||||
|
|
|
@ -9,15 +9,10 @@
|
|||
package ch.ethz.seb.sebserver.gbl.model.session;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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.JsonIgnore;
|
||||
|
@ -27,9 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
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.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
@ -48,7 +41,7 @@ public class ClientConnectionData implements GrantEntity {
|
|||
@JsonProperty(ATTR_INDICATOR_VALUE)
|
||||
public final List<? extends IndicatorValue> indicatorValues;
|
||||
@JsonProperty(ATTR_CLIENT_GROUPS)
|
||||
public Set<Long> groups = null;
|
||||
public final Set<Long> groups;
|
||||
|
||||
public final Boolean missingPing;
|
||||
public final Boolean pendingNotification;
|
||||
|
@ -58,30 +51,26 @@ public class ClientConnectionData implements GrantEntity {
|
|||
@JsonProperty(ATTR_MISSING_PING) final Boolean missingPing,
|
||||
@JsonProperty(ATTR_PENDING_NOTIFICATION) final Boolean pendingNotification,
|
||||
@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.pendingNotification = pendingNotification;
|
||||
this.clientConnection = clientConnection;
|
||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
|
||||
}
|
||||
|
||||
public ClientConnectionData(
|
||||
final ClientConnection clientConnection,
|
||||
final List<? extends IndicatorValue> indicatorValues) {
|
||||
final List<? extends IndicatorValue> indicatorValues,
|
||||
final Set<Long> groups) {
|
||||
|
||||
this.missingPing = null;
|
||||
this.pendingNotification = Boolean.FALSE;
|
||||
this.clientConnection = clientConnection;
|
||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public void addToClientGroup(final ClientGroup group) {
|
||||
if (this.groups == null) {
|
||||
this.groups = new HashSet<>(1);
|
||||
}
|
||||
this.groups.add(group.id);
|
||||
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
|
@ -221,59 +210,4 @@ public class ClientConnectionData implements GrantEntity {
|
|||
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.Objects;
|
||||
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;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ClientMonitoringData implements ClientMonitoringDataView {
|
||||
|
||||
public final Long id;
|
||||
public final ConnectionStatus status;
|
||||
public final String connectionToken;
|
||||
public final String userSessionId;
|
||||
public final String info;
|
||||
public final Map<Long, String> indicatorVals;
|
||||
public final Set<Long> groups;
|
||||
public final boolean missingPing;
|
||||
public final boolean pendingNotification;
|
||||
|
||||
@JsonCreator
|
||||
public ClientMonitoringData(
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
|
||||
@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_CLIENT_GROUPS) final Set<Long> groups,
|
||||
@JsonProperty(ATTR_MISSING_PING) final boolean missingPing,
|
||||
@JsonProperty(ATTR_PENDING_NOTIFICATION) final boolean pendingNotification) {
|
||||
|
||||
this.id = id;
|
||||
this.status = status;
|
||||
this.connectionToken = connectionToken;
|
||||
this.userSessionId = userSessionId;
|
||||
this.info = info;
|
||||
this.indicatorVals = indicatorVals;
|
||||
this.groups = groups;
|
||||
this.missingPing = missingPing;
|
||||
this.pendingNotification = pendingNotification;
|
||||
}
|
||||
|
@ -61,31 +52,11 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
|
|||
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
|
||||
public Map<Long, String> getIndicatorValues() {
|
||||
return this.indicatorVals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getGroups() {
|
||||
return this.groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMissingPing() {
|
||||
return this.missingPing;
|
||||
|
@ -96,33 +67,6 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
|
|||
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) {
|
||||
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.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
@ -36,21 +35,9 @@ public interface ClientMonitoringDataView {
|
|||
@JsonProperty(ATTR_STATUS)
|
||||
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)
|
||||
Map<Long, String> getIndicatorValues();
|
||||
|
||||
@JsonProperty(ATTR_CLIENT_GROUPS)
|
||||
Set<Long> getGroups();
|
||||
|
||||
@JsonProperty(ATTR_MISSING_PING)
|
||||
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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,16 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MonitoringSEBConnectionData {
|
||||
|
||||
public static final String ATTR_CONNECTIONS = "connections";
|
||||
public static final String ATTR_STATUS_MAPPING = "statusMapping";
|
||||
public static final String ATTR_CLIENT_GROUP_MAPPING = "clientGroupMapping";
|
||||
public static final String ATTR_CONNECTIONS = "cons";
|
||||
public static final String ATTR_STATUS_MAPPING = "sm";
|
||||
public static final String ATTR_CLIENT_GROUP_MAPPING = "cgm";
|
||||
|
||||
@JsonProperty(ATTR_CONNECTIONS)
|
||||
public final Collection<? extends ClientMonitoringDataView> monitoringData;
|
||||
|
||||
@JsonProperty(ATTR_STATUS_MAPPING)
|
||||
public final int[] connectionsPerStatus;
|
||||
|
||||
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
|
||||
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 {
|
||||
return ipToLong(InetAddress.getByName(ipV4Address));
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -805,4 +805,12 @@ public final class Utils {
|
|||
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();
|
||||
}
|
||||
|
||||
System.out.println("************** size = " + responseEntity.getBody().length());
|
||||
|
||||
return Result.of(RestCall.this.jsonMapper.readValue(
|
||||
responseEntity.getBody(),
|
||||
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.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
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.TableColumn;
|
||||
import org.eclipse.swt.widgets.TableItem;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.model.Domain;
|
||||
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.Exam;
|
||||
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.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.MonitoringStaticClientData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
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;
|
||||
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.widget.WidgetFactory;
|
||||
|
||||
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[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
|
||||
//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 LinkedHashMap<Long, UpdatableTableItem> tableMapping;
|
||||
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 lightFontColor;
|
||||
|
@ -194,7 +200,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
this.tableMapping = new LinkedHashMap<>();
|
||||
this.sessionIds = new LinkedMultiValueMap<>();
|
||||
this.table.layout();
|
||||
}
|
||||
|
||||
|
@ -244,8 +249,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
final UpdatableTableItem updatableTableItem =
|
||||
new ArrayList<>(this.tableMapping.values())
|
||||
.get(selectionIndices[i]);
|
||||
if (filter.test(updatableTableItem.monitoringData)) {
|
||||
result.add(updatableTableItem.monitoringData.connectionToken);
|
||||
if (filter.test(updatableTableItem.monitoringData)
|
||||
&& updatableTableItem.staticData != ClientStaticData.NULL_DATA) {
|
||||
result.add(updatableTableItem.staticData.connectionToken);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -253,9 +259,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
return this.tableMapping
|
||||
.values()
|
||||
.stream()
|
||||
.map(item -> item.monitoringData)
|
||||
.filter(filter)
|
||||
.map(ClientMonitoringData::getConnectionToken)
|
||||
.filter(item -> filter.test(item.monitoringData) && item.staticData != ClientStaticData.NULL_DATA)
|
||||
.map(item -> item.staticData.connectionToken)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +309,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
(updatableTableItem.connectionId != null)
|
||||
? String.valueOf(updatableTableItem.connectionId)
|
||||
: null,
|
||||
updatableTableItem.monitoringData.connectionToken);
|
||||
updatableTableItem.staticData.connectionToken);
|
||||
}
|
||||
|
||||
public void forceUpdateAll() {
|
||||
|
@ -329,33 +334,32 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
this.toDelete.addAll(this.tableMapping.keySet());
|
||||
}
|
||||
|
||||
this.toUpdateStatic.clear();
|
||||
monitoringStatus.getConnectionData()
|
||||
.forEach(data -> {
|
||||
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
|
||||
data.id,
|
||||
UpdatableTableItem::new);
|
||||
tableItem.push(data);
|
||||
if (tableItem.push(data)) {
|
||||
this.toUpdateStatic.add(data.id);
|
||||
}
|
||||
if (needsSync) {
|
||||
this.toDelete.remove(data.id);
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.toUpdateStatic.isEmpty()) {
|
||||
fetchStaticClientConnectionData();
|
||||
}
|
||||
|
||||
if (!this.toDelete.isEmpty()) {
|
||||
this.toDelete.forEach(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.toDelete.forEach(id -> this.tableMapping.remove(id));
|
||||
monitoringStatus.resetFilterChanged();
|
||||
this.toDelete.clear();
|
||||
}
|
||||
|
||||
this.forceUpdateAll = false;
|
||||
this.needsSort = sizeChanged;
|
||||
this.needsSort = this.needsSort || sizeChanged;
|
||||
updateGUI();
|
||||
}
|
||||
|
||||
|
@ -447,9 +451,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
private boolean dataChanged = false;
|
||||
private boolean indicatorValueChanged = false;
|
||||
private ClientMonitoringData monitoringData;
|
||||
private ClientStaticData staticData = ClientStaticData.NULL_DATA;
|
||||
private int thresholdsWeight;
|
||||
private int[] indicatorWeights = null;
|
||||
private boolean duplicateChecked = false;
|
||||
|
||||
UpdatableTableItem(final Long connectionId) {
|
||||
this.connectionId = connectionId;
|
||||
|
@ -457,6 +461,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
private void update(final TableItem tableItem, final boolean force) {
|
||||
updateDuplicateColor(tableItem);
|
||||
if (force || this.dataChanged) {
|
||||
updateData(tableItem);
|
||||
}
|
||||
|
@ -483,13 +488,26 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
|
||||
.apply(this.monitoringData));
|
||||
}
|
||||
|
||||
if (this.monitoringData != null) {
|
||||
updateConnectionStatusColor(tableItem);
|
||||
updateDuplicateColor(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) {
|
||||
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
|
||||
tableItem.setImage(0,
|
||||
|
@ -509,29 +527,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
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) {
|
||||
return entry -> {
|
||||
final Long id = entry.getKey();
|
||||
|
@ -541,10 +536,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
return;
|
||||
}
|
||||
|
||||
updateIndicatorWeight(displayValue, indicatorData);
|
||||
|
||||
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.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
|
||||
} else {
|
||||
|
@ -571,38 +567,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
.entrySet()
|
||||
.stream()
|
||||
.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
|
||||
|
@ -650,8 +614,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
String getConnectionInfo() {
|
||||
if (this.monitoringData != null && this.monitoringData.info != null) {
|
||||
return this.monitoringData.info;
|
||||
if (this.staticData != null && this.staticData.info != null) {
|
||||
return this.staticData.info;
|
||||
}
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
@ -659,7 +623,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
private String getGroupInfo() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
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);
|
||||
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
|
||||
}
|
||||
|
@ -673,16 +637,17 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
String getConnectionIdentifier() {
|
||||
if (this.monitoringData != null && this.monitoringData.userSessionId != null) {
|
||||
return this.monitoringData.userSessionId;
|
||||
if (this.staticData != null && this.staticData.userSessionId != null) {
|
||||
return this.staticData.userSessionId;
|
||||
}
|
||||
|
||||
return "--";
|
||||
}
|
||||
|
||||
void push(final ClientMonitoringData monitoringData) {
|
||||
boolean push(final ClientMonitoringData monitoringData) {
|
||||
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.monitoringData.status.clientActiveStatus
|
||||
&& !this.monitoringData.indicatorValuesEquals(monitoringData));
|
||||
|
@ -697,55 +662,89 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
if (this.indicatorWeights == null) {
|
||||
this.indicatorWeights = new int[ClientConnectionTable.this.indicatorMapping.size()];
|
||||
}
|
||||
|
||||
// for (int i = 0; i < monitoringData.indicatorValues.size(); i++) {
|
||||
// final IndicatorValue indicatorValue = connectionData.indicatorValues.get(i);
|
||||
// final IndicatorData indicatorData =
|
||||
// ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId());
|
||||
//
|
||||
// if (indicatorData != null) {
|
||||
// updateIndicatorWeight(indicatorValue, indicatorData);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.indicatorValueChanged) {
|
||||
updateIndicatorWeight();
|
||||
}
|
||||
this.monitoringData = monitoringData;
|
||||
|
||||
if (!this.duplicateChecked &&
|
||||
this.monitoringData.status != ConnectionStatus.DISABLED &&
|
||||
StringUtils.isNotBlank(monitoringData.userSessionId)) {
|
||||
|
||||
ClientConnectionTable.this.sessionIds.add(
|
||||
monitoringData.userSessionId,
|
||||
this.connectionId);
|
||||
this.duplicateChecked = true;
|
||||
}
|
||||
return this.staticData == null
|
||||
|| this.staticData == ClientStaticData.NULL_DATA
|
||||
|| this.dataChanged
|
||||
|| this.monitoringData.status.connectingStatus
|
||||
|| StringUtils.isBlank(this.staticData.userSessionId);
|
||||
}
|
||||
|
||||
private void updateIndicatorWeight(
|
||||
final String indicatorValue,
|
||||
final IndicatorData indicatorData) {
|
||||
void push(final ClientStaticData staticData) {
|
||||
this.staticData = staticData;
|
||||
this.dataChanged = true;
|
||||
}
|
||||
|
||||
final int indicatorWeight = IndicatorData.getWeight(
|
||||
indicatorData,
|
||||
IndicatorValue.getFromDisplayValue(indicatorValue));
|
||||
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 void updateIndicatorWeight() {
|
||||
if (this.monitoringData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
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.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
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.ClientConnectionData;
|
||||
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.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||
|
@ -184,6 +186,18 @@ public interface ExamSessionService {
|
|||
final Long examId,
|
||||
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
|
||||
* from persistence storage without caching involved.
|
||||
*
|
||||
|
|
|
@ -13,7 +13,11 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
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.LoggerFactory;
|
||||
|
||||
|
@ -21,8 +25,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
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.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.PendingNotificationIndication;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.PingIntervalClientIndicator;
|
||||
|
@ -39,9 +47,10 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
|||
protected ClientConnectionDataInternal(
|
||||
final ClientConnection clientConnection,
|
||||
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.indicatorMapping = new EnumMap<>(EventType.class);
|
||||
|
@ -100,4 +109,48 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
|||
.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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.ClientMonitoringDataView;
|
||||
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.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||
|
@ -425,7 +427,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
.getConnectionTokens(examId)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.map(token -> getConnectionDataInternal(token))
|
||||
.map(this::getConnectionDataInternal)
|
||||
.filter(Objects::nonNull)
|
||||
.map(c -> {
|
||||
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
|
||||
public Result<Collection<String>> getActiveConnectionTokens(final Long examId) {
|
||||
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.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -58,17 +60,24 @@ public class InternalClientConnectionDataFactory {
|
|||
result = new ClientConnectionDataInternal(
|
||||
clientConnection,
|
||||
() -> false,
|
||||
this.clientIndicatorFactory.createFor(clientConnection));
|
||||
this.clientIndicatorFactory.createFor(clientConnection),
|
||||
getGroupIds(clientConnection));
|
||||
} else {
|
||||
|
||||
result = new ClientConnectionDataInternal(
|
||||
clientConnection,
|
||||
() -> this.sebClientNotificationService
|
||||
.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
|
||||
final Set<Long> clientGroupIds = new HashSet<>();
|
||||
if (clientConnection.examId != null) {
|
||||
final Collection<ClientGroup> clientGroups = this.clientGroupDAO
|
||||
.allForExam(clientConnection.examId)
|
||||
|
@ -80,13 +89,12 @@ public class InternalClientConnectionDataFactory {
|
|||
if (!clientGroups.isEmpty()) {
|
||||
clientGroups.forEach(clientGroup -> {
|
||||
if (this.clientGroupMatcherService.isInGroup(clientConnection, clientGroup)) {
|
||||
result.addToClientGroup(clientGroup);
|
||||
clientGroupIds.add(clientGroup.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return clientGroupIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
private final ExamAdminService examAdminService;
|
||||
private final DistributedIndicatorValueService distributedPingCache;
|
||||
private final ClientIndicatorFactory clientIndicatorFactory;
|
||||
private final InternalClientConnectionDataFactory internalClientConnectionDataFactory;
|
||||
private final boolean isDistributedSetup;
|
||||
|
||||
protected SEBClientConnectionServiceImpl(
|
||||
|
@ -84,7 +85,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
final SEBClientInstructionService sebInstructionService,
|
||||
final ExamAdminService examAdminService,
|
||||
final DistributedIndicatorValueService distributedPingCache,
|
||||
final ClientIndicatorFactory clientIndicatorFactory) {
|
||||
final ClientIndicatorFactory clientIndicatorFactory,
|
||||
final InternalClientConnectionDataFactory internalClientConnectionDataFactory) {
|
||||
|
||||
this.examSessionService = examSessionService;
|
||||
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
|
||||
|
@ -95,6 +97,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
this.examAdminService = examAdminService;
|
||||
this.clientIndicatorFactory = clientIndicatorFactory;
|
||||
this.distributedPingCache = distributedPingCache;
|
||||
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
|
||||
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
|
||||
}
|
||||
|
||||
|
@ -740,7 +743,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
|
||||
return Result.tryCatch(() -> new ClientConnectionData(
|
||||
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) {
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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.monitoring.MonitoringFullPageData;
|
||||
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.util.Utils;
|
||||
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.PermissionDeniedException;
|
||||
|
@ -262,6 +266,32 @@ public class ExamMonitoringController {
|
|||
.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(
|
||||
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
|
||||
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.JSONMapper;
|
||||
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.ExamStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
||||
|
@ -276,21 +275,14 @@ public class ModelObjectJSONGenerator {
|
|||
Arrays.asList(
|
||||
new SimpleIndicatorValue(1L, 1.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(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(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
||||
|
||||
System.out.println("ClientMonitoringData" + ":");
|
||||
System.out.println(writerWithDefaultPrettyPrinter
|
||||
.writeValueAsString(((ClientConnectionData) domainObject).monitoringDataView));
|
||||
|
||||
domainObject = new ClientEvent(1L, 1L, EventType.WARN_LOG,
|
||||
System.currentTimeMillis(), System.currentTimeMillis(), 123.0, "text");
|
||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||
|
|
Loading…
Reference in a new issue