SEBSERV-240 implementation

This commit is contained in:
anhefti 2022-03-23 11:34:54 +01:00
parent 6d56e71dbe
commit a35ba48844
15 changed files with 263 additions and 49 deletions

View file

@ -213,6 +213,7 @@ public final class API {
public static final String EXAM_PROCTORING_ATTR_ALLOW_CHAT = "allow_chat"; public static final String EXAM_PROCTORING_ATTR_ALLOW_CHAT = "allow_chat";
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection"; public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
public static final String SEB_CLIENT_CONNECTION_DATA_ENDPOINT = "/data";
public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event"; public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event";
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search"; public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";

View file

@ -17,10 +17,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ClientConnectionData { public class ClientConnectionData implements GrantEntity {
public static final String ATTR_CLIENT_CONNECTION = "cData"; public static final String ATTR_CLIENT_CONNECTION = "cData";
public static final String ATTR_INDICATOR_VALUE = "iValues"; public static final String ATTR_INDICATOR_VALUE = "iValues";
@ -48,7 +50,7 @@ public class ClientConnectionData {
this.indicatorValues = Utils.immutableListOf(indicatorValues); this.indicatorValues = Utils.immutableListOf(indicatorValues);
} }
protected ClientConnectionData( public ClientConnectionData(
final ClientConnection clientConnection, final ClientConnection clientConnection,
final List<? extends IndicatorValue> indicatorValues) { final List<? extends IndicatorValue> indicatorValues) {
@ -58,6 +60,26 @@ public class ClientConnectionData {
this.indicatorValues = Utils.immutableListOf(indicatorValues); this.indicatorValues = Utils.immutableListOf(indicatorValues);
} }
@Override
public EntityType entityType() {
return this.clientConnection.entityType();
}
@Override
public String getName() {
return this.clientConnection.getName();
}
@Override
public String getModelId() {
return this.clientConnection.getModelId();
}
@Override
public Long getInstitutionId() {
return this.clientConnection.getInstitutionId();
}
@JsonProperty(ATTR_MISSING_PING) @JsonProperty(ATTR_MISSING_PING)
public Boolean getMissingPing() { public Boolean getMissingPing() {
return this.missingPing; return this.missingPing;

View file

@ -9,15 +9,20 @@
package ch.ethz.seb.sebserver.gui.content.monitoring; package ch.ethz.seb.sebserver.gui.content.monitoring;
import java.util.Collection; import java.util.Collection;
import java.util.function.Function;
import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
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.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
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.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
@ -29,7 +34,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.TableBuilder; import ch.ethz.seb.sebserver.gui.table.TableBuilder;
@ -91,29 +96,33 @@ public class FinishedExam implements TemplateComposer {
.call() .call()
.getOrThrow(); .getOrThrow();
final TableBuilder<ClientConnection> tableBuilder = final Composite content = this.pageService.getWidgetFactory().defaultPageLayout(
this.pageService.entityTableBuilder(restService.getRestCall(GetClientConnectionPage.class)) pageContext.getParent(),
TITLE_TEXT_KEY);
final TableBuilder<ClientConnectionData> tableBuilder =
this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamClientConnectionPage.class))
.withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withEmptyMessage(EMPTY_LIST_TEXT_KEY)
.withPaging(10) .withPaging(this.pageSize)
.withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId) .withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId)
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<ClientConnectionData>(
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
TABLE_COLUMN_NAME, TABLE_COLUMN_NAME,
ClientConnection::getUserSessionId) c -> c.clientConnection.getUserSessionId())
.withFilter(this.nameFilter)) .withFilter(this.nameFilter))
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<ClientConnectionData>(
ClientConnection.ATTR_INFO, ClientConnection.ATTR_INFO,
TABLE_COLUMN_INFO, TABLE_COLUMN_INFO,
ClientConnection::getInfo) c -> c.clientConnection.getInfo())
.withFilter(this.infoFilter)) .withFilter(this.infoFilter))
.withColumn(new ColumnDefinition<ClientConnection>( .withColumn(new ColumnDefinition<ClientConnectionData>(
Domain.CLIENT_CONNECTION.ATTR_STATUS, Domain.CLIENT_CONNECTION.ATTR_STATUS,
TABLE_COLUMN_STATUS, TABLE_COLUMN_STATUS,
row -> this.pageService.getResourceService() row -> this.pageService.getResourceService()
.localizedClientConnectionStatusName(row.getStatus())) .localizedClientConnectionStatusName(row.clientConnection.getStatus()))
.withFilter(this.statusFilter)) .withFilter(this.statusFilter))
.withDefaultAction(t -> actionBuilder .withDefaultAction(t -> actionBuilder
@ -121,7 +130,25 @@ public class FinishedExam implements TemplateComposer {
.withParentEntityKey(examKey) .withParentEntityKey(examKey)
.create()); .create());
tableBuilder.compose(pageContext); indicators.stream().forEach(indicator -> {
tableBuilder.withColumn(new ColumnDefinition<>(
indicator.name,
new LocTextKey(indicator.name),
indicatorValueFunction(indicator)));
});
tableBuilder.compose(pageContext.copyOf(content));
}
private Function<ClientConnectionData, String> indicatorValueFunction(final Indicator indicator) {
return clientConnectionData -> {
return clientConnectionData.indicatorValues
.stream()
.filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id))
.findFirst()
.map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type))
.orElse(Constants.EMPTY_NOTE);
};
} }
} }

View file

@ -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.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetFinishedExamClientConnection extends RestCall<ClientConnectionData> {
public GetFinishedExamClientConnection() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.CLIENT_CONNECTION,
new TypeReference<ClientConnectionData>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.SEB_CLIENT_CONNECTION_ENDPOINT
+ API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -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.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetFinishedExamClientConnectionPage extends RestCall<Page<ClientConnectionData>> {
public GetFinishedExamClientConnectionPage() {
super(new TypeKey<>(
CallType.GET_PAGE,
EntityType.CLIENT_CONNECTION,
new TypeReference<Page<ClientConnectionData>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.SEB_CLIENT_CONNECTION_ENDPOINT + API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT);
}
}

View file

@ -13,6 +13,7 @@ import java.util.Collection;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -184,4 +185,11 @@ public interface SEBClientConnectionService {
* @param instructionConfirm the instruction confirm identifier */ * @param instructionConfirm the instruction confirm identifier */
void confirmInstructionDone(String connectionToken, String instructionConfirm); void confirmInstructionDone(String connectionToken, String instructionConfirm);
/** Use this to get the get the specific indicator values for a given client connection.
*
* @param clientConnection The client connection values
* @return Result refer to ClientConnectionData instance containing the given clientConnection plus the indicator
* values or to an error when happened */
Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection);
} }

View file

@ -54,8 +54,14 @@ public class ClientIndicatorFactory {
} }
public List<ClientIndicator> createFor(final ClientConnection clientConnection) { public List<ClientIndicator> createFor(final ClientConnection clientConnection) {
final List<ClientIndicator> result = new ArrayList<>(); return createFor(clientConnection, false);
}
public List<ClientIndicator> createFor(
final ClientConnection clientConnection,
final boolean enableCachingOverride) {
final List<ClientIndicator> result = new ArrayList<>();
if (clientConnection.examId == null) { if (clientConnection.examId == null) {
return result; return result;
} }
@ -82,7 +88,7 @@ public class ClientIndicatorFactory {
indicatorDef, indicatorDef,
clientConnection.id, clientConnection.id,
clientConnection.status.clientActiveStatus, clientConnection.status.clientActiveStatus,
this.enableCaching); this.enableCaching || enableCachingOverride);
result.add(indicator); result.add(indicator);
} catch (final Exception e) { } catch (final Exception e) {
@ -111,7 +117,7 @@ public class ClientIndicatorFactory {
indicator, indicator,
clientConnection.id, clientConnection.id,
clientConnection.status.clientActiveStatus, clientConnection.status.clientActiveStatus,
this.enableCaching); this.enableCaching || enableCachingOverride);
result.add(pingIndicator); result.add(pingIndicator);
} }

View file

