Index: lams_common/src/java/org/lamsfoundation/lams/learning/service/ILearnerService.java =================================================================== diff -u -r20aa6cbca9fc96d341080e6ad39f82593443f792 -r0bcfaf4ee0b1c28201c25f2cc418b883dd971afd --- lams_common/src/java/org/lamsfoundation/lams/learning/service/ILearnerService.java (.../ILearnerService.java) (revision 20aa6cbca9fc96d341080e6ad39f82593443f792) +++ lams_common/src/java/org/lamsfoundation/lams/learning/service/ILearnerService.java (.../ILearnerService.java) (revision 0bcfaf4ee0b1c28201c25f2cc418b883dd971afd) @@ -43,6 +43,8 @@ * All Learner service methods that are available within the core. */ public interface ILearnerService { + // how often Command Websocket Server checks for new commands + public static final long COMMAND_WEBSOCKET_CHECK_INTERVAL = 5000; /** * Gets the lesson object for the given key. @@ -180,6 +182,4 @@ void createCommandForLearner(Long lessonId, String userName, String jsonCommand); void createCommandForLearners(Long toolContentId, Collection userIds, String jsonCommand); - - boolean triggerCommandCheckAndSend(); } \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java =================================================================== diff -u -r673e64304c12d78aa1b4ba819a39ae14f394ca42 -r0bcfaf4ee0b1c28201c25f2cc418b883dd971afd --- lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java (.../CommandWebsocketServer.java) (revision 673e64304c12d78aa1b4ba819a39ae14f394ca42) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java (.../CommandWebsocketServer.java) (revision 0bcfaf4ee0b1c28201c25f2cc418b883dd971afd) @@ -18,6 +18,7 @@ import org.apache.log4j.Logger; import org.lamsfoundation.lams.learning.command.model.Command; import org.lamsfoundation.lams.learning.service.ILearnerFullService; +import org.lamsfoundation.lams.learning.service.ILearnerService; import org.lamsfoundation.lams.util.hibernate.HibernateSessionManager; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; @@ -39,8 +40,6 @@ */ private static class SendWorker extends Thread { private boolean stopFlag = false; - // how often the thread runs - private static final long CHECK_INTERVAL = 5000; // mapping lessonId -> timestamp when the check was last performed, so the thread does not run too often private final Map lastSendTimes = new TreeMap<>(); @@ -50,8 +49,32 @@ try { // websocket communication bypasses standard HTTP filters, so Hibernate session needs to be initialised manually HibernateSessionManager.openSession(); - checkAndSend(); - Thread.sleep(SendWorker.CHECK_INTERVAL); + + Iterator>> entryIterator = CommandWebsocketServer.websockets + .entrySet().iterator(); + + Entry> entry = null; + // go through lessons and update registered learners with messages + do { + entry = entryIterator.hasNext() ? entryIterator.next() : null; + if (entry != null) { + Long lessonId = entry.getKey(); + Long lastSendTime = lastSendTimes.get(lessonId); + if ((lastSendTime == null) || ((System.currentTimeMillis() + - lastSendTime) >= ILearnerService.COMMAND_WEBSOCKET_CHECK_INTERVAL)) { + send(lessonId); + } + + // if all learners left the lesson, remove the obsolete mapping + Map lessonWebsockets = entry.getValue(); + if (lessonWebsockets.isEmpty()) { + entryIterator.remove(); + lastSendTimes.remove(lessonId); + } + } + } while (entry != null); + + Thread.sleep(ILearnerService.COMMAND_WEBSOCKET_CHECK_INTERVAL); } catch (IllegalStateException e) { // do nothing as server is probably shutting down and we could not obtain Hibernate session } catch (Exception e) { @@ -60,7 +83,7 @@ } finally { try { HibernateSessionManager.closeSession(); - Thread.sleep(SendWorker.CHECK_INTERVAL); + Thread.sleep(ILearnerService.COMMAND_WEBSOCKET_CHECK_INTERVAL); } catch (IllegalStateException | InterruptedException e) { stopFlag = true; log.warn("Stopping Command Websocket worker thread"); @@ -69,44 +92,13 @@ } } - public boolean checkAndSend() throws IOException { - boolean sentAnything = false; - - Iterator>> entryIterator = CommandWebsocketServer.websockets.entrySet() - .iterator(); - - Entry> entry = null; - // go through lessons and update registered learners with messages - do { - entry = entryIterator.hasNext() ? entryIterator.next() : null; - if (entry != null) { - Long lessonId = entry.getKey(); - Long lastSendTime = lastSendTimes.get(lessonId); - if ((lastSendTime == null) - || ((System.currentTimeMillis() - lastSendTime) >= SendWorker.CHECK_INTERVAL)) { - sentAnything |= send(lessonId); - } - - // if all learners left the lesson, remove the obsolete mapping - Map lessonWebsockets = entry.getValue(); - if (lessonWebsockets.isEmpty()) { - entryIterator.remove(); - lastSendTimes.remove(lessonId); - } - } - } while (entry != null); - - return sentAnything; - } - /** * Feeds opened websockets with commands. */ - private boolean send(Long lessonId) throws IOException { - boolean sentAnything = false; + private void send(Long lessonId) throws IOException { Long lastSendTime = lastSendTimes.get(lessonId); if (lastSendTime == null) { - lastSendTime = System.currentTimeMillis() - CHECK_INTERVAL; + lastSendTime = System.currentTimeMillis() - ILearnerService.COMMAND_WEBSOCKET_CHECK_INTERVAL; } lastSendTimes.put(lessonId, System.currentTimeMillis()); @@ -117,10 +109,8 @@ Session websocket = lessonWebsockets.get(command.getUserName()); if (websocket != null && websocket.isOpen()) { websocket.getBasicRemote().sendText(command.getCommandText()); - sentAnything = true; } } - return sentAnything; } } @@ -169,24 +159,6 @@ lessonWebsockets.remove(login); } - /** - * Manually trigger sending all waiting commands. - * This methods has to be run in a transactional environment, - * for example is a service method! - * Otherwise manually open and close Hibernate session, - * same as in {@link SendWorker#run()} - */ - public static boolean triggerCheckAndSend() { - try { - return CommandWebsocketServer.sendWorker.checkAndSend(); - } catch (Exception e) { - CommandWebsocketServer.log.error("Error in Command Websocket Server", e); - } - // it is safer to assume that something was sent, - // so the calling method can act accordingly - return true; - } - private static ILearnerFullService getLearnerService() { if (learnerService == null) { WebApplicationContext ctx = WebApplicationContextUtils Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java =================================================================== diff -u -r20aa6cbca9fc96d341080e6ad39f82593443f792 -r0bcfaf4ee0b1c28201c25f2cc418b883dd971afd --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 20aa6cbca9fc96d341080e6ad39f82593443f792) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 0bcfaf4ee0b1c28201c25f2cc418b883dd971afd) @@ -39,7 +39,6 @@ import org.apache.log4j.Logger; import org.lamsfoundation.lams.gradebook.service.IGradebookService; -import org.lamsfoundation.lams.learning.command.CommandWebsocketServer; import org.lamsfoundation.lams.learning.command.dao.ICommandDAO; import org.lamsfoundation.lams.learning.command.model.Command; import org.lamsfoundation.lams.learning.kumalive.model.Kumalive; @@ -1404,6 +1403,7 @@ public void createCommandForLearner(Long lessonId, String userName, String jsonCommand) { Command command = new Command(lessonId, userName, jsonCommand); commandDAO.insert(command); + commandDAO.flush(); } /** @@ -1640,13 +1640,7 @@ } @Override - public boolean triggerCommandCheckAndSend() { - return CommandWebsocketServer.triggerCheckAndSend(); - } - - @Override public IActivityDAO getActivityDAO() { return activityDAO; } - } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r673e64304c12d78aa1b4ba819a39ae14f394ca42 -r0bcfaf4ee0b1c28201c25f2cc418b883dd971afd --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 673e64304c12d78aa1b4ba819a39ae14f394ca42) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 0bcfaf4ee0b1c28201c25f2cc418b883dd971afd) @@ -1319,19 +1319,13 @@ // command websocket on Page.tag understands this message jsonCommand.put("message", "autosave"); learnerService.createCommandForLearner(lessonId, learner.getLogin(), jsonCommand.toString()); - // manually trigger sending of messages - boolean sentAnything = learnerService.triggerCommandCheckAndSend(); - // if the learner does not have lesson window open, probably nothing was sent - if (sentAnything) { - try { - // allow autosave to finish - // a primitive, but way more simple solution than synchronous call to websocket and processing reply - Thread.sleep(2000); - } catch (InterruptedException e) { - log.warn( - "Monitoring service thread interrupted while giving Command Websocket Server time to send autosave message"); - } + try { + // make sure that Command Websocket server picked up the command + // and then some more so autosave completes + Thread.sleep(ILearnerService.COMMAND_WEBSOCKET_CHECK_INTERVAL + 2000); + } catch (InterruptedException e) { + log.warn("Monitoring service thread interrupted while giving time for learner to autosave"); } }