private void makeRemoteOccupantLeaveRoom( @Nonnull final Presence presence )
    {
        // FIXME: better input validation / better problem handling when remote node sends crappy data!
        final Element fmuc = presence.getElement().element(FMUC);
        if ( fmuc == null ) {
            throw new IllegalArgumentException( "Argument 'presence' should be an FMUC presence, but it does not appear to be: it is missing the FMUC child element." );
        }
        final JID userJID = getFMUCFromJID( presence );

        final MUCOccupant leaveOccupantData = room.getOccupantByFullJID( userJID );
        leaveOccupantData.setPresence( createCopyWithoutFMUC(presence) ); // update presence to reflect the 'leave' - this is used later to broadcast to other occupants.

        // Send presence to inform all occupants of the room that the user has left.
        room.sendLeavePresenceToExistingOccupants( leaveOccupantData )
            // DO NOT use 'thenRunAsync', as that will cause issues with clustering (it uses an executor that overrides the contextClassLoader, causing ClassNotFound exceptions in ClusterExternalizableUtil.
            .thenRun( () -> {
                // Update the (local) room state to no longer include this occupant.
                room.removeOccupant(leaveOccupantData);
            });
    }
Processes a presence stanza that is expected to be an FMUC-flavored 'leave' representation, and removes the
remote user from the room.

This method will <em>not</em> make modifications to the state of the FMUC node set. It expects those changes to
be taken care of by the caller.

This method provides the functionally opposite implementation of {@link #makeRemoteOccupantJoinRoom(Presence)}.

@param presence The stanza representing a user on a federated FMUC node leaving the room (cannot be null).
@see #makeRemoteOccupantJoinRoom(Presence)
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/FMUCHandler.java