SEBSERV-445 new SEB event store strategy with background tasks

This commit is contained in:
anhefti 2023-05-26 13:26:03 +02:00
parent 314ce82c00
commit e3b44cb60b
16 changed files with 384 additions and 141 deletions

View file

@ -14,10 +14,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
/** A client indicator is a indicator value holder for a specific Indicator
* on a running client connection.
@ -64,14 +62,9 @@ public interface ClientIndicator extends IndicatorValue {
/** This gets called on a value change e.g.: when a ClientEvent was received.
* NOTE: that this is called only on the same machine (server-instance) on that the ClientEvent was received.
*
* @param event The ClientEvent instance */
void notifyValueChange(ClientEvent event);
/** This gets called on a value change e.g.: when a ClientEvent was received.
* NOTE: that this is called only on the same machine (server-instance) on that the ClientEvent was received.
*
* @param clientEventRecord The ClientEventRecord instance */
void notifyValueChange(ClientEventRecord clientEventRecord);
* @param textValue The text based value
* @param numValue The value number */
void notifyValueChange(String textValue, double numValue);
/** This indicates if the indicator indicates an incident. This is the case if the actual indicator value
* is above or below the max or min value defined by the indicator threshold settings.

View file

@ -14,6 +14,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
/** A exam session SEB client event handling strategy implements a certain strategy to
* store ClientEvent that are coming in within the specified endpoint in height frequency. */
@Deprecated
public interface EventHandlingStrategy extends Consumer<ClientEventRecord> {
String EVENT_CONSUMER_STRATEGY_CONFIG_PROPERTY_KEY = "sebserver.webservice.api.exam.event-handling-strategy";

View file

@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
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.util.Result;
public interface SEBClientSessionService {
@ -39,7 +38,13 @@ public interface SEBClientSessionService {
*
* @param connectionToken the connection token
* @param event The SEB client event data */
void notifyClientEvent(String connectionToken, final ClientEvent event);
void notifyClientEvent(String connectionToken, String jsonBody);
// /** Notify a SEB client event for live indication and storing to database.
// *
// * @param connectionToken the connection token
// * @param event The SEB client event data */
// void notifyClientEvent(String connectionToken, final ClientEvent event);
/** This is used to confirm SEB instructions that must be confirmed by the SEB client.
*

View file

@ -77,7 +77,7 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
private final BlockingDeque<ClientEventRecord> eventQueue = new LinkedBlockingDeque<>();
private final BlockingDeque<ClientNotification> notificationQueue = new LinkedBlockingDeque<>();
private boolean workersRunning = false;
private boolean enabled = false;
private final boolean enabled = false;
public AsyncBatchEventSaveStrategy(
final SEBClientNotificationService sebClientNotificationService,
@ -95,7 +95,8 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
@Override
public void enable() {
this.enabled = true;
log.info("AsyncBatchEventSaveStrategy is deprecated");
//this.enabled = true;
}
@EventListener(SEBServerInitEvent.class)

View file

@ -39,6 +39,7 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
private static final Logger log = LoggerFactory.getLogger(ClientConnectionDataInternal.class);
// TODO why list for type? Is it possible to restrict to one per type?
final EnumMap<EventType, Collection<ClientIndicator>> indicatorMapping;
PingIntervalClientIndicator pingIndicator = null;

View file

@ -19,6 +19,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrate
@Lazy
@Service
@WebServiceProfile
@Deprecated
public class EventHandlingStrategyFactory {
private final EventHandlingStrategy eventHandlingStrategy;

View file

@ -0,0 +1,278 @@
/*
* Copyright (c) 2023 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.webservice.servicelayer.session.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;
import javax.annotation.PreDestroy;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification.NotificationType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Pair;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
@Lazy
@Component
@WebServiceProfile
public class SEBClientEventBatchStore {
private static final Logger log = LoggerFactory.getLogger(SEBClientEventBatchStore.class);
private final SEBClientNotificationService sebClientNotificationService;
private final SqlSessionFactory sqlSessionFactory;
private final TransactionTemplate transactionTemplate;
private final ExamSessionCacheService examSessionCacheService;
private final JSONMapper jsonMapper;
private final SqlSessionTemplate sqlSessionTemplate;
private final ClientEventRecordMapper clientEventMapper;
public SEBClientEventBatchStore(
final SEBClientNotificationService sebClientNotificationService,
final SqlSessionFactory sqlSessionFactory,
final PlatformTransactionManager transactionManager,
final ExamSessionCacheService examSessionCacheService,
final JSONMapper jsonMapper) {
this.sebClientNotificationService = sebClientNotificationService;
this.sqlSessionFactory = sqlSessionFactory;
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
this.examSessionCacheService = examSessionCacheService;
this.jsonMapper = jsonMapper;
this.sqlSessionTemplate = new SqlSessionTemplate(
this.sqlSessionFactory,
ExecutorType.BATCH);
this.clientEventMapper = this.sqlSessionTemplate.getMapper(
ClientEventRecordMapper.class);
}
private final BlockingDeque<EventData> eventDataQueue = new LinkedBlockingDeque<>();
private final Collection<EventData> events = new ArrayList<>();
public void accept(final String connectionToken, final String jsonBody) {
this.eventDataQueue.add(new EventData(
connectionToken,
Utils.getMillisecondsNow(),
jsonBody));
}
public void accept(final EventData eventData) {
this.eventDataQueue.add(eventData);
}
@Scheduled(
fixedDelayString = "${sebserver.webservice.api.exam.session.event.batch.task:1000}",
initialDelay = 1000)
public void processEvents() {
final long startTime = Utils.getMillisecondsNow();
//if (log.isDebugEnabled()) {
final int size = this.eventDataQueue.size();
if (size > 1000) {
log.warn("******* There are more then 1000 SEB client logs in the waiting queue: {}", size);
}
//}
try {
this.events.clear();
this.eventDataQueue.drainTo(this.events);
if (this.events.isEmpty()) {
return;
}
System.out.println("********** processing: " + this.events.size());
final List<ClientEventRecord> events = this.events
.stream()
.map(this::convertData)
.map(this::storeNotifications)
.filter(Objects::nonNull)
.map(this::toEventRecord)
.filter(Objects::nonNull)
.collect(Collectors.toList());
this.transactionTemplate
.execute(status -> {
events.stream().forEach(this.clientEventMapper::insert);
return null;
});
this.sqlSessionTemplate.flushStatements();
//if (log.isTraceEnabled()) {
log.info("****** Processing SEB events tuck: {}", Utils.getMillisecondsNow() - startTime);
//}
} catch (final Exception e) {
log.error("Failed to process SEB events from eventDataQueue: ", e);
}
}
private EventData convertData(final EventData eventData) {
if (eventData == null || eventData.jsonBody == null) {
return eventData;
}
try {
final ClientEvent eventModel = this.jsonMapper.readValue(
eventData.jsonBody,
ClientEvent.class);
eventData.setEvent(eventModel);
return eventData;
} catch (final Exception e) {
log.error("Failed to convert SEB event JSON data to internal data for: {}", eventData);
return eventData;
}
}
private EventData storeNotifications(final EventData eventData) {
try {
if (!eventData.event.eventType.isNotificationEvent) {
return eventData;
}
System.out.println("******* storeNotifications: " + eventData);
final ClientConnectionDataInternal clientConnection = this.examSessionCacheService
.getClientConnection(eventData.connectionToken);
final Pair<NotificationType, String> typeAndPlainText =
ClientNotification.extractTypeAndPlainText(eventData.event.text);
final ClientNotification notification = new ClientNotification(
eventData.event.id,
clientConnection.getConnectionId(),
eventData.event.eventType,
eventData.event.getClientTime(),
eventData.event.getServerTime(),
(eventData.event.numValue != null) ? eventData.event.numValue.doubleValue() : null,
typeAndPlainText.b,
typeAndPlainText.a);
switch (notification.eventType) {
case NOTIFICATION: {
this.sebClientNotificationService.newNotification(notification);
break;
}
case NOTIFICATION_CONFIRMED: {
this.sebClientNotificationService.confirmPendingNotification(notification);
break;
}
default:
}
// skip this for further event processing
return null;
} catch (final Exception e) {
log.error("Failed to verify and process notification for SEB event: {}", eventData);
return eventData;
}
}
private ClientEventRecord toEventRecord(final EventData eventData) {
try {
final ClientConnectionDataInternal clientConnection = this.examSessionCacheService
.getClientConnection(eventData.connectionToken);
if (clientConnection == null) {
log.warn("Failed to retrieve ClientConnection for token {}. Skip this event",
eventData.connectionToken);
return null;
}
// handle indicator update
clientConnection
.getIndicatorMapping(eventData.event.eventType)
.forEach(indicator -> indicator.notifyValueChange(
eventData.event.text,
(eventData.event.numValue != null) ? eventData.event.numValue : Double.NaN));
return ClientEvent.toRecord(eventData.event, clientConnection.clientConnection.id);
} catch (final Exception e) {
log.error(
"Unexpected error while converting SEB event data to record for: {} Skip this event", eventData,
e);
return null;
}
}
@PreDestroy
protected void shutdown() {
log.info("Shutdown SEBClientEventBatchStore...");
if (this.sqlSessionTemplate != null) {
try {
this.sqlSessionTemplate.destroy();
} catch (final Exception e) {
log.error("Failed to close and destroy the SqlSessionTemplate for this thread: {}",
Thread.currentThread(),
e);
}
}
}
public final static class EventData {
final String connectionToken;
final Long serverTime;
final String jsonBody;
ClientEvent event;
public EventData(final String connectionToken, final Long serverTime, final String jsonBody) {
this.connectionToken = connectionToken;
this.serverTime = serverTime;
this.jsonBody = jsonBody;
this.event = null;
}
public EventData(final String connectionToken, final Long serverTime, final ClientEvent event) {
this.connectionToken = connectionToken;
this.serverTime = serverTime;
this.jsonBody = null;
this.event = event;
}
void setEvent(final ClientEvent event) {
this.event = event;
}
}
}

View file

@ -8,7 +8,6 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -26,14 +25,13 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrategy;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientVersionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.SEBClientEventBatchStore.EventData;
@Lazy
@Service
@ -45,7 +43,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
private final ClientConnectionDAO clientConnectionDAO;
private final ExamSessionService examSessionService;
private final ExamSessionCacheService examSessionCacheService;
private final EventHandlingStrategy eventHandlingStrategy;
private final SEBClientEventBatchStore sebClientEventBatchStore;
private final SEBClientInstructionService sebInstructionService;
private final ClientIndicatorFactory clientIndicatorFactory;
private final InternalClientConnectionDataFactory internalClientConnectionDataFactory;
@ -55,7 +53,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
public SEBClientSessionServiceImpl(
final ClientConnectionDAO clientConnectionDAO,
final ExamSessionService examSessionService,
final EventHandlingStrategyFactory eventHandlingStrategyFactory,
final SEBClientEventBatchStore sebClientEventBatchStore,
final SEBClientInstructionService sebInstructionService,
final ClientIndicatorFactory clientIndicatorFactory,
final InternalClientConnectionDataFactory internalClientConnectionDataFactory,
@ -65,7 +63,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
this.clientConnectionDAO = clientConnectionDAO;
this.examSessionService = examSessionService;
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
this.eventHandlingStrategy = eventHandlingStrategyFactory.get();
this.sebClientEventBatchStore = sebClientEventBatchStore;
this.sebInstructionService = sebInstructionService;
this.clientIndicatorFactory = clientIndicatorFactory;
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
@ -127,32 +125,8 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
}
@Override
public void notifyClientEvent(
final String connectionToken,
final ClientEvent event) {
try {
final ClientConnectionDataInternal activeClientConnection =
this.examSessionService.getConnectionDataInternal(connectionToken);
if (activeClientConnection != null) {
// store event
this.eventHandlingStrategy.accept(ClientEvent.toRecord(
event,
activeClientConnection.getConnectionId()));
// handle indicator update
activeClientConnection
.getIndicatorMapping(event.eventType)
.forEach(indicator -> indicator.notifyValueChange(event));
} else {
log.warn("No active ClientConnection found for connectionToken: {}", connectionToken);
}
} catch (final Exception e) {
log.error("Failed to process SEB client event: ", e);
}
public final void notifyClientEvent(final String connectionToken, final String jsonBody) {
this.sebClientEventBatchStore.accept(connectionToken, jsonBody);
}
@Override
@ -183,22 +157,28 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
final boolean missingPing = connection.getMissingPing();
final long millisecondsNow = Utils.getMillisecondsNow();
final ClientEventRecord clientEventRecord = new ClientEventRecord(
null,
connection.getConnectionId(),
(missingPing) ? EventType.ERROR_LOG.id : EventType.INFO_LOG.id,
millisecondsNow,
millisecondsNow,
new BigDecimal(connection.pingIndicator.getValue()),
(missingPing) ? "Missing Client Ping" : "Client Ping Back To Normal");
final String textValue = (missingPing) ? "Missing Client Ping" : "Client Ping Back To Normal";
final double numValue = connection.pingIndicator.getValue();
// store event and and flush cache
this.eventHandlingStrategy.accept(clientEventRecord);
final EventData eventData = new EventData(
connection.getClientConnection().connectionToken,
millisecondsNow,
new ClientEvent(
null,
connection.getConnectionId(),
(missingPing) ? EventType.ERROR_LOG : EventType.INFO_LOG,
millisecondsNow,
millisecondsNow,
numValue,
textValue));
// store missing-ping or ping-back event
this.sebClientEventBatchStore.accept(eventData);
// update indicators
if (clientEventRecord.getType() != null && EventType.ERROR_LOG.id == clientEventRecord.getType()) {
if (EventType.ERROR_LOG == eventData.event.eventType) {
connection.getIndicatorMapping(EventType.ERROR_LOG)
.forEach(indicator -> indicator.notifyValueChange(clientEventRecord));
.forEach(indicator -> indicator.notifyValueChange(textValue, numValue));
}
}
}

View file

@ -15,12 +15,10 @@ import org.mybatis.dynamic.sql.SqlCriterion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicator {
@ -38,13 +36,13 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
}
@Override
public void notifyValueChange(final ClientEvent event) {
valueChanged(event.text);
}
@Override
public void notifyValueChange(final ClientEventRecord clientEventRecord) {
valueChanged(clientEventRecord.getText());
public final void notifyValueChange(final String textValue, final double numValue) {
if (this.tags == null || this.tags.length == 0 || hasTag(textValue)) {
if (super.ditributedIndicatorValueRecordId != null) {
this.distributedIndicatorValueService.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
}
this.currentValue = getValue() + 1d;
}
}
@Override
@ -112,13 +110,4 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
return result;
}
private void valueChanged(final String eventText) {
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) {
if (super.ditributedIndicatorValueRecordId != null) {
this.distributedIndicatorValueService.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
}
this.currentValue = getValue() + 1d;
}
}
}

View file

@ -18,7 +18,6 @@ import org.mybatis.dynamic.sql.SqlCriterion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
@ -41,31 +40,19 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
}
@Override
public void notifyValueChange(final ClientEvent event) {
valueChanged(event.text, event.getValue());
}
@Override
public void notifyValueChange(final ClientEventRecord clientEventRecord) {
final BigDecimal numericValue = clientEventRecord.getNumericValue();
if (numericValue != null) {
valueChanged(clientEventRecord.getText(), numericValue.doubleValue());
}
}
private void valueChanged(final String text, final double value) {
if (this.tags == null || this.tags.length == 0 || hasTag(text)) {
public void notifyValueChange(final String textValue, final double numValue) {
if (this.tags == null || this.tags.length == 0 || hasTag(textValue)) {
if (super.ditributedIndicatorValueRecordId != null) {
if (!this.distributedIndicatorValueService.updateIndicatorValueAsync(
this.ditributedIndicatorValueRecordId,
Double.valueOf(value).longValue())) {
Double.valueOf(numValue).longValue())) {
this.currentValue = computeValueAt(Utils.getMillisecondsNow());
} else {
this.currentValue = value;
this.currentValue = numValue;
}
} else {
this.currentValue = value;
this.currentValue = numValue;
}
}
}

View file

@ -19,8 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
@Lazy
@Component(IndicatorType.Names.LAST_PING)
@ -92,12 +90,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
}
@Override
public void notifyValueChange(final ClientEvent event) {
}
@Override
public void notifyValueChange(final ClientEventRecord clientEventRecord) {
public void notifyValueChange(final String textValue, final double numValue) {
}

View file

@ -45,7 +45,6 @@ import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.RunningExamInfo;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -358,10 +357,10 @@ public class ExamAPI_V1_Controller {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void event(
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
@RequestBody(required = true) final ClientEvent event) {
@RequestBody(required = true) final String jsonBody) {
this.sebClientSessionService
.notifyClientEvent(connectionToken, event);
.notifyClientEvent(connectionToken, jsonBody);
}
private Long getInstitutionId(final Principal principal) {

View file

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

View file

@ -72,7 +72,6 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
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.ClientConnectionDataInternal;
@ -406,15 +405,8 @@ public class ModelObjectJSONGenerator {
}
@Override
public void notifyValueChange(final ClientEvent event) {
public void notifyValueChange(final String textValue, final double numValue) {
// TODO Auto-generated method stub
}
@Override
public void notifyValueChange(final ClientEventRecord clientEventRecord) {
// TODO Auto-generated method stub
}
@Override

View file

@ -42,6 +42,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ClientConnectionDataInternal;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.SEBClientEventBatchStore;
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
public class SebConnectionTest extends ExamAPIIntegrationTester {
@ -56,6 +57,8 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
private ExamDAO examDAO;
@Autowired
private LmsAPIService lmsAPIService;
@Autowired
private SEBClientEventBatchStore sebClientEventBatchStore;
@Before
public void init() {
@ -558,6 +561,8 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
// check correct response
assertTrue(HttpStatus.NO_CONTENT.value() == sendEvent.getStatus());
this.sebClientEventBatchStore.processEvents();
// check event stored on db
List<ClientEventRecord> events = this.clientEventRecordMapper
.selectByExample()
@ -582,6 +587,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
10000.0,
"testEvent2");
this.sebClientEventBatchStore.processEvents();
// check correct response
assertTrue(HttpStatus.NO_CONTENT.value() == sendEvent.getStatus());
@ -616,7 +622,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
"testEvent1");
// check correct response
assertTrue(HttpStatus.NO_CONTENT.value() == sendEvent.getStatus());
this.sebClientEventBatchStore.processEvents();
final List<ClientEventRecord> events = this.clientEventRecordMapper
.selectByExample()
.build()

View file

@ -12,15 +12,12 @@ import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.Executor;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.jdbc.Sql;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
@ -35,6 +32,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.SEBClientEventBatchStore;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.AbstractLogIndicator;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.AbstractLogLevelCountIndicator;
@ -50,8 +48,12 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
@Autowired
private SEBClientSessionService sebClientSessionService;
@Autowired
@Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME)
private Executor executor;
private SEBClientEventBatchStore sebClientEventBatchStore;
// @Autowired
// @Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME)
// private Executor executor;
@Autowired
private JSONMapper jsonMapper;
@Test
public void testCreateLogEvents() {
@ -110,13 +112,15 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
this.sebClientSessionService.notifyClientEvent(
"token1",
new ClientEvent(null, connection.id, EventType.ERROR_LOG, 1L, 1L, 1.0, "some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.ERROR_LOG, 1L, 1L, 1.0, "some error")));
waitForExecutor();
assertEquals("1", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.ERROR_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token1",
new ClientEvent(null, connection.id, EventType.ERROR_LOG, 1L, 1L, 1.0, "some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.ERROR_LOG, 1L, 1L, 1.0, "some error")));
waitForExecutor();
assertEquals("2", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.ERROR_COUNT));
@ -126,6 +130,15 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
}
private String writeValueAsString(final ClientEvent event) {
try {
return this.jsonMapper.writeValueAsString(event);
} catch (final Exception e) {
e.printStackTrace();
return null;
}
}
@Test
public void testInfoLogWithTagCountIndicator() {
@ -156,33 +169,39 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error")));
waitForExecutor();
assertEquals("0", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.INFO_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<top> some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<top> some error")));
waitForExecutor();
assertEquals("1", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.INFO_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error")));
waitForExecutor();
assertEquals("1", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.INFO_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<vip> some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<vip> some error")));
waitForExecutor();
assertEquals("2", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.INFO_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some error")));
waitForExecutor();
assertEquals("2", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.INFO_COUNT));
this.sebClientSessionService.notifyClientEvent(
"token2",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<vip> some error"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<vip> some error")));
waitForExecutor();
// test reset indicator value and load it from persistent storage
((AbstractLogLevelCountIndicator) clientIndicator).reset();
@ -191,13 +210,7 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
}
private void waitForExecutor() {
try {
while (((ThreadPoolTaskExecutor) this.executor).getActiveCount() > 0) {
Thread.sleep(20);
}
} catch (final Exception e) {
e.printStackTrace();
}
this.sebClientEventBatchStore.processEvents();
}
@Test
@ -231,23 +244,27 @@ public class ClientEventServiceTest extends AdministrationAPIIntegrationTester {
this.sebClientSessionService.notifyClientEvent(
"token3",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some info other"));
writeValueAsString(
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "some info other")));
waitForExecutor();
this.sebClientSessionService.notifyClientEvent(
"token3",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0, "<vip> some info other"));
writeValueAsString(new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 1.0,
"<vip> some info other")));
waitForExecutor();
assertEquals("--", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.BATTERY_STATUS));
this.sebClientSessionService.notifyClientEvent(
"token3",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 90.0, "<battery> some info other"));
writeValueAsString(new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 90.0,
"<battery> some info other")));
waitForExecutor();
assertEquals("90", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.BATTERY_STATUS));
this.sebClientSessionService.notifyClientEvent(
"token3",
new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 40.0, "<battery> some info other"));
writeValueAsString(new ClientEvent(null, connection.id, EventType.INFO_LOG, 1L, 1L, 40.0,
"<battery> some info other")));
waitForExecutor();
assertEquals("40", IndicatorValue.getDisplayValue(clientIndicator, IndicatorType.BATTERY_STATUS));