private void makeOccupantsOnConnectedClusterNodeJoin(@Nullable final Set<OccupantManager.Occupant> occupantsOnConnectedNodes, @Nonnull NodeID remoteNodeID)
    {
        Log.debug("Going to send 'join' presence stanzas on the local cluster node for {} occupant(s) of cluster node {} that is now part of our cluster.", occupantsOnConnectedNodes == null || occupantsOnConnectedNodes.isEmpty() ? 0 : occupantsOnConnectedNodes.size(), remoteNodeID);
        if (occupantsOnConnectedNodes == null || occupantsOnConnectedNodes.isEmpty()) {
            return;
        }

        if (!MUCRoom.JOIN_PRESENCE_ENABLE.getValue()) {
            Log.trace("Skipping, as presences are disabled by configuration.");
            return;
        }

        // Find all occupants that were not already on any other node in the cluster. This intends to prevent sending 'join'
        // presences for occupants that are in the same room, using the same nickname, but using a client that is
        // connected to a cluster node that already was in the cluster.
        final Set<OccupantManager.Occupant> toAdd = occupantsOnConnectedNodes.stream()
            .filter(occupant -> !occupantManager.exists(occupant, remoteNodeID))
            .collect(Collectors.toSet());
        Log.trace("After accounting for occupants already in the room by virtue of having another connected device using the same nickname, {} presence stanza(s) are to be sent.", toAdd.size());

        // For each, broadcast a 'join' presence in the room(s).
        for (final OccupantManager.Occupant occupant : toAdd)
        {
            Log.trace("Preparing to send 'join' stanza for occupant '{}' (using nickname '{}') of room '{}'", occupant.getRealJID(), occupant.getNickname(), occupant.getRoomName());

            final MUCRoom chatRoom = getChatRoom(occupant.roomName);
            if (chatRoom == null) {
                Log.info("User {} seems to be an occupant (using nickname '{}') of a non-existent room named '{}' on newly connected cluster node(s).", occupant.realJID, occupant.nickname, occupant.roomName);
                continue;
            }

            // At this stage, the occupant information must already be available through the clustered cache.
            final MUCOccupant occupantData = chatRoom.getOccupantByFullJID(occupant.getRealJID());
            if (occupantData == null) {
                Log.warn("A remote cluster node ({}) tells us that user {} is supposed to be an occupant (using nickname '{}') of a room named '{}' but the data in the cluster cache does not indicate that this is true.", remoteNodeID, occupant.realJID, occupant.nickname, occupant.roomName);
                continue;
            }

            if (!chatRoom.canBroadcastPresence(occupantData.getRole())) {
                Log.trace("Skipping join stanza, as room is configured to not broadcast presence for role {}", occupantData.getRole());
                continue;
            }

            // To prevent each (remaining) cluster node from broadcasting the same presence to all occupants of all cluster nodes,
            // this broadcasts only to occupants on the local node.
            // This also broadcasts to all federated occupants, as we've not yet identified a reliable way to make at least one cluster node inform these occupants. TODO: find such a reliable way, to prevent each (remaining) node sending presence updates.
            final Set<OccupantManager.Occupant> recipients = occupantManager.occupantsForRoomByNode(occupant.roomName, XMPPServer.getInstance().getNodeID(), true);
            for (OccupantManager.Occupant recipient : recipients) {
                Log.trace("Preparing stanza for recipient {} (nickname: {})", recipient.getRealJID(), recipient.getNickname());
                try {
                    // Note that we cannot use org.jivesoftware.openfire.muc.MUCRoom.broadcastPresence() as this would attempt to
                    // broadcast to the user that is 'joining' to all users. We only want to broadcast locally here.

                    // We _need_ to go through the MUCOccupant for sending this stanza, as that has some additional logic (eg: FMUC).
                    final MUCOccupant recipientOccupantData = chatRoom.getOccupantByFullJID(recipient.getRealJID());
                    if (recipientOccupantData != null) {
                        recipientOccupantData.send(occupantData.getPresence());
                    } else {
                        Log.warn("Unable to find MUCOccupant for recipient '{}' in room {} while broadcasting 'join' presence for occupants on joining cluster node {}.", recipient.getRealJID(), chatRoom.getJID(), remoteNodeID);
                        XMPPServer.getInstance().getPacketRouter().route(occupantData.getPresence()); // This is a partial fix that will _probably_ work if FMUC is not used. Better than nothing? (although an argument for failing-fast can be made).
                    }
                } catch (Exception e) {
                    Log.warn("A problem occurred while notifying local occupant that user '{}' joined room '{}' as a result of a cluster node {} joining the cluster.", occupant.nickname, occupant.roomName, remoteNodeID, e);
                }
            }
        }
        Log.debug("Finished sending 'join' presence stanzas on the local cluster node.");
    }
Sends presence 'join' stanzas on behalf of chatroom occupants that are now connected to the cluster (by virtue
of a cluster node joining the cluster) to other room occupants are local to this cluster node.

Note that this method does not change state (in either the clustered cache, or local equivalents): it only sends stanzas.

@param occupantsOnConnectedNodes The occupants for which to send stanzas.
@param remoteNodeID The cluster node ID of the server that joined the cluster.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java