@ -74,6 +74,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
private final SEBClientConfigDAO sebClientConfigDAO; private final SEBClientConfigDAO sebClientConfigDAO;
private final SEBClientInstructionService sebInstructionService; private final SEBClientInstructionService sebInstructionService;
private final ExamAdminService examAdminService; private final ExamAdminService examAdminService;
private final ClientIndicatorFactory clientIndicatorFactory;
// TODO get rid of this dependency and use application events for signaling client connection state changes // TODO get rid of this dependency and use application events for signaling client connection state changes
private final DistributedIndicatorValueService distributedPingCache; private final DistributedIndicatorValueService distributedPingCache;
private final boolean isDistributedSetup; private final boolean isDistributedSetup;
@ -84,6 +85,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
final SEBClientConfigDAO sebClientConfigDAO, final SEBClientConfigDAO sebClientConfigDAO,
final SEBClientInstructionService sebInstructionService, final SEBClientInstructionService sebInstructionService,
final ExamAdminService examAdminService, final ExamAdminService examAdminService,
final ClientIndicatorFactory clientIndicatorFactory,
final DistributedIndicatorValueService distributedPingCache) { final DistributedIndicatorValueService distributedPingCache) {
this.examSessionService = examSessionService; this.examSessionService = examSessionService;
@ -94,6 +96,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
this.sebClientConfigDAO = sebClientConfigDAO; this.sebClientConfigDAO = sebClientConfigDAO;
this.sebInstructionService = sebInstructionService; this.sebInstructionService = sebInstructionService;
this.examAdminService = examAdminService; this.examAdminService = examAdminService;
this.clientIndicatorFactory = clientIndicatorFactory;
this.distributedPingCache = distributedPingCache; this.distributedPingCache = distributedPingCache;
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed(); this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
} }
@ -702,6 +705,13 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
this.sebInstructionService.confirmInstructionDone(connectionToken, instructionConfirm); this.sebInstructionService.confirmInstructionDone(connectionToken, instructionConfirm);
} }
@Override
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
return Result.tryCatch(() -> new ClientConnectionData(
clientConnection,
this.clientIndicatorFactory.createFor(clientConnection, true)));
}
private void checkExamRunning(final Long examId) { private void checkExamRunning(final Long examId) {
if (examId != null && !this.examSessionService.isExamRunning(examId)) { if (examId != null && !this.examSessionService.isExamRunning(examId)) {
examNotRunningException(examId); examNotRunningException(examId);

View file

@ -21,7 +21,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
private static final Logger log = LoggerFactory.getLogger(AbstractClientIndicator.class); private static final Logger log = LoggerFactory.getLogger(AbstractClientIndicator.class);
protected final DistributedIndicatorValueService distributedPingCache; protected final DistributedIndicatorValueService distributedIndicatorValueService;
protected Long indicatorId = -1L; protected Long indicatorId = -1L;
protected Long examId = -1L; protected Long examId = -1L;
@ -38,9 +38,9 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
protected long lastUpdate = 0; protected long lastUpdate = 0;
public AbstractClientIndicator(final DistributedIndicatorValueService distributedPingCache) { public AbstractClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) {
super(); super();
this.distributedPingCache = distributedPingCache; this.distributedIndicatorValueService = distributedIndicatorValueService;
} }
@Override @Override
@ -70,10 +70,11 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
if (!this.cachingEnabled && this.active) { if (!this.cachingEnabled && this.active) {
try { try {
this.ditributedIndicatorValueRecordId = this.distributedPingCache.initIndicatorForConnection( this.ditributedIndicatorValueRecordId =
connectionId, this.distributedIndicatorValueService.initIndicatorForConnection(
getType(), connectionId,
initValue()); getType(),
initValue());
} catch (final Exception e) { } catch (final Exception e) {
tryRecoverIndicatorRecord(); tryRecoverIndicatorRecord();
} }
@ -94,7 +95,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
} }
try { try {
this.ditributedIndicatorValueRecordId = this.distributedPingCache.initIndicatorForConnection( this.ditributedIndicatorValueRecordId = this.distributedIndicatorValueService.initIndicatorForConnection(
this.connectionId, this.connectionId,
getType(), getType(),
initValue()); initValue());
@ -126,18 +127,18 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
public double getValue() { public double getValue() {
if (this.initialized && !this.cachingEnabled && this.active if (this.initialized && !this.cachingEnabled && this.active
&& this.lastUpdate != this.distributedPingCache.lastUpdate()) { && this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) {
if (this.ditributedIndicatorValueRecordId == null) { if (this.ditributedIndicatorValueRecordId == null) {
this.tryRecoverIndicatorRecord(); this.tryRecoverIndicatorRecord();
} }
final Long indicatorValue = this.distributedPingCache final Long indicatorValue = this.distributedIndicatorValueService
.getIndicatorValue(this.ditributedIndicatorValueRecordId); .getIndicatorValue(this.ditributedIndicatorValueRecordId);
if (indicatorValue != null) { if (indicatorValue != null) {
this.currentValue = indicatorValue.doubleValue(); this.currentValue = indicatorValue.doubleValue();
} }
this.lastUpdate = this.distributedPingCache.lastUpdate(); this.lastUpdate = this.distributedIndicatorValueService.lastUpdate();
} }
return this.currentValue; return this.currentValue;

View file

@ -75,7 +75,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
// update active indicator value record on persistent when caching is not enabled // update active indicator value record on persistent when caching is not enabled
if (this.active && this.ditributedIndicatorValueRecordId != null) { if (this.active && this.ditributedIndicatorValueRecordId != null) {
this.distributedPingCache.updateIndicatorValue( this.distributedIndicatorValueService.updateIndicatorValue(
this.ditributedIndicatorValueRecordId, this.ditributedIndicatorValueRecordId,
numberOfLogs.longValue()); numberOfLogs.longValue());
} }
@ -115,7 +115,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
private void valueChanged(final String eventText) { private void valueChanged(final String eventText) {
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) { if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) {
if (super.ditributedIndicatorValueRecordId != null) { if (super.ditributedIndicatorValueRecordId != null) {
this.distributedPingCache.incrementIndicatorValue(super.ditributedIndicatorValueRecordId); this.distributedIndicatorValueService.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
} }
this.currentValue = getValue() + 1d; this.currentValue = getValue() + 1d;
} }

View file

@ -56,7 +56,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
private void valueChanged(final String text, final double value) { private void valueChanged(final String text, final double value) {
if (this.tags == null || this.tags.length == 0 || hasTag(text)) { if (this.tags == null || this.tags.length == 0 || hasTag(text)) {
if (super.ditributedIndicatorValueRecordId != null) { if (super.ditributedIndicatorValueRecordId != null) {
if (!this.distributedPingCache.updateIndicatorValueAsync( if (!this.distributedIndicatorValueService.updateIndicatorValueAsync(
this.ditributedIndicatorValueRecordId, this.ditributedIndicatorValueRecordId,
Double.valueOf(value).longValue())) { Double.valueOf(value).longValue())) {
@ -100,7 +100,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
// update active indicator value record on persistent when caching is not enabled // update active indicator value record on persistent when caching is not enabled
if (this.active && this.ditributedIndicatorValueRecordId != null) { if (this.active && this.ditributedIndicatorValueRecordId != null) {
this.distributedPingCache.updateIndicatorValue( this.distributedIndicatorValueService.updateIndicatorValue(
this.ditributedIndicatorValueRecordId, this.ditributedIndicatorValueRecordId,
numericValue.longValue()); numericValue.longValue());
} }

View file

@ -12,7 +12,6 @@ import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Set; import java.util.Set;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
public abstract class AbstractPingIndicator extends AbstractClientIndicator { public abstract class AbstractPingIndicator extends AbstractClientIndicator {
@ -23,16 +22,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
super(distributedPingCache); super(distributedPingCache);
} }
@Override
public void init(
final Indicator indicatorDefinition,
final Long connectionId,
final boolean active,
final boolean cachingEnabled) {
super.init(indicatorDefinition, connectionId, active, cachingEnabled);
}
@Override @Override
public Set<EventType> observedEvents() { public Set<EventType> observedEvents() {
return this.EMPTY_SET; return this.EMPTY_SET;
@ -50,7 +39,7 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
} }
} }
this.distributedPingCache.updatePingAsync(this.ditributedIndicatorValueRecordId); this.distributedIndicatorValueService.updatePingAsync(this.ditributedIndicatorValueRecordId);
} }
} }

View file

@ -35,8 +35,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
private boolean hidden = false; private boolean hidden = false;
public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedPingCache) { public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) {
super(distributedPingCache); super(distributedIndicatorValueService);
this.cachingEnabled = true; this.cachingEnabled = true;
} }
@ -83,7 +83,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
} }
if (this.initialized && !this.cachingEnabled && this.active if (this.initialized && !this.cachingEnabled && this.active
&& this.lastUpdate != this.distributedPingCache.lastUpdate()) { && this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) {
final long currentTimeMillis = DateTimeUtils.currentTimeMillis(); final long currentTimeMillis = DateTimeUtils.currentTimeMillis();
this.currentValue = computeValueAt(currentTimeMillis); this.currentValue = computeValueAt(currentTimeMillis);
@ -110,7 +110,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
public final double computeValueAt(final long timestamp) { public final double computeValueAt(final long timestamp) {
if (super.ditributedIndicatorValueRecordId != null) { if (super.ditributedIndicatorValueRecordId != null) {
final Long lastPing = this.distributedPingCache final Long lastPing = this.distributedIndicatorValueService
.getIndicatorValue(super.ditributedIndicatorValueRecordId); .getIndicatorValue(super.ditributedIndicatorValueRecordId);
return (lastPing != null) return (lastPing != null)

View file

@ -12,26 +12,37 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.Page;
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.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
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.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
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.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
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.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@WebServiceProfile @WebServiceProfile
@ -39,13 +50,16 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT) @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT)
public class ClientConnectionController extends ReadonlyEntityController<ClientConnection, ClientConnection> { public class ClientConnectionController extends ReadonlyEntityController<ClientConnection, ClientConnection> {
private final SEBClientConnectionService sebClientConnectionService;
protected ClientConnectionController( protected ClientConnectionController(
final AuthorizationService authorization, final AuthorizationService authorization,
final BulkActionService bulkActionService, final BulkActionService bulkActionService,
final ClientConnectionDAO clientConnectionDAO, final ClientConnectionDAO clientConnectionDAO,
final UserActivityLogDAO userActivityLogDAO, final UserActivityLogDAO userActivityLogDAO,
final PaginationService paginationService, final PaginationService paginationService,
final BeanValidationService beanValidationService) { final BeanValidationService beanValidationService,
final SEBClientConnectionService sebClientConnectionService) {
super(authorization, super(authorization,
bulkActionService, bulkActionService,
@ -53,6 +67,60 @@ public class ClientConnectionController extends ReadonlyEntityController<ClientC
userActivityLogDAO, userActivityLogDAO,
paginationService, paginationService,
beanValidationService); beanValidationService);
this.sebClientConnectionService = sebClientConnectionService;
}
@RequestMapping(
path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT,
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Page<ClientConnectionData> getClientConnectionDataPage(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
@RequestParam final MultiValueMap<String, String> allRequestParams,
final HttpServletRequest request) {
// at least current user must have read access for specified entity type within its own institution
checkReadPrivilege(institutionId);
final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
populateFilterMap(filterMap, institutionId, sort);
final Page<ClientConnectionData> page = this.paginationService.getPage(
pageNumber,
pageSize,
sort,
getSQLTableOfEntity().name(),
() -> getAllData(filterMap))
.getOrThrow();
return page;
}
@RequestMapping(
path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ClientConnectionData getClientConnectionDataBy(@PathVariable final String modelId) {
return this.sebClientConnectionService
.getIndicatorValues(super.getBy(modelId))
.getOrThrow();
}
private Result<Collection<ClientConnectionData>> getAllData(final FilterMap filterMap) {
return getAll(filterMap)
.map(connection -> connection.stream()
.map(this.sebClientConnectionService::getIndicatorValues)
.flatMap(Result::onErrorLogAndSkip)
.collect(Collectors.toList()));
} }
@Override @Override

View file

@ -25,7 +25,7 @@ sebserver.webservice.clean-db-on-startup=false
# webservice configuration # webservice configuration
sebserver.init.adminaccount.gen-on-init=false sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.distributed=false sebserver.webservice.distributed=true
#sebserver.webservice.master.delay.threshold=10000 #sebserver.webservice.master.delay.threshold=10000
sebserver.webservice.http.external.scheme=http sebserver.webservice.http.external.scheme=http
sebserver.webservice.http.external.servername=localhost sebserver.webservice.http.external.servername=localhost