/**
   * {@inheritDoc}
   *
   * <p>This is called by the Generic ResolverServiceImpl when processing a response to a query.
   */
  public void processResponse(ResolverResponseMsg response) {

    if (!useRouteResolver) { // Route resolver disabled
      return;
    }

    if (LOG.isEnabledFor(Level.DEBUG)) {
      LOG.debug("processResponse got a response");
    }

    // convert the response into a RouteResponse
    Reader ip = null;
    RouteResponse doc = null;

    try {
      ip = new StringReader(response.getResponse());

      StructuredTextDocument asDoc =
          (StructuredTextDocument)
              StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, ip);

      doc = new RouteResponse(asDoc);
    } catch (Throwable e) {
      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("processResponse: malformed response - discard", e);
      }
      return;
    } finally {
      try {
        if (null != ip) {
          ip.close();
          ip = null;
        }
      } catch (Throwable ignored) {
      }
    }

    RouteAdvertisement dstRoute = doc.getDestRoute();
    RouteAdvertisement srcRoute = doc.getSrcRoute();
    int queryId = response.getQueryId();

    if ((dstRoute == null) || (srcRoute == null)) {
      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("processResponse: malformed response - discard.");
      }
      // Malformed response. Just discard.
      return;
    }

    EndpointAddress routingPeer = router.pid2addr(srcRoute.getDestPeerID());
    EndpointAddress destPeer = router.pid2addr(dstRoute.getDestPeerID());

    if ((routingPeer == null) || (destPeer == null)) {
      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("processResponse: malformed PeerID in response - discard.");
      }
      // Malformed response. Just discard.
      return;
    }

    // check if we have a negative route response
    if (queryId == NACKROUTE_QUERYID) {

      AccessPointAdvertisement badHop = dstRoute.nextHop(router.addr2pid(routingPeer));

      PeerID badPeer = null;

      if (badHop != null) {
        badPeer = badHop.getPeerID();
      } else { // the bad hop is the final destination
        badPeer = dstRoute.getDestPeerID();
      }

      processBadRoute(badPeer, dstRoute);
      return;
    }

    // This is not our own peer adv, so we must not keep it
    // for more than its expiration time.
    // we only need to publish this route if
    // we don't know about it yet
    // XXX: here is where we could be more conservative and use isNormallyReachable() instead, thus
    // excluding
    // incoming messengers.
    if ((!router.isLocalRoute(router.pid2addr(srcRoute.getDestPeerID())))
        && (!router.isRoutedRoute(router.pid2addr(srcRoute.getDestPeerID())))) {
      router.updateRouteAdv(srcRoute);
    }

    if (destPeer.equals(routingPeer)) {
      // The dest peer itself managed to respond to us. That means we
      // learned the route from the reverseRoute in the message
      // itself. So, there's nothing we need to do.
      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("learn route directly from the destination");
      }
    } else {
      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("learn route:" + routingPeer);
      }

      try {
        // build the candidate route using the
        // route response from the respondant peer
        RouteAdvertisement candidateRoute =
            RouteAdvertisement.newRoute(
                router.addr2pid(destPeer),
                router.addr2pid(routingPeer),
                (Vector) dstRoute.getVectorHops().clone());

        // cleanup the candidate route from any loop and remove the local peer extra
        // cycle
        RouteAdvertisement.cleanupLoop(candidateRoute, (PeerID) localPeerId);

        // Is there anything left in that route (or did the respondant
        // believe that we are the last hop on the route - which
        // obviously we are not.
        if (candidateRoute.size() == 0) {
          if (LOG.isEnabledFor(Level.DEBUG)) {
            LOG.debug("Route response outdated: NACK responder");
          }
          generateNACKRoute(
              router.addr2pid(routingPeer), router.addr2pid(destPeer), dstRoute.getVectorHops());
          return;
        }

        // get the address of the first hop in the route to verify that
        // we have a route (direct or long) to the first hop, so the route
        // is valid
        EndpointAddress candidateRouter = router.pid2addr(candidateRoute.getFirstHop().getPeerID());

        // check that we have a direct connection to the first hop
        if (router.ensureLocalRoute(candidateRouter, null) == null) {
          // If we do not have a direct route to the candidate router check
          // for a long route in that case stich the route
          RouteAdvertisement routeToRouter = router.getRoute(candidateRouter, false);

          if (routeToRouter == null) {
            if (LOG.isEnabledFor(Level.DEBUG)) {
              LOG.debug("Route response useless: no route to next router hop");
            }
            return;
          }

          // stich the route removing any loops and localPeer cycle
          if (RouteAdvertisement.stichRoute(candidateRoute, routeToRouter, (PeerID) localPeerId)) {
            router.setRoute(candidateRoute, false);
          } else {
            if (LOG.isEnabledFor(Level.DEBUG)) {
              LOG.debug("Route response error stiching route response");
            }
            return;
          }
        } else {
          // we have a direct connection with the first hop of the candidate route
          // set the new route, which starts with the peer that replied to us.
          router.setRoute(candidateRoute, false);
        }
      } catch (Exception ex) {
        if (LOG.isEnabledFor(Level.DEBUG)) {
          LOG.debug("Route response exception when building response route" + ex);
          LOG.debug("               bad dstRoute: " + dstRoute.display());
        }
      }

      if (LOG.isEnabledFor(Level.DEBUG)) {
        LOG.debug("finish process route response successfully");
      }
    }
  }
  /**
   * {@inheritDoc}
   *
   * @param encodeAs Description of the Parameter
   * @return The document value
   */
  public Document getDocument(MimeMediaType encodeAs) {
    StructuredDocument adv = (StructuredDocument) super.getDocument(encodeAs);

    if (hasALoop()) {
      throw new IllegalStateException("I won't write a doc for a route with a loop");
    }

    PeerID pid = getDestPeerID();
    AccessPointAdvertisement dest = getDest();

    if ((null != pid) && (null != dest) && (null != dest.getPeerID())) {
      if (!pid.equals(dest.getPeerID())) {
        throw new IllegalStateException(
            "Destination peer id and destination access point adv don't refer to the same peer");
      }
    }

    // HACK Backwards Compatibility
    if ((null == pid) && (null != dest)) {
      pid = dest.getPeerID();
    }

    if (pid != null) {
      Element e0 = adv.createElement(DEST_PID_TAG, pid.toString());

      adv.appendChild(e0);
    }

    if (dest != null) {
      // create the copy without the PID
      AccessPointAdvertisement ap =
          (AccessPointAdvertisement)
              AdvertisementFactory.newAdvertisement(
                  AccessPointAdvertisement.getAdvertisementType());

      ap.setEndpointAddresses(dest.getVectorEndpointAddresses());

      StructuredTextDocument xptDoc = (StructuredTextDocument) ap.getDocument(encodeAs);

      Element e1 = adv.createElement("Dst");

      adv.appendChild(e1);
      StructuredDocumentUtils.copyElements(adv, e1, xptDoc);
    }

    // only include hops if we have some
    if (getHops().hasMoreElements()) {

      Element e2 = adv.createElement("Hops");

      adv.appendChild(e2);

      for (Enumeration e = getHops(); e.hasMoreElements(); ) {
        AccessPointAdvertisement hop = (AccessPointAdvertisement) e.nextElement();

        if (hop != null) {
          StructuredTextDocument xptDoc = (StructuredTextDocument) hop.getDocument(encodeAs);

          StructuredDocumentUtils.copyElements(adv, e2, xptDoc);
        }
      }
    }

    return adv;
  }