private synchronized Set<JID> doStopOutbound()
    {
        Log.debug("(room: '{}'): Stopping federation with remote node that we joined (if any).", room.getJID());
        final Set<JID> result = new HashSet<>();

        if ( outboundJoinProgress == null ) {
            Log.trace("(room: '{}'): We are not in progress of joining a remote node. No need to abort such an effort.", room.getJID());
        } else {
            Log.trace("(room: '{}'): Aborting the ongoing effort of joining remote node '{}'.", room.getJID(), outboundJoinProgress.getPeer());
            abortOutboundJoinProgress();
        }

        if ( outboundJoin == null) {
            Log.trace("(room: '{}'): We did not join a remote node. No need to inform one that we have left.", room.getJID());
        } else {
            Log.trace("(room: '{}'): Informing joined node '{}' that we are leaving the FMUC node set.", room.getJID(), outboundJoin.getPeer());

            // Remove outboundJoin so that it's no longer send stanzas, and incoming stanzas are being treated as if from an unconnected entity.
            // We'll need to store some state to be able to process things later though.
            final JID peer = outboundJoin.getPeer();
            final Set<PendingCallback> pendingEcho = outboundJoin.pendingEcho;
            final Set<JID> theirOccupants = outboundJoin.occupants;

            outboundJoin = null;

            result.addAll( theirOccupants );

            // If we're waiting for an echo of a stanza that we've sent to this MUC, that now will no longer arrive. Make sure that we unblock all threads waiting for such an echo.
            if ( !pendingEcho.isEmpty() )
            {
                Log.trace("(room: '{}'): Completing {} callbacks that were waiting for an echo from peer '{}' that is being disconnected from.", room.getJID(), pendingEcho.size(), peer );
                for ( final PendingCallback pendingCallback : pendingEcho )
                {
                    try {
                        pendingCallback.complete(); // TODO maybe completeExceptionally?
                    } catch ( Exception e ) {
                        Log.warn("(room: '{}'): An exception occurred while completing callback pending echo from peer '{}' (that we're disconnecting from).", room.getJID(), peer, e);
                    }
                }
            }

            // Find all the occupants that the local node contributed to the FMUC set (those are the occupants that are
            // not joined through the remote, joined node). Note that these can include occupants that are on other nodes!
            final Set<MUCOccupant> occupantsToLeave = new HashSet<>( room.getOccupants() );
            occupantsToLeave.removeIf(occupant -> occupant.getReportedFmucAddress() != null && theirOccupants.contains( occupant.getReportedFmucAddress() ));
            Log.trace("(room: '{}'): Identified {} occupants that the local node contributed to the FMUC set.", room.getJID(), occupantsToLeave.size());

            // Inform the remote, joined node that these are now all gone.
            for ( final MUCOccupant occupantToLeave : occupantsToLeave ) {
                try
                {
                    Log.trace("(room: '{}'): Informing joined node '{}' that occupant '{}' left the MUC.", room.getJID(), peer, occupantToLeave.getUserAddress());

                    final Presence leave = occupantToLeave.getPresence(); // This returns a copy. Modifications will not be applied to the original.
                    leave.setType(Presence.Type.unavailable);
                    leave.setTo(new JID(peer.getNode(), peer.getDomain(), occupantToLeave.getNickname()));
                    leave.setFrom(occupantToLeave.getOccupantJID());

                    // Change (or add) presence information about roles and affiliations
                    Element childElement = leave.getChildElement("x", "http://jabber.org/protocol/muc#user");
                    if ( childElement == null )
                    {
                        childElement = leave.addChildElement("x", "http://jabber.org/protocol/muc#user");
                    }
                    Element item = childElement.element("item");
                    if ( item == null )
                    {
                        item = childElement.addElement("item");
                    }
                    item.addAttribute("role", "none");

                    final Presence enriched = enrichWithFMUCElement(leave, occupantToLeave.getReportedFmucAddress() != null ? occupantToLeave.getReportedFmucAddress() : occupantToLeave.getUserAddress());

                    XMPPServer.getInstance().getPacketRouter().route(enriched);
                } catch ( Exception e ) {
                    Log.warn("(room: '{}'): An exception occurred while informing joined node '{}' that occupant '{}' left the MUC.", room.getJID(), peer, occupantToLeave.getUserAddress(), e);
                }
            }
        }

        Log.trace("(room: '{}'): Finished stopping federation with remote node that we joined (if any).", room.getJID());
        return result;
    }
The workhorse implementation of stopping outbound federation, that returns a list of JIDs representing the
occupants from the remote, joined node that are no longer in the room as a result of stopping the federation. For
these occupants, a presence stanza should be shared with the remainder of the occupants. It is desirable to send
these stanzas only after all any other nodes that are to be disconnected have been disconnected (to prevent
sharing updates with other remote nodes that we might also be disconnecting from. This method does
therefor not send these stanzas. Instead, it returns the addresses that should be send stanzas. This allows
callers to aggregate addresses, and send the stanzas in one iteration.

The implementation in this method will inform the remote, joined node that the local, joining node has left, by
sending it presence stanzas for all occupants that the local, joining node has contributed to the FMUC set. After
the remote, joined, node has received the last stanza, it should conclude that the local, joining node has left
(it should respond with a 'left' message, although this implementation does not depend on that).

@return The JIDs of occupants on the remote, joined node that are no longer in the room.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/FMUCHandler.java