Index: lams_learning/src/java/org/lamsfoundation/lams/learning/presence/PresenceWebsocketServer.java =================================================================== diff -u -r5e319c889c9776238ecaf338e3b2d330b2186914 -ra2ea0c07d7661bc6af2037debc499a23a412ee1e --- lams_learning/src/java/org/lamsfoundation/lams/learning/presence/PresenceWebsocketServer.java (.../PresenceWebsocketServer.java) (revision 5e319c889c9776238ecaf338e3b2d330b2186914) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/presence/PresenceWebsocketServer.java (.../PresenceWebsocketServer.java) (revision a2ea0c07d7661bc6af2037debc499a23a412ee1e) @@ -1,16 +1,15 @@ package org.lamsfoundation.lams.learning.presence; import java.io.IOException; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import javax.websocket.CloseReason; import javax.websocket.CloseReason.CloseCodes; @@ -69,30 +68,27 @@ @Override public void run() { while (!stopFlag) { - // websocket communication bypasses standard HTTP filters, so Hibernate session needs to be initialised manually - HibernateSessionManager.openSession(); try { - // synchronize websockets as a new Learner entering chat could modify this collection - synchronized (PresenceWebsocketServer.websockets) { - Iterator>> entryIterator = PresenceWebsocketServer.websockets - .entrySet().iterator(); - // go through lessons and update registered learners with messages and roster - while (entryIterator.hasNext()) { - Entry> entry = entryIterator.next(); - Long lessonId = entry.getKey(); - Long lastSendTime = lastSendTimes.get(lessonId); - if ((lastSendTime == null) - || ((System.currentTimeMillis() - lastSendTime) >= SendWorker.CHECK_INTERVAL)) { - send(lessonId, null); - } + // websocket communication bypasses standard HTTP filters, so Hibernate session needs to be initialised manually + HibernateSessionManager.openSession(); + Iterator>> entryIterator = PresenceWebsocketServer.websockets.entrySet() + .iterator(); + // go through lessons and update registered learners with messages and roster + while (entryIterator.hasNext()) { + Entry> entry = entryIterator.next(); + Long lessonId = entry.getKey(); + Long lastSendTime = lastSendTimes.get(lessonId); + if ((lastSendTime == null) + || ((System.currentTimeMillis() - lastSendTime) >= SendWorker.CHECK_INTERVAL)) { + send(lessonId, null); + } - // if all learners left the chat, remove the obsolete mapping - Set lessonWebsockets = entry.getValue(); - if (lessonWebsockets.isEmpty()) { - entryIterator.remove(); - PresenceWebsocketServer.rosters.remove(lessonId); - lastSendTimes.remove(lessonId); - } + // if all learners left the chat, remove the obsolete mapping + Set lessonWebsockets = entry.getValue(); + if (lessonWebsockets.isEmpty()) { + entryIterator.remove(); + PresenceWebsocketServer.rosters.remove(lessonId); + lastSendTimes.remove(lessonId); } } } catch (Exception e) { @@ -125,10 +121,9 @@ lastSendTimes.put(lessonId, System.currentTimeMillis()); } - Set lessonWebsockets = new HashSet(PresenceWebsocketServer.websockets.get(lessonId)); + Set lessonWebsockets = PresenceWebsocketServer.websockets.get(lessonId); Roster roster = PresenceWebsocketServer.rosters.get(lessonId); JSONArray rosterJSON = roster.getRosterJSON(); - // make a copy of the websocket collection so it does not get blocked while sending messages for (Websocket websocket : lessonWebsockets) { // if this run is meant only for one learner, skip the others if ((nickName != null) && !nickName.equals(websocket.nickName)) { @@ -216,9 +211,8 @@ private static IPresenceChatService presenceChatService; private static final SendWorker sendWorker = new SendWorker(); - private static final Map rosters = Collections.synchronizedMap(new TreeMap()); - private static final Map> websockets = Collections - .synchronizedMap(new TreeMap>()); + private static final Map rosters = new ConcurrentHashMap(); + private static final Map> websockets = new ConcurrentHashMap>(); static { // run the singleton thread @@ -233,7 +227,7 @@ Long lessonId = Long.valueOf(session.getRequestParameterMap().get(AttributeNames.PARAM_LESSON_ID).get(0)); Set sessionWebsockets = PresenceWebsocketServer.websockets.get(lessonId); if (sessionWebsockets == null) { - sessionWebsockets = Collections.synchronizedSet(new HashSet()); + sessionWebsockets = ConcurrentHashMap.newKeySet(); PresenceWebsocketServer.websockets.put(lessonId, sessionWebsockets); } Websocket websocket = new Websocket(session); @@ -270,14 +264,12 @@ public void unregisterUser(Session session, CloseReason reason) { Long lessonId = Long.valueOf(session.getRequestParameterMap().get(AttributeNames.PARAM_LESSON_ID).get(0)); Set lessonWebsockets = PresenceWebsocketServer.websockets.get(lessonId); - synchronized (lessonWebsockets) { - Iterator websocketIterator = lessonWebsockets.iterator(); - while (websocketIterator.hasNext()) { - Websocket websocket = websocketIterator.next(); - if (websocket.session.equals(session)) { - websocketIterator.remove(); - break; - } + Iterator websocketIterator = lessonWebsockets.iterator(); + while (websocketIterator.hasNext()) { + Websocket websocket = websocketIterator.next(); + if (websocket.session.equals(session)) { + websocketIterator.remove(); + break; } } @@ -302,6 +294,11 @@ if (StringUtils.isBlank(input)) { return; } + if (input.equalsIgnoreCase("ping")) { + // just a ping every few minutes + return; + } + JSONObject requestJSON = new JSONObject(input); switch (requestJSON.getString("type")) { case "message": @@ -411,10 +408,8 @@ } // there can be few websockets (browser windows) for a single learner Set activeNicknames = new TreeSet(); - synchronized (lessonWebsockets) { - for (Websocket websocket : lessonWebsockets) { - activeNicknames.add(websocket.nickName); - } + for (Websocket websocket : lessonWebsockets) { + activeNicknames.add(websocket.nickName); } return activeNicknames.size(); }