@Override
        public void run()
        {
            final Instant start = Instant.now();
            Log.debug("Starting new cache consistency check.");

            final RoutingTableImpl routingTable = (RoutingTableImpl) XMPPServer.getInstance().getRoutingTable();
            final SessionManager sessionManager = XMPPServer.getInstance().getSessionManager();
            final MultiUserChatManager multiUserChatManager = XMPPServer.getInstance().getMultiUserChatManager();

            final Set<String> offenders = new HashSet<>();

            final Collection<String> serverRoutesFailures = routingTable.clusteringStateConsistencyReportForServerRoutes().get("fail");
            if (serverRoutesFailures != null && !serverRoutesFailures.isEmpty()) {
                offenders.add(RoutingTableImpl.S2S_CACHE_NAME);
            }

            final Collection<String> componentRoutesFailures = routingTable.clusteringStateConsistencyReportForComponentRoutes().get("fail");
            if (componentRoutesFailures != null && !componentRoutesFailures.isEmpty()) {
                offenders.add(RoutingTableImpl.COMPONENT_CACHE_NAME);
            }

            final Collection<String> clientRoutesFailures = routingTable.clusteringStateConsistencyReportForClientRoutes().get("fail");
            if (clientRoutesFailures != null && !clientRoutesFailures.isEmpty()) {
                // This check operates on multiple caches.
                offenders.add(RoutingTableImpl.C2S_CACHE_NAME);
            }

            final Collection<String> usersSessionsFailures = routingTable.clusteringStateConsistencyReportForUsersSessions().get("fail");
            if (usersSessionsFailures != null && !usersSessionsFailures.isEmpty()) {
                offenders.add(RoutingTableImpl.C2S_SESSION_NAME);
            }

            final Collection<String> incomingServerSessionsFailures = sessionManager.clusteringStateConsistencyReportForIncomingServerSessionInfos().get("fail");
            if (incomingServerSessionsFailures != null && !incomingServerSessionsFailures.isEmpty()) {
                offenders.add(SessionManager.ISS_CACHE_NAME);
            }

            final Collection<String> sessionInfosFailures = sessionManager.clusteringStateConsistencyReportForSessionInfos().get("fail");
            if (sessionInfosFailures != null && !sessionInfosFailures.isEmpty()) {
                offenders.add(SessionManager.C2S_INFO_CACHE_NAME);
            }

            final List<Multimap<String, String>> mucReportsList = multiUserChatManager.clusteringStateConsistencyReportForMucRoomsAndOccupant();
            final Collection<String> mucOccupantFailures = new ArrayList<>();
            for (Multimap<String, String> mucReport : mucReportsList) {
                mucReport.get("fail").addAll(mucOccupantFailures);
            }
            if (!mucOccupantFailures.isEmpty()) {
                offenders.add("MUC Service");
            }

            if (offenders.isEmpty()) {
                Log.info("Cache consistency check completed in {}. No issues found.", Duration.between(start, Instant.now()));
            } else {
                Log.warn("Cache consistency check completed in {}. Detected issues in: {}", Duration.between(start, Instant.now()), String.join(", ", offenders));
                XMPPServer.getInstance().sendMessageToAdmins("Cache inconsistencies were detected. This can cause bugs, especially when running in a cluster. " +
                    "If this problem persists, all Openfire instances in the cluster need to be restarted at the same time. Affected cache(s): " + String.join(", ", offenders));
            }
        }
Caches, especially clustered caches, have various supporting data structures that allow them to work properly in
certain conditions. The content of these caches and their supporting data structures are expected to be in a certain
consistent state. This monitor periodically performs a check if this is true.

Note that validation of consistency is a resource intensive process. Enabling the monitor might have severe effects
on the availability and performance of the Openfire domain.

@author Guus der Kinderen, guus.der.kinderen@gmail.com
xmppserver/src/main/java/org/jivesoftware/util/cache/ConsistencyMonitor.java