public void cancelSubscription(NodeSubscription subscription, boolean sendToCluster) {
        getLogger().trace("Cancelling subscription for '{}' (state: {}). Send-to-cluster: {}", subscription.getJID(), subscription.getState(), sendToCluster);
        // Remove subscription from memory
        subscriptionsByID.remove(subscription.getID());
        subscriptionsByJID.remove(subscription.getJID().toString());
        // Check if user has affiliation of type "none" and there are no more subscriptions
        NodeAffiliate affiliate = subscription.getAffiliate();
        if (affiliate != null && affiliate.getAffiliation() == NodeAffiliate.Affiliation.none &&
                getSubscriptions(subscription.getOwner()).isEmpty()) {
            // Remove affiliation of type "none"
            removeAffiliation(affiliate);
        }
        if (savedToDB) {
            // Remove the subscription from the database
            XMPPServer.getInstance().getPubSubModule().getPersistenceProvider().removeSubscription(subscription);
        }
        if (sendToCluster) {
            CacheFactory.doClusterTask(new CancelSubscriptionTask(subscription));
        }

        // Check if we need to unsubscribe from the presence of the owner
        if (isPresenceBasedDelivery() && getSubscriptions(subscription.getOwner()).isEmpty()) {
            getService().presenceSubscriptionNotRequired(this, subscription.getOwner());
        }
    }
Cancels an existing subscription to the node. If the subscriber does not have any
other subscription to the node and his affiliation was of type {@code none} then
remove the existing affiliation too.

@param subscription the subscription to cancel.
@param sendToCluster True to forward cancel order to cluster peers
xmppserver/src/main/java/org/jivesoftware/openfire/pubsub/Node.java