SEBSERV-445 new SEB event store strategy with background tasks
This commit is contained in:
		
							parent
							
								
									314ce82c00
								
							
						
					
					
						commit
						e3b44cb60b
					
				
					 16 changed files with 384 additions and 141 deletions
				
			
		|  | @ -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. | ||||
|  |  | |||
|  | @ -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"; | ||||
|  |  | |||
|  | @ -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. | ||||
|      * | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrate | |||
| @Lazy | ||||
| @Service | ||||
| @WebServiceProfile | ||||
| @Deprecated | ||||
| public class EventHandlingStrategyFactory { | ||||
| 
 | ||||
|     private final EventHandlingStrategy eventHandlingStrategy; | ||||
|  |  | |||
|  | @ -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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -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)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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)); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti