/** * Process the Query, and generate response * * @param query the query to process */ public int processQuery(ResolverQueryMsg query) { if (!useRouteResolver) { // Route resolver disabled return ResolverService.OK; } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery starts"); } RouteQuery routeQuery = null; Reader ip = null; try { ip = new StringReader(query.getQuery()); StructuredTextDocument asDoc = (StructuredTextDocument) StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, ip); routeQuery = new RouteQuery(asDoc); } catch (Throwable e) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Malformed Route query ", e); } return ResolverService.OK; } finally { try { if (null != ip) { ip.close(); ip = null; } } catch (Throwable ignored) {; } } PeerID pId = routeQuery.getDestPeerID(); if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery: looking for route to " + pId); } RouteAdvertisement srcRoute = routeQuery.getSrcRoute(); List badHops = routeQuery.getBadHops(); if (LOG.isEnabledFor(Level.DEBUG)) { if (badHops != null) { LOG.debug("processQuery: bad Hops"); for (int i = 0; i < badHops.size(); i++) { LOG.debug("processQuery: :" + ((PeerID) badHops.get(i)).toString()); } } } // if our source route is not null, then publish it if (srcRoute != null) { if (!(srcRoute.getDestPeerID()).equals(localPeerId)) { // This is not our own peer adv so we must not keep it // longer than its expiration time. try { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery: publishing sender route info " + srcRoute.getDestPeerID()); } // 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())))) { routeCM.publishRoute(srcRoute); } } catch (Exception e) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Could not publish Route Adv from query - discard", e); } return ResolverService.OK; } } } else { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("No src Route in route query - discard "); } return ResolverService.OK; } if (pId == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Malformed route query request, no PeerId - discard"); } return ResolverService.OK; } // We have more luck with that one because, since it is part of OUR // message, and not part of the resolver protocol, it is in OUR // format. EndpointAddress qReqAddr = router.pid2addr(pId); RouteAdvertisement route = null; // check if this peer has a route to the destination // requested boolean found = false; if (qReqAddr.equals(localPeerAddr)) { found = true; // return the route that is my local route route = router.getMyLocalRoute(); } else { // only rendezvous can respond to route requests // if not we are generating too much traffic // XXX: here is where we could be more conservative and use isNormallyReachable() instead, // thus excluding // incoming messengers. if (router.isLocalRoute(qReqAddr)) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery: peer has direct route to destination "); } // we should set the route to something :-) found = true; // this peer has a direct route to the destination // return the short route advertisement we know for this peer // (For us it is zero hop, and we advertise ourself as the routing // peer in the response. The stiching is done by whoever gets that // response). May be there are more than one hop advertised in-there... // alternate routing peers...should we leave them ? // For now, we keep the full dest, but wack the hops. route = (RouteAdvertisement) AdvertisementFactory.newAdvertisement(RouteAdvertisement.getAdvertisementType()); AccessPointAdvertisement ap = (AccessPointAdvertisement) AdvertisementFactory.newAdvertisement( AccessPointAdvertisement.getAdvertisementType()); ap.setPeerID(pId); route.setDest(ap); } else { route = router.getRoute(qReqAddr, false); if (route != null) { found = true; // check if we were given some bad hops info // and see if the found route contains // any of these bad hops. In that case, we need // to mark this route as bad for (int i = 0; i < badHops.size(); i++) { // destination is known to be bad if (router.addr2pid(qReqAddr).equals(((PeerID) badHops.get(i)))) { processBadRoute((PeerID) badHops.get(i), route); found = false; break; } if (route.containsHop((PeerID) badHops.get(i))) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug( "processQuery: peer has bad route due to " + ((PeerID) badHops.get(i)).toString()); } processBadRoute((PeerID) badHops.get(i), route); found = false; break; } } } } } if (!found) { // discard the request if we are not a rendezvous // else forward to the next peers if (!group.isRendezvous()) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("discard query forwarding as not a rendezvous"); } return ResolverService.OK; } // did not find a route, check our srdi cache // make sure we protect against out of sync // SRDI index // srdi forwarding is only involved once the Index entry has // been found and we forwarded the resolver query. Afterward a // normal walk proceeds from the initial SRDI index pointing // rdv. This is done to protect against potential loopback // entries in the SRDI cache index due to out of sync peerview // and index. if (query.getHopCount() < 2) { // check local SRDI cache to see if we have the entry // we look for 10 entries, will pickup one randomly Vector results = srdiIndex.query("route", "DstPID", pId.toString(), 10); if (results.size() > 0) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery srdiIndex lookup match :" + results.size()); } // remove any non-rdv peers to avoid sending // to a non-rdv peers and garbage collect the SRDI // index in the process Vector clean = cleanupAnyEdges(query.getSrc(), results); if (clean.size() > 0) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("found an srdi entry forwarding query to SRDI peer"); } // The purpose of incrementing the hopcount // when an SRDI index match is found (we got a // pointer to a rdv that should have the route) is to // restrict any further forwarding. The increment // count is only done when a matching SRDI index is // found. Not when the replica is selected as we // still need to forward the query. This restriction // is purposelly done to avoid too many longjumps // within a walk. query.incrementHopCount(); // Note: this forwards the query to 1 peer randomly // selected from the result srdi.forwardQuery(clean, query, 1); // tell the resolver no further action is needed. return ResolverService.OK; } } } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("did not find a route or SRDI index"); } // force a walk return ResolverService.Repropagate; } // we found a route send the response try { if (route == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("we should have had a route at this point"); } return ResolverService.OK; } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery: we have a route build route response" + route.display()); } RouteAdvertisement myRoute = router.getMyLocalRoute(); // make sure we initialized our local // route info as we will need it to respond. We may // not have our route if we are still // waiting for a relay connection. if (myRoute == null) { return ResolverService.OK; } RouteResponse routeResponse = new RouteResponse(); routeResponse.setDestRoute(route); routeResponse.setSrcRoute(myRoute); if (routeResponse == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("error creating route response"); } return ResolverService.OK; } // construct a response from the query ResolverResponseMsg res = query.makeResponse(); res.setCredential(credentialDoc); res.setResponse(routeResponse.toString()); resolver.sendResponse(query.getSrc(), res); return ResolverService.OK; } catch (Exception ee) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery: error while processing query ", ee); } return ResolverService.OK; } }
/** * bad route, so let's remove everything we have so we can start from scratch. We are maintaining * a bad route up to DEFAULT_ROUTE experition after that we consider it to be ok to retry the same * route We are removing both the route and peer advertisement to force a new route query * * @param src peer that reply with the NACK route info * @param dest original route information */ private void processBadRoute(PeerID badHop, RouteAdvertisement dest) { EndpointAddress addr = router.pid2addr(dest.getDestPeerID()); if (addr == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("remove bad route has a bad route info - discard"); } return; } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("remove bad route info for dest " + dest.display()); if (badHop != null) { LOG.debug("remove bad route bad hop " + badHop); } } try { // check first that we still have the same route, we may already // using a new route RouteAdvertisement currentRoute = router.getRoute(addr, false); if (currentRoute == null) { // we already cleanup the route info return; } // check if we still have the old bad route, we may have // already updated the route if (!currentRoute.equals(dest)) { // check if the bad hop is not the destination // if it is then we still have a bad route if (!(badHop.equals(router.addr2pid(addr)))) { // check if the new route may still contain the bad hop // the known bad hop is the hop after the src peer that // responded with a NACK route // In this case we also consider the route bad if (badHop != null) { // this should really not be null if (!currentRoute.containsHop(badHop)) { return; // we are ok } else { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("current route is bad because it contains known bad hop" + badHop); } } } else { // we could get the bad hop, so consider the route ok return; } } else { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("current route is bad because it contains known bad destination" + badHop); } } } // keep the bad one in a cache table so we don't retry them // right away. We use the default route timeout BadRoute badRoute = ((BadRoute) router.getBadRoute(addr)); if (badRoute != null) { Long nextTry = badRoute.getExpiration(); if (nextTry.longValue() > System.currentTimeMillis()) { // nothing to do the information is still valid } else { // It is ancient knowlege update it nextTry = new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)); badRoute.setExpiration(nextTry); } List badHops = badRoute.getHops(); // check if we have to add a new bad hop // to our bad route if (badHop != null) { if (!badHops.contains(badHop)) { badHops.add(badHop); // new expiration nextTry = new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)); badRoute.setHops(badHops); badRoute.setExpiration(nextTry); } } router.setBadRoute(addr, badRoute); return; } else { // create a new NACK route entry Vector badHops = new Vector(); if (badHop != null) { badHops.add(badHop); } badRoute = new BadRoute( dest, new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)), badHops); router.setBadRoute(addr, badRoute); } // remove route from route CM routeCM.flushRoute(addr); // let's remove the remote route info from the routing table // we do this after we removed the entries from the CM // to avoid that another thread is putting back the entry router.removeRoute(addr); } catch (Exception ex) { if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("exception during bad route removal", ex); } } }