use of org.jivesoftware.openfire.cluster.ClusterNodeInfo in project Openfire by igniterealtime.
the class ClusteredCacheFactory method getClusterNodeInfo.
public ClusterNodeInfo getClusterNodeInfo(byte[] nodeID) {
if (cluster == null) {
return null;
}
ClusterNodeInfo result = null;
Member member = getMember(nodeID);
if (member != null) {
result = new HazelcastClusterNodeInfo(member, cluster.getClusterTime());
}
return result;
}
use of org.jivesoftware.openfire.cluster.ClusterNodeInfo in project Openfire by igniterealtime.
the class RemoteSession method doSynchronousClusterTask.
/**
* Invokes a task on the remote cluster member synchronously and returns the result of
* the remote operation.
*
* @param task the ClusterTask object to be invoked on a given cluster member.
* @return result of remote operation.
*/
protected Object doSynchronousClusterTask(ClusterTask task) {
ClusterNodeInfo info = CacheFactory.getClusterNodeInfo(nodeID);
Object result = null;
if (info == null && task instanceof RemoteSessionTask) {
// clean up invalid session
Session remoteSession = ((RemoteSessionTask) task).getSession();
if (remoteSession instanceof ClientSession) {
SessionManager.getInstance().removeSession(null, remoteSession.getAddress(), false, false);
}
} else {
result = (info == null) ? null : CacheFactory.doSynchronousClusterTask(task, nodeID);
}
return result;
}
use of org.jivesoftware.openfire.cluster.ClusterNodeInfo in project Openfire by igniterealtime.
the class ConsistencyChecks method generateReportForUserSessions.
public static Multimap<String, String> generateReportForUserSessions(@Nonnull final Cache<String, HashSet<String>> usersSessionsCache, @Nonnull final Cache<String, ClientRoute> usersCache, @Nonnull final Cache<String, ClientRoute> anonymousUsersCache) {
final Set<NodeID> clusterNodeIDs = ClusterManager.getNodesInfo().stream().map(ClusterNodeInfo::getNodeID).collect(Collectors.toSet());
// Take snapshots of all data structures at as much the same time as possible.
final ConcurrentMap<String, HashSet<String>> cache = new ConcurrentHashMap<>(usersSessionsCache);
final Set<String> usersCacheKeys = usersCache.keySet();
final Set<String> anonymousUsersCacheKeys = anonymousUsersCache.keySet();
final Set<String> userCacheKeysNotInSessionsCache = usersCacheKeys.stream().filter(fullJid -> {
HashSet<String> fullJids = cache.get(new JID(fullJid).toBareJID());
return fullJids == null || !fullJids.contains(fullJid);
}).collect(Collectors.toSet());
final Set<String> anonymousUserCacheKeysNotInSessionsCache = anonymousUsersCacheKeys.stream().filter(fullJid -> {
HashSet<String> fullJids = cache.get(new JID(fullJid).toBareJID());
return fullJids == null || !fullJids.contains(fullJid);
}).collect(Collectors.toSet());
final Set<String> sessionCacheItemsNotInUserCaches = cache.values().stream().flatMap(HashSet::stream).filter(fullJid -> !usersCacheKeys.contains(fullJid) && !anonymousUsersCacheKeys.contains(fullJid)).collect(Collectors.toSet());
final Set<String> duplicatesBetweenAnonAndNonAnonUsers = CollectionUtils.findDuplicates(usersCacheKeys, anonymousUsersCacheKeys);
// Generate report
final Multimap<String, String> result = HashMultimap.create();
result.put("info", String.format("The cache named %s is used to share data in the cluster, which contains %d session infos.", usersSessionsCache.getName(), cache.size()));
result.put("data", String.format("%s contains these entries (these are shared in the cluster):\n%s", usersSessionsCache.getName(), cache.entrySet().stream().map(e -> e.getKey() + " -> " + e.getValue()).sorted().collect(Collectors.joining("\n"))));
result.put("data", String.format("%s contains these entries (these are shared in the cluster):\n%s", usersCache.getName(), usersCacheKeys.stream().sorted().collect(Collectors.joining("\n"))));
result.put("data", String.format("%s contains these entries (these are shared in the cluster):\n%s", anonymousUsersCache.getName(), anonymousUsersCacheKeys.stream().sorted().collect(Collectors.joining("\n"))));
if (userCacheKeysNotInSessionsCache.isEmpty()) {
result.put("pass", "All user cache entries exist in the user sessions cache.");
} else {
result.put("fail", String.format("User sessions cache is missing entries that are present in the user cache. These %d entries are missing: %s", userCacheKeysNotInSessionsCache.size(), String.join(", ", userCacheKeysNotInSessionsCache)));
}
if (anonymousUserCacheKeysNotInSessionsCache.isEmpty()) {
result.put("pass", "All anonymous user cache entries exist in the user sessions cache.");
} else {
result.put("fail", String.format("User sessions cache is missing entries that are present in the anonymous user cache. These %d entries are missing: %s", anonymousUserCacheKeysNotInSessionsCache.size(), String.join(", ", anonymousUserCacheKeysNotInSessionsCache)));
}
if (sessionCacheItemsNotInUserCaches.isEmpty()) {
result.put("pass", "All user sessions cache entries exist in either the user cache or the anonymous user cache.");
} else {
result.put("fail", String.format("User cache and/or anonymous user cache is missing entries that are present in the user sessions cache. These %d entries are missing: %s", sessionCacheItemsNotInUserCaches.size(), String.join(", ", sessionCacheItemsNotInUserCaches)));
}
if (duplicatesBetweenAnonAndNonAnonUsers.isEmpty()) {
result.put("pass", "There are no duplicates between non-anonymous users cache and anonymous users cache.");
} else {
result.put("fail", String.format("There are users both present in non-anonymous users cache and anonymous users cache. These %d entries are duplicates: %s", duplicatesBetweenAnonAndNonAnonUsers.size(), String.join(", ", duplicatesBetweenAnonAndNonAnonUsers)));
}
return result;
}
use of org.jivesoftware.openfire.cluster.ClusterNodeInfo in project Openfire by igniterealtime.
the class ConsistencyChecks method generateReportForSessionManagerIncomingServerSessions.
/**
* Verifies that #incomingServerSessionsCache, #localIncomingServerSessions and #incomingServerSessionsByClusterNode
* of {@link org.jivesoftware.openfire.SessionManager} are in a consistent state.
* <p>
* Note that this operation can be costly in terms of resource usage. Use with caution in large / busy systems.
* <p>
* The returned multi-map can contain up to four keys: info, fail, pass, data. All entry values are a human readable
* description of a checked characteristic. When the state is consistent, no 'fail' entries will be returned.
*
* @param incomingServerSessionsCache The cache that is used to share data across cluster nodes
* @param localIncomingServerSessions The data structure that keeps track of what data was added to the cache by the local cluster node.
* @param incomingServerSessionsByClusterNode The data structure that keeps track of what data was added to the cache by the remote cluster nodes.
* @return A consistency state report.
*/
public static Multimap<String, String> generateReportForSessionManagerIncomingServerSessions(@Nonnull final Cache<StreamID, IncomingServerSessionInfo> incomingServerSessionsCache, @Nonnull final Collection<LocalIncomingServerSession> localIncomingServerSessions, @Nonnull final Map<NodeID, Set<StreamID>> incomingServerSessionsByClusterNode) {
final Set<NodeID> clusterNodeIDs = ClusterManager.getNodesInfo().stream().map(ClusterNodeInfo::getNodeID).collect(Collectors.toSet());
// Take snapshots of all data structures at as much the same time as possible.
final ConcurrentMap<StreamID, IncomingServerSessionInfo> cache = new ConcurrentHashMap<>(incomingServerSessionsCache);
final List<StreamID> localIncomingServerSessionsStreamIDs = localIncomingServerSessions.stream().map(LocalIncomingServerSession::getStreamID).collect(Collectors.toList());
final List<StreamID> remoteIncomingServerSessions = incomingServerSessionsByClusterNode.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
final List<String> remoteIncomingServerSessionsWithNodeId = new ArrayList<>();
for (Map.Entry<NodeID, Set<StreamID>> entry : incomingServerSessionsByClusterNode.entrySet()) {
for (StreamID item : entry.getValue()) {
remoteIncomingServerSessionsWithNodeId.add(item + " (" + entry.getKey() + ")");
}
}
// Duplicates detection
final Set<StreamID> localIncomingServerSessionsDuplicates = CollectionUtils.findDuplicates(localIncomingServerSessionsStreamIDs);
final Set<StreamID> remoteIncomingServerSessionsDuplicates = CollectionUtils.findDuplicates(remoteIncomingServerSessions);
final Set<StreamID> incomingServerSessionsBothLocalAndRemote = CollectionUtils.findDuplicates(localIncomingServerSessionsStreamIDs, remoteIncomingServerSessions);
// Detection of other inconsistencies
final Set<StreamID> nonLocallyStoredCachedIncomingServerSessions = cache.keySet().stream().filter(v -> !localIncomingServerSessionsStreamIDs.contains(v)).filter(v -> !remoteIncomingServerSessions.contains(v)).collect(Collectors.toSet());
final Set<StreamID> nonCachedLocalIncomingServerSessions = localIncomingServerSessionsStreamIDs.stream().filter(v -> !cache.containsKey(v)).collect(Collectors.toSet());
final Set<StreamID> nonCachedRemoteIncomingServerSessions = remoteIncomingServerSessions.stream().filter(v -> !cache.containsKey(v)).collect(Collectors.toSet());
// Generate report
final Multimap<String, String> result = HashMultimap.create();
result.put("info", String.format("The cache named %s is used to share data in the cluster, which contains %d incoming server sessions.", incomingServerSessionsCache.getName(), cache.size()));
result.put("info", String.format("SessionManager's TODO response is used to track 'local' data to be restored after a cache switch-over. It tracks %d incoming server sessions.", localIncomingServerSessionsStreamIDs.size()));
result.put("info", String.format("The field incomingServerSessionsByClusterNode is used to track data in the cache from every other cluster node. It contains %d routes for %d cluster nodes.", incomingServerSessionsByClusterNode.values().stream().reduce(0, (subtotal, values) -> subtotal + values.size(), Integer::sum), incomingServerSessionsByClusterNode.size()));
result.put("data", String.format("%s contains these entries (these are shared in the cluster):\n%s", incomingServerSessionsCache.getName(), cache.keySet().stream().map(StreamID::getID).collect(Collectors.joining("\n"))));
result.put("data", String.format("SessionManager's localSessionManager contains these entries (these represent 'local' data):\n%s", localIncomingServerSessionsStreamIDs.stream().map(StreamID::getID).collect(Collectors.joining("\n"))));
result.put("data", String.format("incomingServerSessionsByClusterNode contains these entries (these represent 'remote' data):\n%s", String.join("\n", remoteIncomingServerSessionsWithNodeId)));
if (!incomingServerSessionsByClusterNode.containsKey(XMPPServer.getInstance().getNodeID())) {
result.put("pass", "incomingServerSessionsByClusterNode does not track data for the local cluster node.");
} else {
result.put("fail", "incomingServerSessionsByClusterNode tracks data for the local cluster node.");
}
if (clusterNodeIDs.containsAll(incomingServerSessionsByClusterNode.keySet())) {
result.put("pass", "incomingServerSessionsByClusterNode tracks data for cluster nodes that are recognized in the cluster.");
} else {
result.put("fail", String.format("incomingServerSessionsByClusterNode tracks data for cluster nodes that are not recognized. All cluster nodeIDs as recognized: %s All cluster nodeIDs for which data is tracked: %s.", clusterNodeIDs.stream().map(NodeID::toString).collect(Collectors.joining(", ")), incomingServerSessionsByClusterNode.keySet().stream().map(NodeID::toString).collect(Collectors.joining(", "))));
}
if (localIncomingServerSessionsDuplicates.isEmpty()) {
result.put("pass", "There is no overlap in local incoming server sessions (they are all unique values).");
} else {
result.put("fail", String.format("There is overlap in local incoming server sessions (they are not all unique values). These %d values are duplicated: %s", localIncomingServerSessionsDuplicates.size(), localIncomingServerSessionsDuplicates.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
if (remoteIncomingServerSessionsDuplicates.isEmpty()) {
result.put("pass", "There is no overlap in incomingServerSessionsByClusterNode (they are all unique values).");
} else {
result.put("fail", String.format("There is overlap in incomingServerSessionsByClusterNode (they are not all unique values). These %d values are duplicated: %s", remoteIncomingServerSessionsDuplicates.size(), remoteIncomingServerSessionsDuplicates.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
if (incomingServerSessionsBothLocalAndRemote.isEmpty()) {
result.put("pass", "There are no elements that are both 'remote' (in incomingServerSessionsByClusterNode) as well as 'local' (in SessionManager's localSessionManager).");
} else {
result.put("fail", String.format("There are %d elements that are both 'remote' (in incomingServerSessionsByClusterNode) as well as 'local' (in SessionManager's localSessionManager): %s", incomingServerSessionsBothLocalAndRemote.size(), incomingServerSessionsBothLocalAndRemote.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
if (nonCachedLocalIncomingServerSessions.isEmpty()) {
result.put("pass", String.format("All elements in SessionManager's localSessionManager exist in %s.", incomingServerSessionsCache.getName()));
} else {
result.put("fail", String.format("Not all elements in SessionManager's localSessionManager exist in %s. These %d entries do not: %s", incomingServerSessionsCache.getName(), nonCachedLocalIncomingServerSessions.size(), nonCachedLocalIncomingServerSessions.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
if (nonCachedRemoteIncomingServerSessions.isEmpty()) {
result.put("pass", String.format("All elements inincomingServerSessionsByClusterNode exist in %s.", incomingServerSessionsCache.getName()));
} else {
result.put("fail", String.format("Not all elements in incomingServerSessionsByClusterNode exist in %s. These %d entries do not: %s", incomingServerSessionsCache.getName(), nonCachedRemoteIncomingServerSessions.size(), nonCachedRemoteIncomingServerSessions.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
if (nonLocallyStoredCachedIncomingServerSessions.isEmpty()) {
result.put("pass", String.format("All cache entries of %s exist in incomingServerSessionsByClusterNode and/or SessionManager's localSessionManager.", incomingServerSessionsCache.getName()));
} else {
result.put("fail", String.format("Not all cache entries of %s exist in incomingServerSessionsByClusterNode and/or SessionManager's localSessionManager. These %d entries do not: %s", incomingServerSessionsCache.getName(), nonLocallyStoredCachedIncomingServerSessions.size(), nonLocallyStoredCachedIncomingServerSessions.stream().map(StreamID::getID).collect(Collectors.joining(", "))));
}
return result;
}
use of org.jivesoftware.openfire.cluster.ClusterNodeInfo in project Openfire by igniterealtime.
the class RemoteSession method doSynchronousClusterTask.
/**
* Invokes a task on the remote cluster member synchronously and returns the result of
* the remote operation.
*
* @param task the ClusterTask object to be invoked on a given cluster member.
* @return result of remote operation.
*/
protected Object doSynchronousClusterTask(ClusterTask<Object> task) {
ClusterNodeInfo info = CacheFactory.getClusterNodeInfo(nodeID);
Object result = null;
if (info == null && task instanceof RemoteSessionTask) {
// clean up invalid session
Session remoteSession = ((RemoteSessionTask) task).getSession();
if (remoteSession instanceof ClientSession) {
SessionManager.getInstance().removeSession(null, remoteSession.getAddress(), false, false);
}
} else {
result = (info == null) ? null : CacheFactory.doSynchronousClusterTask(task, nodeID);
}
return result;
}
Aggregations