Author: toad
Date: 2007-06-12 23:28:55 +0000 (Tue, 12 Jun 2007)
New Revision: 13548
Modified:
trunk/freenet/src/freenet/node/NodeDispatcher.java
trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
trunk/freenet/src/freenet/node/updater/NodeUpdater.java
trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
Log:
Update over Mandatory for the main jar ... maybe.
Modified: trunk/freenet/src/freenet/node/NodeDispatcher.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeDispatcher.java 2007-06-12 22:53:40 UTC
(rev 13547)
+++ trunk/freenet/src/freenet/node/NodeDispatcher.java 2007-06-12 23:28:55 UTC
(rev 13548)
@@ -135,6 +135,10 @@
return node.nodeUpdater.uom.handleRequestRevocation(m,
source);
} else if(spec == DMT.UOMSendingRevocation) {
return node.nodeUpdater.uom.handleSendingRevocation(m,
source);
+ } else if(spec == DMT.UOMRequestMain) {
+ return node.nodeUpdater.uom.handleRequestMain(m,
source);
+ } else if(spec == DMT.UOMSendingMain) {
+ return node.nodeUpdater.uom.handleSendingMain(m,
source);
}
return false;
}
Modified: trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
2007-06-12 22:53:40 UTC (rev 13547)
+++ trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
2007-06-12 23:28:55 UTC (rev 13548)
@@ -39,7 +39,8 @@
public final static String REVOCATION_URI = "SSK at
tHlY8BK2KFB7JiO2bgeAw~e4sWU43YdJ6kmn73gjrIw,DnQzl0BYed15V8WQn~eRJxxIA-yADuI8XW7mnzEbut8,AQACAAE/revoked";
public final static String EXT_URI = "freenet:USK at
BFa1voWr5PunINSZ5BGMqFwhkJTiDBBUrOZ0MYBXseg,BOrxeLzUMb6R9tEZzexymY0zyKAmBNvrU4A9Q0tAqu0,AQACAAE/ext/"+NodeStarter.extBuildNumber;
- public static final long MAX_REVOCATION_KEY_LENGTH = 16*1024*1024; //
16MB
+ public static final long MAX_REVOCATION_KEY_LENGTH = 4*1024*1024; // 4MB
+ public static final long MAX_MAIN_JAR_LENGTH = 16*1024*1024; // 16MB
FreenetURI updateURI;
FreenetURI extURI;
@@ -811,4 +812,14 @@
}
}
+ public File getMainBlob(int version) {
+ NodeUpdater updater;
+ synchronized(this) {
+ if(hasBeenBlown) return null;
+ updater = mainUpdater;
+ if(updater == null) return null;
+ }
+ return updater.getBlobFile(version);
+ }
+
}
Modified: trunk/freenet/src/freenet/node/updater/NodeUpdater.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/NodeUpdater.java 2007-06-12
22:53:40 UTC (rev 13547)
+++ trunk/freenet/src/freenet/node/updater/NodeUpdater.java 2007-06-12
23:28:55 UTC (rev 13548)
@@ -148,7 +148,7 @@
}
}
- private File getBlobFile(int availableVersion) {
+ File getBlobFile(int availableVersion) {
return new File(node.getNodeDir(),
blobFilenamePrefix+availableVersion+".fblob");
}
@@ -177,6 +177,10 @@
}
public void onSuccess(FetchResult result, ClientGetter state) {
+ onSuccess(result, state, tempBlobFile);
+ }
+
+ void onSuccess(FetchResult result, ClientGetter state, File
tempBlobFile) {
logMINOR = Logger.shouldLog(Logger.MINOR, this);
boolean isNew;
synchronized(this) {
@@ -335,4 +339,8 @@
public long getBlobSize() {
return getBlobFile(getFetchedVersion()).length();
}
+
+ public File getBlobFile() {
+ return getBlobFile(getFetchedVersion());
+ }
}
Modified: trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
2007-06-12 22:53:40 UTC (rev 13547)
+++ trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
2007-06-12 23:28:55 UTC (rev 13548)
@@ -37,6 +37,7 @@
import freenet.node.Node;
import freenet.node.PeerNode;
import freenet.node.RequestStarter;
+import freenet.node.Version;
import freenet.node.useralerts.UserAlert;
import freenet.support.HTMLNode;
import freenet.support.Logger;
@@ -55,12 +56,22 @@
final NodeUpdateManager updateManager;
- /** Set of WeakReferences to PeerNode's which say (or said before they
disconnected)
+ /** Set of PeerNode's which say (or said before they disconnected)
* the key has been revoked */
private final HashSet nodesSayKeyRevoked;
- /** Set of WeakReferences to PeerNode's which say the key has been
revoked but failed
+ /** Set of PeerNode's which say the key has been revoked but failed
* to transfer the revocation key. */
private final HashSet nodesSayKeyRevokedFailedTransfer;
+ /** PeerNode's which have offered the main jar which we are not
fetching it from right now */
+ private final HashSet nodesOfferedMainJar;
+ /** PeerNode's we've asked to send the main jar */
+ private final HashSet nodesAskedSendMainJar;
+ /** PeerNode's sending us the main jar */
+ private final HashSet nodesSendingMainJar;
+ // 2 for reliability, no more as gets very slow/wasteful
+ static final int MAX_NODES_SENDING_MAIN_JAR = 2;
+ /** Maximum time between asking for the main jar and it starting to
transfer */
+ static final int REQUEST_MAIN_JAR_TIMEOUT = 60*1000;
private UserAlert alert;
@@ -68,6 +79,9 @@
this.updateManager = manager;
nodesSayKeyRevoked = new HashSet();
nodesSayKeyRevokedFailedTransfer = new HashSet();
+ nodesOfferedMainJar = new HashSet();
+ nodesAskedSendMainJar = new HashSet();
+ nodesSendingMainJar = new HashSet();
}
/**
@@ -179,9 +193,95 @@
}
+ if(updateManager.isBlown()) return true; // We already know
+
+ if(!updateManager.isEnabled()) return true; // Don't care if
not enabled, except for the revocation URI
+
+ if(mainJarVersion > Version.buildNumber() && mainJarFileLength
> 0) {
+ // Fetch it
+ try {
+ FreenetURI mainJarURI = new FreenetURI(jarKey);
+ if(mainJarURI.equals(updateManager.updateURI)) {
+ System.err.println("Fetching main jar
from "+source.userToString());
+ sendUOMRequestMain(source);
+ }
+ } catch (MalformedURLException e) {
+ // Should maybe be a useralert?
+ Logger.error(this, "Node "+source+" sent us a
UOMAnnounce claiming to have a new jar, but it had an invalid URI:
"+revocationKey+" : "+e, e);
+ System.err.println("Node "+source.getPeer()+" :
"+source.getName()+" sent us a UOMAnnounce claiming to have a new jar, but it
had an invalid URI: "+revocationKey+" : "+e);
+ }
+ }
+
return true;
}
+ protected void sendUOMRequestMain(final PeerNode source) {
+ Message msg =
DMT.createUOMRequestMain(updateManager.node.random.nextLong());
+
+ try {
+ synchronized(this) {
+ if(nodesAskedSendMainJar.size() +
nodesSendingMainJar.size() >= MAX_NODES_SENDING_MAIN_JAR) {
+ nodesOfferedMainJar.add(source);
+ return;
+ } else {
+ nodesAskedSendMainJar.add(source);
+ }
+ }
+ source.sendAsync(msg, new AsyncMessageCallback() {
+ public void acknowledged() {
+ // Cool! Wait for the actual transfer.
+ }
+ public void disconnected() {
+ Logger.normal(this, "Disconnected from
"+source.userToString()+" after sending UOMRequestMain");
+
synchronized(UpdateOverMandatoryManager.this) {
+
nodesAskedSendMainJar.remove(source);
+ }
+ maybeRequestMainJar();
+ }
+ public void fatalError() {
+ Logger.normal(this, "Fatal error from
"+source.userToString()+" after sending UOMRequestMain");
+
synchronized(UpdateOverMandatoryManager.this) {
+
nodesAskedSendMainJar.remove(source);
+ }
+ maybeRequestMainJar();
+ }
+ public void sent() {
+ // Timeout...
+ updateManager.node.ps.queueTimedJob(new
Runnable() {
+ public void run() {
+
synchronized(UpdateOverMandatoryManager.this) {
+
if(!nodesAskedSendMainJar.contains(source)) return;
+
nodesAskedSendMainJar.remove(source); // free up a slot
+ }
+ maybeRequestMainJar();
+ }
+ }, REQUEST_MAIN_JAR_TIMEOUT);
+ }
+ }, 0, null);
+ } catch (NotConnectedException e) {
+ synchronized(this) {
+ nodesAskedSendMainJar.remove(source);
+ }
+ maybeRequestMainJar();
+ }
+ }
+
+
+ protected void maybeRequestMainJar() {
+ while(true) {
+ PeerNode pn;
+ synchronized(this) {
+ if(nodesAskedSendMainJar.size() +
nodesSendingMainJar.size()
+ >= MAX_NODES_SENDING_MAIN_JAR)
+ return;
+ if(nodesOfferedMainJar.isEmpty()) return;
+ pn = (PeerNode)
(nodesOfferedMainJar.iterator().next());
+ }
+ if(!pn.isConnected()) continue;
+ sendUOMRequestMain(pn);
+ }
+ }
+
private void alertUser() {
synchronized(this) {
if(alert != null) return;
@@ -619,7 +719,7 @@
updateManager.revocationChecker.onFailure(e, state, cleanedBlobFile);
temp.delete();
- insertBlob();
+
insertBlob(updateManager.revocationChecker.getBlobFile());
} else {
Logger.error(this, "Failed to fetch
revocation certificate from blob from "+source.userToString());
@@ -650,7 +750,7 @@
System.err.println("Got revocation certificate
from "+source.userToString());
updateManager.revocationChecker.onSuccess(result, state, cleanedBlobFile);
temp.delete();
- insertBlob();
+
insertBlob(updateManager.revocationChecker.getBlobFile());
}
public void onSuccess(BaseClientPutter state) {
@@ -672,7 +772,7 @@
}
- protected void insertBlob() {
+ protected void insertBlob(final File blob) {
ClientCallback callback = new ClientCallback() {
public void onFailure(FetchException e, ClientGetter
state) {
// Ignore, can't happen
@@ -697,7 +797,7 @@
Logger.normal(this, "Inserted binary blob for
revocation key");
}
};
- FileBucket bucket = new
FileBucket(updateManager.revocationChecker.getBlobFile(), true, false, false,
false, false);
+ FileBucket bucket = new FileBucket(blob, true, false, false,
false, false);
ClientPutter putter = new ClientPutter(callback, bucket,
FreenetURI.EMPTY_CHK_URI, null,
updateManager.node.clientCore.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS).getInsertContext(true),
updateManager.node.clientCore.requestStarters.chkPutScheduler,
@@ -722,5 +822,307 @@
public void killAlert() {
updateManager.node.clientCore.alerts.unregister(alert);
}
+
+ public boolean handleRequestMain(Message m, final PeerNode source) {
+ // Do we have the data?
+
+ int version = updateManager.newMainJarVersion();
+ File data = updateManager.getMainBlob(version);
+
+ if(data == null) {
+ Logger.normal(this, "Peer "+source+" asked us for the
blob file for the revocation key for the main jar but we don't have it!");
+ // Probably a race condition on reconnect, hopefully
we'll be asked again
+ return true;
+ }
+
+ final long uid = m.getLong(DMT.UID);
+
+ RandomAccessFileWrapper raf;
+ try {
+ raf = new RandomAccessFileWrapper(data, "r");
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the main jar, we have downloaded it but don't have the file even
though we did have it when we checked!: "+e, e);
+ return true;
+ }
+
+ final PartiallyReceivedBulk prb;
+ long length;
+ try {
+ length = raf.size();
+ prb = new
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
+ Node.PACKET_SIZE, raf, true);
+ } catch (IOException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the main jar, we have downloaded it but we can't determine the
file size: "+e, e);
+ return true;
+ }
+
+ final BulkTransmitter bt;
+ try {
+ bt = new BulkTransmitter(prb, source, uid,
updateManager.node.outputThrottle);
+ } catch (DisconnectedException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the main jar, then disconnected: "+e, e);
+ return true;
+ }
+
+ final Runnable r = new Runnable() {
+ public void run() {
+ if(!bt.send()) {
+ Logger.error(this, "Failed to send main
jar blob to "+source.getPeer()+" : "+source.getName());
+ } else {
+ Logger.normal(this, "Sent main jar blob
to "+source.getPeer()+" : "+source.getName());
+ }
+ }
+
+ };
+
+ Message msg = DMT.createUOMSendingMain(uid, length,
updateManager.updateURI.toString(), version);
+
+ try {
+ source.sendAsync(msg, new AsyncMessageCallback() {
+ public void acknowledged() {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Sending
data...");
+ // Send the data
+ Thread t = new Thread(r, "Main jar send
for "+uid+" to "+source.getPeer()+" : "+source.getName());
+ t.setDaemon(true);
+ t.start();
+ }
+ public void disconnected() {
+ // Argh
+ Logger.error(this, "Peer "+source+"
asked us for the blob file for the main jar, then disconnected when we tried to
send the UOMSendingMain");
+ }
+
+ public void fatalError() {
+ // Argh
+ Logger.error(this, "Peer "+source+"
asked us for the blob file for the main jar, then got a fatal error when we
tried to send the UOMSendingMain");
+ }
+
+ public void sent() {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Message
sent, data soon");
+ }
+
+ public String toString() {
+ return super.toString() +
"("+uid+":"+source.getPeer()+")";
+ }
+
+ }, 0, null);
+ } catch (NotConnectedException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the main jar, then disconnected when we tried to send the
UOMSendingMain: "+e, e);
+ return true;
+ }
+
+ return true;
+ }
+
+ public boolean handleSendingMain(Message m, final PeerNode source) {
+ final long uid = m.getLong(DMT.UID);
+ final long length = m.getLong(DMT.FILE_LENGTH);
+ String key = m.getString(DMT.MAIN_JAR_KEY);
+ final int version = m.getInt(DMT.MAIN_JAR_VERSION);
+ final FreenetURI jarURI;
+ try {
+ jarURI = new
FreenetURI(key).setSuggestedEdition(version);
+ } catch (MalformedURLException e) {
+ Logger.error(this, "Failed receiving main jar
"+version+" because URI not parsable: "+e+" for "+key, e);
+ System.err.println("Failed receiving main jar
"+version+" because URI not parsable: "+e+" for "+key);
+ e.printStackTrace();
+ cancelSend(source, uid);
+ return true;
+ }
+
+
if(!jarURI.equals(updateManager.updateURI.setSuggestedEdition(version))) {
+ System.err.println("Node sending us a main jar update
("+version+") from the wrong URI:\n"+
+ "Node: "+source.getPeer()+" :
"+source.getName()+"\n"+
+ "Our URI:
"+updateManager.updateURI+"\n"+
+ "Their URI: "+jarURI);
+ cancelSend(source, uid);
+ return true;
+ }
+
+ if(updateManager.isBlown()) {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Key blown, so not receiving
main jar from "+source+ "("+uid+")");
+ cancelSend(source, uid);
+ return true;
+ }
+
+ if(length > NodeUpdateManager.MAX_MAIN_JAR_LENGTH) {
+ System.err.println("Node "+source.getPeer()+" :
"+source.getName()+" offered us a main jar ("+version+")
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have
refused the transfer.");
+ Logger.error(this, "Node "+source.getPeer()+" :
"+source.getName()+" offered us a main jar ("+version+")
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have
refused the transfer.");
+ // If the transfer fails, we don't try again.
+ cancelSend(source, uid);
+ return true;
+ }
+
+ // Okay, we can receive it
+
+ final File temp;
+
+ try {
+ temp = File.createTempFile("main-", ".fblob.tmp",
updateManager.node.getNodeDir());
+ temp.deleteOnExit();
+ } catch (IOException e) {
+ System.err.println("Cannot save new main jar to disk
and therefore cannot fetch it from our peer!: "+e);
+ e.printStackTrace();
+ cancelSend(source, uid);
+ return true;
+ }
+
+ RandomAccessFileWrapper raf;
+ try {
+ raf = new RandomAccessFileWrapper(temp, "rw");
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Peer "+source+" sending us a main
jar binary blob, but we lost the temp file "+temp+" : "+e, e);
+ return true;
+ }
+
+ PartiallyReceivedBulk prb = new
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
+ Node.PACKET_SIZE, raf, false);
+
+ final BulkReceiver br = new BulkReceiver(prb, source, uid);
+
+ Thread t = new Thread(new Runnable() {
+
+ public void run() {
+ if(br.receive()) {
+ // Success!
+ processMainJarBlob(temp, source,
version, jarURI);
+ } else {
+ Logger.error(this, "Failed to transfer
main jar "+version+" from "+source);
+ System.err.println("Failed to transfer
main jar "+version+" from "+source);
+ }
+ }
+
+ }, "Main jar ("+version+") receive for "+uid+" from
"+source.getPeer()+" : "+source.getName());
+
+ t.setDaemon(true);
+ t.start();
+
+ return true;
+ }
+
+ protected void processMainJarBlob(final File temp, final PeerNode
source, final int version, FreenetURI uri) {
+ SimpleBlockSet blocks = new SimpleBlockSet();
+
+ DataInputStream dis = null;
+ try {
+ dis = new DataInputStream(new BufferedInputStream(new
FileInputStream(temp)));
+ BinaryBlob.readBinaryBlob(dis, blocks, true);
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Somebody deleted "+temp+" ? We lost
the main jar ("+version+") from "+source.userToString()+"!");
+ System.err.println("Somebody deleted "+temp+" ? We lost
the main jar ("+version+") from "+source.userToString()+"!");
+ return;
+ } catch (IOException e) {
+ Logger.error(this, "Could not read main jar
("+version+") from temp file "+temp+" from node "+source.userToString()+" !");
+ System.err.println("Could not read main jar
("+version+") from temp file "+temp+" from node "+source.userToString()+" !");
+ // FIXME will be kept until exit for debugging purposes
+ return;
+ } catch (BinaryBlobFormatException e) {
+ Logger.error(this, "Peer "+source.userToString()+" sent
us an invalid main jar ("+version+")!: "+e, e);
+ System.err.println("Peer "+source.userToString()+" sent
us an invalid main jar ("+version+")!: "+e);
+ e.printStackTrace();
+ // FIXME will be kept until exit for debugging purposes
+ return;
+ } finally {
+ if(dis != null)
+ try {
+ dis.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ // Fetch the jar from the datastore plus the binary blob
+
+ FetchContext tempContext =
updateManager.node.clientCore.makeClient((short)0, true).getFetchContext();
+ tempContext.localRequestOnly = true;
+ tempContext.blocks = blocks;
+
+ File f;
+ FileBucket b = null;
+ try {
+ f = File.createTempFile("main-", ".fblob.tmp",
updateManager.node.getNodeDir());
+ b = new FileBucket(f, false, false, true, true, true);
+ } catch (IOException e) {
+ Logger.error(this, "Cannot share main jar from
"+source.userToString()+" with our peers because cannot write the cleaned
version to disk: "+e, e);
+ System.err.println("Cannot share main jar from
"+source.userToString()+" with our peers because cannot write the cleaned
version to disk: "+e);
+ e.printStackTrace();
+ b = null;
+ f = null;
+ }
+ final FileBucket cleanedBlob = b;
+ final File cleanedBlobFile = f;
+
+ ClientCallback myCallback = new ClientCallback() {
+
+ public void onFailure(FetchException e, ClientGetter
state) {
+ if(e.mode == FetchException.CANCELLED) {
+ // Eh?
+ Logger.error(this, "Cancelled fetch
from store/blob of main jar ("+version+") from "+source.userToString());
+ System.err.println("Cancelled fetch
from store/blob of main jar ("+version+") from "+source.userToString()+" to
"+temp+" - please report to developers");
+ // Probably best to keep files around
for now.
+ } else if(e.isFatal()) {
+ // Bogus as inserted. Ignore.
+ temp.delete();
+ Logger.error(this, "Failed to fetch
main jar "+version+" from "+source.userToString()+" : fatal error (update was
probably inserted badly): "+e, e);
+ System.err.println("Failed to fetch
main jar "+version+" from "+source.userToString()+" : fatal error (update was
probably inserted badly): "+e);
+ } else {
+ Logger.error(this, "Failed to fetch
main jar "+version+" from blob from "+source.userToString());
+ System.err.println("Failed to fetch
main jar "+version+" from blob from "+source.userToString());
+ }
+ }
+
+ public void onFailure(InsertException e,
BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ public void onFetchable(BaseClientPutter state) {
+ // Irrelevant
+ }
+
+ public void onGeneratedURI(FreenetURI uri,
BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ public void onMajorProgress() {
+ // Ignore
+ }
+
+ public void onSuccess(FetchResult result, ClientGetter
state) {
+ System.err.println("Got main jar version
"+version+" from "+source.userToString());
+ if(result.size() == 0) {
+ System.err.println("Ignoring because 0
bytes long");
+ return;
+ }
+
+ NodeUpdater mainUpdater =
updateManager.mainUpdater;
+ if(mainUpdater == null) {
+ System.err.println("Not updating
because updater is disabled!");
+ return;
+ }
+ mainUpdater.onSuccess(result, state,
cleanedBlobFile);
+ temp.delete();
+ insertBlob(mainUpdater.getBlobFile(version));
+ }
+
+ public void onSuccess(BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ };
+
+ ClientGetter cg = new ClientGetter(myCallback,
+
updateManager.node.clientCore.requestStarters.chkFetchScheduler,
+
updateManager.node.clientCore.requestStarters.sskFetchScheduler,
+ uri, tempContext, (short)0, this, null,
cleanedBlob);
+
+ try {
+ cg.start();
+ } catch (FetchException e1) {
+ myCallback.onFailure(e1, cg);
+ }
+
+ }
}