Skip to content

MYFACES-4660 5.0 view destroying fix + keeping track of user connection in multiple tabs #707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,26 +161,12 @@ public static class ViewScope extends AbstractUserScope implements Serializable
@Inject private WebsocketScopeManager scopeManager;
@Inject private WebsocketSessionManager sessionManager;

/*
* If the view is discarded, destroy the websocket sessions associated with the view because they are no
* longer valid
*/
@PreDestroy
public void destroy()
{
// destroy parent scope ("session")
SessionScope sessionScope = (SessionScope) scopeManager.getScope(SCOPE_SESSION, false);
if (sessionScope != null)
{
for (String token : tokens.keySet())
{
sessionScope.destroyChannelToken(token);
}
}

channelTokens.clear();
tokens.clear();
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public class WebsocketSessionManager
{
private Lazy<ConcurrentLRUCache<String, Collection<Reference<Session>>>> sessionMap;

private Lazy<ConcurrentHashMap<UserChannelKey, Set<String>>> userMap;
// map is holding relation between user / channel and channel token with number of used connections
private Lazy<ConcurrentHashMap<UserChannelKey, ConcurrentMap<String, Integer>>> userMap;
private Queue<String> restoreQueue;

private static final CloseReason REASON_EXPIRED = new CloseReason(NORMAL_CLOSURE, "Expired");
Expand All @@ -83,7 +84,7 @@ public ConcurrentLRUCache<String, Collection<Reference<Session>>> getSessionMap(
return sessionMap.get();
}

public ConcurrentMap<UserChannelKey, Set<String>> getUserMap()
public ConcurrentMap<UserChannelKey, ConcurrentMap<String, Integer>> getUserMap()
{
return userMap.get();
}
Expand All @@ -101,8 +102,10 @@ public void registerUser(Serializable user, String channel, String channelToken)
{
UserChannelKey userChannelKey = new UserChannelKey(user, channel);

Set<String> channelTokenSet = getUserMap().computeIfAbsent(userChannelKey, k -> new HashSet<>(1));
channelTokenSet.add(channelToken);
ConcurrentMap<String, Integer> channelTokenMap = getUserMap().computeIfAbsent(userChannelKey,
k -> new ConcurrentHashMap<>(1));
// +1 for new connection of user
channelTokenMap.compute(channelToken, (key, value) -> value == null ? 1 : ++value);
}

public void deregisterUser(Serializable user, String channel, String channelToken)
Expand All @@ -111,11 +114,23 @@ public void deregisterUser(Serializable user, String channel, String channelToke

synchronized (getUserMap())
{
Set<String> channelTokenSet = getUserMap().get(userChannelKey);
if (channelTokenSet != null)
ConcurrentMap<String, Integer> channelTokenMap = getUserMap().get(userChannelKey);
if (channelTokenMap != null)
{
channelTokenSet.remove(channelToken);
if (channelTokenSet.isEmpty())
int value = channelTokenMap.get(channelToken);
if (value == 1)
{
// let's remove it from the map
channelTokenMap.remove(channelToken);
}
else
{
// -1 for connection of user
channelTokenMap.put(channelToken, --value);
}

// let's remove channelToken if no more user connections
if (channelTokenMap.isEmpty())
{
getUserMap().remove(userChannelKey);
}
Expand All @@ -126,7 +141,8 @@ public void deregisterUser(Serializable user, String channel, String channelToke
public Set<String> getChannelTokensForUser(Serializable user, String channel)
{
UserChannelKey userChannelKey = new UserChannelKey(user, channel);
return getUserMap().get(userChannelKey);
// just for compatibility, returning keySet
return getUserMap().get(userChannelKey).keySet();
}

public void initSessionMap(ExternalContext context)
Expand Down