package net.i2p.router.networkdb.kademlia; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.Map; import java.util.Set; import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.router.RouterInfo; import net.i2p.router.RouterContext; import net.i2p.util.Log; /** * Stores in-memory only. See extension. */ class TransientDataStore implements DataStore { protected final Log _log; private final ConcurrentHashMap<Hash, DatabaseEntry> _data; protected final RouterContext _context; public TransientDataStore(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(getClass()); _data = new ConcurrentHashMap<Hash, DatabaseEntry>(1024); if (_log.shouldLog(Log.INFO)) _log.info("Data Store initialized"); } public boolean isInitialized() { return true; } public void stop() { _data.clear(); } public void restart() { stop(); } public void rescan() {} /** * @return total size (RI and LS) * @since 0.8.8 */ public int size() { return _data.size(); } /** * @return Unmodifiable view, not a copy */ public Set<Hash> getKeys() { return Collections.unmodifiableSet(_data.keySet()); } /** * @return Unmodifiable view, not a copy * @since 0.8.3 */ public Collection<DatabaseEntry> getEntries() { return Collections.unmodifiableCollection(_data.values()); } /** * @return Unmodifiable view, not a copy * @since 0.8.3 */ public Set<Map.Entry<Hash, DatabaseEntry>> getMapEntries() { return Collections.unmodifiableSet(_data.entrySet()); } /** for PersistentDataStore only - don't use here * @throws UnsupportedOperationException always */ public DatabaseEntry get(Hash key, boolean persist) { throw new UnsupportedOperationException(); } public DatabaseEntry get(Hash key) { return _data.get(key); } public boolean isKnown(Hash key) { return _data.containsKey(key); } public int countLeaseSets() { int count = 0; for (DatabaseEntry d : _data.values()) { if (d.getType() == DatabaseEntry.KEY_TYPE_LEASESET) count++; } return count; } /** for PersistentDataStore only - don't use here * @throws UnsupportedOperationException always */ public boolean put(Hash key, DatabaseEntry data, boolean persist) { throw new UnsupportedOperationException(); } /** * @param data must be validated before here * @return success */ public boolean put(Hash key, DatabaseEntry data) { if (data == null) return false; if (_log.shouldLog(Log.DEBUG)) _log.debug("Storing key " + key); DatabaseEntry old = _data.putIfAbsent(key, data); boolean rv = false; if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { // Don't do this here so we don't reset it at router startup; // the StoreMessageJob calls this //_context.profileManager().heardAbout(key); RouterInfo ri = (RouterInfo)data; if (old != null) { RouterInfo ori = (RouterInfo)old; if (ri.getPublished() < ori.getPublished()) { if (_log.shouldLog(Log.INFO)) _log.info("Almost clobbered an old router! " + key + ": [old published on " + new Date(ori.getPublished()) + " new on " + new Date(ri.getPublished()) + ']'); } else if (ri.getPublished() == ori.getPublished()) { if (_log.shouldLog(Log.INFO)) _log.info("Duplicate " + key); } else { if (_log.shouldLog(Log.INFO)) _log.info("Updated the old router for " + key + ": [old published on " + new Date(ori.getPublished()) + " new on " + new Date(ri.getPublished()) + ']'); _data.put(key, data); rv = true; } } else { if (_log.shouldLog(Log.INFO)) _log.info("New router for " + key + ": published on " + new Date(ri.getPublished())); rv = true; } } else if (data.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { LeaseSet ls = (LeaseSet)data; if (old != null) { LeaseSet ols = (LeaseSet)old; if (ls.getEarliestLeaseDate() < ols.getEarliestLeaseDate()) { if (_log.shouldLog(Log.INFO)) _log.info("Almost clobbered an old leaseSet! " + key + ": [old expires " + new Date(ols.getEarliestLeaseDate()) + " new on " + new Date(ls.getEarliestLeaseDate()) + ']'); } else if (ls.getEarliestLeaseDate() == ols.getEarliestLeaseDate()) { if (_log.shouldLog(Log.INFO)) _log.info("Duplicate " + key); } else { if (_log.shouldLog(Log.INFO)) { _log.info("Updated old leaseSet " + key + ": [old expires " + new Date(ols.getEarliestLeaseDate()) + " new on " + new Date(ls.getEarliestLeaseDate()) + ']'); if (_log.shouldLog(Log.DEBUG)) _log.debug("RAP? " + ls.getReceivedAsPublished() + " RAR? " + ls.getReceivedAsReply()); } _data.put(key, data); rv = true; } } else { if (_log.shouldLog(Log.INFO)) { _log.info("New leaseset for " + key + ": expires " + new Date(ls.getEarliestLeaseDate())); if (_log.shouldLog(Log.DEBUG)) _log.debug("RAP? " + ls.getReceivedAsPublished() + " RAR? " + ls.getReceivedAsReply()); } rv = true; } } return rv; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("Transient DataStore: ").append(_data.size()).append("\nKeys: "); for (Map.Entry<Hash, DatabaseEntry> e : _data.entrySet()) { Hash key = e.getKey(); DatabaseEntry dp = e.getValue(); buf.append("\n\t*Key: ").append(key.toString()).append("\n\tContent: ").append(dp.toString()); } buf.append("\n"); return buf.toString(); } /** for PersistentDataStore only - don't use here * @throws UnsupportedOperationException always */ public DatabaseEntry remove(Hash key, boolean persist) { throw new UnsupportedOperationException(); } public DatabaseEntry remove(Hash key) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing key " + key); return _data.remove(key); } }