From f6996c7d8b616d55657b5323e08ffbc19a40251d Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 2 Nov 2008 21:41:08 +0000 Subject: [PATCH] * NamingServices: Implement caching in the abstract class --- .../client/naming/EepGetNamingService.java | 19 ++--- .../i2p/client/naming/ExecNamingService.java | 19 ++--- .../client/naming/HostsTxtNamingService.java | 19 +++-- .../net/i2p/client/naming/NamingService.java | 81 +++++++++++++++++++ 4 files changed, 110 insertions(+), 28 deletions(-) diff --git a/core/java/src/net/i2p/client/naming/EepGetNamingService.java b/core/java/src/net/i2p/client/naming/EepGetNamingService.java index 0b3c7d08e5..a90b53d4dc 100644 --- a/core/java/src/net/i2p/client/naming/EepGetNamingService.java +++ b/core/java/src/net/i2p/client/naming/EepGetNamingService.java @@ -7,7 +7,6 @@ package net.i2p.client.naming; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; -import java.util.Properties; import java.util.StringTokenizer; import net.i2p.I2PAppContext; @@ -38,7 +37,6 @@ public class EepGetNamingService extends NamingService { private final static String PROP_EEPGET_LIST = "i2p.naming.eepget.list"; private final static String DEFAULT_EEPGET_LIST = "http://i2host.i2p/cgi-bin/i2hostquery?"; - private static Properties _hosts; private final static Log _log = new Log(EepGetNamingService.class); /** @@ -49,7 +47,6 @@ public class EepGetNamingService extends NamingService { */ public EepGetNamingService(I2PAppContext context) { super(context); - _hosts = new Properties(); } private List getURLs() { @@ -69,11 +66,9 @@ public class EepGetNamingService extends NamingService { hostname = hostname.toLowerCase(); // check the cache - String key = _hosts.getProperty(hostname); - if (key != null) { - _log.error("Found in cache: " + hostname); - return lookupBase64(key); - } + Destination d = getCache(hostname); + if (d != null) + return d; List URLs = getURLs(); if (URLs.size() == 0) @@ -91,16 +86,18 @@ public class EepGetNamingService extends NamingService { // lookup for (int i = 0; i < URLs.size(); i++) { String url = (String)URLs.get(i); - key = fetchAddr(url, hostname); + String key = fetchAddr(url, hostname); if (key != null) { _log.error("Success: " + url + hostname); - _hosts.setProperty(hostname, key); // cache - return lookupBase64(key); + d = lookupBase64(key); + putCache(hostname, d); + return d; } } return null; } + // FIXME allow larger Dests for non-null Certs private static final int DEST_SIZE = 516; // Std. Base64 length (no certificate) private static final int MAX_RESPONSE = DEST_SIZE + 68 + 10; // allow for hostname= and some trailing stuff private String fetchAddr(String url, String hostname) { diff --git a/core/java/src/net/i2p/client/naming/ExecNamingService.java b/core/java/src/net/i2p/client/naming/ExecNamingService.java index 15b770ec88..b5dd442465 100644 --- a/core/java/src/net/i2p/client/naming/ExecNamingService.java +++ b/core/java/src/net/i2p/client/naming/ExecNamingService.java @@ -5,7 +5,6 @@ package net.i2p.client.naming; import java.io.InputStream; -import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.data.Destination; @@ -47,7 +46,6 @@ public class ExecNamingService extends NamingService { private final static String DEFAULT_EXEC_CMD = "/usr/local/bin/i2presolve"; private final static String PROP_SHELL_CMD = "i2p.naming.exec.shell"; private final static String DEFAULT_SHELL_CMD = "/bin/bash"; - private static Properties _hosts; private final static Log _log = new Log(ExecNamingService.class); /** @@ -58,7 +56,6 @@ public class ExecNamingService extends NamingService { */ public ExecNamingService(I2PAppContext context) { super(context); - _hosts = new Properties(); } public Destination lookup(String hostname) { @@ -69,22 +66,22 @@ public class ExecNamingService extends NamingService { hostname = hostname.toLowerCase(); // check the cache - String key = _hosts.getProperty(hostname); - if (key != null) { - _log.error("Found in cache: " + hostname); - return lookupBase64(key); - } + Destination d = getCache(hostname); + if (d != null) + return d; // lookup - key = fetchAddr(hostname); + String key = fetchAddr(hostname); if (key != null) { _log.error("Success: " + hostname); - _hosts.setProperty(hostname, key); // cache - return lookupBase64(key); + d = lookupBase64(key); + putCache(hostname, d); + return d; } return null; } + // FIXME allow larger Dests for non-null Certs private static final int DEST_SIZE = 516; // Std. Base64 length (no certificate) private static final int MAX_RESPONSE = DEST_SIZE + 68 + 10; // allow for hostname= and some trailing stuff private String fetchAddr(String hostname) { diff --git a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java index c07c1a7711..2307816be8 100644 --- a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java +++ b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java @@ -56,13 +56,18 @@ public class HostsTxtNamingService extends NamingService { } public Destination lookup(String hostname) { + Destination d = getCache(hostname); + if (d != null) + return d; + // If it's long, assume it's a key. - if (hostname.length() >= 516) - return lookupBase64(hostname); + if (hostname.length() >= 516) { + d = lookupBase64(hostname); + // What the heck, cache these too + putCache(hostname, d); + return d; + } - // check the list each time, reloading the file on each - // lookup - List filenames = getFilenames(); for (int i = 0; i < filenames.size(); i++) { String hostsfile = (String)filenames.get(i); @@ -74,7 +79,9 @@ public class HostsTxtNamingService extends NamingService { String key = hosts.getProperty(hostname.toLowerCase()); if ( (key != null) && (key.trim().length() > 0) ) { - return lookupBase64(key); + d = lookupBase64(key); + putCache(hostname, d); + return d; } } else { diff --git a/core/java/src/net/i2p/client/naming/NamingService.java b/core/java/src/net/i2p/client/naming/NamingService.java index 5f8f80bf95..5b61b1bcf8 100644 --- a/core/java/src/net/i2p/client/naming/NamingService.java +++ b/core/java/src/net/i2p/client/naming/NamingService.java @@ -8,6 +8,10 @@ package net.i2p.client.naming; import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; @@ -21,11 +25,14 @@ public abstract class NamingService { private final static Log _log = new Log(NamingService.class); protected I2PAppContext _context; + private HashMap _cache; /** what classname should be used as the naming service impl? */ public static final String PROP_IMPL = "i2p.naming.impl"; private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService"; + protected static final int CACHE_MAX_SIZE = 8; + /** * The naming service should only be constructed and accessed through the @@ -35,6 +42,7 @@ public abstract class NamingService { */ protected NamingService(I2PAppContext context) { _context = context; + _cache = new HashMap(CACHE_MAX_SIZE); } private NamingService() { // nop } @@ -89,4 +97,77 @@ public abstract class NamingService { } return instance; } + + /** + * Provide basic caching for the service + * The service may override the age and/or size limit + */ + /** Don't know why a dest would ever change but keep this short anyway */ + protected static final long CACHE_MAX_AGE = 60*1000; + + private class CacheEntry { + public Destination dest; + public long exp; + public CacheEntry(Destination d) { + dest = d; + exp = _context.clock().now() + CACHE_MAX_AGE; + } + public boolean isExpired() { + return exp < _context.clock().now(); + } + } + + /** + * Clean up when full. + * Don't bother removing old entries unless full. + * Caller must synchronize on _cache. + */ + private void cacheClean() { + if (_cache.size() < CACHE_MAX_SIZE) + return; + boolean full = true; + Object oldestKey = null; + long oldestExp = Long.MAX_VALUE; + ArrayList deleteList = new ArrayList(CACHE_MAX_SIZE); + for (Iterator iter = _cache.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = (Map.Entry) iter.next(); + CacheEntry ce = (CacheEntry) entry.getValue(); + if (ce.isExpired()) { + deleteList.add(entry.getKey()); + full = false; + continue; + } + if (oldestKey == null || ce.exp < oldestExp) { + oldestKey = entry.getKey(); + oldestExp = ce.exp; + } + } + if (full && oldestKey != null) + deleteList.add(oldestKey); + for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) { + _cache.remove(iter.next()); + } + } + + protected void putCache(String s, Destination d) { + if (d == null) + return; + synchronized (_cache) { + _cache.put(s, new CacheEntry(d)); + cacheClean(); + } + } + + protected Destination getCache(String s) { + synchronized (_cache) { + CacheEntry ce = (CacheEntry) _cache.get(s); + if (ce == null) + return null; + if (ce.isExpired()) { + _cache.remove(s); + return null; + } + return ce.dest; + } + } } -- GitLab