Skip to content

Commit

Permalink
Handle race condition on accessing session info
Browse files Browse the repository at this point in the history
  • Loading branch information
slominskir committed Feb 14, 2024
1 parent 6ba2751 commit c4b7c53
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/main/java/org/jlab/epics2web/controller/WebConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.Session;
import org.jlab.epics2web.Application;
import org.jlab.epics2web.epics.ChannelMonitor;
import org.jlab.epics2web.websocket.SessionInfo;
import org.jlab.epics2web.websocket.WebSocketSessionManager;
import org.jlab.epics2web.epics.ChannelManager;

Expand Down Expand Up @@ -40,7 +40,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)


Map<String, ChannelMonitor> monitorMap = channelManager.getMonitorMap();
Map<Session, Set<String>> clientMap = sessionManager.getClientMap();
Map<SessionInfo, Set<String>> clientMap = sessionManager.getClientMap();

request.setAttribute("monitorMap", monitorMap);
request.setAttribute("clientMap", clientMap);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jlab/epics2web/epics/ChannelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public Map<String, ChannelMonitor> getMonitorMap() {
*
* @return The listener to PVs map
*/
public Map<PvListener, Set<String>> getClientMap() {
public Map<PvListener, Set<String>> getListenerMap() {
return Collections.unmodifiableMap(clientMap);
}
}
43 changes: 43 additions & 0 deletions src/main/java/org/jlab/epics2web/websocket/SessionInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.jlab.epics2web.websocket;

/**
* This class provides a snapshot of websocket information that will not throw Exceptions if you try to interrogate it
* after the session happens to have closed. There is a race condition if you hand a list of javax.websocket.Session
* objects to a debug console for example as if the session happens to close between the time you return it and the
* time the debug console calls the userProperties method for example you get an exception.
*/
public class SessionInfo {
private String id;
private String ip;
private String name;
private String agent;
private long droppedMessageCount;

public SessionInfo(String id, String ip, String name, String agent, long droppedMessageCount) {
this.id = id;
this.ip = ip;
this.name = name;
this.agent = agent;
this.droppedMessageCount = droppedMessageCount;
}

public String getId() {
return id;
}

public String getIp() {
return ip;
}

public String getName() {
return name;
}

public String getAgent() {
return agent;
}

public long getDroppedMessageCount() {
return droppedMessageCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,33 @@ public void removePvs(Session session, Set<String> pvSet) {
*
* @return The map
*/
public Map<Session, Set<String>> getClientMap() {
Map<PvListener, Set<String>> pvMap = Application.channelManager.getClientMap();
Map<Session, Set<String>> clientMap = new HashMap<>();
public Map<SessionInfo, Set<String>> getClientMap() {
Map<PvListener, Set<String>> pvMap = Application.channelManager.getListenerMap();
Map<SessionInfo, Set<String>> clientMap = new HashMap<>();

for (Session session : listenerMap.keySet()) {
WebSocketSessionMonitor listener = listenerMap.get(session);
Set<String> pvSet = pvMap.get(listener);
clientMap.put(session, pvSet);

if(session.isOpen()) {
String id = null;

try {
id = session.getId();
String ip = (String)session.getUserProperties().get("ip");
String name = (String)session.getUserProperties().get("name");
String agent = (String)session.getUserProperties().get("agent");
AtomicLong droppedMessageCount = (AtomicLong)session.getUserProperties().get("droppedMessageCount");

SessionInfo info = new SessionInfo(id, ip, name, agent, droppedMessageCount.get());

clientMap.put(info, pvSet);
} catch(Exception e) {
// Even id may be null if closed before getId() called. Oh well.
LOGGER.log(Level.FINEST, "Session '{0}' closed while preparing info report", id);
// Ignore
}
}
}

return clientMap;
Expand Down
8 changes: 4 additions & 4 deletions src/main/webapp/WEB-INF/views/console.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@
<c:forEach items="${clientMap}" var="client">
<tr>
<td><c:out value="${client.key.id}"/></td>
<td><c:out value="${client.key.userProperties.ip eq null ? client.key.remoteAddr : client.key.userProperties.ip}"/></td>
<td><c:out value="${client.key.userProperties.agent}"/></td>
<td><c:out value="${client.key.userProperties.name}"/></td>
<td><c:out value="${client.key.ip}"/></td>
<td><c:out value="${client.key.agent}"/></td>
<td><c:out value="${client.key.name}"/></td>
<td>(${client.value == null ? '0' : client.value.size()}) <c:out value="${client.value}"/></td>
<td><fmt:formatNumber value="${client.key.userProperties.droppedMessageCount}"/></td>
<td><fmt:formatNumber value="${client.key.droppedMessageCount}"/></td>
</tr>
</c:forEach>
</tbody>
Expand Down

0 comments on commit c4b7c53

Please sign in to comment.