private void startResume(String namespace, String previd, long h) {
        Log.debug("Attempting resumption for {}, h={}", previd, h);
        this.namespace = namespace;
        // Ensure that resource binding has NOT occurred.
        if (!allowResume() ) {
            Log.debug("Unable to process session resumption attempt, as session {} is in a state where session resumption is not allowed.", session);
            sendUnexpectedError();
            return;
        }
        if (session.isAuthenticated()) {
            Log.debug("Unable to process session resumption attempt, as session {} is not authenticated.", session);
            sendUnexpectedError();
            return;
        }
        AuthToken authToken = null;
        // Ensure that resource binding has occurred.
        if (session instanceof ClientSession) {
            authToken = ((LocalClientSession) session).getAuthToken();
        }
        if (authToken == null) {
            Log.debug("Unable to process session resumption attempt, as session {} does not provide any auth context.", session);
            sendUnexpectedError();
            return;
        }
        // Decode previd.
        String resource;
        String streamId;
        try {
            StringTokenizer toks = new StringTokenizer(new String(Base64.getDecoder().decode(previd), StandardCharsets.UTF_8), "\0");
            resource = toks.nextToken();
            streamId = toks.nextToken();
        } catch (Exception e) {
            Log.debug("Exception from previd decode:", e);
            sendUnexpectedError();
            return;
        }
        final JID fullJid;
        if ( authToken.isAnonymous() ){
            fullJid = new JID(resource, session.getServerName(), resource, true);
        } else {
            fullJid = new JID(authToken.getUsername(), session.getServerName(), resource, true);
        }
        Log.debug("Resuming session for '{}'. Current session: {}", fullJid, session.getStreamID());

        // Locate existing session.
        final ClientSession route = XMPPServer.getInstance().getRoutingTable().getClientRoute(fullJid);
        if (route == null) {
            Log.debug("Not able for client of '{}' to resume a session on this cluster node. No session was found for this client.", fullJid);
            if (LOCATION_TERMINATE_OTHERS_ENABLED.getValue()) {
                // When the client tries to resume a connection on this host, it is unlikely to try other hosts. Remove any detached sessions living elsewhere in the cluster. (OF-2753)
                CacheFactory.doClusterTask(new ClientSessionTask(fullJid, RemoteSessionTask.Operation.removeDetached));
            }
            sendError(new PacketError(PacketError.Condition.item_not_found));
            return;
        }

        if (!(route instanceof LocalClientSession)) {
            Log.debug("Not allowing a client of '{}' to resume a session on this cluster node. The session can only be resumed on the Openfire cluster node where the original session was connected.", fullJid);
            if (LOCATION_TERMINATE_OTHERS_ENABLED.getValue()) {
                // When the client tries to resume a connection on this host, it is unlikely to try other hosts. Remove any detached sessions living elsewhere in the cluster. (OF-2753)
                CacheFactory.doClusterTask(new ClientSessionTask(fullJid, RemoteSessionTask.Operation.removeDetached));
            }
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }

        final LocalClientSession otherSession = (LocalClientSession) route;
        if (!otherSession.getStreamID().getID().equals(streamId)) {
            sendError(new PacketError(PacketError.Condition.item_not_found));
            return;
        }
        Log.debug("Found existing session for '{}', checking status", fullJid);

        // OF-2811: Cannot resume a session that's already closed. That session is likely busy firing its 'closeListeners'.
        if (route.isClosed()) {
            Log.debug("Not allowing a client of '{}' to resume a session, as the preexisting session is already in process of being closed.", fullJid);
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }

        // Previd identifies proper session. Now check SM status
        if (!otherSession.getStreamManager().resume) {
            Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed does not have the stream management resumption feature enabled.", fullJid);
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }
        if (otherSession.getStreamManager().namespace == null) {
            Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed disabled SM functionality as a response to an earlier error.", fullJid);
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }
        if (!otherSession.getStreamManager().namespace.equals(namespace)) {
            Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed used a different version ({}) of the session management resumption feature as compared to the version that's requested now: {}.", fullJid, otherSession.getStreamManager().namespace, namespace);
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }
        if (!otherSession.getStreamManager().validateClientAcknowledgement(h)) {
            Log.debug("Not allowing a client of '{}' to resume a session, as it reports it received more stanzas from us than that we've send it.", fullJid);
            sendError(new PacketError(PacketError.Condition.unexpected_request));
            return;
        }
        if (!otherSession.isDetached()) {
            Log.debug("Existing session {} of '{}' is not detached; detaching.", otherSession.getStreamID(), fullJid);
            Connection oldConnection = otherSession.getConnection();
            otherSession.setDetached();
            assert oldConnection != null; // If the other session is not detached, the connection can't be null.
            oldConnection.close(new StreamError(StreamError.Condition.conflict, "The stream previously served over this connection is resumed on a new connection."), true);
        }
        Log.debug("Attaching to other session '{}' of '{}'.", otherSession.getStreamID(), fullJid);
        // If we're all happy, re-attach the connection from the pre-existing session to the new session, discarding the old session.
        otherSession.reattach(session, h);
        Log.debug("Perform resumption of session {} for '{}', using connection from session {}", otherSession.getStreamID(), fullJid, session.getStreamID());
    }
Attempts to enable Stream Management for the entity identified by the provided JID.

@param namespace The namespace that defines what version of SM is to be enabled.
@param resume Whether the client is requesting a resumable session.
xmppserver/src/main/java/org/jivesoftware/openfire/streammanagement/StreamManager.java