package net.i2p.router.networkdb.kademlia; import java.util.Map; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.router.RouterContext; import net.i2p.util.LHMCache; import net.i2p.util.ObjectCounter; import net.i2p.util.SimpleTimer; import net.i2p.util.SimpleTimer2; /** * Track lookup fails * * @since 0.9.4 */ class NegativeLookupCache { private final ObjectCounter<Hash> counter; private final Map<Hash, Destination> badDests; private final int _maxFails; private static final int MAX_FAILS = 3; private static final int MAX_BAD_DESTS = 128; private static final long CLEAN_TIME = 2*60*1000; public NegativeLookupCache(RouterContext context) { this.counter = new ObjectCounter<Hash>(); this.badDests = new LHMCache<Hash, Destination>(MAX_BAD_DESTS); this._maxFails = context.getProperty("netdb.negativeCache.maxFails",MAX_FAILS); final long cleanTime = context.getProperty("netdb.negativeCache.cleanupInterval", CLEAN_TIME); SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), cleanTime); } public void lookupFailed(Hash h) { this.counter.increment(h); } public boolean isCached(Hash h) { if (counter.count(h) >= _maxFails) return true; synchronized(badDests) { return badDests.get(h) != null; } } /** * Negative cache the hash until restart, * but cache the destination. * * @since 0.9.16 */ public void failPermanently(Destination dest) { Hash h = dest.calculateHash(); synchronized(badDests) { badDests.put(h, dest); } } /** * Get an unsupported but cached Destination * * @return dest or null if not cached * @since 0.9.16 */ public Destination getBadDest(Hash h) { synchronized(badDests) { return badDests.get(h); } } /** * @since 0.9.16 */ public void clear() { counter.clear(); synchronized(badDests) { badDests.clear(); } } private class Cleaner implements SimpleTimer.TimedEvent { public void timeReached() { NegativeLookupCache.this.counter.clear(); } } }