forked from I2P_Developers/i2p.i2p
NamingService: Add export methods,
fill in subclass methods for efficient exporting, fill in getBase64Entires() and getNames() where unimplemented SusiDNS: Add export support, no UI yet
This commit is contained in:
@ -18,11 +18,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -860,7 +863,7 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Map<String, Destination> rv = new HashMap<String, Destination>();
|
||||
Map<String, Destination> rv = new TreeMap<String, Destination>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
// don't bother validating here
|
||||
iter.next();
|
||||
@ -896,6 +899,188 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
* Key "skip": skip that many entries
|
||||
* Key "limit": max number to return
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* Key "beginWith": start here in the iteration
|
||||
* Don't use both startsWith and beginWith.
|
||||
* Search, startsWith, and beginWith values must be lower case.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
String listname = FALLBACK_LIST;
|
||||
String search = null;
|
||||
String startsWith = null;
|
||||
String beginWith = null;
|
||||
int limit = Integer.MAX_VALUE;
|
||||
int skip = 0;
|
||||
if (options != null) {
|
||||
String ln = options.getProperty("list");
|
||||
if (ln != null)
|
||||
listname = ln;
|
||||
search = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
beginWith = options.getProperty("beginWith");
|
||||
if (beginWith == null && startsWith != null) {
|
||||
if (startsWith.equals("[0-9]"))
|
||||
beginWith = "0";
|
||||
else
|
||||
beginWith = startsWith;
|
||||
}
|
||||
String lim = options.getProperty("limit");
|
||||
try {
|
||||
limit = Integer.parseInt(lim);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
String sk = options.getProperty("skip");
|
||||
try {
|
||||
skip = Integer.parseInt(sk);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
synchronized(_bf) {
|
||||
if (_isClosed)
|
||||
return Collections.emptyMap();
|
||||
try {
|
||||
SkipList sl = _bf.getIndex(listname, _stringSerializer, _destSerializer);
|
||||
if (sl == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No skiplist found for lookup in " + listname);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
SkipIterator iter;
|
||||
if (beginWith != null)
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Map<String, String> rv = new TreeMap<String, String>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
// don't bother validating here
|
||||
iter.next();
|
||||
}
|
||||
for (int i = 0; i < limit && iter.hasNext(); ) {
|
||||
String key = (String) iter.nextKey();
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (key.charAt(0) > '9')
|
||||
break;
|
||||
} else if (!key.startsWith(startsWith)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
DestEntry de = (DestEntry) iter.next();
|
||||
if (!validate(key, de, listname))
|
||||
continue;
|
||||
if (search != null && key.indexOf(search) < 0)
|
||||
continue;
|
||||
rv.put(key, de.dest.toBase64());
|
||||
i++;
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("DB lookup error", ioe);
|
||||
return Collections.emptyMap();
|
||||
} catch (RuntimeException re) {
|
||||
_log.error("DB lookup error", re);
|
||||
return Collections.emptyMap();
|
||||
} finally {
|
||||
deleteInvalid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
* Key "skip": skip that many entries
|
||||
* Key "limit": max number to return
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* Key "beginWith": start here in the iteration
|
||||
* Don't use both startsWith and beginWith.
|
||||
* Search, startsWith, and beginWith values must be lower case.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getNames(Properties options) {
|
||||
String listname = FALLBACK_LIST;
|
||||
String search = null;
|
||||
String startsWith = null;
|
||||
String beginWith = null;
|
||||
int limit = Integer.MAX_VALUE;
|
||||
int skip = 0;
|
||||
if (options != null) {
|
||||
String ln = options.getProperty("list");
|
||||
if (ln != null)
|
||||
listname = ln;
|
||||
search = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
beginWith = options.getProperty("beginWith");
|
||||
if (beginWith == null && startsWith != null) {
|
||||
if (startsWith.equals("[0-9]"))
|
||||
beginWith = "0";
|
||||
else
|
||||
beginWith = startsWith;
|
||||
}
|
||||
String lim = options.getProperty("limit");
|
||||
try {
|
||||
limit = Integer.parseInt(lim);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
String sk = options.getProperty("skip");
|
||||
try {
|
||||
skip = Integer.parseInt(sk);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
synchronized(_bf) {
|
||||
if (_isClosed)
|
||||
return Collections.emptySet();
|
||||
try {
|
||||
SkipList sl = _bf.getIndex(listname, _stringSerializer, _destSerializer);
|
||||
if (sl == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No skiplist found for lookup in " + listname);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
SkipIterator iter;
|
||||
if (beginWith != null)
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Set<String> rv = new HashSet<String>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
iter.next();
|
||||
}
|
||||
for (int i = 0; i < limit && iter.hasNext(); ) {
|
||||
String key = (String) iter.nextKey();
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (key.charAt(0) > '9')
|
||||
break;
|
||||
} else if (!key.startsWith(startsWith)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (search != null && key.indexOf(search) < 0)
|
||||
continue;
|
||||
rv.add(key);
|
||||
i++;
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("DB lookup error", ioe);
|
||||
return Collections.emptySet();
|
||||
} catch (RuntimeException re) {
|
||||
_log.error("DB lookup error", re);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options ignored
|
||||
* @since 0.8.9
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -179,6 +181,19 @@ public class MetaNamingService extends DummyNamingService {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
Map<String, String> rv = new HashMap<String, String>();
|
||||
for (NamingService ns : _services) {
|
||||
rv.putAll(ns.getBase64Entries(options));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
*/
|
||||
@ -191,6 +206,17 @@ public class MetaNamingService extends DummyNamingService {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated.
|
||||
* Duplicates not removed (for efficiency)
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
for (NamingService ns : _services) {
|
||||
export(out, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
*/
|
||||
|
@ -7,12 +7,16 @@
|
||||
*/
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -235,14 +239,75 @@ public abstract class NamingService {
|
||||
* Warning - This will bring the whole database into memory
|
||||
* if options is null, empty, or unsupported, use with caution.
|
||||
*
|
||||
* This implementation calls getEntries(options) and returns a SortedMap.
|
||||
* Subclasses should override if they store base64 natively.
|
||||
*
|
||||
* @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.7
|
||||
* @since 0.8.7, implemented in 0.9.20
|
||||
*/
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
return Collections.emptyMap();
|
||||
Map<String, Destination> entries = getEntries(options);
|
||||
if (entries.size() <= 0)
|
||||
return Collections.emptyMap();
|
||||
Map<String, String> rv = new TreeMap<String, String>();
|
||||
for (Map.Entry<String, Destination> e : entries.entrySet()) {
|
||||
rv.put(e.getKey(), e.getValue().toBase64());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export in a hosts.txt format.
|
||||
* Output is not necessarily sorted, implementation dependent.
|
||||
* Output may or may not contain comment lines, implementation dependent.
|
||||
* Caller must close writer.
|
||||
*
|
||||
* This implementation calls getBase64Entries().
|
||||
* Subclasses should override if they store in a hosts.txt format natively.
|
||||
*
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out) throws IOException {
|
||||
export(out, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export in a hosts.txt format.
|
||||
* Output is not necessarily sorted, implementation dependent.
|
||||
* Output may or may not contain comment lines, implementation dependent.
|
||||
* Caller must close writer.
|
||||
*
|
||||
* This implementation calls getBase64Entries(options).
|
||||
* Subclasses should override if they store in a hosts.txt format natively.
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
Map<String, String> entries = getBase64Entries(options);
|
||||
out.write("# Address book: ");
|
||||
out.write(getName());
|
||||
out.write('\n');
|
||||
int sz = entries.size();
|
||||
if (sz <= 0) {
|
||||
out.write("# No entries\n");
|
||||
return;
|
||||
}
|
||||
out.write("# Exported: ");
|
||||
out.write((new Date()).toString());
|
||||
out.write('\n');
|
||||
if (sz > 1) {
|
||||
out.write("# " + sz + " entries\n");
|
||||
}
|
||||
for (Map.Entry<String, String> e : entries.entrySet()) {
|
||||
out.write(e.getKey());
|
||||
out.write('=');
|
||||
out.write(e.getValue());
|
||||
out.write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,9 @@ import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -364,6 +366,102 @@ public class SingleFileNamingService extends NamingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden since we store base64 natively.
|
||||
*
|
||||
* @param options As follows:
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* @return all mappings (matching the options if non-null)
|
||||
* or empty Map if none.
|
||||
* Returned Map is not sorted.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
if (!_file.exists())
|
||||
return Collections.emptyMap();
|
||||
String searchOpt = null;
|
||||
String startsWith = null;
|
||||
if (options != null) {
|
||||
searchOpt = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
}
|
||||
BufferedReader in = null;
|
||||
getReadLock();
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_file), "UTF-8"), 16*1024);
|
||||
String line = null;
|
||||
Map<String, String> rv = new HashMap<String, String>();
|
||||
while ( (line = in.readLine()) != null) {
|
||||
if (line.length() <= 0)
|
||||
continue;
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (line.charAt(0) < '0' || line.charAt(0) > '9')
|
||||
continue;
|
||||
} else if (!line.startsWith(startsWith)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
if (line.indexOf('#') > 0) // trim off any end of line comment
|
||||
line = line.substring(0, line.indexOf('#')).trim();
|
||||
int split = line.indexOf('=');
|
||||
if (split <= 0)
|
||||
continue;
|
||||
String key = line.substring(0, split);
|
||||
if (searchOpt != null && key.indexOf(searchOpt) < 0)
|
||||
continue;
|
||||
String b64 = line.substring(split+1); //.trim() ??????????????
|
||||
if (b64.length() < 387)
|
||||
continue;
|
||||
rv.put(key, b64);
|
||||
}
|
||||
if (searchOpt == null && startsWith == null) {
|
||||
_lastWrite = _file.lastModified();
|
||||
_size = rv.size();
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("getEntries error", ioe);
|
||||
return Collections.emptyMap();
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden for efficiency.
|
||||
* Output is not sorted.
|
||||
*
|
||||
* @param options ignored
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
out.write("# Address book: ");
|
||||
out.write(getName());
|
||||
out.write('\n');
|
||||
out.write("# Exported: ");
|
||||
out.write((new Date()).toString());
|
||||
out.write('\n');
|
||||
BufferedReader in = null;
|
||||
getReadLock();
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_file), "UTF-8"), 16*1024);
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
out.write(line);
|
||||
out.write('\n');
|
||||
}
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options ignored
|
||||
* @return all known host names, unsorted
|
||||
|
Reference in New Issue
Block a user