@Override
    public void close(@Nullable final StreamError error, final boolean networkInterruption) {
        if (state.compareAndSet(State.OPEN, State.CLOSED)) {
            
            if (session != null) {
                if (!networkInterruption) {
                    // A 'clean' closure should never be resumed (see #onRemoteDisconnect for handling of unclean disconnects). OF-2752
                    session.getStreamManager().formalClose();
                }
                session.setStatus(Session.Status.CLOSED);
            }

            // See OF-1596
            // The notification will trigger some shutdown procedures that, amongst other things,
            // check what type of session (eg: anonymous) is being closed. This check depends on the
            // session still being available.
            //
            // For that reason, it's important to first notify the listeners, and then close the
            // session - not the other way around.
            //
            // This fixes a very visible bug where MUC users would remain in the MUC room long after
            // their session was closed. Effectively, the bug prevents the MUC room from getting a
            // presence update to notify it that the user logged off.
            notifyCloseListeners().whenComplete((v,t) -> {
                closeListeners.clear();
                if (t != null) {
                    Log.warn("Exception while invoking close listeners for {}", this, t);
                }

                try {
                    closeVirtualConnection(error);
                } catch (Exception e) {
                    Log.error(LocaleUtils.getLocalizedString("admin.error.close") + "\n" + toString(), e);
                }
            });
        }
    }
Closes the session, the virtual connection and notifies listeners that the connection
has been closed.

@param error If non-null, the end-stream tag will be preceded with this error.
xmppserver/src/main/java/org/jivesoftware/openfire/net/VirtualConnection.java