* NamingServices: Implement caching in the abstract class
This commit is contained in:
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -56,12 +56,17 @@ public class HostsTxtNamingService extends NamingService {
|
||||
}
|
||||
|
||||
public Destination lookup(String hostname) {
|
||||
// If it's long, assume it's a key.
|
||||
if (hostname.length() >= 516)
|
||||
return lookupBase64(hostname);
|
||||
Destination d = getCache(hostname);
|
||||
if (d != null)
|
||||
return d;
|
||||
|
||||
// check the list each time, reloading the file on each
|
||||
// lookup
|
||||
// If it's long, assume it's a key.
|
||||
if (hostname.length() >= 516) {
|
||||
d = lookupBase64(hostname);
|
||||
// What the heck, cache these too
|
||||
putCache(hostname, d);
|
||||
return d;
|
||||
}
|
||||
|
||||
List filenames = getFilenames();
|
||||
for (int i = 0; i < filenames.size(); 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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user