SEBSERV-139 refactoring of townhall and instruction service
This commit is contained in:
		
							parent
							
								
									8e04e43bfa
								
							
						
					
					
						commit
						53bb378d0b
					
				
					 8 changed files with 184 additions and 73 deletions
				
			
		|  | @ -0,0 +1,34 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gbl.util; | ||||||
|  | 
 | ||||||
|  | import java.util.concurrent.ArrayBlockingQueue; | ||||||
|  | 
 | ||||||
|  | public class SizedArrayNonBlockingQueue<T> extends ArrayBlockingQueue<T> { | ||||||
|  | 
 | ||||||
|  |     private static final long serialVersionUID = -4235702373708064610L; | ||||||
|  | 
 | ||||||
|  |     private final int size; | ||||||
|  | 
 | ||||||
|  |     public SizedArrayNonBlockingQueue(final int size) { | ||||||
|  |         super(size); | ||||||
|  |         this.size = size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     synchronized public boolean add(final T element) { | ||||||
|  |         // Check if queue full already? | ||||||
|  |         if (super.size() == this.size) { | ||||||
|  |             // remove element from queue if queue is full | ||||||
|  |             this.remove(); | ||||||
|  |         } | ||||||
|  |         return super.add(element); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -334,26 +334,19 @@ public class MonitoringRunningExam implements TemplateComposer { | ||||||
| 
 | 
 | ||||||
|         if (proctoringSettings != null && proctoringSettings.enableProctoring) { |         if (proctoringSettings != null && proctoringSettings.enableProctoring) { | ||||||
| 
 | 
 | ||||||
|             final RemoteProctoringRoom townhall = restService.getBuilder(GetTownhallRoom.class) |  | ||||||
|                     .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) |  | ||||||
|                     .call() |  | ||||||
|                     .getOr(null); |  | ||||||
| 
 |  | ||||||
|             final boolean townhallActive = townhall != null && townhall.id != null; |  | ||||||
|             actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM) |             actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM) | ||||||
|                     .withEntityKey(entityKey) |                     .withEntityKey(entityKey) | ||||||
|                     .withExec(this::openTownhallRoom) |                     .withExec(this::toggleTownhallRoom) | ||||||
|                     .noEventPropagation() |                     .noEventPropagation() | ||||||
|                     .publish(); |                     .publish(); | ||||||
| 
 | 
 | ||||||
|             actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM) |             if (isTownhallRoomActive(entityKey.modelId)) { | ||||||
|                     .withEntityKey(entityKey) |  | ||||||
|                     .withExec(this::closeTownhallRoom) |  | ||||||
|                     .noEventPropagation() |  | ||||||
|                     .publish(); |  | ||||||
|             if (!townhallActive) { |  | ||||||
|                 this.pageService.firePageEvent( |                 this.pageService.firePageEvent( | ||||||
|                         new ActionActivationEvent(false, ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), |                         new ActionActivationEvent( | ||||||
|  |                                 true, | ||||||
|  |                                 new Tuple<>( | ||||||
|  |                                         ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, | ||||||
|  |                                         ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)), | ||||||
|                         pageContext); |                         pageContext); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -376,6 +369,40 @@ public class MonitoringRunningExam implements TemplateComposer { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private boolean isTownhallRoomActive(final String examModelId) { | ||||||
|  |         final RemoteProctoringRoom townhall = this.pageService.getRestService() | ||||||
|  |                 .getBuilder(GetTownhallRoom.class) | ||||||
|  |                 .withURIVariable(API.PARAM_MODEL_ID, examModelId) | ||||||
|  |                 .call() | ||||||
|  |                 .getOr(null); | ||||||
|  | 
 | ||||||
|  |         return townhall != null && townhall.id != null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private PageAction toggleTownhallRoom(final PageAction action) { | ||||||
|  |         if (isTownhallRoomActive(action.getEntityKey().modelId)) { | ||||||
|  |             closeTownhallRoom(action); | ||||||
|  |             this.pageService.firePageEvent( | ||||||
|  |                     new ActionActivationEvent( | ||||||
|  |                             true, | ||||||
|  |                             new Tuple<>( | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)), | ||||||
|  |                     action.pageContext()); | ||||||
|  |             return action; | ||||||
|  |         } else { | ||||||
|  |             openTownhallRoom(action); | ||||||
|  |             this.pageService.firePageEvent( | ||||||
|  |                     new ActionActivationEvent( | ||||||
|  |                             true, | ||||||
|  |                             new Tuple<>( | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)), | ||||||
|  |                     action.pageContext()); | ||||||
|  |             return action; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private PageAction openTownhallRoom(final PageAction action) { |     private PageAction openTownhallRoom(final PageAction action) { | ||||||
|         final EntityKey examId = action.getEntityKey(); |         final EntityKey examId = action.getEntityKey(); | ||||||
| 
 | 
 | ||||||
|  | @ -407,11 +434,6 @@ public class MonitoringRunningExam implements TemplateComposer { | ||||||
|                 this.remoteProctoringEndpoint); |                 this.remoteProctoringEndpoint); | ||||||
|         javaScriptExecutor.execute(script); |         javaScriptExecutor.execute(script); | ||||||
|         proctoringGUIService.registerProctoringWindow(activeAllRoomName); |         proctoringGUIService.registerProctoringWindow(activeAllRoomName); | ||||||
|         this.pageService.firePageEvent( |  | ||||||
|                 new ActionActivationEvent( |  | ||||||
|                         true, |  | ||||||
|                         ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), |  | ||||||
|                 action.pageContext()); |  | ||||||
|         return action; |         return action; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -431,14 +453,29 @@ public class MonitoringRunningExam implements TemplateComposer { | ||||||
|                 .getProctoringGUIService(); |                 .getProctoringGUIService(); | ||||||
| 
 | 
 | ||||||
|         proctoringGUIService.closeRoom(townhall.name); |         proctoringGUIService.closeRoom(townhall.name); | ||||||
|         this.pageService.firePageEvent( |  | ||||||
|                 new ActionActivationEvent( |  | ||||||
|                         false, |  | ||||||
|                         ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), |  | ||||||
|                 action.pageContext()); |  | ||||||
|         return action; |         return action; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private void updateTownhallButton(final EntityKey entityKey, final PageContext pageContext) { | ||||||
|  |         if (isTownhallRoomActive(entityKey.modelId)) { | ||||||
|  |             this.pageService.firePageEvent( | ||||||
|  |                     new ActionActivationEvent( | ||||||
|  |                             true, | ||||||
|  |                             new Tuple<>( | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)), | ||||||
|  |                     pageContext); | ||||||
|  |         } else { | ||||||
|  |             this.pageService.firePageEvent( | ||||||
|  |                     new ActionActivationEvent( | ||||||
|  |                             true, | ||||||
|  |                             new Tuple<>( | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, | ||||||
|  |                                     ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)), | ||||||
|  |                     pageContext); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private void updateRoomActions( |     private void updateRoomActions( | ||||||
|             final EntityKey entityKey, |             final EntityKey entityKey, | ||||||
|             final PageContext pageContext, |             final PageContext pageContext, | ||||||
|  | @ -446,6 +483,7 @@ public class MonitoringRunningExam implements TemplateComposer { | ||||||
|             final PageActionBuilder actionBuilder, |             final PageActionBuilder actionBuilder, | ||||||
|             final ProctoringSettings proctoringSettings) { |             final ProctoringSettings proctoringSettings) { | ||||||
| 
 | 
 | ||||||
|  |         updateTownhallButton(entityKey, pageContext); | ||||||
|         final I18nSupport i18nSupport = this.pageService.getI18nSupport(); |         final I18nSupport i18nSupport = this.pageService.getI18nSupport(); | ||||||
|         this.pageService.getRestService().getBuilder(GetProcotringRooms.class) |         this.pageService.getRestService().getBuilder(GetProcotringRooms.class) | ||||||
|                 .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) |                 .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) | ||||||
|  |  | ||||||
|  | @ -711,7 +711,7 @@ public enum ActionDefinition { | ||||||
|             ActionCategory.PROCTORING), |             ActionCategory.PROCTORING), | ||||||
|     MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM( |     MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM( | ||||||
|             new LocTextKey("sebserver.monitoring.exam.action.proctoring.closeTownhall"), |             new LocTextKey("sebserver.monitoring.exam.action.proctoring.closeTownhall"), | ||||||
|             ImageIcon.PROCTOR_ROOM, |             ImageIcon.CANCEL, | ||||||
|             PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, |             PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, | ||||||
|             ActionCategory.PROCTORING), |             ActionCategory.PROCTORING), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -135,13 +135,11 @@ public class ActionPane implements TemplateComposer { | ||||||
| 
 | 
 | ||||||
|                     if (event.decoration != null) { |                     if (event.decoration != null) { | ||||||
|                         final TreeItem actionItemToDecorate = findAction(actionTrees, parent, event.decoration._1); |                         final TreeItem actionItemToDecorate = findAction(actionTrees, parent, event.decoration._1); | ||||||
|                         final PageAction action = (PageAction) actionItemToDecorate.getData(ACTION_EVENT_CALL_KEY); |  | ||||||
|                         if (actionItemToDecorate != null && event.decoration._2 != null) { |                         if (actionItemToDecorate != null && event.decoration._2 != null) { | ||||||
|                             actionItemToDecorate.setImage(0, |                             actionItemToDecorate.setImage(0, | ||||||
|                                     event.decoration._2.icon.getImage(parent.getDisplay())); |                                     event.decoration._2.icon.getImage(parent.getDisplay())); | ||||||
|                             ActionPane.this.pageService.getPolyglotPageService().injectI18n( |                             ActionPane.this.pageService.getPolyglotPageService().injectI18n( | ||||||
|                                     actionItemToDecorate, |                                     actionItemToDecorate, event.decoration._2.title); | ||||||
|                                     (action != null) ? action.getTitle() : event.decoration._2.title); |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  | @ -29,12 +29,10 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerT | ||||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; | import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gui.GuiServiceInfo; | import ch.ethz.seb.sebserver.gui.GuiServiceInfo; | ||||||
| import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageService; | import ch.ethz.seb.sebserver.gui.service.page.PageService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.RemoteProctoringView; | import ch.ethz.seb.sebserver.gui.service.page.RemoteProctoringView; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEvent; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SendProctoringBroadcastAttributes; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SendProctoringBroadcastAttributes; | ||||||
| import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService; | import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService.ProctoringWindowData; | import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService.ProctoringWindowData; | ||||||
|  | @ -92,7 +90,7 @@ public class JitsiMeetProctoringView implements RemoteProctoringView { | ||||||
|         final GridData headerCell = new GridData(SWT.FILL, SWT.FILL, true, true); |         final GridData headerCell = new GridData(SWT.FILL, SWT.FILL, true, true); | ||||||
|         content.setLayoutData(headerCell); |         content.setLayoutData(headerCell); | ||||||
| 
 | 
 | ||||||
|         parent.addListener(SWT.Dispose, event -> closeRoom(proctoringWindowData, pageContext)); |         parent.addListener(SWT.Dispose, event -> closeRoom(proctoringWindowData)); | ||||||
| 
 | 
 | ||||||
|         final String url = this.guiServiceInfo |         final String url = this.guiServiceInfo | ||||||
|                 .getExternalServerURIBuilder() |                 .getExternalServerURIBuilder() | ||||||
|  | @ -122,7 +120,7 @@ public class JitsiMeetProctoringView implements RemoteProctoringView { | ||||||
| 
 | 
 | ||||||
|         final Button closeAction = widgetFactory.buttonLocalized(footer, CLOSE_WINDOW_TEXT_KEY); |         final Button closeAction = widgetFactory.buttonLocalized(footer, CLOSE_WINDOW_TEXT_KEY); | ||||||
|         closeAction.setLayoutData(new RowData(150, 30)); |         closeAction.setLayoutData(new RowData(150, 30)); | ||||||
|         closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringWindowData, pageContext)); |         closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringWindowData)); | ||||||
| 
 | 
 | ||||||
|         final BroadcastActionState broadcastActionState = new BroadcastActionState(); |         final BroadcastActionState broadcastActionState = new BroadcastActionState(); | ||||||
|         final String connectionTokens = getConnectionTokens(proctoringWindowData); |         final String connectionTokens = getConnectionTokens(proctoringWindowData); | ||||||
|  | @ -258,16 +256,11 @@ public class JitsiMeetProctoringView implements RemoteProctoringView { | ||||||
|         boolean chat = false; |         boolean chat = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void closeRoom(final ProctoringWindowData proctoringWindowData, final PageContext pageContext) { |     private void closeRoom(final ProctoringWindowData proctoringWindowData) { | ||||||
|         this.pageService |         this.pageService | ||||||
|                 .getCurrentUser() |                 .getCurrentUser() | ||||||
|                 .getProctoringGUIService() |                 .getProctoringGUIService() | ||||||
|                 .closeRoom(proctoringWindowData.connectionData.roomName); |                 .closeRoom(proctoringWindowData.connectionData.roomName); | ||||||
|         this.pageService.firePageEvent( |  | ||||||
|                 new ActionActivationEvent( |  | ||||||
|                         false, |  | ||||||
|                         ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), |  | ||||||
|                 pageContext); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -199,6 +199,11 @@ public class ProctoringGUIService { | ||||||
|             this.restService.getBuilder(SendProctoringBroadcastAttributes.class) |             this.restService.getBuilder(SendProctoringBroadcastAttributes.class) | ||||||
|                     .withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId) |                     .withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId) | ||||||
|                     .withFormParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, roomConnectionData.roomName) |                     .withFormParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, roomConnectionData.roomName) | ||||||
|  |                     .withFormParam( | ||||||
|  |                             API.EXAM_API_SEB_CONNECTION_TOKEN, | ||||||
|  |                             roomConnectionData.connections.isEmpty() | ||||||
|  |                                     ? "" | ||||||
|  |                                     : StringUtils.join(roomConnectionData.connections, Constants.LIST_SEPARATOR_CHAR)) | ||||||
|                     .call() |                     .call() | ||||||
|                     .onError(error -> log.error( |                     .onError(error -> log.error( | ||||||
|                             "Failed to send reset broadcast attribute instruction call for room: {}, cause: {}", |                             "Failed to send reset broadcast attribute instruction call for room: {}, cause: {}", | ||||||
|  | @ -229,7 +234,9 @@ public class ProctoringGUIService { | ||||||
|     public void clear() { |     public void clear() { | ||||||
| 
 | 
 | ||||||
|         if (!this.rooms.isEmpty()) { |         if (!this.rooms.isEmpty()) { | ||||||
|             this.rooms.keySet().stream().forEach(this::closeRoom); |             this.rooms.keySet() | ||||||
|  |                     .stream() | ||||||
|  |                     .forEach(this::closeRoom); | ||||||
|             this.rooms.clear(); |             this.rooms.clear(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,8 +16,6 @@ import java.util.concurrent.ConcurrentHashMap; | ||||||
| 
 | 
 | ||||||
| import org.apache.commons.lang3.BooleanUtils; | import org.apache.commons.lang3.BooleanUtils; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.joda.time.DateTime; |  | ||||||
| import org.joda.time.DateTimeZone; |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.context.annotation.Lazy; | import org.springframework.context.annotation.Lazy; | ||||||
|  | @ -31,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType; | import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType; | ||||||
| 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.gbl.util.SizedArrayNonBlockingQueue; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||||
| import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | ||||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientInstructionRecord; | import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientInstructionRecord; | ||||||
|  | @ -45,6 +44,7 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
| 
 | 
 | ||||||
|     private static final Logger log = LoggerFactory.getLogger(SEBInstructionServiceImpl.class); |     private static final Logger log = LoggerFactory.getLogger(SEBInstructionServiceImpl.class); | ||||||
| 
 | 
 | ||||||
|  |     private static final int INSTRUCTION_QUEUE_MAX_SIZE = 10; | ||||||
|     private static final String JSON_INST = "instruction"; |     private static final String JSON_INST = "instruction"; | ||||||
|     private static final String JSON_ATTR = "attributes"; |     private static final String JSON_ATTR = "attributes"; | ||||||
| 
 | 
 | ||||||
|  | @ -53,7 +53,7 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|     private final ClientInstructionDAO clientInstructionDAO; |     private final ClientInstructionDAO clientInstructionDAO; | ||||||
|     private final JSONMapper jsonMapper; |     private final JSONMapper jsonMapper; | ||||||
| 
 | 
 | ||||||
|     private final Map<String, ClientInstructionRecord> instructions; |     private final Map<String, SizedArrayNonBlockingQueue<ClientInstructionRecord>> instructions; | ||||||
| 
 | 
 | ||||||
|     private long lastRefresh = 0; |     private long lastRefresh = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -113,7 +113,7 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|                     final String attributesString = this.jsonMapper.writeValueAsString(attributes); |                     final String attributesString = this.jsonMapper.writeValueAsString(attributes); | ||||||
|                     this.clientInstructionDAO |                     this.clientInstructionDAO | ||||||
|                             .insert(examId, type, attributesString, connectionToken, needsConfirm) |                             .insert(examId, type, attributesString, connectionToken, needsConfirm) | ||||||
|                             .map(this::chacheInstruction) |                             .map(this::putToCacheIfAbsent) | ||||||
|                             .onError(error -> log.error("Failed to register instruction: {}", error.getMessage())) |                             .onError(error -> log.error("Failed to register instruction: {}", error.getMessage())) | ||||||
|                             .getOrThrow(); |                             .getOrThrow(); | ||||||
|                 } catch (final Exception e) { |                 } catch (final Exception e) { | ||||||
|  | @ -146,7 +146,7 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|                             error -> log.error("Failed to register instruction: {}", error.getMessage()), |                             error -> log.error("Failed to register instruction: {}", error.getMessage()), | ||||||
|                             () -> null)) |                             () -> null)) | ||||||
|                     .filter(Objects::nonNull) |                     .filter(Objects::nonNull) | ||||||
|                     .forEach(this::chacheInstruction); |                     .forEach(this::putToCacheIfAbsent); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | @ -162,10 +162,19 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         final ClientInstructionRecord clientInstruction = this.instructions.get(connectionToken); |         final SizedArrayNonBlockingQueue<ClientInstructionRecord> queue = this.instructions.get(connectionToken); | ||||||
|  |         if (queue.isEmpty()) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         final ClientInstructionRecord clientInstruction = queue.peek(); | ||||||
|  |         if (clientInstruction == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         final boolean needsConfirm = BooleanUtils.toBoolean(clientInstruction.getNeedsConfirmation()); |         final boolean needsConfirm = BooleanUtils.toBoolean(clientInstruction.getNeedsConfirmation()); | ||||||
|         if (!needsConfirm) { |         if (!needsConfirm) { | ||||||
|             this.instructions.remove(connectionToken); |             queue.poll(); | ||||||
|             final Result<Void> delete = this.clientInstructionDAO.delete(clientInstruction.getId()); |             final Result<Void> delete = this.clientInstructionDAO.delete(clientInstruction.getId()); | ||||||
|             if (delete.hasError()) { |             if (delete.hasError()) { | ||||||
|                 log.error("Failed to delete SEB client instruction on persistent storage: ", delete.getError()); |                 log.error("Failed to delete SEB client instruction on persistent storage: ", delete.getError()); | ||||||
|  | @ -207,13 +216,26 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|     @Override |     @Override | ||||||
|     public void confirmInstructionDone(final String connectionToken, final String instructionConfirm) { |     public void confirmInstructionDone(final String connectionToken, final String instructionConfirm) { | ||||||
|         try { |         try { | ||||||
|             this.instructions.remove(connectionToken); |             final SizedArrayNonBlockingQueue<ClientInstructionRecord> queue = this.instructions.get(connectionToken); | ||||||
|             this.clientInstructionDAO.delete(Long.valueOf(instructionConfirm)); |             if (queue.isEmpty()) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             final ClientInstructionRecord instruction = queue.peek(); | ||||||
|  |             if (String.valueOf(instruction.getId()).equals(String.valueOf(instruction.getId()))) { | ||||||
|  |                 queue.poll(); | ||||||
|  |                 this.clientInstructionDAO.delete(Long.valueOf(instructionConfirm)); | ||||||
|  |             } else { | ||||||
|  |                 log.warn("SEB instruction confirmation mismatch. Sent instructionConfirm: {} pending instruction: {}", | ||||||
|  |                         instructionConfirm, | ||||||
|  |                         instruction.getId()); | ||||||
|  |             } | ||||||
|         } catch (final Exception e) { |         } catch (final Exception e) { | ||||||
|             log.error( |             log.error( | ||||||
|                     "Failed to remove SEB instruction after confirmation: connectionToken: {} instructionConfirm: {}", |                     "Failed to remove SEB instruction after confirmation: connectionToken: {} instructionConfirm: {} connectionToken: {}", | ||||||
|                     connectionToken, |                     connectionToken, | ||||||
|                     instructionConfirm); |                     instructionConfirm, | ||||||
|  |                     connectionToken); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -236,33 +258,53 @@ public class SEBInstructionServiceImpl implements SEBInstructionService { | ||||||
|     private Result<Void> loadInstructions() { |     private Result<Void> loadInstructions() { | ||||||
|         return Result.tryCatch(() -> this.clientInstructionDAO.getAllActive() |         return Result.tryCatch(() -> this.clientInstructionDAO.getAllActive() | ||||||
|                 .getOrThrow() |                 .getOrThrow() | ||||||
|                 .forEach(inst -> this.instructions.putIfAbsent(inst.getConnectionToken(), inst))); |                 .forEach(this::putToCacheIfAbsent)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) { | //    private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) { | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | //        final String connectionToken = instruction.getConnectionToken(); | ||||||
|  | //        if (this.instructions.containsKey(connectionToken)) { | ||||||
|  | //            // check if previous instruction is still valid | ||||||
|  | //            final ClientInstructionRecord clientInstructionRecord = this.instructions.get(connectionToken); | ||||||
|  | // | ||||||
|  | //            System.out.println("************* previous instruction still active: " + clientInstructionRecord); | ||||||
|  | // | ||||||
|  | //            if (BooleanUtils.toBoolean(BooleanUtils.toBooleanObject(clientInstructionRecord.getNeedsConfirmation()))) { | ||||||
|  | //                // check if time is out | ||||||
|  | //                final long now = DateTime.now(DateTimeZone.UTC).getMillis(); | ||||||
|  | //                final Long timestamp = clientInstructionRecord.getTimestamp(); | ||||||
|  | //                if (timestamp != null && now - timestamp > Constants.MINUTE_IN_MILLIS) { | ||||||
|  | //                    // remove old instruction and add new one | ||||||
|  | //                    System.out.println("************* remove old instruction and put new: "); | ||||||
|  | //                    this.instructions.put(connectionToken, instruction); | ||||||
|  | //                } | ||||||
|  | //            } | ||||||
|  | //        } else { | ||||||
|  | //            this.instructions.put(connectionToken, instruction); | ||||||
|  | //        } | ||||||
|  | //        return instruction; | ||||||
|  | //    } | ||||||
|  | 
 | ||||||
|  |     private ClientInstructionRecord putToCacheIfAbsent(final ClientInstructionRecord instruction) { | ||||||
|  |         final SizedArrayNonBlockingQueue<ClientInstructionRecord> queue = this.instructions.computeIfAbsent( | ||||||
|  |                 instruction.getConnectionToken(), | ||||||
|  |                 key -> new SizedArrayNonBlockingQueue<>(INSTRUCTION_QUEUE_MAX_SIZE)); | ||||||
|  | 
 | ||||||
|  |         if (queue.contains(instruction)) { | ||||||
|  |             log.warn("Instruction alread in the queue: {}", instruction); | ||||||
|  |             return instruction; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (log.isDebugEnabled()) { | ||||||
|  |             log.debug("Put SEB instruction into instruction queue: {}", instruction); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         System.out.println("************* register instruction: " + instruction); |         System.out.println("************* register instruction: " + instruction); | ||||||
| 
 | 
 | ||||||
|         final String connectionToken = instruction.getConnectionToken(); |         queue.add(instruction); | ||||||
|         if (this.instructions.containsKey(connectionToken)) { |  | ||||||
|             // check if previous instruction is still valid |  | ||||||
|             final ClientInstructionRecord clientInstructionRecord = this.instructions.get(connectionToken); |  | ||||||
| 
 |  | ||||||
|             System.out.println("************* previous instruction still active: " + clientInstructionRecord); |  | ||||||
| 
 |  | ||||||
|             if (BooleanUtils.toBoolean(BooleanUtils.toBooleanObject(clientInstructionRecord.getNeedsConfirmation()))) { |  | ||||||
|                 // check if time is out |  | ||||||
|                 final long now = DateTime.now(DateTimeZone.UTC).getMillis(); |  | ||||||
|                 final Long timestamp = clientInstructionRecord.getTimestamp(); |  | ||||||
|                 if (timestamp != null && now - timestamp > Constants.MINUTE_IN_MILLIS) { |  | ||||||
|                     // remove old instruction and add new one |  | ||||||
|                     System.out.println("************* remove old instruction and put new: "); |  | ||||||
|                     this.instructions.put(connectionToken, instruction); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             this.instructions.put(connectionToken, instruction); |  | ||||||
|         } |  | ||||||
|         return instruction; |         return instruction; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| ################################ |  | ||||||
| # Overall | # Overall | ||||||
| ################################ | ################################ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti