Search in sources :

Example 16 with NodeID

use of org.jivesoftware.openfire.cluster.NodeID 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;
}
Also used : java.util(java.util) ClusterManager(org.jivesoftware.openfire.cluster.ClusterManager) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) MUCRoom(org.jivesoftware.openfire.muc.MUCRoom) Multimap(com.google.common.collect.Multimap) StreamID(org.jivesoftware.openfire.StreamID) JID(org.xmpp.packet.JID) Collectors(java.util.stream.Collectors) org.jivesoftware.openfire.session(org.jivesoftware.openfire.session) ClientRoute(org.jivesoftware.openfire.spi.ClientRoute) ConcurrentMap(java.util.concurrent.ConcurrentMap) OccupantManager(org.jivesoftware.openfire.muc.spi.OccupantManager) ClusterNodeInfo(org.jivesoftware.openfire.cluster.ClusterNodeInfo) MUCRole(org.jivesoftware.openfire.muc.MUCRole) HashMultimap(com.google.common.collect.HashMultimap) NodeID(org.jivesoftware.openfire.cluster.NodeID) XMPPServer(org.jivesoftware.openfire.XMPPServer) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) Nonnull(javax.annotation.Nonnull) CollectionUtils(org.jivesoftware.util.CollectionUtils) JID(org.xmpp.packet.JID) NodeID(org.jivesoftware.openfire.cluster.NodeID) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 17 with NodeID

use of org.jivesoftware.openfire.cluster.NodeID 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;
}
Also used : java.util(java.util) ClusterManager(org.jivesoftware.openfire.cluster.ClusterManager) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) MUCRoom(org.jivesoftware.openfire.muc.MUCRoom) Multimap(com.google.common.collect.Multimap) StreamID(org.jivesoftware.openfire.StreamID) JID(org.xmpp.packet.JID) Collectors(java.util.stream.Collectors) org.jivesoftware.openfire.session(org.jivesoftware.openfire.session) ClientRoute(org.jivesoftware.openfire.spi.ClientRoute) ConcurrentMap(java.util.concurrent.ConcurrentMap) OccupantManager(org.jivesoftware.openfire.muc.spi.OccupantManager) ClusterNodeInfo(org.jivesoftware.openfire.cluster.ClusterNodeInfo) MUCRole(org.jivesoftware.openfire.muc.MUCRole) HashMultimap(com.google.common.collect.HashMultimap) NodeID(org.jivesoftware.openfire.cluster.NodeID) XMPPServer(org.jivesoftware.openfire.XMPPServer) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) Nonnull(javax.annotation.Nonnull) CollectionUtils(org.jivesoftware.util.CollectionUtils) StreamID(org.jivesoftware.openfire.StreamID) NodeID(org.jivesoftware.openfire.cluster.NodeID) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentMap(java.util.concurrent.ConcurrentMap)

Example 18 with NodeID

use of org.jivesoftware.openfire.cluster.NodeID in project Openfire by igniterealtime.

the class ReverseLookupComputingCacheEntryListener method add.

private void add(K key, Set<NodeID> newNodesWithThisKey) {
    // Step 1 : update existing entries to the updated situation
    final Iterator<Map.Entry<NodeID, Set<K>>> iter = reverseCacheRepresentation.entrySet().iterator();
    while (iter.hasNext()) {
        final Map.Entry<NodeID, Set<K>> existingEntry = iter.next();
        final NodeID existingEntryNodeID = existingEntry.getKey();
        if (newNodesWithThisKey.contains(existingEntryNodeID)) {
            existingEntry.getValue().add(key);
        }
    }
    // Step 2 : add entries for node ids that were not in the reverse lookup before
    for (final NodeID nodeIdForWhichTheValueExists : newNodesWithThisKey) {
        if (!reverseCacheRepresentation.containsKey(nodeIdForWhichTheValueExists)) {
            reverseCacheRepresentation.computeIfAbsent(nodeIdForWhichTheValueExists, k -> new HashSet<>()).add(key);
        }
    }
}
Also used : Logger(org.slf4j.Logger) Iterator(java.util.Iterator) ClusteredCacheEntryListener(org.jivesoftware.openfire.cluster.ClusteredCacheEntryListener) LoggerFactory(org.slf4j.LoggerFactory) Set(java.util.Set) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) ConcurrentMap(java.util.concurrent.ConcurrentMap) HashSet(java.util.HashSet) NodeID(org.jivesoftware.openfire.cluster.NodeID) Map(java.util.Map) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) Set(java.util.Set) HashSet(java.util.HashSet) NodeID(org.jivesoftware.openfire.cluster.NodeID) ConcurrentMap(java.util.concurrent.ConcurrentMap) Map(java.util.Map) HashSet(java.util.HashSet)

