forked from I2P_Developers/i2p.i2p
stub out new API, needs testing
This commit is contained in:
@@ -335,8 +335,7 @@ public class BlockfileNamingService extends DummyNamingService {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
public Destination lookup(String hostname) {
|
|
||||||
Destination d = super.lookup(hostname);
|
Destination d = super.lookup(hostname);
|
||||||
if (d != null)
|
if (d != null)
|
||||||
return d;
|
return d;
|
||||||
|
@@ -7,6 +7,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.i2p.client.naming;
|
package net.i2p.client.naming;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
@@ -14,8 +20,12 @@ import net.i2p.data.Destination;
|
|||||||
* A Dummy naming service that can only handle base64 and b32 destinations.
|
* A Dummy naming service that can only handle base64 and b32 destinations.
|
||||||
*/
|
*/
|
||||||
class DummyNamingService extends NamingService {
|
class DummyNamingService extends NamingService {
|
||||||
|
private final Map<String, CacheEntry> _cache;
|
||||||
|
|
||||||
private static final int BASE32_HASH_LENGTH = 52; // 1 + Hash.HASH_LENGTH * 8 / 5
|
private static final int BASE32_HASH_LENGTH = 52; // 1 + Hash.HASH_LENGTH * 8 / 5
|
||||||
public final static String PROP_B32 = "i2p.naming.hostsTxt.useB32";
|
public final static String PROP_B32 = "i2p.naming.hostsTxt.useB32";
|
||||||
|
protected static final int CACHE_MAX_SIZE = 16;
|
||||||
|
public static final int DEST_SIZE = 516; // Std. Base64 length (no certificate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The naming service should only be constructed and accessed through the
|
* The naming service should only be constructed and accessed through the
|
||||||
@@ -23,11 +33,13 @@ class DummyNamingService extends NamingService {
|
|||||||
* appropriate application context itself.
|
* appropriate application context itself.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected DummyNamingService(I2PAppContext context) { super(context); }
|
protected DummyNamingService(I2PAppContext context) {
|
||||||
private DummyNamingService() { super(null); }
|
super(context);
|
||||||
|
_cache = new HashMap(CACHE_MAX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
Destination d = getCache(hostname);
|
Destination d = getCache(hostname);
|
||||||
if (d != null)
|
if (d != null)
|
||||||
return d;
|
return d;
|
||||||
@@ -52,4 +64,90 @@ class DummyNamingService extends NamingService {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = 7*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;
|
||||||
|
String oldestKey = null;
|
||||||
|
long oldestExp = Long.MAX_VALUE;
|
||||||
|
List<String> deleteList = new ArrayList(CACHE_MAX_SIZE);
|
||||||
|
for (Map.Entry<String, CacheEntry> entry : _cache.entrySet()) {
|
||||||
|
CacheEntry ce = 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 (String s : deleteList) {
|
||||||
|
_cache.remove(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = _cache.get(s);
|
||||||
|
if (ce == null)
|
||||||
|
return null;
|
||||||
|
if (ce.isExpired()) {
|
||||||
|
_cache.remove(s);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ce.dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.5 */
|
||||||
|
protected void removeCache(String s) {
|
||||||
|
synchronized (_cache) {
|
||||||
|
_cache.remove(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
public void clearCache() {
|
||||||
|
synchronized (_cache) {
|
||||||
|
_cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ import net.i2p.data.Destination;
|
|||||||
* i2p.naming.eepget.list=http://stats.i2p/cgi-bin/hostquery.cgi?a=,http://i2host.i2p/cgi-bin/i2hostquery?
|
* i2p.naming.eepget.list=http://stats.i2p/cgi-bin/hostquery.cgi?a=,http://i2host.i2p/cgi-bin/i2hostquery?
|
||||||
*
|
*
|
||||||
* @author zzz
|
* @author zzz
|
||||||
|
* @deprecated use HostsTxtNamingService.put()
|
||||||
* @since 0.7.9
|
* @since 0.7.9
|
||||||
*/
|
*/
|
||||||
public class EepGetAndAddNamingService extends EepGetNamingService {
|
public class EepGetAndAddNamingService extends EepGetNamingService {
|
||||||
|
@@ -7,6 +7,7 @@ package net.i2p.client.naming;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
@@ -25,6 +26,7 @@ import net.i2p.util.Log;
|
|||||||
* Should be used from MetaNamingService, after HostsTxtNamingService.
|
* Should be used from MetaNamingService, after HostsTxtNamingService.
|
||||||
* Cannot be used as the only NamingService! Be sure any naming service hosts
|
* Cannot be used as the only NamingService! Be sure any naming service hosts
|
||||||
* are in hosts.txt.
|
* are in hosts.txt.
|
||||||
|
* Supports caching, b32, and b64.
|
||||||
*
|
*
|
||||||
* Sample config to put in configadvanced.jsp (restart required):
|
* Sample config to put in configadvanced.jsp (restart required):
|
||||||
*
|
*
|
||||||
@@ -33,7 +35,7 @@ import net.i2p.util.Log;
|
|||||||
* i2p.naming.eepget.list=http://namingservice.i2p/cgi-bin/lkup.cgi?host=,http://i2host.i2p/cgi-bin/i2hostquery?
|
* i2p.naming.eepget.list=http://namingservice.i2p/cgi-bin/lkup.cgi?host=,http://i2host.i2p/cgi-bin/i2hostquery?
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class EepGetNamingService extends NamingService {
|
public class EepGetNamingService extends DummyNamingService {
|
||||||
|
|
||||||
private final static String PROP_EEPGET_LIST = "i2p.naming.eepget.list";
|
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 final static String DEFAULT_EEPGET_LIST = "http://i2host.i2p/cgi-bin/i2hostquery?";
|
||||||
@@ -59,22 +61,13 @@ public class EepGetNamingService extends NamingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
// If it's long, assume it's a key.
|
Destination d = super.lookup(hostname, null, null);
|
||||||
if (hostname.length() >= DEST_SIZE)
|
|
||||||
return lookupBase64(hostname);
|
|
||||||
|
|
||||||
hostname = hostname.toLowerCase();
|
|
||||||
|
|
||||||
// If you want b32, chain with HostsTxtNamingService
|
|
||||||
if (hostname.length() == 60 && hostname.endsWith(".b32.i2p"))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// check the cache
|
|
||||||
Destination d = getCache(hostname);
|
|
||||||
if (d != null)
|
if (d != null)
|
||||||
return d;
|
return d;
|
||||||
|
|
||||||
|
hostname = hostname.toLowerCase();
|
||||||
|
|
||||||
List URLs = getURLs();
|
List URLs = getURLs();
|
||||||
if (URLs.isEmpty())
|
if (URLs.isEmpty())
|
||||||
return null;
|
return null;
|
||||||
@@ -103,7 +96,6 @@ public class EepGetNamingService extends NamingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME allow larger Dests for non-null Certs
|
// 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 static final int MAX_RESPONSE = DEST_SIZE + 68 + 10; // allow for hostname= and some trailing stuff
|
||||||
private String fetchAddr(String url, String hostname) {
|
private String fetchAddr(String url, String hostname) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(MAX_RESPONSE);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(MAX_RESPONSE);
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
package net.i2p.client.naming;
|
package net.i2p.client.naming;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@@ -27,6 +28,7 @@ import net.i2p.util.Log;
|
|||||||
*
|
*
|
||||||
* Can be used from MetaNamingService, (e.g. after HostsTxtNamingService),
|
* Can be used from MetaNamingService, (e.g. after HostsTxtNamingService),
|
||||||
* or as the sole naming service.
|
* or as the sole naming service.
|
||||||
|
* Supports caching, b32, and b64.
|
||||||
*
|
*
|
||||||
* Sample chained config to put in configadvanced.jsp (restart required):
|
* Sample chained config to put in configadvanced.jsp (restart required):
|
||||||
*
|
*
|
||||||
@@ -40,7 +42,7 @@ import net.i2p.util.Log;
|
|||||||
* i2p.naming.exec.command=/usr/local/bin/i2presolve
|
* i2p.naming.exec.command=/usr/local/bin/i2presolve
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ExecNamingService extends NamingService {
|
public class ExecNamingService extends DummyNamingService {
|
||||||
|
|
||||||
private final static String PROP_EXEC_CMD = "i2p.naming.exec.command";
|
private final static String PROP_EXEC_CMD = "i2p.naming.exec.command";
|
||||||
private final static String DEFAULT_EXEC_CMD = "/usr/local/bin/i2presolve";
|
private final static String DEFAULT_EXEC_CMD = "/usr/local/bin/i2presolve";
|
||||||
@@ -59,22 +61,13 @@ public class ExecNamingService extends NamingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
// If it's long, assume it's a key.
|
Destination d = super.lookup(hostname, null, null);
|
||||||
if (hostname.length() >= DEST_SIZE)
|
|
||||||
return lookupBase64(hostname);
|
|
||||||
|
|
||||||
hostname = hostname.toLowerCase();
|
|
||||||
|
|
||||||
// If you want b32, chain with HostsTxtNamingService
|
|
||||||
if (hostname.length() == 60 && hostname.endsWith(".b32.i2p"))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// check the cache
|
|
||||||
Destination d = getCache(hostname);
|
|
||||||
if (d != null)
|
if (d != null)
|
||||||
return d;
|
return d;
|
||||||
|
|
||||||
|
hostname = hostname.toLowerCase();
|
||||||
|
|
||||||
// lookup
|
// lookup
|
||||||
String key = fetchAddr(hostname);
|
String key = fetchAddr(hostname);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
@@ -87,7 +80,6 @@ public class ExecNamingService extends NamingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME allow larger Dests for non-null Certs
|
// 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 static final int MAX_RESPONSE = DEST_SIZE + 68 + 10; // allow for hostname= and some trailing stuff
|
||||||
private String fetchAddr(String hostname) {
|
private String fetchAddr(String hostname) {
|
||||||
String[] commandArr = new String[3];
|
String[] commandArr = new String[3];
|
||||||
|
@@ -7,29 +7,21 @@
|
|||||||
*/
|
*/
|
||||||
package net.i2p.client.naming;
|
package net.i2p.client.naming;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataFormatException;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A naming service based on the "hosts.txt" file.
|
* A naming service based on multiple "hosts.txt" files.
|
||||||
|
* Supports .b32.i2p and {b64} lookups.
|
||||||
|
* Supports caching.
|
||||||
|
* All host names are converted to lower case.
|
||||||
*/
|
*/
|
||||||
public class HostsTxtNamingService extends DummyNamingService {
|
public class HostsTxtNamingService extends MetaNamingService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The naming service should only be constructed and accessed through the
|
* The naming service should only be constructed and accessed through the
|
||||||
@@ -37,8 +29,12 @@ public class HostsTxtNamingService extends DummyNamingService {
|
|||||||
* appropriate application context itself.
|
* appropriate application context itself.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public HostsTxtNamingService(I2PAppContext context) { super(context); }
|
public HostsTxtNamingService(I2PAppContext context) {
|
||||||
private HostsTxtNamingService() { super(null); }
|
super(context, null);
|
||||||
|
for (String name : getFilenames()) {
|
||||||
|
addNamingService(new SingleFileNamingService(context, name), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this system property is specified, the tunnel will read the
|
* If this system property is specified, the tunnel will read the
|
||||||
@@ -46,132 +42,39 @@ public class HostsTxtNamingService extends DummyNamingService {
|
|||||||
*/
|
*/
|
||||||
public final static String PROP_HOSTS_FILE = "i2p.hostsfilelist";
|
public final static String PROP_HOSTS_FILE = "i2p.hostsfilelist";
|
||||||
|
|
||||||
/** default hosts.txt filename */
|
/** default hosts.txt filenames */
|
||||||
public final static String DEFAULT_HOSTS_FILE =
|
public final static String DEFAULT_HOSTS_FILE =
|
||||||
"privatehosts.txt,userhosts.txt,hosts.txt";
|
"privatehosts.txt,userhosts.txt,hosts.txt";
|
||||||
|
|
||||||
private final static Log _log = new Log(HostsTxtNamingService.class);
|
private List<String> getFilenames() {
|
||||||
|
|
||||||
private List getFilenames() {
|
|
||||||
String list = _context.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
|
String list = _context.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
|
||||||
StringTokenizer tok = new StringTokenizer(list, ",");
|
StringTokenizer tok = new StringTokenizer(list, ",");
|
||||||
List rv = new ArrayList(tok.countTokens());
|
List<String> rv = new ArrayList(tok.countTokens());
|
||||||
while (tok.hasMoreTokens())
|
while (tok.hasMoreTokens())
|
||||||
rv.add(tok.nextToken());
|
rv.add(tok.nextToken());
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
Destination d = super.lookup(hostname);
|
// If it's long, assume it's a key.
|
||||||
if (d != null)
|
if (hostname.length() >= DEST_SIZE)
|
||||||
return d;
|
return lookupBase64(hostname);
|
||||||
|
return super.lookup(hostname.toLowerCase(), lookupOptions, storedOptions);
|
||||||
List filenames = getFilenames();
|
|
||||||
for (int i = 0; i < filenames.size(); i++) {
|
|
||||||
String hostsfile = (String)filenames.get(i);
|
|
||||||
try {
|
|
||||||
File f = new File(_context.getRouterDir(), hostsfile);
|
|
||||||
if ( (f.exists()) && (f.canRead()) ) {
|
|
||||||
String key = getKey(f, hostname.toLowerCase());
|
|
||||||
if ( (key != null) && (key.trim().length() > 0) ) {
|
|
||||||
d = lookupBase64(key);
|
|
||||||
putCache(hostname, d);
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
_log.warn("Hosts file " + hostsfile + " does not exist.");
|
|
||||||
}
|
|
||||||
} catch (Exception ioe) {
|
|
||||||
_log.error("Error loading hosts file " + hostsfile, ioe);
|
|
||||||
}
|
|
||||||
// not found, continue to the next file
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String reverseLookup(Destination dest) {
|
public boolean put(String hostname, Destination d, Properties options) {
|
||||||
String destkey = dest.toBase64();
|
return super.put(hostname.toLowerCase(), d, options);
|
||||||
List filenames = getFilenames();
|
|
||||||
for (int i = 0; i < filenames.size(); i++) {
|
|
||||||
String hostsfile = (String)filenames.get(i);
|
|
||||||
Properties hosts = new Properties();
|
|
||||||
try {
|
|
||||||
File f = new File(_context.getRouterDir(), hostsfile);
|
|
||||||
if ( (f.exists()) && (f.canRead()) ) {
|
|
||||||
DataHelper.loadProps(hosts, f, true);
|
|
||||||
Set keyset = hosts.keySet();
|
|
||||||
Iterator iter = keyset.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
String host = (String)iter.next();
|
|
||||||
String key = hosts.getProperty(host);
|
|
||||||
if (destkey.equals(key))
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ioe) {
|
|
||||||
_log.error("Error loading hosts file " + hostsfile, ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated unused */
|
|
||||||
@Override
|
@Override
|
||||||
public String reverseLookup(Hash h) {
|
public boolean putIfAbsent(String hostname, Destination d, Properties options) {
|
||||||
List filenames = getFilenames();
|
return super.putIfAbsent(hostname.toLowerCase(), d, options);
|
||||||
for (int i = 0; i < filenames.size(); i++) {
|
|
||||||
String hostsfile = (String)filenames.get(i);
|
|
||||||
Properties hosts = new Properties();
|
|
||||||
try {
|
|
||||||
File f = new File(_context.getRouterDir(), hostsfile);
|
|
||||||
if ( (f.exists()) && (f.canRead()) ) {
|
|
||||||
DataHelper.loadProps(hosts, f, true);
|
|
||||||
Set keyset = hosts.keySet();
|
|
||||||
Iterator iter = keyset.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
String host = (String)iter.next();
|
|
||||||
String key = hosts.getProperty(host);
|
|
||||||
try {
|
|
||||||
Destination destkey = new Destination();
|
|
||||||
destkey.fromBase64(key);
|
|
||||||
if (h.equals(destkey.calculateHash()))
|
|
||||||
return host;
|
|
||||||
} catch (DataFormatException dfe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ioe) {
|
|
||||||
_log.error("Error loading hosts file " + hostsfile, ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Better than DataHelper.loadProps(), doesn't load the whole file into memory,
|
public boolean remove(String hostname, Properties options) {
|
||||||
* and stops when it finds a match.
|
return super.remove(hostname.toLowerCase(), options);
|
||||||
*
|
|
||||||
* @param host lower case
|
|
||||||
* @since 0.7.13
|
|
||||||
*/
|
|
||||||
private static String getKey(File file, String host) throws IOException {
|
|
||||||
BufferedReader in = null;
|
|
||||||
try {
|
|
||||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"), 16*1024);
|
|
||||||
String line = null;
|
|
||||||
while ( (line = in.readLine()) != null) {
|
|
||||||
if (!line.toLowerCase().startsWith(host + '='))
|
|
||||||
continue;
|
|
||||||
if (line.indexOf('#') > 0) // trim off any end of line comment
|
|
||||||
line = line.substring(0, line.indexOf('#')).trim();
|
|
||||||
int split = line.indexOf('=');
|
|
||||||
return line.substring(split+1); //.trim() ??????????????
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,61 +2,154 @@ package net.i2p.client.naming;
|
|||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
public class MetaNamingService extends NamingService {
|
/**
|
||||||
|
* A naming service of multiple naming services.
|
||||||
|
* Supports .b32.i2p and {b64} lookups.
|
||||||
|
* Supports caching.
|
||||||
|
*/
|
||||||
|
public class MetaNamingService extends DummyNamingService {
|
||||||
|
|
||||||
private final static String PROP_NAME_SERVICES = "i2p.nameservicelist";
|
private final static String PROP_NAME_SERVICES = "i2p.nameservicelist";
|
||||||
private final static String DEFAULT_NAME_SERVICES =
|
private final static String DEFAULT_NAME_SERVICES =
|
||||||
"net.i2p.client.naming.PetNameNamingService,net.i2p.client.naming.HostsTxtNamingService";
|
"net.i2p.client.naming.HostsTxtNamingService";
|
||||||
private List _services;
|
|
||||||
|
protected final List<NamingService> _services;
|
||||||
|
|
||||||
public MetaNamingService(I2PAppContext context) {
|
public MetaNamingService(I2PAppContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
String list = _context.getProperty(PROP_NAME_SERVICES, DEFAULT_NAME_SERVICES);
|
String list = _context.getProperty(PROP_NAME_SERVICES, DEFAULT_NAME_SERVICES);
|
||||||
StringTokenizer tok = new StringTokenizer(list, ",");
|
StringTokenizer tok = new StringTokenizer(list, ",");
|
||||||
_services = new ArrayList(tok.countTokens());
|
_services = new CopyOnWriteArrayList();
|
||||||
while (tok.hasMoreTokens()) {
|
while (tok.hasMoreTokens()) {
|
||||||
try {
|
try {
|
||||||
Class cls = Class.forName(tok.nextToken());
|
Class cls = Class.forName(tok.nextToken());
|
||||||
Constructor con = cls.getConstructor(new Class[] { I2PAppContext.class });
|
Constructor con = cls.getConstructor(new Class[] { I2PAppContext.class });
|
||||||
_services.add(con.newInstance(new Object[] { context }));
|
addNamingService((NamingService)con.newInstance(new Object[] { context }), false);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
_services.add(new DummyNamingService(context)); // fallback
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param if non-null, services to be added. If null, this will only handle b32 and b64.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public MetaNamingService(I2PAppContext context, List<NamingService> services) {
|
||||||
|
super(context);
|
||||||
|
_services = new CopyOnWriteArrayList();
|
||||||
|
if (services != null) {
|
||||||
|
for (NamingService ns : services) {
|
||||||
|
addNamingService(ns, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public boolean addNamingService(NamingService ns, boolean head) {
|
||||||
Iterator iter = _services.iterator();
|
if (head)
|
||||||
while (iter.hasNext()) {
|
_services.add(0, ns);
|
||||||
NamingService ns = (NamingService)iter.next();
|
else
|
||||||
Destination dest = ns.lookup(hostname);
|
_services.add(ns);
|
||||||
if (dest != null) {
|
return true;
|
||||||
return dest;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<NamingService> getNamingServices() {
|
||||||
|
return new Collections.unmodifiableList(_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeNamingService(NamingService ns) {
|
||||||
|
return _services.remove(ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerListener(NamingServiceListener nsl) {
|
||||||
|
for (NamingService ns : _services) {
|
||||||
|
ns.registerListener(nsl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterListener(NamingServiceListener nsl) {
|
||||||
|
for (NamingService ns : _services) {
|
||||||
|
ns.unregisterListener(nsl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||||
|
Destination d = super.lookup(hostname, null, null);
|
||||||
|
if (d != null)
|
||||||
|
return d;
|
||||||
|
|
||||||
|
for (NamingService ns : _services) {
|
||||||
|
d = ns.lookup(hostname, lookupOptions, storedOptions);
|
||||||
|
if (d != null) {
|
||||||
|
putCache(hostname, d);
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lookupBase64(hostname);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String reverseLookup(Destination dest) {
|
public String reverseLookup(Destination dest, Properties options) {
|
||||||
Iterator iter = _services.iterator();
|
for (NamingService ns : _services) {
|
||||||
while (iter.hasNext()) {
|
String host = ns.reverseLookup(dest, options);
|
||||||
NamingService ns = (NamingService)iter.next();
|
if (host != null) {
|
||||||
String hostname = ns.reverseLookup(dest);
|
return host;
|
||||||
if (hostname != null) {
|
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores in the last service
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean put(String hostname, Destination d, Properties options) {
|
||||||
|
if (_services.isEmpty())
|
||||||
|
return false;
|
||||||
|
boolean rv = _services.get(_services.size() - 1).put(hostname, d, options);
|
||||||
|
// overwrite any previous entry in case it changed
|
||||||
|
if (rv)
|
||||||
|
putCache(hostname, d);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores in the last service
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean putIfAbsent(String hostname, Destination d, Properties options) {
|
||||||
|
if (_services.isEmpty())
|
||||||
|
return false;
|
||||||
|
return _services.get(_services.size() - 1).putIfAbsent(hostname, d, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes from all services
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean remove(String hostname, Properties options) {
|
||||||
|
boolean rv = false;
|
||||||
|
for (NamingService ns : _services) {
|
||||||
|
if (ns.remove(hostname, options))
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
if (rv)
|
||||||
|
removeCache(hostname);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,13 @@ package net.i2p.client.naming;
|
|||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
@@ -25,15 +29,12 @@ import net.i2p.util.Log;
|
|||||||
public abstract class NamingService {
|
public abstract class NamingService {
|
||||||
|
|
||||||
private final static Log _log = new Log(NamingService.class);
|
private final static Log _log = new Log(NamingService.class);
|
||||||
protected I2PAppContext _context;
|
protected final I2PAppContext _context;
|
||||||
private /* FIXME final FIXME */ HashMap _cache;
|
protected final Set<NamingServiceListener> _listeners;
|
||||||
|
|
||||||
/** what classname should be used as the naming service impl? */
|
/** what classname should be used as the naming service impl? */
|
||||||
public static final String PROP_IMPL = "i2p.naming.impl";
|
public static final String PROP_IMPL = "i2p.naming.impl";
|
||||||
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
|
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
|
||||||
|
|
||||||
protected static final int CACHE_MAX_SIZE = 16;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The naming service should only be constructed and accessed through the
|
* The naming service should only be constructed and accessed through the
|
||||||
@@ -43,9 +44,7 @@ public abstract class NamingService {
|
|||||||
*/
|
*/
|
||||||
protected NamingService(I2PAppContext context) {
|
protected NamingService(I2PAppContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
_cache = new HashMap(CACHE_MAX_SIZE);
|
_listeners = new CopyOnWriteArraySet();
|
||||||
}
|
|
||||||
private NamingService() { // nop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +52,9 @@ public abstract class NamingService {
|
|||||||
* @return the Destination for this host name, or
|
* @return the Destination for this host name, or
|
||||||
* <code>null</code> if name is unknown.
|
* <code>null</code> if name is unknown.
|
||||||
*/
|
*/
|
||||||
public abstract Destination lookup(String hostname);
|
public Destination lookup(String hostname) {
|
||||||
|
return lookup(hostname, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse look up a destination
|
* Reverse look up a destination
|
||||||
@@ -61,10 +62,12 @@ public abstract class NamingService {
|
|||||||
* if none is known. It is safe for subclasses to always return
|
* if none is known. It is safe for subclasses to always return
|
||||||
* <code>null</code> if no reverse lookup is possible.
|
* <code>null</code> if no reverse lookup is possible.
|
||||||
*/
|
*/
|
||||||
public String reverseLookup(Destination dest) { return null; };
|
public String reverseLookup(Destination dest) {
|
||||||
|
return reverseLookup(dest, null);
|
||||||
|
}
|
||||||
|
|
||||||
/** @deprecated unused */
|
/** @deprecated unused */
|
||||||
public String reverseLookup(Hash h) { return null; };
|
public String reverseLookup(Hash h) { return null; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if host name is valid Base64 encoded dest and return this
|
* Check if host name is valid Base64 encoded dest and return this
|
||||||
@@ -82,6 +85,313 @@ public abstract class NamingService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///// New API Starts Here
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Class simple name by default
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return NamingService-specific options or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Properties getConfiguration() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean setConfiguration(Properties p) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are for daisy chaining (MetaNamingService)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return chained naming services or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public List<NamingService> getNamingServices() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return parent naming service or null if this is the root
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public NamingService getParent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for chaining-capable NamingServices. Add to end of the list.
|
||||||
|
* @return success
|
||||||
|
*/
|
||||||
|
public boolean addNamingService(NamingService ns) {
|
||||||
|
return addNamingService(ns, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for chaining-capable NamingServices
|
||||||
|
* @param head or tail
|
||||||
|
* @return success
|
||||||
|
*/
|
||||||
|
public boolean addNamingService(NamingService ns, boolean head) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for chaining-capable NamingServices
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean removeNamingService(NamingService ns) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// options would be used to specify public / private / master ...
|
||||||
|
// or should we just daisy chain 3 HostsTxtNamingServices ?
|
||||||
|
// that might be better... then addressbook only talks to the 'router' HostsTxtNamingService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return number of entries or -1 if unknown
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return size(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return number of entries (matching the options if non-null) or -1 if unknown
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public int size(Properties options) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all mappings
|
||||||
|
* or empty Map if none;
|
||||||
|
* Returned Map is not necessarily sorted, implementation dependent
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Map<String, Destination> getEntries() {
|
||||||
|
return getEntries(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return all mappings (matching the options if non-null)
|
||||||
|
* or empty Map if none;
|
||||||
|
* Returned Map is not necessarily sorted, implementation dependent
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Map<String, Destination> getEntries(Properties options) {
|
||||||
|
return Collections.EMPTY_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This may be more or less efficient than getEntries()
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return all mappings (matching the options if non-null)
|
||||||
|
* or empty Map if none;
|
||||||
|
* Returned Map is not necessarily sorted, implementation dependent
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Map<String, String> getBase64Entries(Properties options) {
|
||||||
|
return Collections.EMPTY_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all known host names
|
||||||
|
* or empty Set if none;
|
||||||
|
* Returned Set is not necessarily sorted, implementation dependent
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Set<String> getNames() {
|
||||||
|
return getNames(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return all known host names (matching the options if non-null)
|
||||||
|
* or empty Set if none;
|
||||||
|
* Returned Set is not necessarily sorted, implementation dependent
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Set<String> getNames(Properties options) {
|
||||||
|
return Collections.EMPTY_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean put(String hostname, Destination d) {
|
||||||
|
return put(hostname, d, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean put(String hostname, Destination d, Properties options) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails if entry previously exists
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean putIfAbsent(String hostname, Destination d) {
|
||||||
|
return putIfAbsent(hostname, d, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails if entry previously exists
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean putIfAbsent(String hostname, Destination d, Properties options) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean putAll(Map<String, Destination> entries, Properties options) {
|
||||||
|
boolean rv = true;
|
||||||
|
for (Map.Entry<String, Destination> entry : entries.entrySet()) {
|
||||||
|
if (!put(entry.getKey(), entry.getValue(), options))
|
||||||
|
rv = false;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails if entry did not previously exist
|
||||||
|
* @param d may be null if only options are changing
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean update(String hostname, Destination d, Properties options) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean remove(String hostname) {
|
||||||
|
return remove(hostname, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return success
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public boolean remove(String hostname, Properties options) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the NamingService to update its database
|
||||||
|
* Should this be a separate interface? This is what addressbook needs
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public void requestUpdate(Properties options) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public void registerListener(NamingServiceListener nsl) {
|
||||||
|
_listeners.add(nsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public void unregisterListener(NamingServiceListener nsl) {
|
||||||
|
_listeners.remove(nsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as lookup(hostname) but with in and out options
|
||||||
|
* Note that whether this (and lookup(hostname)) resolve B32 addresses is
|
||||||
|
* NamingService-specific.
|
||||||
|
* @param lookupOptions input parameter, NamingService-specific, can be null
|
||||||
|
* @param storedOptions output parameter, NamingService-specific, any stored properties will be added if non-null
|
||||||
|
* @return dest or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public abstract Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as reverseLookup(dest) but with options
|
||||||
|
* @param options NamingService-specific, can be null
|
||||||
|
* @return host name or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public String reverseLookup(Destination d, Properties options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup a Base 32 address. This may require the router to fetch the LeaseSet,
|
||||||
|
* which may take quite a while.
|
||||||
|
* @param hostname must be {52 chars}.b32.i2p
|
||||||
|
* @param timeout in seconds; <= 0 means use router default
|
||||||
|
* @return dest or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Destination lookupBase32(String hostname, int timeout) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as lookupB32 but with the SHA256 Hash precalculated
|
||||||
|
* @param timeout in seconds; <= 0 means use router default
|
||||||
|
* @return dest or null
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public Destination lookup(Hash hash, int timeout) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent will call when added.
|
||||||
|
* If this is the root naming service, the core will start it.
|
||||||
|
* Should not be called by others.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public void start() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent will call when removed.
|
||||||
|
* If this is the root naming service, the core will stop it.
|
||||||
|
* Should not be called by others.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
public void stop() {}
|
||||||
|
|
||||||
|
//// End New API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a naming service instance. This method ensures that there
|
* Get a naming service instance. This method ensures that there
|
||||||
* will be only one naming service instance (singleton) as well as
|
* will be only one naming service instance (singleton) as well as
|
||||||
@@ -102,83 +412,4 @@ public abstract class NamingService {
|
|||||||
return instance;
|
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 = 7*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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @since 0.8.1 */
|
|
||||||
public void clearCache() {
|
|
||||||
synchronized (_cache) {
|
|
||||||
_cache.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user