/** * Public Key Store (with LRU memory cache) */ package freenet.node; import java.io.IOException; import freenet.crypt.DSAPublicKey; import freenet.store.BlockMetadata; import freenet.store.GetPubkey; import freenet.store.PubkeyStore; import freenet.support.ByteArrayWrapper; import freenet.support.HexUtil; import freenet.support.LRUMap; import freenet.support.Logger; public class NodeGetPubkey implements GetPubkey { private static volatile boolean logMINOR; static { Logger.registerClass(NodeGetPubkey.class); } // Debugging stuff private static final boolean USE_RAM_PUBKEYS_CACHE = true; private static final int MAX_MEMORY_CACHED_PUBKEYS = 1000; private final LRUMap<ByteArrayWrapper, DSAPublicKey> cachedPubKeys; private PubkeyStore pubKeyDatastore; private PubkeyStore pubKeyDatacache; private PubkeyStore pubKeyClientcache; private PubkeyStore pubKeySlashdotcache; private final Node node; NodeGetPubkey(Node node) { cachedPubKeys = LRUMap.createSafeMap(ByteArrayWrapper.FAST_COMPARATOR); this.node = node; } void setDataStore(PubkeyStore pubKeyDatastore, PubkeyStore pubKeyDatacache) { this.pubKeyDatastore = pubKeyDatastore; this.pubKeyDatacache = pubKeyDatacache; } /* (non-Javadoc) * @see freenet.node.GetPubkey#getKey(byte[], boolean, boolean, freenet.store.BlockMetadata) */ @Override public DSAPublicKey getKey(byte[] hash, boolean canReadClientCache, boolean forULPR, BlockMetadata meta) { boolean ignoreOldBlocks = !node.getWriteLocalToDatastore(); if(canReadClientCache) ignoreOldBlocks = false; ByteArrayWrapper w = new ByteArrayWrapper(hash); if (logMINOR) Logger.minor(this, "Getting pubkey: " + HexUtil.bytesToHex(hash)); if (USE_RAM_PUBKEYS_CACHE) { synchronized (cachedPubKeys) { DSAPublicKey key = cachedPubKeys.get(w); if (key != null) { cachedPubKeys.push(w, key); if (logMINOR) Logger.minor(this, "Got " + HexUtil.bytesToHex(hash) + " from in-memory cache"); return key; } } } try { DSAPublicKey key = null; if(pubKeyClientcache != null && canReadClientCache) key = pubKeyClientcache.fetch(hash, false, false, meta); if(node.oldPKClientCache != null && canReadClientCache && key == null) { PubkeyStore pks = node.oldPKClientCache; if(pks != null) key = pks.fetch(hash, false, false, meta); if(key != null && logMINOR) Logger.minor(this, "Got "+HexUtil.bytesToHex(hash)+" from old client cache"); } // We can *read* from the datastore even if nearby, but we cannot promote in that case. if(key == null) { key = pubKeyDatastore.fetch(hash, false, ignoreOldBlocks, meta); if(key != null && logMINOR) Logger.minor(this, "Got "+HexUtil.bytesToHex(hash)+" from store"); } if(key == null) { PubkeyStore pks = node.oldPK; if(pks != null) key = pks.fetch(hash, false, ignoreOldBlocks, meta); if(key != null && logMINOR) Logger.minor(this, "Got "+HexUtil.bytesToHex(hash)+" from old store"); } if (key == null) { key = pubKeyDatacache.fetch(hash, false, ignoreOldBlocks, meta); if(key != null && logMINOR) Logger.minor(this, "Got "+HexUtil.bytesToHex(hash)+" from cache"); } if(key == null) { PubkeyStore pks = node.oldPKCache; if(pks != null) key = pks.fetch(hash, false, ignoreOldBlocks, meta); if(key != null && logMINOR) Logger.minor(this, "Got "+HexUtil.bytesToHex(hash)+" from old cache"); } if(key == null && pubKeySlashdotcache != null && forULPR) { key = pubKeySlashdotcache.fetch(hash, false, ignoreOldBlocks, meta); if (logMINOR) Logger.minor(this, "Got " + HexUtil.bytesToHex(hash) + " from slashdot cache"); } if (key != null) { // Just put into the in-memory cache cacheKey(hash, key, false, false, false, false, false); } return key; } catch (IOException e) { // FIXME deal with disk full, access perms etc; tell user about it. Logger.error(this, "Error accessing pubkey store: " + e, e); return null; } } /* (non-Javadoc) * @see freenet.node.GetPubkey#cacheKey(byte[], freenet.crypt.DSAPublicKey, boolean, boolean, boolean, boolean, boolean) */ @Override public void cacheKey(byte[] hash, DSAPublicKey key, boolean deep, boolean canWriteClientCache, boolean canWriteDatastore, boolean forULPR, boolean writeLocalToDatastore) { if (logMINOR) Logger.minor(this, "Cache key: " + HexUtil.bytesToHex(hash) + " : " + key); ByteArrayWrapper w = new ByteArrayWrapper(hash); synchronized (cachedPubKeys) { DSAPublicKey key2 = cachedPubKeys.get(w); if ((key2 != null) && !key2.equals(key)) throw new IllegalArgumentException("Wrong hash?? Already have different key with same hash!"); cachedPubKeys.push(w, key); while (cachedPubKeys.size() > MAX_MEMORY_CACHED_PUBKEYS) cachedPubKeys.popKey(); } try { if (canWriteClientCache && !(canWriteDatastore || writeLocalToDatastore)) { if(pubKeyClientcache != null) { pubKeyClientcache.put(hash, key, false); } } if (forULPR && !(canWriteDatastore || writeLocalToDatastore)) { if(pubKeySlashdotcache!= null) { pubKeySlashdotcache.put(hash, key, false); } } // Cannot write to the store or cache if request started nearby. if(!(canWriteDatastore || writeLocalToDatastore)) return; if (deep) { pubKeyDatastore.put(hash, key, !canWriteDatastore); } pubKeyDatacache.put(hash, key, !canWriteDatastore); } catch (IOException e) { // FIXME deal with disk full, access perms etc; tell user about it. Logger.error(this, "Error accessing pubkey store: " + e, e); } } public void setLocalDataStore(PubkeyStore pubKeyClientcache) { this.pubKeyClientcache = pubKeyClientcache; } public void setLocalSlashdotcache(PubkeyStore pubKeySlashdotcache) { this.pubKeySlashdotcache = pubKeySlashdotcache; } }