public synchronized void terminateDetached(LocalSession session) {
        if (!(session instanceof LocalClientSession)) {
            Log.trace("Silently ignoring a request to terminate a non LocalClientSession: {}", session);
            return;
        }
        Log.debug("Terminating detached session '{}' ({})", session.getAddress(), session.getStreamID());
        if (!removeDetached(session)) {
            Log.info("Unable to terminate detachment of session '{}' ({}), as it was not registered as being a detached.", session.getAddress(), session.getStreamID());
            return;
        }
        final LocalClientSession clientSession = (LocalClientSession)session;

        // OF-1923: Only close the session if it has not been replaced by another session (if the session
        // has been replaced, then the condition below will compare to distinct instances). This *should* not
        // occur (but has been observed, prior to the fix of OF-1923). This check is left in as a safeguard.
        final ClientSession currentSession = routingTable.getClientRoute(session.getAddress());
        if (session == currentSession) {
            try {
                if ((clientSession.getPresence().isAvailable() || !clientSession.wasAvailable()) &&
                    routingTable.hasClientRoute(session.getAddress())) {
                    // Send an unavailable presence to the user's subscribers
                    // Note: This gives us a chance to send an unavailable presence to the
                    // entities that the user sent directed presences
                    Presence presence = new Presence();
                    presence.setType(Presence.Type.unavailable);
                    presence.setFrom(session.getAddress());
                    router.route(presence);
                }

                session.getStreamManager().onClose(router, serverAddress);
            } finally {
                // Remove the session
                removeSession(clientSession);
            }
        } else {
            // This could be the start of data state inconsistency. See OF-3044.
            Log.warn("Not removing detached session '{}' ({}) that appears to have been replaced by another session. New session: {} - original session {}", session.getAddress(), session.getStreamID(), currentSession, session);
        }
    }
Terminate a session that is detached.

A presence 'unavailable' is broadcast for the session that's being terminated. The session will be removed from
the routing table.

When the provided session is not recognized as a detached session (on this cluster node), then this method will
log a message but not apply any changes.

@param session The (detached) session to be terminated.
xmppserver/src/main/java/org/jivesoftware/openfire/SessionManager.java