public void removingSubscription(
      Subscription subscription, String presRulesAUID, String presRulesDocumentName) {

    if (logger.isDebugEnabled()) {
      logger.debug("removingSubscription(" + subscription + ")");
    }
    // get combined rules cmp
    Map combinedRules = sbb.getCombinedRules();
    if (combinedRules != null) {
      // remove subscription from map
      if (logger.isDebugEnabled()) {
        logger.debug("combined rules: " + combinedRules.keySet());
      }
      PresRuleCMPKey cmpKey =
          new PresRuleCMPKey(
              subscription.getSubscriber(),
              subscription.getNotifier(),
              subscription.getKey().getEventPackage(),
              subscription.getKey().getRealEventId());
      if (combinedRules.remove(cmpKey) != null) {
        if (logger.isDebugEnabled()) {
          logger.debug("removed rule from combined rules map (" + subscription + ")");
        }
        // check if subscription to pres-rules still needed
        boolean subscriptionNeeded = false;
        for (Object k : combinedRules.keySet()) {
          PresRuleCMPKey presRuleCMPKey = (PresRuleCMPKey) k;
          if (presRuleCMPKey.getNotifierWithoutParams().equals(cmpKey.getNotifierWithoutParams())
              && presRuleCMPKey.getEventPackage().equals(subscription.getKey().getEventPackage())) {
            if (logger.isDebugEnabled()) {
              logger.debug("subscription needed due to rule " + presRuleCMPKey);
            }
            subscriptionNeeded = true;
            break;
          }
        }
        if (!subscriptionNeeded) {
          if (logger.isDebugEnabled()) {
            logger.debug("subscription not needed");
          }
          DocumentSelector documentSelector =
              getDocumentSelector(
                  cmpKey.getNotifierWithoutParams(), presRulesAUID, presRulesDocumentName);
          sbb.getXDMClientControlSbb().unsubscribeDocument(documentSelector);
        }
      }
    }
  }
  /** async get response from xdm client */
  public void getResponse(XcapUriKey key, int responseCode, String mimetype, String content) {

    DocumentSelector documentSelector = null;
    if (key instanceof DocumentUriKey) {
      documentSelector = ((DocumentUriKey) key).getDocumentSelector();
    } else if (key instanceof AttributeUriKey) {
      documentSelector = ((AttributeUriKey) key).getDocumentSelector();
    } else if (key instanceof ElementUriKey) {
      documentSelector = ((ElementUriKey) key).getDocumentSelector();
    } else {
      try {
        documentSelector =
            Parser.parseDocumentSelector(key.getResourceSelector().getDocumentSelector());
      } catch (ParseException e) {
        // won't happen
        logger.error("bug, a xcap uri key document selector string could not be parsed", e);
      }
    }

    if (responseCode == 200) {
      // just simulate a document update
      documentUpdated(documentSelector, null, null, content);
    } else {
      // let's be friendly with clients without xcap, allow subscription
      String notifierWithoutParams = getUser(documentSelector);
      if (logger.isInfoEnabled()) {
        logger.info(notifierWithoutParams + " pres-rules not found, allowing subscription");
      }
      Map combinedRules = sbb.getCombinedRules();
      if (combinedRules != null) {
        for (Object object : combinedRules.keySet()) {
          PresRuleCMPKey cmpKey = (PresRuleCMPKey) object;
          if (cmpKey.getNotifierWithoutParams().equals(notifierWithoutParams)) {
            OMAPresRule combinedRule = (OMAPresRule) combinedRules.get(cmpKey);
            combinedRule.setProvideAllDevices(true);
            combinedRule.setProvideAllAttributes(true);
            combinedRule.setProvideAllPersons(true);
            combinedRule.setProvideAllServices(true);
            combinedRule.setSubHandling(SubHandlingAction.allow);
            // notify auth changed
            sbb.getParentSbbCMP()
                .authorizationChanged(
                    cmpKey.getSubscriber(),
                    cmpKey.getNotifier(),
                    "presence",
                    cmpKey.getEventId(),
                    combinedRule.getSubHandling().getResponseCode());
          }
        }
        sbb.setCombinedRules(combinedRules);
      }
    }

    // subscribe to changes in this pres-rules doc
    sbb.getXDMClientControlSbb().subscribeDocument(documentSelector);
  }
  public void documentUpdated(
      DocumentSelector documentSelector, String oldETag, String newETag, String documentAsString) {

    if (!documentSelector.getAUID().equals("org.openmobilealliance.pres-rules")
        && !documentSelector.getAUID().equals("pres-rules")) {
      // not a pres-rules doc, maybe it's coming here because of
      // integrated servers using xdm client
      return;
    }

    String notifierWithoutParams = getUser(documentSelector);

    // unmarshall doc
    Ruleset ruleset = unmarshallRuleset(documentAsString);
    if (ruleset == null) {
      logger.error(
          "rcvd ruleset update from xdm client but unmarshalling of ruleset failed, ignoring update");
      return;
    }

    // get current combined rules from cmp
    Map combinedRules = sbb.getCombinedRules();
    if (combinedRules == null) {
      combinedRules = new HashMap();
    }
    // for each combined rules that has the user that updated the doc as
    // notifier reprocess the rules
    for (Object key : combinedRules.keySet()) {
      PresRuleCMPKey cmpKey = (PresRuleCMPKey) key;
      if (cmpKey.getNotifierWithoutParams().equals(notifierWithoutParams)) {
        OMAPresRule oldCombinedRule = (OMAPresRule) combinedRules.get(cmpKey);
        RulesetProcessor rulesetProcessor =
            new RulesetProcessor(cmpKey.getSubscriber(), notifierWithoutParams, ruleset, sbb);
        OMAPresRule newCombinedRule = rulesetProcessor.getCombinedRule();
        combinedRules.put(cmpKey, newCombinedRule);
        // check permission changed
        if (oldCombinedRule.getSubHandling().getResponseCode()
            != newCombinedRule.getSubHandling().getResponseCode()) {
          sbb.getParentSbbCMP()
              .authorizationChanged(
                  cmpKey.getSubscriber(),
                  cmpKey.getNotifier(),
                  "presence",
                  cmpKey.getEventId(),
                  newCombinedRule.getSubHandling().getResponseCode());
        }
      }
    }

    sbb.setCombinedRules(combinedRules);
  }
  public void isSubscriberAuthorized(
      String subscriber,
      String subscriberDisplayName,
      String notifier,
      SubscriptionKey key,
      int expires,
      String content,
      String contentType,
      String contentSubtype,
      boolean eventList,
      String presRulesAUID,
      String presRulesDocumentName,
      ServerTransaction serverTransaction) {

    // get current combined rule from cmp
    Map combinedRules = sbb.getCombinedRules();
    if (combinedRules == null) {
      combinedRules = new HashMap();
    }
    PresRuleCMPKey cmpKey =
        new PresRuleCMPKey(subscriber, notifier, key.getEventPackage(), key.getRealEventId());
    OMAPresRule combinedRule = (OMAPresRule) combinedRules.get(cmpKey);

    if (combinedRule == null) {
      // rule not found, lookup for another key with same subscriber and
      // notifier
      for (Object keyObject : combinedRules.keySet()) {
        PresRuleCMPKey k = (PresRuleCMPKey) keyObject;
        if (k.getSubscriber().equals(subscriber)
            && k.getNotifierWithoutParams().equals(cmpKey.getNotifierWithoutParams())) {
          // found compatible key, get rule and assign to this key
          // also
          combinedRule = (OMAPresRule) combinedRules.get(k);
          combinedRules.put(cmpKey, combinedRule);
          sbb.setCombinedRules(combinedRules);
          break;
        }
      }
    }
    if (combinedRule == null) {
      // get pres-rules doc on xdm
      DocumentSelector documentSelector =
          getDocumentSelector(
              cmpKey.getNotifierWithoutParams(), presRulesAUID, presRulesDocumentName);
      if (documentSelector == null) {
        sbb.getParentSbbCMP()
            .newSubscriptionAuthorization(
                subscriber,
                subscriberDisplayName,
                notifier,
                key,
                expires,
                Response.FORBIDDEN,
                eventList,
                serverTransaction);
      } else {
        // we need to go to the xdm, reply accepted for subscription
        // state be pending
        combinedRules.put(cmpKey, new OMAPresRule());
        sbb.setCombinedRules(combinedRules);
        sbb.getParentSbbCMP()
            .newSubscriptionAuthorization(
                subscriber,
                subscriberDisplayName,
                notifier,
                key,
                expires,
                Response.ACCEPTED,
                eventList,
                serverTransaction);
        // ask for pres-rules doc
        sbb.getXDMClientControlSbb().get(new DocumentUriKey(documentSelector), null);
      }
    } else {
      // great, we already had the doc, send response code to the abstract
      // subscription control
      sbb.getParentSbbCMP()
          .newSubscriptionAuthorization(
              subscriber,
              subscriberDisplayName,
              notifier,
              key,
              expires,
              combinedRule.getSubHandling().getResponseCode(),
              eventList,
              serverTransaction);
    }
  }