forked from I2P_Developers/i2p.i2p
* Addressbook
- Store last-fetched time so we don't always fetch subscriptions after restart - Randomize first fetch time - Make most classes package private
This commit is contained in:
@ -38,7 +38,7 @@ import net.i2p.util.EepGet;
|
|||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class AddressBook {
|
class AddressBook {
|
||||||
|
|
||||||
private String location;
|
private String location;
|
||||||
|
|
||||||
@ -88,6 +88,8 @@ public class AddressBook {
|
|||||||
* read or cannot be read, return an empty AddressBook.
|
* read or cannot be read, return an empty AddressBook.
|
||||||
* Set a maximum size of the remote book to make it a little harder for a malicious book-sender.
|
* Set a maximum size of the remote book to make it a little harder for a malicious book-sender.
|
||||||
*
|
*
|
||||||
|
* Yes, the EepGet fetch() is done in this constructor.
|
||||||
|
*
|
||||||
* @param subscription
|
* @param subscription
|
||||||
* A Subscription instance pointing at a remote address book.
|
* A Subscription instance pointing at a remote address book.
|
||||||
* @param proxyHost hostname of proxy
|
* @param proxyHost hostname of proxy
|
||||||
@ -102,6 +104,7 @@ public class AddressBook {
|
|||||||
if (get.fetch()) {
|
if (get.fetch()) {
|
||||||
subscription.setEtag(get.getETag());
|
subscription.setEtag(get.getETag());
|
||||||
subscription.setLastModified(get.getLastModified());
|
subscription.setLastModified(get.getLastModified());
|
||||||
|
subscription.setLastFetched(I2PAppContext.getGlobalContext().clock().now());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.addresses = ConfigParser.parse(tmp);
|
this.addresses = ConfigParser.parse(tmp);
|
||||||
|
@ -47,7 +47,7 @@ import net.i2p.util.SecureFileOutputStream;
|
|||||||
*
|
*
|
||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*/
|
*/
|
||||||
public class ConfigParser {
|
class ConfigParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip the comments from a String. Lines that begin with '#' and ';' are
|
* Strip the comments from a String. Lines that begin with '#' and ';' are
|
||||||
@ -143,7 +143,7 @@ public class ConfigParser {
|
|||||||
* @param file
|
* @param file
|
||||||
* A File to attempt to parse.
|
* A File to attempt to parse.
|
||||||
* @param map
|
* @param map
|
||||||
* A Map to use as the default, if file fails.
|
* A Map containing values to use as defaults.
|
||||||
* @return A Map containing the key, value pairs from file, or if file
|
* @return A Map containing the key, value pairs from file, or if file
|
||||||
* cannot be read, map.
|
* cannot be read, map.
|
||||||
*/
|
*/
|
||||||
@ -151,6 +151,11 @@ public class ConfigParser {
|
|||||||
Map result;
|
Map result;
|
||||||
try {
|
try {
|
||||||
result = ConfigParser.parse(file);
|
result = ConfigParser.parse(file);
|
||||||
|
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
String key = (String) iter.next();
|
||||||
|
if (!result.containsKey(key))
|
||||||
|
result.put(key, map.get(key));
|
||||||
|
}
|
||||||
} catch (IOException exp) {
|
} catch (IOException exp) {
|
||||||
result = map;
|
result = map;
|
||||||
try {
|
try {
|
||||||
|
@ -38,7 +38,7 @@ import net.i2p.util.SecureDirectory;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Daemon {
|
public class Daemon {
|
||||||
public static final String VERSION = "2.0.3";
|
public static final String VERSION = "2.0.4";
|
||||||
private static final Daemon _instance = new Daemon();
|
private static final Daemon _instance = new Daemon();
|
||||||
private boolean _running;
|
private boolean _running;
|
||||||
|
|
||||||
@ -66,6 +66,7 @@ public class Daemon {
|
|||||||
router.merge(master, true, null);
|
router.merge(master, true, null);
|
||||||
Iterator iter = subscriptions.iterator();
|
Iterator iter = subscriptions.iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
|
// yes, the EepGet fetch() is done in next()
|
||||||
router.merge((AddressBook) iter.next(), false, log);
|
router.merge((AddressBook) iter.next(), false, log);
|
||||||
}
|
}
|
||||||
router.write();
|
router.write();
|
||||||
@ -97,6 +98,15 @@ public class Daemon {
|
|||||||
File etagsFile = new File(home, (String) settings.get("etags"));
|
File etagsFile = new File(home, (String) settings.get("etags"));
|
||||||
File lastModifiedFile = new File(home, (String) settings
|
File lastModifiedFile = new File(home, (String) settings
|
||||||
.get("last_modified"));
|
.get("last_modified"));
|
||||||
|
File lastFetchedFile = new File(home, (String) settings
|
||||||
|
.get("last_fetched"));
|
||||||
|
long delay;
|
||||||
|
try {
|
||||||
|
delay = Long.parseLong((String) settings.get("update_delay"));
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
delay = 12;
|
||||||
|
}
|
||||||
|
delay *= 60 * 60 * 1000;
|
||||||
|
|
||||||
AddressBook master = new AddressBook(masterFile);
|
AddressBook master = new AddressBook(masterFile);
|
||||||
AddressBook router = new AddressBook(routerFile);
|
AddressBook router = new AddressBook(routerFile);
|
||||||
@ -106,7 +116,7 @@ public class Daemon {
|
|||||||
defaultSubs.add("http://www.i2p2.i2p/hosts.txt");
|
defaultSubs.add("http://www.i2p2.i2p/hosts.txt");
|
||||||
|
|
||||||
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
||||||
etagsFile, lastModifiedFile, defaultSubs, (String) settings
|
etagsFile, lastModifiedFile, lastFetchedFile, delay, defaultSubs, (String) settings
|
||||||
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
|
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
|
||||||
Log log = new Log(logFile);
|
Log log = new Log(logFile);
|
||||||
|
|
||||||
@ -150,6 +160,7 @@ public class Daemon {
|
|||||||
defaultSettings.put("subscriptions", "subscriptions.txt");
|
defaultSettings.put("subscriptions", "subscriptions.txt");
|
||||||
defaultSettings.put("etags", "etags");
|
defaultSettings.put("etags", "etags");
|
||||||
defaultSettings.put("last_modified", "last_modified");
|
defaultSettings.put("last_modified", "last_modified");
|
||||||
|
defaultSettings.put("last_fetched", "last_fetched");
|
||||||
defaultSettings.put("update_delay", "12");
|
defaultSettings.put("update_delay", "12");
|
||||||
|
|
||||||
if (!homeFile.exists()) {
|
if (!homeFile.exists()) {
|
||||||
@ -165,7 +176,7 @@ public class Daemon {
|
|||||||
Map settings = ConfigParser.parse(settingsFile, defaultSettings);
|
Map settings = ConfigParser.parse(settingsFile, defaultSettings);
|
||||||
// wait
|
// wait
|
||||||
try {
|
try {
|
||||||
Thread.sleep(5*60*1000);
|
Thread.sleep(5*60*1000 + I2PAppContext.getGlobalContext().random().nextLong(5*60*1000));
|
||||||
// Static method, and redundent Thread.currentThread().sleep(5*60*1000);
|
// Static method, and redundent Thread.currentThread().sleep(5*60*1000);
|
||||||
} catch (InterruptedException ie) {}
|
} catch (InterruptedException ie) {}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ package net.i2p.addressbook;
|
|||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DaemonThread extends Thread {
|
class DaemonThread extends Thread {
|
||||||
|
|
||||||
private String[] args;
|
private String[] args;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import java.util.Date;
|
|||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Log {
|
class Log {
|
||||||
|
|
||||||
private File file;
|
private File file;
|
||||||
|
|
||||||
|
@ -27,13 +27,14 @@ package net.i2p.addressbook;
|
|||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Subscription {
|
class Subscription {
|
||||||
|
|
||||||
private String location;
|
private String location;
|
||||||
|
|
||||||
private String etag;
|
private String etag;
|
||||||
|
|
||||||
private String lastModified;
|
private String lastModified;
|
||||||
|
private long lastFetched;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a Subscription pointing to the address book at location, that
|
* Construct a Subscription pointing to the address book at location, that
|
||||||
@ -47,11 +48,17 @@ public class Subscription {
|
|||||||
* @param lastModified
|
* @param lastModified
|
||||||
* the last-modified header we recieved the last time we read
|
* the last-modified header we recieved the last time we read
|
||||||
* this subscription.
|
* this subscription.
|
||||||
|
* @param lastFetched when the subscription was last fetched (Java time, as a String)
|
||||||
*/
|
*/
|
||||||
public Subscription(String location, String etag, String lastModified) {
|
public Subscription(String location, String etag, String lastModified, String lastFetched) {
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.etag = etag;
|
this.etag = etag;
|
||||||
this.lastModified = lastModified;
|
this.lastModified = lastModified;
|
||||||
|
if (lastFetched != null) {
|
||||||
|
try {
|
||||||
|
this.lastFetched = Long.parseLong(lastFetched);
|
||||||
|
} catch (NumberFormatException nfe) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,4 +109,14 @@ public class Subscription {
|
|||||||
public void setLastModified(String lastModified) {
|
public void setLastModified(String lastModified) {
|
||||||
this.lastModified = lastModified;
|
this.lastModified = lastModified;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/** @since 0.8.2 */
|
||||||
|
public long getLastFetched() {
|
||||||
|
return this.lastFetched;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.2 */
|
||||||
|
public void setLastFetched(long t) {
|
||||||
|
this.lastFetched = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,31 +21,39 @@
|
|||||||
|
|
||||||
package net.i2p.addressbook;
|
package net.i2p.addressbook;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.DataHelper; // debug
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
|
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
|
||||||
* returns AddressBook objects, and not Subscription objects.
|
* returns AddressBook objects, and not Subscription objects.
|
||||||
|
* Yes, the EepGet fetch() is done in here in next().
|
||||||
*
|
*
|
||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*/
|
*/
|
||||||
public class SubscriptionIterator implements Iterator {
|
class SubscriptionIterator implements Iterator {
|
||||||
|
|
||||||
private Iterator subIterator;
|
private Iterator subIterator;
|
||||||
private String proxyHost;
|
private String proxyHost;
|
||||||
private int proxyPort;
|
private int proxyPort;
|
||||||
|
private final long delay;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
|
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
|
||||||
*
|
*
|
||||||
* @param subscriptions
|
* @param subscriptions
|
||||||
* List of Subscription objects that represent address books.
|
* List of Subscription objects that represent address books.
|
||||||
|
* @param delay the minimum delay since last fetched for the iterator to actually fetch
|
||||||
* @param proxyHost proxy hostname
|
* @param proxyHost proxy hostname
|
||||||
* @param proxyPort proxt port number
|
* @param proxyPort proxt port number
|
||||||
*/
|
*/
|
||||||
public SubscriptionIterator(List subscriptions, String proxyHost, int proxyPort) {
|
public SubscriptionIterator(List subscriptions, long delay, String proxyHost, int proxyPort) {
|
||||||
this.subIterator = subscriptions.iterator();
|
this.subIterator = subscriptions.iterator();
|
||||||
|
this.delay = delay;
|
||||||
this.proxyHost = proxyHost;
|
this.proxyHost = proxyHost;
|
||||||
this.proxyPort = proxyPort;
|
this.proxyPort = proxyPort;
|
||||||
}
|
}
|
||||||
@ -58,12 +66,24 @@ public class SubscriptionIterator implements Iterator {
|
|||||||
return this.subIterator.hasNext();
|
return this.subIterator.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see java.util.Iterator#next()
|
* Yes, the EepGet fetch() is done in here in next().
|
||||||
|
*
|
||||||
|
* see java.util.Iterator#next()
|
||||||
|
* @return an AddressBook (empty if the minimum delay has not been met)
|
||||||
*/
|
*/
|
||||||
public Object next() {
|
public Object next() {
|
||||||
Subscription sub = (Subscription) this.subIterator.next();
|
Subscription sub = (Subscription) this.subIterator.next();
|
||||||
return new AddressBook(sub, this.proxyHost, this.proxyPort);
|
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now()) {
|
||||||
|
//System.err.println("Fetching addressbook from " + sub.getLocation());
|
||||||
|
return new AddressBook(sub, this.proxyHost, this.proxyPort);
|
||||||
|
} else {
|
||||||
|
//System.err.println("Addressbook " + sub.getLocation() + " was last fetched " +
|
||||||
|
// DataHelper.formatDuration(I2PAppContext.getGlobalContext().clock().now() - sub.getLastFetched()) +
|
||||||
|
// " ago but the minimum delay is " +
|
||||||
|
// DataHelper.formatDuration(this.delay));
|
||||||
|
return new AddressBook(Collections.EMPTY_MAP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@ -72,4 +92,4 @@ public class SubscriptionIterator implements Iterator {
|
|||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,15 @@ import java.util.Map;
|
|||||||
* @author Ragnarok
|
* @author Ragnarok
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SubscriptionList {
|
class SubscriptionList {
|
||||||
|
|
||||||
private List subscriptions;
|
private List subscriptions;
|
||||||
|
|
||||||
private File etagsFile;
|
private File etagsFile;
|
||||||
|
|
||||||
private File lastModifiedFile;
|
private File lastModifiedFile;
|
||||||
|
private File lastFetchedFile;
|
||||||
|
private final long delay;
|
||||||
|
|
||||||
private String proxyHost;
|
private String proxyHost;
|
||||||
|
|
||||||
@ -60,20 +62,24 @@ public class SubscriptionList {
|
|||||||
* @param lastModifiedFile
|
* @param lastModifiedFile
|
||||||
* A file containg the last-modified headers used for conditional
|
* A file containg the last-modified headers used for conditional
|
||||||
* GET. The file is in the format "url=leastmodified".
|
* GET. The file is in the format "url=leastmodified".
|
||||||
|
* @param delay the minimum delay since last fetched for the iterator to actually fetch
|
||||||
* @param defaultSubs default subscription file
|
* @param defaultSubs default subscription file
|
||||||
* @param proxyHost proxy hostname
|
* @param proxyHost proxy hostname
|
||||||
* @param proxyPort proxy port number
|
* @param proxyPort proxy port number
|
||||||
*/
|
*/
|
||||||
public SubscriptionList(File locationsFile, File etagsFile,
|
public SubscriptionList(File locationsFile, File etagsFile,
|
||||||
File lastModifiedFile, List defaultSubs, String proxyHost,
|
File lastModifiedFile, File lastFetchedFile, long delay, List defaultSubs, String proxyHost,
|
||||||
int proxyPort) {
|
int proxyPort) {
|
||||||
this.subscriptions = new LinkedList();
|
this.subscriptions = new LinkedList();
|
||||||
this.etagsFile = etagsFile;
|
this.etagsFile = etagsFile;
|
||||||
this.lastModifiedFile = lastModifiedFile;
|
this.lastModifiedFile = lastModifiedFile;
|
||||||
|
this.lastFetchedFile = lastFetchedFile;
|
||||||
|
this.delay = delay;
|
||||||
this.proxyHost = proxyHost;
|
this.proxyHost = proxyHost;
|
||||||
this.proxyPort = proxyPort;
|
this.proxyPort = proxyPort;
|
||||||
Map etags;
|
Map etags;
|
||||||
Map lastModified;
|
Map lastModified;
|
||||||
|
Map lastFetched;
|
||||||
String location;
|
String location;
|
||||||
List locations = ConfigParser.parseSubscriptions(locationsFile,
|
List locations = ConfigParser.parseSubscriptions(locationsFile,
|
||||||
defaultSubs);
|
defaultSubs);
|
||||||
@ -87,11 +93,17 @@ public class SubscriptionList {
|
|||||||
} catch (IOException exp) {
|
} catch (IOException exp) {
|
||||||
lastModified = new HashMap();
|
lastModified = new HashMap();
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
lastFetched = ConfigParser.parse(lastFetchedFile);
|
||||||
|
} catch (IOException exp) {
|
||||||
|
lastFetched = new HashMap();
|
||||||
|
}
|
||||||
Iterator iter = locations.iterator();
|
Iterator iter = locations.iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
location = (String) iter.next();
|
location = (String) iter.next();
|
||||||
this.subscriptions.add(new Subscription(location, (String) etags
|
this.subscriptions.add(new Subscription(location, (String) etags.get(location),
|
||||||
.get(location), (String) lastModified.get(location)));
|
(String) lastModified.get(location),
|
||||||
|
(String) lastFetched.get(location)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,18 +114,22 @@ public class SubscriptionList {
|
|||||||
* @return A SubscriptionIterator.
|
* @return A SubscriptionIterator.
|
||||||
*/
|
*/
|
||||||
public SubscriptionIterator iterator() {
|
public SubscriptionIterator iterator() {
|
||||||
return new SubscriptionIterator(this.subscriptions, this.proxyHost,
|
return new SubscriptionIterator(this.subscriptions, this.delay, this.proxyHost,
|
||||||
this.proxyPort);
|
this.proxyPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the etag and last-modified headers for each Subscription to files.
|
* Write the etag and last-modified headers,
|
||||||
|
* and the last-fetched time, for each Subscription to files.
|
||||||
|
* BUG - If the subscription URL is a cgi containing an '=' the files
|
||||||
|
* won't be read back correctly; the '=' should be escaped.
|
||||||
*/
|
*/
|
||||||
public void write() {
|
public void write() {
|
||||||
Iterator iter = this.subscriptions.iterator();
|
Iterator iter = this.subscriptions.iterator();
|
||||||
Subscription sub;
|
Subscription sub;
|
||||||
Map etags = new HashMap();
|
Map etags = new HashMap();
|
||||||
Map lastModified = new HashMap();
|
Map lastModified = new HashMap();
|
||||||
|
Map lastFetched = new HashMap();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
sub = (Subscription) iter.next();
|
sub = (Subscription) iter.next();
|
||||||
if (sub.getEtag() != null) {
|
if (sub.getEtag() != null) {
|
||||||
@ -122,11 +138,13 @@ public class SubscriptionList {
|
|||||||
if (sub.getLastModified() != null) {
|
if (sub.getLastModified() != null) {
|
||||||
lastModified.put(sub.getLocation(), sub.getLastModified());
|
lastModified.put(sub.getLocation(), sub.getLastModified());
|
||||||
}
|
}
|
||||||
|
lastFetched.put(sub.getLocation(), "" + sub.getLastFetched());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ConfigParser.write(etags, this.etagsFile);
|
ConfigParser.write(etags, this.etagsFile);
|
||||||
ConfigParser.write(lastModified, this.lastModifiedFile);
|
ConfigParser.write(lastModified, this.lastModifiedFile);
|
||||||
|
ConfigParser.write(lastFetched, this.lastFetchedFile);
|
||||||
} catch (IOException exp) {
|
} catch (IOException exp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user