Example 19 with NodeID

use of org.jivesoftware.openfire.cluster.NodeID in project Openfire by igniterealtime.

the class SessionManager method getConnectionMultiplexerSessions.

/**
 * Returns a collection with all the sessions originated from the connection manager
 * whose domain matches the specified domain. If there is no connection manager with
 * the specified domain then an empty list is going to be returned.
 *
 * @param domain the domain of the connection manager.
 * @return a collection with all the sessions originated from the connection manager
 *         whose domain matches the specified domain.
 */
public List<ConnectionMultiplexerSession> getConnectionMultiplexerSessions(String domain) {
    List<ConnectionMultiplexerSession> sessions = new ArrayList<>();
    // Add sessions of CMs connected to this JVM
    for (String address : localSessionManager.getConnnectionManagerSessions().keySet()) {
        JID jid = new JID(address);
        if (domain.equals(jid.getDomain())) {
            sessions.add(localSessionManager.getConnnectionManagerSessions().get(address));
        }
    }
    // Add sessions of CMs connected to other cluster nodes
    RemoteSessionLocator locator = server.getRemoteSessionLocator();
    if (locator != null) {
        for (Map.Entry<String, NodeID> entry : multiplexerSessionsCache.entrySet()) {
            if (!server.getNodeID().equals(entry.getValue())) {
                JID jid = new JID(entry.getKey());
                if (domain.equals(jid.getDomain())) {
                    sessions.add(locator.getConnectionMultiplexerSession(entry.getValue().toByteArray(), new JID(entry.getKey())));
                }
            }
        }
    }
    return sessions;
}
Also used : JID(org.xmpp.packet.JID) ArrayList(java.util.ArrayList) NodeID(org.jivesoftware.openfire.cluster.NodeID) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentMap(java.util.concurrent.ConcurrentMap)

Example 20 with NodeID

use of org.jivesoftware.openfire.cluster.NodeID in project Openfire by igniterealtime.

the class IQDiscoItemsHandler method leftCluster.

@Override
public void leftCluster(byte[] nodeID) {
    // senior cluster node to perform the cleanup.
    if (ClusterManager.isSeniorClusterMember()) {
        NodeID leftNode = NodeID.getInstance(nodeID);
        for (Map.Entry<String, ClusteredServerItem> entry : serverItems.entrySet()) {
            String jid = entry.getKey();
            Lock lock = serverItems.getLock(jid);
            lock.lock();
            try {
                ClusteredServerItem item = entry.getValue();
                if (item.nodes.remove(leftNode)) {
                    // Update the cache with latest info
                    if (item.nodes.isEmpty()) {
                        serverItems.remove(jid);
                    } else {
                        serverItems.put(jid, item);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
Also used : NodeID(org.jivesoftware.openfire.cluster.NodeID) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Lock(java.util.concurrent.locks.Lock)

Aggregations

NodeID (org.jivesoftware.openfire.cluster.NodeID)41 JID (org.xmpp.packet.JID)18 Lock (java.util.concurrent.locks.Lock)15 java.util (java.util)12 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)12 ConcurrentMap (java.util.concurrent.ConcurrentMap)12 Collectors (java.util.stream.Collectors)11 Nonnull (javax.annotation.Nonnull)10 XMPPServer (org.jivesoftware.openfire.XMPPServer)10 UnauthorizedException (org.jivesoftware.openfire.auth.UnauthorizedException)9 MUCRole (org.jivesoftware.openfire.muc.MUCRole)8 MUCRoom (org.jivesoftware.openfire.muc.MUCRoom)8 Logger (org.slf4j.Logger)8 LoggerFactory (org.slf4j.LoggerFactory)8 Nullable (javax.annotation.Nullable)7 ClusterManager (org.jivesoftware.openfire.cluster.ClusterManager)7 CacheFactory (org.jivesoftware.util.cache.CacheFactory)7 Map (java.util.Map)6 PacketException (org.jivesoftware.openfire.PacketException)6 DomainPair (org.jivesoftware.openfire.session.DomainPair)6