@Override
        public CompletableFuture<Void> onConnectionClosing(Object handback)
        {
            final LocalClientSession session = (LocalClientSession) handback;
            if (session.isDetached()) {
                Log.debug("Closing client session with address {} and streamID {} is detached already; this is a no-op.", session.getAddress(), session.getStreamID());
                return CompletableFuture.completedFuture(null);
            }
            if (session.getStreamManager().getResume()) {
                Log.debug("Closing client session with address {} and streamID {} has SM enabled; detaching.", session.getAddress(), session.getStreamID());
                session.setDetached();
                return CompletableFuture.completedFuture(null);
            }

            CompletableFuture<Void> result = CompletableFuture.runAsync(() -> Log.debug("Closing client session with address {} and streamID {} that does not have SM resume.", session.getAddress(), session.getStreamID()));

            if ((session.getPresence().isAvailable() || !session.wasAvailable()) && routingTable.hasClientRoute(session.getAddress())) {
                // Send an unavailable presence to the user's subscribers. This gives us a chance to send an
                // unavailable presence to the entities that the user sent directed presences
                final Presence presence = new Presence();
                presence.setType(Presence.Type.unavailable);
                presence.setFrom(session.getAddress());

                result = result.thenRunAsync(() -> router.route(presence));
            }

            // In the completion stage remove the session (which means it'll be removed no matter if the previous stage had exceptions).
            return result.whenComplete((v,t) -> {
                try {
                    session.getStreamManager().onClose(router, serverAddress);
                } finally {
                    // Note that the session can't be removed before the unavailable presence has been sent (as session-provided data is used by the broadcast).
                    removeSession(session);
                }
            });
        }
Handle a client session that just closed.

@param handback The session that just closed
@return a Future representing pending completion of the event listener invocation.
xmppserver/src/main/java/org/jivesoftware/openfire/SessionManager.java