Author: toad
Date: 2008-09-24 22:16:49 +0000 (Wed, 24 Sep 2008)
New Revision: 22824

Added:
   branches/db4o/freenet/test/freenet/support/io/MockInputStream.java
Removed:
   branches/db4o/freenet/test/freenet/support/api/
Modified:
   branches/db4o/freenet/build.xml
   branches/db4o/freenet/src/freenet/client/ArchiveManager.java
   branches/db4o/freenet/src/freenet/client/async/USKFetcher.java
   branches/db4o/freenet/src/freenet/client/async/USKManager.java
   branches/db4o/freenet/src/freenet/crypt/SSL.java
   branches/db4o/freenet/src/freenet/keys/Key.java
   branches/db4o/freenet/src/freenet/l10n/freenet.l10n.de.properties
   branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties
   branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties
   branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties
   branches/db4o/freenet/src/freenet/node/KeyTracker.java
   branches/db4o/freenet/src/freenet/node/Node.java
   branches/db4o/freenet/src/freenet/node/NodeClientCore.java
   branches/db4o/freenet/src/freenet/node/PeerManager.java
   branches/db4o/freenet/src/freenet/node/PeerNode.java
   branches/db4o/freenet/src/freenet/node/TestnetHandler.java
   branches/db4o/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
   branches/db4o/freenet/src/freenet/support/DoublyLinkedList.java
   branches/db4o/freenet/src/freenet/support/DoublyLinkedListImpl.java
   branches/db4o/freenet/src/freenet/support/HTMLNode.java
   branches/db4o/freenet/src/freenet/support/LRUHashtable.java
   branches/db4o/freenet/src/freenet/support/LRUQueue.java
   branches/db4o/freenet/src/freenet/support/io/ArrayBucket.java
   branches/db4o/freenet/src/freenet/support/io/ArrayBucketFactory.java
   branches/db4o/freenet/src/freenet/support/io/LineReadingInputStream.java
   branches/db4o/freenet/src/freenet/support/io/TempBucketFactory.java
   branches/db4o/freenet/src/freenet/support/io/TooLongException.java
   branches/db4o/freenet/test/freenet/clients/http/filter/ContentFilterTest.java
   branches/db4o/freenet/test/freenet/support/compress/GzipCompressorTest.java
   branches/db4o/freenet/test/freenet/support/io/LineReadingInputStreamTest.java
Log:
Merge build 1157 to db4o branch: changes from 21939 to 22001.


Modified: branches/db4o/freenet/build.xml
===================================================================
--- branches/db4o/freenet/build.xml     2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/build.xml     2008-09-24 22:16:49 UTC (rev 22824)
@@ -82,6 +82,7 @@
                                <pathelement location="javax-security.jar"/>
                                <pathelement location="javax-crypto.jar"/>
                        </classpath>
+                       <compilerarg value="-Xlint"/>

                        <!-- following a very temporary list of files to be 
build -->
                        <include name="org/**/*.java"/>
@@ -128,12 +129,16 @@
        <!-- ================================================== -->

        <target name="unit-build" depends="compile" if="junit.present" 
unless="skip_tests">
+               <delete dir="${build-test}"/>
+               <mkdir dir="${build-test}"/>
+
                <javac srcdir="${test}" destdir="${build-test}" debug="on" 
optimize="on" source="1.5">
                        <classpath>
                                <pathelement path="${build}"/>
                                <pathelement 
location="${freenet-ext.location}"/>
                                <pathelement location="${junit.location}"/>
                        </classpath>
+                       <compilerarg value="-Xlint"/>
                        <include name="**/*.java"/>
                        <exclude name="*.java"/>
                </javac>
@@ -152,7 +157,7 @@

                        <batchtest fork="yes">
                                <fileset dir="${build-test}">
-                                       <include name="**/*.class"/>
+                                       <include name="**/*Test.class"/>
                                </fileset>
                        </batchtest>
                        <sysproperty key="benchmark" value="${benchmark}" />

Modified: branches/db4o/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- branches/db4o/freenet/src/freenet/client/ArchiveManager.java        
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/client/ArchiveManager.java        
2008-09-24 22:16:49 UTC (rev 22824)
@@ -38,7 +38,8 @@
        public static final String METADATA_NAME = ".metadata";
        private static boolean logMINOR;

-       final long maxArchiveSize;
+       private long maxArchiveSize;
+
        final long maxArchivedFileSize;

        // ArchiveHandler's
@@ -215,7 +216,7 @@
                        ctx.setLastHash(realHash);
                }
                if(data.size() > maxArchiveSize)
-                       throw new ArchiveFailureException("Archive too big");
+                       throw new ArchiveFailureException("Archive too big 
("+data.size()+" > "+maxArchiveSize+")!");
                if(archiveType != Metadata.ARCHIVE_ZIP)
                        throw new ArchiveFailureException("Unknown or 
unsupported archive algorithm "+archiveType);

@@ -491,4 +492,12 @@
        public static void init(ObjectContainer container, ClientContext 
context, final long nodeDBHandle) {
                ArchiveHandlerImpl.init(container, context, nodeDBHandle);
        }
+       
+       public synchronized long getMaxArchiveSize() {
+               return maxArchiveSize;
+       }
+
+       public synchronized void setMaxArchiveSize(long maxArchiveSize) {
+               this.maxArchiveSize = maxArchiveSize;
+       }
 }

Modified: branches/db4o/freenet/src/freenet/client/async/USKFetcher.java
===================================================================
--- branches/db4o/freenet/src/freenet/client/async/USKFetcher.java      
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/client/async/USKFetcher.java      
2008-09-24 22:16:49 UTC (rev 22824)
@@ -162,6 +162,7 @@
                                checker.schedule(container, context);
                }

+               @Override
                public String toString() {
                        return "USKAttempt for "+number+" for 
"+origUSK.getURI()+" for "+USKFetcher.this;
                }
@@ -471,7 +472,7 @@
                if (delay<=0) {
                        schedule(container, context);
                } else {
-                       uskManager.ticker.queueTimedJob(new Runnable() {
+                       context.ticker.queueTimedJob(new Runnable() {
                                public void run() {
                                        USKFetcher.this.schedule(null, context);
                                }

Modified: branches/db4o/freenet/src/freenet/client/async/USKManager.java
===================================================================
--- branches/db4o/freenet/src/freenet/client/async/USKManager.java      
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/client/async/USKManager.java      
2008-09-24 22:16:49 UTC (rev 22824)
@@ -13,7 +13,7 @@
 import freenet.node.NodeClientCore;
 import freenet.node.RequestClient;
 import freenet.node.RequestStarter;
-import freenet.node.Ticker;
+import freenet.support.Executor;
 import freenet.support.LRUQueue;
 import freenet.support.Logger;

@@ -45,7 +45,7 @@

        final FetchContext backgroundFetchContext;

-       final Ticker ticker;
+       final Executor executor;

        private ClientContext context;

@@ -58,7 +58,7 @@
                checkersByUSK = new HashMap();
                backgroundFetchersByClearUSK = new HashMap();
                temporaryBackgroundFetchersLRU = new LRUQueue();
-               ticker = core.getTicker();
+               executor = core.getExecutor();
        }

        public void init(ObjectContainer container, ClientContext context) {
@@ -160,15 +160,15 @@
                }
                if(callbacks != null) {
                        // Run off-thread, because of locking, and because 
client callbacks may take some time
-                       ticker.queueTimedJob(new Runnable() {
-                               public void run() {
-                                       USK usk = origUSK.copy(number);
-                                       for(int i=0;i<callbacks.length;i++)
-                                               
callbacks[i].onFoundEdition(number, usk, null, // non-persistent
-                                                               context, false, 
(short)-1, null);
+                                       final USK usk = origUSK.copy(number);
+                                       for(final USKCallback callback : 
callbacks)
+                                               
context.mainExecutor.execute(new Runnable() {
+                                                       public void run() {
+                                                               
callback.onFoundEdition(number, usk, null, // non-persistent
+                                                                               
context, false, (short)-1, null);
+                                                       }
+                                               }, "USKManager callback 
executor for " +callback);
                                }
-                       }, 0);
-               }
        }

        /**
@@ -214,11 +214,11 @@
                        cb.onFoundEdition(curEd, origUSK.copy(curEd), null, 
context, false, (short)-1, null);
                final USKFetcher fetcher = sched;
                if(fetcher != null) {
-                       ticker.queueTimedJob(new Runnable() {
+                       executor.execute(new Runnable() {
                                public void run() {
                                        fetcher.schedule(null, context);
                                }
-                       }, 0);
+                       }, "USKManager.schedule for "+fetcher);
                }
        }


Modified: branches/db4o/freenet/src/freenet/crypt/SSL.java
===================================================================
--- branches/db4o/freenet/src/freenet/crypt/SSL.java    2008-09-24 20:42:20 UTC 
(rev 22823)
+++ branches/db4o/freenet/src/freenet/crypt/SSL.java    2008-09-24 22:16:49 UTC 
(rev 22824)
@@ -28,8 +28,6 @@
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;

-import com.sleepycat.je.utilint.NotImplementedYetException;
-
 import freenet.config.InvalidConfigValueException;
 import freenet.config.SubConfig;
 import freenet.support.Logger;

Modified: branches/db4o/freenet/src/freenet/keys/Key.java
===================================================================
--- branches/db4o/freenet/src/freenet/keys/Key.java     2008-09-24 20:42:20 UTC 
(rev 22823)
+++ branches/db4o/freenet/src/freenet/keys/Key.java     2008-09-24 22:16:49 UTC 
(rev 22824)
@@ -123,16 +123,20 @@

     public abstract short getType();

+       @Override
     public int hashCode() {
         return hash;
     }

+       @Override
     public boolean equals(Object o){
        if(o == null || !(o instanceof Key)) return false;
        return Arrays.equals(routingKey, ((Key)o).routingKey);
     }

-    static Bucket decompress(boolean isCompressed, byte[] output, int 
outputLength, BucketFactory bf, int maxLength, short compressionAlgorithm, 
boolean shortLength) throws CHKDecodeException, IOException {
+    static Bucket decompress(boolean isCompressed, byte[] output, int 
outputLength, BucketFactory bf, long maxLength, short compressionAlgorithm, 
boolean shortLength) throws CHKDecodeException, IOException {
+           if(maxLength < 0)
+                   throw new IllegalArgumentException("maxlength="+maxLength);
         if(isCompressed) {
                if(Logger.shouldLog(Logger.MINOR, Key.class))
                        Logger.minor(Key.class, "Decompressing 
"+output.length+" bytes in decode with codec "+compressionAlgorithm);
@@ -146,7 +150,7 @@
                len = ((((((output[0] & 0xff) << 8) + (output[1] & 0xff)) << 8) 
+ (output[2] & 0xff)) << 8) +
                        (output[3] & 0xff);
             if(len > maxLength)
-                throw new TooBigException("Invalid precompressed size: "+len);
+                throw new TooBigException("Invalid precompressed size: "+len + 
" maxlength="+maxLength);
             Compressor decompressor = 
Compressor.getCompressionAlgorithmByMetadataID(compressionAlgorithm);
             Bucket inputBucket = new SimpleReadOnlyArrayBucket(output, 
shortLength?2:4, outputLength-(shortLength?2:4));
             try {

Modified: branches/db4o/freenet/src/freenet/l10n/freenet.l10n.de.properties
===================================================================
--- branches/db4o/freenet/src/freenet/l10n/freenet.l10n.de.properties   
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/l10n/freenet.l10n.de.properties   
2008-09-24 22:16:49 UTC (rev 22824)
@@ -86,11 +86,15 @@
 ConfigToadlet.fproxy=FProxy
 ConfigToadlet.fullTitle=Freenet-Knoten-Konfiguration von ${name}
 ConfigToadlet.logger=Logger
+ConfigToadlet.needRestart=Eine Einstellung ben?tigt einen Neustart um in Kraft 
treten zu k?nnen. Bitte starten Sie den Knoten umgehend neu.
+ConfigToadlet.needRestartShort=Eine Einstellung ben?tigt einen Neustart um in 
Kraft treten zu k?nnen. Bitte starten Sie den Knoten umgehend neu.
+ConfigToadlet.needRestartTitle=Neustart des Knotens erforderlich
 ConfigToadlet.node=Knoten
 ConfigToadlet.pluginmanager=Plugin-Manager
 ConfigToadlet.pluginmanager2=Plugin-Manager 2
 ConfigToadlet.possibilitiesTitle=Fortsetzen
 ConfigToadlet.reset=Zur?cksetzen
+ConfigToadlet.restartNode=Jetzt Neustarten
 ConfigToadlet.returnToNodeConfig=Zur?ck zur Knoten-Konfiguration
 ConfigToadlet.shortTitle=Konfiguration
 ConfigToadlet.ssl=SSL (ben?tigt Neustart)
@@ -421,12 +425,12 @@
 FirstTimeWizardToadlet.congratz=Willkommen an Bord!
 FirstTimeWizardToadlet.congratzLong=Herzlichen Gl?ckwunsch, die 
Basis-Konfiguration Ihres Freenet-Knotens ist nun abgeschlossen. Sie k?nnen 
alle Parameter, die Sie soeben eingestellt haben, ?ndern indem Sie auf die 
"Konfiguration"s-Seite gehen, diese ist jederzeit vom Men? auf der linken Seite 
der Oberfl?che erreichbar. Bitte beachten Sie, dass Freenet zu Anfang langsam 
sein wird, dies wird sich mit der Zeit verbessern. Sie k?nnen beginnen indem 
Sie auf die Lesezeichen auf der n?chsten Seite klicken. Wir w?nschen Ihnen ein 
angenehmes Freenet-Erlebnis.
 FirstTimeWizardToadlet.connectToStrangers=Mit Fremden verbinden?
-FirstTimeWizardToadlet.connectToStrangersLong=Im Idealfall w?rden sich alle 
Freenet-Benutzer nur mit Leuten verbinden, die sie kennen. Dies ist weitaus 
sicherer, da es es sehr schwer f?r andere macht herauszufinden, dass Sie 
Freenet benutzen. Wenn Sie jedoch nicht mindestens 5 Leute kennen, die bereits 
Freenet benutzen, k?nnen Sie entscheiden sich mit Fremden zu verbinden. 
Beachten Sie, dass Sie dies jederzeit wieder ausschalten k?nnen.
+FirstTimeWizardToadlet.connectToStrangersLong=Um Freenet zu benutzen m?ssen 
Sie sich mit mindestens f?nf anderen Knoten verbinden. Diese sollten im 
Idealfall von Leuten betrieben werden, die Sie kennen und denen Sie vertrauen. 
Wenn Sie nicht genug Leute kennen, die Freenet benutzen, k?nnen Sie sich dazu 
entschlie?en sich auch mit den Knoten von Fremden zu verbinden. Sich mit 
Fremden zu verbinden erm?glicht es jedoch anderen herauszufinden, dass Sie 
Freenet benutzen und birgt andere zus?tzliche Sicherheits-Risiken. Wenn Sie die 
Tatsache, dass Sie Freenet benutzen, geheim halten wollen, k?nnte Sie dies in 
Schwierigkeiten bringen.
 FirstTimeWizardToadlet.continue=Fortfahren
 FirstTimeWizardToadlet.continueEnd=Hier klicken, um anzufangen, Freenet zu 
benutzen!
 FirstTimeWizardToadlet.datastoreSize=Gr??e des Datenspeichers
 FirstTimeWizardToadlet.datastoreSizeLong=Bitte w?hlen Sie eine Gr??e f?r Ihren 
Datenspeicher. Der Datenspeicher verh?lt sich wie ein Zwischenspeicher (Cache); 
Daten f?r das Netzwerk zu speichern wird Ihnen zu einem besseren 
Daten-Durchsatz verhelfen, wenn Sie popul?re Dateien anfordern. Je mehr Platz 
Sie zur Verf?gung stellen k?nnen, desto besser ist es f?r die Gemeinschaft und 
desto schneller wird Ihr Knoten, besonders das Herunterladen, sein.
-FirstTimeWizardToadlet.enableOpennet=Kennen Sie jemanden, der bereits Freenet 
benutzt?
+FirstTimeWizardToadlet.enableOpennet=Es ist auch sp?ter, wenn Sie genug 
Freunde hinzugef?gt haben, noch m?glich die Verbindung zu Fremden 
auszuschalten. Sie werden dann jedoch in der Zwischenzeit bereits ein paar 
Informationen an Fremde preisgegeben haben. Freenet ist immer noch 
experimentell und wir geben keine Garantie f?r die Sicherheit.
 FirstTimeWizardToadlet.fivePercentDisk=(= 5% des freien Speicherplatzes)
 FirstTimeWizardToadlet.homepageTitle=Freenet-Einrichtungs-Assistent!
 FirstTimeWizardToadlet.iDoTrust=Trauen Sie Leuten die mit ${interface} (${ip}) 
verbunden sind?
@@ -436,9 +440,9 @@
 FirstTimeWizardToadlet.memoryLimitLong=Wie viel (Arbeits-)Speicher m?chten Sie 
Freenet erlauben zu benutzen? Wenn Sie viele Downloads oder Uploads in der 
Warteschlange haben, wird Freenet mehr Speicher brauchen. Wir empfehlen Ihnen 
dies nicht auf weniger als 128MB zu setzen, es sei denn Sie haben wirklich 
wenig RAM. Wenn Sie 1GB oder mehr haben, sollten Sie dies wahrscheinlich auf 
mindestens 256MB setzen. Diese Einstellung wird nach einem Neustart von Freenet 
wirksam.
 FirstTimeWizardToadlet.noNetworkIF=Keine zus?tzliche Netzwerk-Schnittstelle 
gefunden
 FirstTimeWizardToadlet.noNetworkIFLong=Freenet hat keine zus?tzliche 
Netzwerk-Schnittstelle gefunden. Es wird annehmen, dass Sie sich von Ihrem 
Computer (und nur von Ihrem Computer) mit ihm verbinden.
-FirstTimeWizardToadlet.opennetNo=Ja, ich habe mindestens 5 Freunde, die 
bereits Freenet benutzen und werde Ihre Kontaktdaten auf der Freunde-Seite 
eintragen.
+FirstTimeWizardToadlet.opennetNo=Ich habe mindestens 5 Freunde, die bereits 
Freenet benutzen und werde Ihre Kontaktdaten auf der Freunde-Seite eintragen.
 FirstTimeWizardToadlet.opennetWarning=Wenn Freenet dort wo Sie leben illegal 
ist oder wenn Sie es benutzen um Inhalte abzurufen, die Sie in Schwierigkeiten 
bringen k?nnten, kann es sein, dass dem Knoten zu sagen, dass er sich 
automatisch mit Fremden verbinden soll, gef?hrlich ist, da es das Leben eines 
Angreifers deutlich erleichtert. Freenet ist immer noch experimentell und wir 
geben keine Garantie f?r die Sicherheit.
-FirstTimeWizardToadlet.opennetYes=Nein, ich m?chte, dass der Knoten 
automatisch Fremde findet mit denen er sich verbinden kann.
+FirstTimeWizardToadlet.opennetYes=Ich m?chte, dass der Knoten automatisch 
Fremde findet mit denen er sich verbinden kann.
 FirstTimeWizardToadlet.skipWizard=Ich bin kein Neuling, bitte ?berspringe den 
Assistenten!
 FirstTimeWizardToadlet.step1Title=Freenet-Einrichtungs-Assistent! - Freunde 
und Fremde
 FirstTimeWizardToadlet.step2Title=Freenet-Einrichtungs-Assistent! - W?hlen Sie 
einen Knoten-Namen
@@ -719,16 +723,24 @@
 NodeClientCore.downloadAllowedDirsLong=Per Semikolon getrennte Liste von 
Verzeichnissen, in welche das Herunterladen erlaubt ist. "downloads" steht f?r 
das Download-Verzeichnis, wenn das Feld leer ist bedeutet dies, dass keine 
Dateien auf Datentr?ger heruntergeladen werden d?rfen, "all" erlaubt das 
Herunterladen ?berall hin. WARNUNG! Wenn diese Option auf "all" gesetzt wird, 
kann jeder Benutzer jede Datei an jeden Ort auf Ihrem Computer herunterladen!
 NodeClientCore.downloadDir=Standard Download-Verzeichnis
 NodeClientCore.downloadDirLong=Das Verzeichnis in welchem heruntergeladene 
Dateien standardm??ig gespeichert werden
+NodeClientCore.encryptPersistentTempBuckets=Die tempor?ren persistenten 
Buckets verschl?sseln? LASSEN SIE DIES IN RUHE!
+NodeClientCore.encryptPersistentTempBucketsLong=Die tempor?ren persistenten 
Buckets verschl?sseln? In manchen F?llen (wenn Sie Festplatten- und 
Swap-Verschl?sselung benutzen) kann es sein, dass es keinen Sinn macht die 
tempor?ren persistenten Buckets zu verschl?sseln. LASSEN SIE DIE FINGER DAVON, 
WENN SIE NICHT GENAU WISSEN WAS SIE TUN!
+NodeClientCore.encryptTempBuckets=Die tempor?ren Buckets verschl?sseln? LASSEN 
SIE DIES IN RUHE!
+NodeClientCore.encryptTempBucketsLong=Die tempor?ren Buckets verschl?sseln? In 
manchen F?llen (wenn Sie Festplatten- und Swap-Verschl?sselung benutzen) kann 
es sein, dass es keinen Sinn macht die tempor?ren Buckets zu verschl?sseln. 
LASSEN SIE DIE FINGER DAVON, WENN SIE NICHT GENAU WISSEN WAS SIE TUN!
 NodeClientCore.fileForClientStats=Datei in welcher die Statistiken gespeichert 
werden
 NodeClientCore.fileForClientStatsLong=Datei in der die Drosselungs-Statistiken 
gespeichert werden (wird ben?tigt um herauszufinden wie oft Anfragen gesendet 
werden sollen).
 NodeClientCore.lazyResume=Vervollst?ndige das Laden persistenter Anfragen beim 
Start? (Braucht mehr Speicher)
 NodeClientCore.lazyResumeLong=Der Knoten kann persistente (dauerhafte) 
anstehende Anfragen beim Starten laden, oder er kann die Daten in den Speicher 
einlesen und dann die Anfragen nachdem der Knoten vollst?ndig gestartet ist 
komplettieren. K?rzere Start-Zeiten, braucht aber mehr Speicher.
+NodeClientCore.maxRAMBucketSize=Maximale Gr??e eines RAMBuckets
+NodeClientCore.maxRAMBucketSizeLong=Maximale Gr??e eines RAMBuckets (gr??ere 
Buckets werden als Dateien auf der Festplatte gespeichert)
 NodeClientCore.maxUSKFetchers=Maximal erlaubte Anzahl von USK-Abrufern
 NodeClientCore.maxUSKFetchersLong=Maximale Anzahl zugelassener USK-Abrufer
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=Muss gr??er als Null sein
 NodeClientCore.movingTempDirOnTheFlyNotSupported=Das Verschieben des 
tempor?ren Verzeichnisses im laufenden Betrieb, wird im Moment (noch?) nicht 
unterst?tzt
 NodeClientCore.persistentTempDir=Verzeichnis f?r persistente tempor?re Dateien
 NodeClientCore.persistentTempDirLong=Name des Verzeichnisses in welchem 
persistente (dauerhafte) tempor?re Dateien gespeichert werden
+NodeClientCore.ramBucketPoolSize=Menge an RAM das f?r tempor?re Buckets 
reserviert wird
+NodeClientCore.ramBucketPoolSizeLong=Menge an RAM das f?r tempor?re Buckets 
reserviert wird. Es ist ein Kompromiss zwischen mehr Speicherauslastung und 
mehr IO-Zugriffen.
 NodeClientCore.startingUp=Bitte geben Sie Freenet etwas Zeit, um komplett zu 
starten. In der Zwischenzeit k?nnte es sein, dass einige Dinge nicht 
funkionieren und der Knoten langsamer als gew?hnlich ist.
 NodeClientCore.startingUpShort=Freenet startet noch, einige Dinge k?nnten noch 
nicht funktionieren und der Knoten k?nnte langsam sein.
 NodeClientCore.startingUpTitle=Freenet startet gerade.
@@ -1060,6 +1072,8 @@
 SimpleToadletServer.illegalCSSName=CSS-Namen d?rfen keine Schr?gstriche oder 
Doppelpunkte enthalten!
 SimpleToadletServer.panicButton=Den Alarm-Knopf anzeigen?
 SimpleToadletServer.panicButtonLong=Zeigt einen "Alarm-Knopf" auf der 
Warteschlangen-Seite an, welcher alle Anfragen ohne Nachfrage entfernt.
+SimpleToadletServer.passthroughMaxSize=Maximale Gr??e f?r das transparente 
Durchreichen in FProxy
+SimpleToadletServer.passthroughMaxSizeLong=Maximale Gr??e einer Datei f?r das 
transparente Durchreichen in FProxy
 SimpleToadletServer.port=FProxy-Port
 SimpleToadletServer.portLong=Der TCP-Port auf dem FProxy Nachrichten empfangen 
soll
 SimpleToadletServer.ssl=SSL aktivieren?

Modified: branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties   
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties   
2008-09-24 22:16:49 UTC (rev 22824)
@@ -717,6 +717,8 @@
 Node.tooSmallMTUShort=Connection problems: Your connection's MTU is too short 
for Freenet to work well. Expect problems.
 Node.withAnnouncement=Allow the node to bootstrap itself using seednodes?
 Node.withAnnouncementLong=Allow the node to bootstrap itself using seednodes? 
While this has to be the default behaviour, it's insecure by design.
+NodeClientCore.maxArchiveSize=Maximum size of any given archive
+NodeClientCore.maxArchiveSizeLong=Maximum size of any given archive
 NodeClientCore.couldNotFindOrCreateDir=Could not find or create directory
 NodeClientCore.downloadAllowedDirs=Directories downloading is allowed to
 NodeClientCore.downloadAllowedDirsLong=Semicolon separated list of directories 
to which downloads are allowed. "downloads" means downloadsDir, empty means no 
downloads to disk allowed, "all" means downloads allowed from anywhere. 
WARNING! If this is set to "all" any user can download any file to anywhere on 
your computer!

Modified: branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties
===================================================================
--- branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties        
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties        
2008-09-24 22:16:49 UTC (rev 22824)
@@ -90,11 +90,15 @@
 ConfigToadlet.logger=???
 ConfigToadlet.modeAdvanced=??????
 ConfigToadlet.modeSimple=??????
+ConfigToadlet.needRestart=?????????????, ???????.
+ConfigToadlet.needRestartShort=?????????????, ???????.
+ConfigToadlet.needRestartTitle=??????
 ConfigToadlet.node=??
 ConfigToadlet.pluginmanager=?????
 ConfigToadlet.pluginmanager2=?????2
 ConfigToadlet.possibilitiesTitle=??
 ConfigToadlet.reset=??
+ConfigToadlet.restartNode=????
 ConfigToadlet.returnToNodeConfig=??????
 ConfigToadlet.shortTitle=??
 ConfigToadlet.ssl=SSL (????)
@@ -728,16 +732,26 @@
 NodeClientCore.downloadAllowedDirsLong=?????????????, ??????. ???? downloads 
?????????, ???????????????????, ??? all ??????????????. ??! ????? all ??, 
?????????????????, ??????????????!
 NodeClientCore.downloadDir=??????
 NodeClientCore.downloadDirLong=??????????????
+NodeClientCore.encryptPersistentTempBuckets=??????????? ????!
+NodeClientCore.encryptPersistentTempBucketsLong=??????????????? 
????????????????????(?????????????????). ????????????????????!
+NodeClientCore.encryptTempBuckets=???????? ????!
+NodeClientCore.encryptTempBucketsLong=???????????? 
????????????????(?????????????????). ????????????????????!
 NodeClientCore.fileForClientStats=????????????
 NodeClientCore.fileForClientStatsLong=??????????????(???????????)
 NodeClientCore.lazyResume=?????????????? (????????)
 NodeClientCore.lazyResumeLong=???????????????, ??????????????, 
????????????????. ???????, ??????????.
+NodeClientCore.maxArchiveSize=??????????
+NodeClientCore.maxArchiveSizeLong=??????????
+NodeClientCore.maxRAMBucketSize=??????????
+NodeClientCore.maxRAMBucketSizeLong=?????(RAMBucket)?????(??????????????)
 NodeClientCore.maxUSKFetchers=??? USK ???????
 NodeClientCore.maxUSKFetchersLong=??? USK ???????
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=?????
 NodeClientCore.movingTempDirOnTheFlyNotSupported=????????????????
 NodeClientCore.persistentTempDir=?????????
 NodeClientCore.persistentTempDirLong=?????????????
+NodeClientCore.ramBucketPoolSize=???????????
+NodeClientCore.ramBucketPoolSizeLong=???????????. ??????????????????.
 NodeClientCore.startingUp=?? Freenet ???????????, ???????????????, 
?????????????.
 NodeClientCore.startingUpShort=Freenet ?????, ??????????, ?????????.
 NodeClientCore.startingUpTitle=Freenet ?????
@@ -1072,6 +1086,8 @@
 SimpleToadletServer.illegalCSSName=CSS ???????????!
 SimpleToadletServer.panicButton=?????????
 SimpleToadletServer.panicButtonLong=???????'????', ????????????????, ?????????.
+SimpleToadletServer.passthroughMaxSize=Fproxy ????????
+SimpleToadletServer.passthroughMaxSizeLong=Fproxy ????????????
 SimpleToadletServer.port=FProxy ????
 SimpleToadletServer.portLong=FProxy ????? TCP ????
 SimpleToadletServer.ssl=?? SSL?

Modified: branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties
===================================================================
--- branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties        
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties        
2008-09-24 22:16:49 UTC (rev 22824)
@@ -89,11 +89,15 @@
 ConfigToadlet.logger=???
 ConfigToadlet.modeAdvanced=??????
 ConfigToadlet.modeSimple=??????
+ConfigToadlet.needRestart=?????????????, ???????.
+ConfigToadlet.needRestartShort=?????????????, ???????.
+ConfigToadlet.needRestartTitle=??????
 ConfigToadlet.node=??
 ConfigToadlet.pluginmanager=?????
 ConfigToadlet.pluginmanager2=?????2
 ConfigToadlet.possibilitiesTitle=??
 ConfigToadlet.reset=??
+ConfigToadlet.restartNode=????
 ConfigToadlet.returnToNodeConfig=??????
 ConfigToadlet.shortTitle=??
 ConfigToadlet.ssl=SSL (????)
@@ -727,16 +731,26 @@
 NodeClientCore.downloadAllowedDirsLong=?????????????, ??????. ???? downloads 
?????????, ???????????????????, ??? all ??????????????. ??! ????? all ??, 
?????????????????, ?????????????!
 NodeClientCore.downloadDir=??????
 NodeClientCore.downloadDirLong=??????????????
+NodeClientCore.encryptPersistentTempBuckets=??????????? ????!
+NodeClientCore.encryptPersistentTempBucketsLong=??????????????? 
????????????????????(?????????????????). ????????????????????!
+NodeClientCore.encryptTempBuckets=???????? ????!
+NodeClientCore.encryptTempBucketsLong=???????????? 
????????????????(?????????????????). ????????????????????!
 NodeClientCore.fileForClientStats=????????????
 NodeClientCore.fileForClientStatsLong=??????????????(???????????)
 NodeClientCore.lazyResume=?????????????? (?????????)
 NodeClientCore.lazyResumeLong=???????????????, ???????????????, 
????????????????. ???????, ???????????.
+NodeClientCore.maxArchiveSize=??????????
+NodeClientCore.maxArchiveSizeLong=??????????
+NodeClientCore.maxRAMBucketSize=??????????
+NodeClientCore.maxRAMBucketSizeLong=?????(RAMBucket)?????(??????????????)
 NodeClientCore.maxUSKFetchers=??? USK ???????
 NodeClientCore.maxUSKFetchersLong=??? USK ???????
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=?????
 NodeClientCore.movingTempDirOnTheFlyNotSupported=????????????????
 NodeClientCore.persistentTempDir=?????????
 NodeClientCore.persistentTempDirLong=?????????????
+NodeClientCore.ramBucketPoolSize=????????????
+NodeClientCore.ramBucketPoolSizeLong=????????????. ???????????????????.
 NodeClientCore.startingUp=?? Freenet ???????????, ???????????????, 
?????????????.
 NodeClientCore.startingUpShort=Freenet ?????, ??????????, ?????????.
 NodeClientCore.startingUpTitle=Freenet ?????
@@ -1071,6 +1085,8 @@
 SimpleToadletServer.illegalCSSName=CSS ???????????!
 SimpleToadletServer.panicButton=?????????
 SimpleToadletServer.panicButtonLong=???????'????', ????????????????, ?????????.
+SimpleToadletServer.passthroughMaxSize=Fproxy ????????
+SimpleToadletServer.passthroughMaxSizeLong=Fproxy ????????????
 SimpleToadletServer.port=FProxy ???
 SimpleToadletServer.portLong=FProxy ????? TCP ???
 SimpleToadletServer.ssl=?? SSL?

Modified: branches/db4o/freenet/src/freenet/node/KeyTracker.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/KeyTracker.java      2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/KeyTracker.java      2008-09-24 
22:16:49 UTC (rev 22824)
@@ -37,254 +37,251 @@
 public class KeyTracker {

        private static boolean logMINOR;
-       
-    /** Parent PeerNode */
-    public final PeerNode pn;
-    
-    /** Are we the secondary key? */
-    private boolean isDeprecated;
-    
-    /** Cipher to both encrypt outgoing packets with and decrypt
-     * incoming ones. */
-    public final BlockCipher sessionCipher;
-    
-    /** Key for above cipher, so far for debugging */
-    public final byte[] sessionKey;
-    
-    /** Packets we have sent to the node, minus those that have
-     * been acknowledged. */
-    private final LimitedRangeIntByteArrayMap sentPacketsContents;
-    
-    /** Serial numbers of packets that we want to acknowledge,
-     * and when they become urgent. We always add to the end,
-     * and we always remove from the beginning, so should always
-     * be consistent. */
-    private final List ackQueue;
-    
-    /** Serial numbers of packets that we have forgotten. Usually
-     * when we have forgotten a packet it just means that it has 
-     * been shifted to another KeyTracker because this one was
-     * deprecated; the messages will get through in the end.
-     */
-    private final List forgottenQueue;
-    
-    /** The highest incoming serial number we have ever seen
-     * from the other side. Includes actual packets and resend
-     * requests (provided they are within range). */
-    private int highestSeenIncomingSerialNumber;
-    
-    /** Serial numbers of packets we want to be resent by the
-     * other side to us, the time at which they become sendable,
-     * and the time at which they become urgent. In order of
-     * the latter. */
-    private final UpdatableSortedLinkedListWithForeignIndex resendRequestQueue;
-    
-    /** Serial numbers of packets we want to be acknowledged by
-     * the other side, the time at which they become sendable,
-     * and the time at which they become urgent. In order of
-     * the latter. */
-    private final UpdatableSortedLinkedListWithForeignIndex ackRequestQueue;
-    
-    /** Numbered packets that we need to send to the other side
-     * because they asked for them. Just contains the numbers. */
-    private final HashSet packetsToResend;
-    
-    /** Ranges of packet numbers we have received from the other
-     * side. */
-    private final ReceivedPacketNumbers packetNumbersReceived;
-    
-    /** Counter to generate the next packet number */
-    private int nextPacketNumber;
-    
-    final long createdTime;
-    
+       /** Parent PeerNode */
+       public final PeerNode pn;
+       /** Are we the secondary key? */
+       private volatile boolean isDeprecated;
+       /** Cipher to both encrypt outgoing packets with and decrypt
+        * incoming ones. */
+       public final BlockCipher sessionCipher;
+       /** Key for above cipher, so far for debugging */
+       public final byte[] sessionKey;
+       /** Packets we have sent to the node, minus those that have
+        * been acknowledged. */
+       private final LimitedRangeIntByteArrayMap sentPacketsContents;
+       /** Serial numbers of packets that we want to acknowledge,
+        * and when they become urgent. We always add to the end,
+        * and we always remove from the beginning, so should always
+        * be consistent. */
+       private final List ackQueue;
+       /** Serial numbers of packets that we have forgotten. Usually
+        * when we have forgotten a packet it just means that it has 
+        * been shifted to another KeyTracker because this one was
+        * deprecated; the messages will get through in the end.
+        */
+       private final List forgottenQueue;
+       /** The highest incoming serial number we have ever seen
+        * from the other side. Includes actual packets and resend
+        * requests (provided they are within range). */
+       private int highestSeenIncomingSerialNumber;
+       /** Serial numbers of packets we want to be resent by the
+        * other side to us, the time at which they become sendable,
+        * and the time at which they become urgent. In order of
+        * the latter. */
+       private final UpdatableSortedLinkedListWithForeignIndex 
resendRequestQueue;
+       /** Serial numbers of packets we want to be acknowledged by
+        * the other side, the time at which they become sendable,
+        * and the time at which they become urgent. In order of
+        * the latter. */
+       private final UpdatableSortedLinkedListWithForeignIndex ackRequestQueue;
+       /** Numbered packets that we need to send to the other side
+        * because they asked for them. Just contains the numbers. */
+       private final HashSet packetsToResend;
+       /** Ranges of packet numbers we have received from the other
+        * side. */
+       private final ReceivedPacketNumbers packetNumbersReceived;
+       /** Counter to generate the next packet number */
+       private int nextPacketNumber;
+       final long createdTime;
        /** The time at which we last successfully decoded a packet. */
        private long timeLastDecodedPacket;
-       
-    /** Everything is clear to start with */
-    KeyTracker(PeerNode pn, BlockCipher cipher, byte[] sessionKey) {
-        this.pn = pn;
-        this.sessionCipher = cipher;
-        this.sessionKey = sessionKey;
-        ackQueue = new LinkedList();
-        forgottenQueue = new LinkedList();
-        highestSeenIncomingSerialNumber = -1;
-        // give some leeway
-        sentPacketsContents = new LimitedRangeIntByteArrayMap(128);
-        resendRequestQueue = new UpdatableSortedLinkedListWithForeignIndex();
-        ackRequestQueue = new UpdatableSortedLinkedListWithForeignIndex();
-        packetsToResend = new HashSet();
-        packetNumbersReceived = new ReceivedPacketNumbers(512);
-        isDeprecated = false;
-        nextPacketNumber = pn.node.random.nextInt(100*1000);
-        createdTime = System.currentTimeMillis();
-        logMINOR = Logger.shouldLog(Logger.MINOR, this);
-    }

-    /**
-     * Set the deprecated flag to indicate that we are now
-     * no longer the primary key. And wake up any threads trying to lock
-     * a packet number; they can be sent with the new KT.
-     * 
-     * After this, new packets will not be sent. It will not be possible to 
allocate a new
-     * packet number. However, old resend requests etc may still be sent.
-     */
-    public void deprecated() {
-        logMINOR = Logger.shouldLog(Logger.MINOR, this);
-        isDeprecated = true;
-        sentPacketsContents.interrupt();
-    }
-    
-    /**
-     * @return The highest received incoming serial number.
-     */
-    public int highestReceivedIncomingSeqNumber() {
-        return this.highestSeenIncomingSerialNumber;
-    }
+       /** Everything is clear to start with */
+       KeyTracker(PeerNode pn, BlockCipher cipher, byte[] sessionKey) {
+               this.pn = pn;
+               this.sessionCipher = cipher;
+               this.sessionKey = sessionKey;
+               ackQueue = new LinkedList();
+               forgottenQueue = new LinkedList();
+               highestSeenIncomingSerialNumber = -1;
+               // give some leeway
+               sentPacketsContents = new LimitedRangeIntByteArrayMap(128);
+               resendRequestQueue = new 
UpdatableSortedLinkedListWithForeignIndex();
+               ackRequestQueue = new 
UpdatableSortedLinkedListWithForeignIndex();
+               packetsToResend = new HashSet();
+               packetNumbersReceived = new ReceivedPacketNumbers(512);
+               isDeprecated = false;
+               nextPacketNumber = pn.node.random.nextInt(100 * 1000);
+               createdTime = System.currentTimeMillis();
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       }

-    /**
-     * Received this packet??
-     */
-    public boolean alreadyReceived(int seqNumber) {
-        return packetNumbersReceived.contains(seqNumber);
-    }
-    
-    /** toString() - don't leak the key unless asked to */
-    public String toString() {
-        return super.toString()+" for "+pn.shortToString();
-    }
+       /**
+        * Set the deprecated flag to indicate that we are now
+        * no longer the primary key. And wake up any threads trying to lock
+        * a packet number; they can be sent with the new KT.
+        * 
+        * After this, new packets will not be sent. It will not be possible to 
allocate a new
+        * packet number. However, old resend requests etc may still be sent.
+        */
+       public void deprecated() {
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               isDeprecated = true;
+               sentPacketsContents.interrupt();
+       }

-    /**
-     * Queue an ack to be sent back to the node soon.
-     * @param seqNumber The number of the packet to be acked.
-     */
-    public void queueAck(int seqNumber) {
-        if(logMINOR) Logger.minor(this, "Queueing ack for "+seqNumber);
-        QueuedAck qa = new QueuedAck(seqNumber);
-        synchronized(ackQueue) {
-            ackQueue.add(qa);
-        }
-        // Will go urgent in 200ms
-    }
-    
-    public void queueForgotten(int seqNumber) {
-       queueForgotten(seqNumber, true);
-    }
-    
-    public void queueForgotten(int seqNumber, boolean log) {
-       if(log && ((!isDeprecated) || logMINOR)) {
-               String msg = "Queueing forgotten for "+seqNumber+" for "+this;
-               if(!isDeprecated) Logger.error(this, msg);
-               else Logger.minor(this, msg);
-       }
-       QueuedForgotten qf = new QueuedForgotten(seqNumber);
-       synchronized(forgottenQueue) {
-               forgottenQueue.add(qf);
-       }
-    }
-    
-    static class  PacketActionItem { // anyone got a better name?
-        /** Packet sequence number */
-        int packetNumber;
-        /** Time at which this packet's ack or resend request becomes urgent
-         * and can trigger an otherwise empty packet to be sent. */
-        long urgentTime;
-        
-        public String toString() {
-            return super.toString()+": packet "+packetNumber+" 
urgent@"+urgentTime+ '(' +(System.currentTimeMillis()-urgentTime)+ ')';
-        }
-    }
-    
-    private final static class QueuedAck extends PacketActionItem {
-        QueuedAck(int packet) {
-            long now = System.currentTimeMillis();
-            packetNumber = packet;
-            /** If not included on a packet in next 200ms, then
-             * force a send of an otherwise empty packet.
-             */
-            urgentTime = now + 200;
-        }
-    }
+       /**
+        * @return The highest received incoming serial number.
+        */
+       public int highestReceivedIncomingSeqNumber() {
+               return this.highestSeenIncomingSerialNumber;
+       }

-    // FIXME this is almost identical to QueuedAck, coalesce the classes
-    private final static class QueuedForgotten extends PacketActionItem {
-        QueuedForgotten(int packet) {
-            long now = System.currentTimeMillis();
-            packetNumber = packet;
-            /** If not included on a packet in next 500ms, then
-             * force a send of an otherwise empty packet.
-             */
-            urgentTime = now + 500;
-        }
-    }
+       /**
+        * Received this packet??
+        */
+       public boolean alreadyReceived(int seqNumber) {
+               return packetNumbersReceived.contains(seqNumber);
+       }

-    private abstract class BaseQueuedResend extends PacketActionItem 
implements IndexableUpdatableSortedLinkedListItem {
-        /** Time at which this item becomes sendable.
-         * When we send a resend request, this is reset to t+500ms.
-         * 
-         * Constraint: urgentTime is always greater than activeTime.
-         */
-        long activeTime;
-        final Integer packetNumberAsInteger;
-        
-        void sent() throws UpdatableSortedLinkedListKilledException {
-            long now = System.currentTimeMillis();
-            activeTime = now + 500;
-            urgentTime = activeTime + urgentDelay();
-            // This is only removed when we actually receive the packet
-            // But for now it will sleep
-        }
-        
-        BaseQueuedResend(int packetNumber) {
-            this.packetNumber = packetNumber;
-            packetNumberAsInteger = new Integer(packetNumber);
-            long now = System.currentTimeMillis();
-            activeTime = initialActiveTime(now);
-            urgentTime = activeTime + urgentDelay();
-        }
+       /** toString() - don't leak the key unless asked to */
+       @Override
+       public String toString() {
+               return super.toString() + " for " + pn.shortToString();
+       }

-        abstract long urgentDelay();
-        
-        abstract long initialActiveTime(long now);
+       /**
+        * Queue an ack to be sent back to the node soon.
+        * @param seqNumber The number of the packet to be acked.
+        */
+       public void queueAck(int seqNumber) {
+               if(logMINOR)
+                       Logger.minor(this, "Queueing ack for " + seqNumber);
+               QueuedAck qa = new QueuedAck(seqNumber);
+               synchronized(ackQueue) {
+                       ackQueue.add(qa);
+               }
+       // Will go urgent in 200ms
+       }

-        private Item next;
-        private Item prev;
-        
-        public final Item getNext() {
-            return next;
-        }
+       public void queueForgotten(int seqNumber) {
+               queueForgotten(seqNumber, true);
+       }

-        public final Item setNext(Item i) {
-            Item old = next;
-            next = i;
-            return old;
-        }
+       public void queueForgotten(int seqNumber, boolean log) {
+               if(log && ((!isDeprecated) || logMINOR)) {
+                       String msg = "Queueing forgotten for " + seqNumber + " 
for " + this;
+                       if(!isDeprecated)
+                               Logger.error(this, msg);
+                       else
+                               Logger.minor(this, msg);
+               }
+               QueuedForgotten qf = new QueuedForgotten(seqNumber);
+               synchronized(forgottenQueue) {
+                       forgottenQueue.add(qf);
+               }
+       }

-        public Item getPrev() {
-            return prev;
-        }
+       static class PacketActionItem { // anyone got a better name?

-        public Item setPrev(Item i) {
-            Item old = prev;
-            prev = i;
-            return old;
-        }
+               /** Packet sequence number */
+               int packetNumber;
+               /** Time at which this packet's ack or resend request becomes 
urgent
+                * and can trigger an otherwise empty packet to be sent. */
+               long urgentTime;

-        public int compareTo(Object o) {
-            BaseQueuedResend r = (BaseQueuedResend)o;
-            if(urgentTime > r.urgentTime) return 1;
-            if(urgentTime < r.urgentTime) return -1;
-            if(packetNumber > r.packetNumber) return 1;
-            if(packetNumber < r.packetNumber) return -1;
-            return 0;
-        }
-        
-        public Object indexValue() {
-            return packetNumberAsInteger;
-        }
-        
-        private DoublyLinkedList parent;
-        
+               @Override
+               public String toString() {
+                       return super.toString() + ": packet " + packetNumber + 
" urgent@" + urgentTime + '(' + (System.currentTimeMillis() - urgentTime) + ')';
+               }
+       }
+
+       private final static class QueuedAck extends PacketActionItem {
+
+               QueuedAck(int packet) {
+                       long now = System.currentTimeMillis();
+                       packetNumber = packet;
+                       /** If not included on a packet in next 200ms, then
+                        * force a send of an otherwise empty packet.
+                        */
+                       urgentTime = now + 200;
+               }
+       }
+
+       // FIXME this is almost identical to QueuedAck, coalesce the classes
+       private final static class QueuedForgotten extends PacketActionItem {
+
+               QueuedForgotten(int packet) {
+                       long now = System.currentTimeMillis();
+                       packetNumber = packet;
+                       /** If not included on a packet in next 500ms, then
+                        * force a send of an otherwise empty packet.
+                        */
+                       urgentTime = now + 500;
+               }
+       }
+
+       private abstract class BaseQueuedResend extends PacketActionItem
+               implements IndexableUpdatableSortedLinkedListItem {
+
+               /** Time at which this item becomes sendable.
+                * When we send a resend request, this is reset to t+500ms.
+                * 
+                * Constraint: urgentTime is always greater than activeTime.
+                */
+               long activeTime;
+               final Integer packetNumberAsInteger;
+
+               void sent() throws UpdatableSortedLinkedListKilledException {
+                       long now = System.currentTimeMillis();
+                       activeTime = now + 500;
+                       urgentTime = activeTime + urgentDelay();
+               // This is only removed when we actually receive the packet
+               // But for now it will sleep
+               }
+
+               BaseQueuedResend(int packetNumber) {
+                       this.packetNumber = packetNumber;
+                       packetNumberAsInteger = new Integer(packetNumber);
+                       long now = System.currentTimeMillis();
+                       activeTime = initialActiveTime(now);
+                       urgentTime = activeTime + urgentDelay();
+               }
+
+               abstract long urgentDelay();
+
+               abstract long initialActiveTime(long now);
+               private Item next;
+               private Item prev;
+
+               public final Item getNext() {
+                       return next;
+               }
+
+               public final Item setNext(Item i) {
+                       Item old = next;
+                       next = i;
+                       return old;
+               }
+
+               public Item getPrev() {
+                       return prev;
+               }
+
+               public Item setPrev(Item i) {
+                       Item old = prev;
+                       prev = i;
+                       return old;
+               }
+
+               public int compareTo(Object o) {
+                       BaseQueuedResend r = (BaseQueuedResend) o;
+                       if(urgentTime > r.urgentTime)
+                               return 1;
+                       if(urgentTime < r.urgentTime)
+                               return -1;
+                       if(packetNumber > r.packetNumber)
+                               return 1;
+                       if(packetNumber < r.packetNumber)
+                               return -1;
+                       return 0;
+               }
+
+               public Object indexValue() {
+                       return packetNumberAsInteger;
+               }
+               private DoublyLinkedList parent;
+
                public DoublyLinkedList getParent() {
                        return parent;
                }
@@ -294,722 +291,756 @@
                        parent = l;
                        return old;
                }
-    }
-    
-    private class QueuedResendRequest extends BaseQueuedResend {
-        long initialActiveTime(long now) {
-            return now; // Active immediately; reordering is rare
-        }
-        
-        QueuedResendRequest(int packetNumber) {
-            super(packetNumber);
-        }
-        
-        void sent() throws UpdatableSortedLinkedListKilledException {
-            synchronized(resendRequestQueue) {
-                super.sent();
-                resendRequestQueue.update(this);
-            }
-        }
+       }

+       private class QueuedResendRequest extends BaseQueuedResend {
+
+               long initialActiveTime(long now) {
+                       return now; // Active immediately; reordering is rare
+               }
+
+               QueuedResendRequest(int packetNumber) {
+                       super(packetNumber);
+               }
+
+               @Override
+               void sent() throws UpdatableSortedLinkedListKilledException {
+                       synchronized(resendRequestQueue) {
+                               super.sent();
+                               resendRequestQueue.update(this);
+                       }
+               }
+
                long urgentDelay() {
                        return PacketSender.MAX_COALESCING_DELAY; // Urgent 
pretty soon
                }
-    }
-    
-    private class QueuedAckRequest extends BaseQueuedResend {
-       
-       final long createdTime;
-       long activeDelay;
-       
-       long initialActiveTime(long now) {
-               // Request an ack after four RTTs
-               activeDelay = twoRTTs();
-               return now + activeDelay;
-        }
-        
-        QueuedAckRequest(int packetNumber, boolean sendSoon) {
-            super(packetNumber);
-            this.createdTime = System.currentTimeMillis();
-            if(sendSoon) {
-                activeTime -= activeDelay;
-                urgentTime -= activeDelay;
-            }
-        }
-        
-        void sent() throws UpdatableSortedLinkedListKilledException {
-            synchronized(ackRequestQueue) {
-                super.sent();
-                ackRequestQueue.update(this);
-            }
-        }
+       }

-        /**
-         * Acknowledged.
-         */
+       private class QueuedAckRequest extends BaseQueuedResend {
+
+               final long createdTime;
+               long activeDelay;
+
+               long initialActiveTime(long now) {
+                       // Request an ack after four RTTs
+                       activeDelay = twoRTTs();
+                       return now + activeDelay;
+               }
+
+               QueuedAckRequest(int packetNumber, boolean sendSoon) {
+                       super(packetNumber);
+                       this.createdTime = System.currentTimeMillis();
+                       if(sendSoon) {
+                               activeTime -= activeDelay;
+                               urgentTime -= activeDelay;
+                       }
+               }
+
+               @Override
+               void sent() throws UpdatableSortedLinkedListKilledException {
+                       synchronized(ackRequestQueue) {
+                               super.sent();
+                               ackRequestQueue.update(this);
+                       }
+               }
+
+               /**
+                * Acknowledged.
+                */
                public void onAcked() {
                        long t = Math.max(0, System.currentTimeMillis() - 
createdTime);
                        pn.reportPing(t);
-                       if(logMINOR) Logger.minor(this, "Reported round-trip 
time of "+TimeUtil.formatTime(t, 2, true)+" on "+pn.getPeer()+" (avg 
"+TimeUtil.formatTime((long)pn.averagePingTime(),2,true)+", #"+packetNumber+ 
')');
+                       if(logMINOR)
+                               Logger.minor(this, "Reported round-trip time of 
" + TimeUtil.formatTime(t, 2, true) + " on " + pn.getPeer() + " (avg " + 
TimeUtil.formatTime((long) pn.averagePingTime(), 2, true) + ", #" + 
packetNumber + ')');
                }

                long urgentDelay() {
                        return PacketSender.MAX_COALESCING_DELAY;
                }
-    }
-    
-    /**
-     * Called when we receive a packet.
-     * @param seqNumber The packet's serial number.
-     * See the comments in FNPPacketMangler.processOutgoing* for
-     * the reason for the locking.
-     */
-    public synchronized void receivedPacket(int seqNumber) {
-       timeLastDecodedPacket = System.currentTimeMillis();
-        logMINOR = Logger.shouldLog(Logger.MINOR, this);
-       if(logMINOR) Logger.minor(this, "Received packet "+seqNumber+" from 
"+pn.shortToString());
-        if(seqNumber == -1) return;
-        // FIXME delete this log statement
-        if(logMINOR) Logger.minor(this, "Still received packet: "+seqNumber);
-        // Received packet
-        receivedPacketNumber(seqNumber);
-        // Ack it even if it is a resend
-        queueAck(seqNumber);
-    }
+       }

-    // TCP uses four RTTs with no ack to resend ... but we have a more drawn 
out protocol, we
-    // should use only two.
-    public long twoRTTs() {
-       // FIXME upper bound necessary ?
-       return (long) Math.min(Math.max(250, pn.averagePingTime()*2), 2500);
-    }
-    
-    protected void receivedPacketNumber(int seqNumber) {
-       if(logMINOR) Logger.minor(this, "Handling received packet number 
"+seqNumber);
-        queueResendRequests(seqNumber);
-        packetNumbersReceived.got(seqNumber);
-        try {
+       /**
+        * Called when we receive a packet.
+        * @param seqNumber The packet's serial number.
+        * See the comments in FNPPacketMangler.processOutgoing* for
+        * the reason for the locking.
+        */
+       public synchronized void receivedPacket(int seqNumber) {
+               timeLastDecodedPacket = System.currentTimeMillis();
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               if(logMINOR)
+                       Logger.minor(this, "Received packet " + seqNumber + " 
from " + pn.shortToString());
+               if(seqNumber == -1)
+                       return;
+               // FIXME delete this log statement
+               if(logMINOR)
+                       Logger.minor(this, "Still received packet: " + 
seqNumber);
+               // Received packet
+               receivedPacketNumber(seqNumber);
+               // Ack it even if it is a resend
+               queueAck(seqNumber);
+       }
+
+       // TCP uses four RTTs with no ack to resend ... but we have a more 
drawn out protocol, we
+       // should use only two.
+       public long twoRTTs() {
+               // FIXME upper bound necessary ?
+               return (long) Math.min(Math.max(250, pn.averagePingTime() * 2), 
2500);
+       }
+
+       protected void receivedPacketNumber(int seqNumber) {
+               if(logMINOR)
+                       Logger.minor(this, "Handling received packet number " + 
seqNumber);
+               queueResendRequests(seqNumber);
+               packetNumbersReceived.got(seqNumber);
+               try {
                        removeResendRequest(seqNumber);
-               } catch (UpdatableSortedLinkedListKilledException e) {
+               } catch(UpdatableSortedLinkedListKilledException e) {
                        // Ignore, not our problem
                }
-        synchronized(this) {
-               highestSeenIncomingSerialNumber = 
Math.max(highestSeenIncomingSerialNumber, seqNumber);
-        }
-        if(logMINOR) Logger.minor(this, "Handled received packet number 
"+seqNumber);
-    }
-    
-    /**
-     * Remove a resend request from the queue.
-     * @param seqNumber
-     * @throws UpdatableSortedLinkedListKilledException 
-     */
-    private void removeResendRequest(int seqNumber) throws 
UpdatableSortedLinkedListKilledException {
-       resendRequestQueue.removeByKey(new Integer(seqNumber));
-    }
+               synchronized(this) {
+                       highestSeenIncomingSerialNumber = 
Math.max(highestSeenIncomingSerialNumber, seqNumber);
+               }
+               if(logMINOR)
+                       Logger.minor(this, "Handled received packet number " + 
seqNumber);
+       }

-    /**
-     * Add some resend requests if necessary.
-     * @param seqNumber The number of the packet we just received.
-     */
-    private void queueResendRequests(int seqNumber) {
-        int max;
-        synchronized(this) {
-               max = packetNumbersReceived.highest();
-        }
-        if(seqNumber > max) {
-               try {
-            if((max != -1) && (seqNumber - max > 1)) {
-               if(logMINOR) Logger.minor(this, "Queueing resends from "+max+" 
to "+seqNumber);
-                // Missed some packets out
-                for(int i=max+1;i<seqNumber;i++) {
-                    queueResendRequest(i);
-                }
-            }
-               } catch (UpdatableSortedLinkedListKilledException e) {
-                       // Ignore (we are decoding packet, not sending one)
-               }
-        }
-    }
+       /**
+        * Remove a resend request from the queue.
+        * @param seqNumber
+        * @throws UpdatableSortedLinkedListKilledException 
+        */
+       private void removeResendRequest(int seqNumber) throws 
UpdatableSortedLinkedListKilledException {
+               resendRequestQueue.removeByKey(seqNumber);
+       }

-    /**
-     * Queue a resend request
-     * @param packetNumber the packet serial number to queue a
-     * resend request for
-     * @throws UpdatableSortedLinkedListKilledException 
-     */
-    private void queueResendRequest(int packetNumber) throws 
UpdatableSortedLinkedListKilledException {
-       synchronized(resendRequestQueue) {
-               if(queuedResendRequest(packetNumber)) {
-                       if(logMINOR) Logger.minor(this, "Not queueing resend 
request for "+packetNumber+" - already queued");
-                       return;
-               }
-               if(logMINOR) Logger.minor(this, "Queueing resend request for 
"+packetNumber);
-               QueuedResendRequest qrr = new QueuedResendRequest(packetNumber);
-               resendRequestQueue.add(qrr);
-       }
-    }
+       /**
+        * Add some resend requests if necessary.
+        * @param seqNumber The number of the packet we just received.
+        */
+       private void queueResendRequests(int seqNumber) {
+               int max;
+               synchronized(this) {
+                       max = packetNumbersReceived.highest();
+               }
+               if(seqNumber > max)
+                       try {
+                               if((max != -1) && (seqNumber - max > 1)) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Queueing 
resends from " + max + " to " + seqNumber);
+                                       // Missed some packets out
+                                       for(int i = max + 1; i < seqNumber; 
i++) {
+                                               queueResendRequest(i);
+                                       }
+                               }
+                       } catch(UpdatableSortedLinkedListKilledException e) {
+                               // Ignore (we are decoding packet, not sending 
one)
+                       }
+       }

-    /**
-     * Queue an ack request
-     * @param packetNumber the packet serial number to queue a
-     * resend request for
-     * @throws UpdatableSortedLinkedListKilledException 
-     */
-    private void queueAckRequest(int packetNumber) throws 
UpdatableSortedLinkedListKilledException {
-        synchronized(ackRequestQueue) {
-               // FIXME should we just remove the existing ack request? If we 
do, we get a better
-               // estimate of RTT on lossy links... if we don't, lossy links 
will include the average
-               // time to send a packet including all resends. The latter may 
be useful, and in fact
-               // the former is unreliable...
-            if(queuedAckRequest(packetNumber)) {
-               if(logMINOR) Logger.minor(this, "Not queueing ack request for 
"+packetNumber+" - already queued");
-                return;
-            }
-            if(logMINOR) Logger.minor(this, "Queueing ack request for 
"+packetNumber+" on "+this);
-            QueuedAckRequest qrr = new QueuedAckRequest(packetNumber, false);
-            ackRequestQueue.add(qrr);
-        }
-    }
+       /**
+        * Queue a resend request
+        * @param packetNumber the packet serial number to queue a
+        * resend request for
+        * @throws UpdatableSortedLinkedListKilledException 
+        */
+       private void queueResendRequest(int packetNumber) throws 
UpdatableSortedLinkedListKilledException {
+               synchronized(resendRequestQueue) {
+                       if(queuedResendRequest(packetNumber)) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Not queueing resend 
request for " + packetNumber + " - already queued");
+                               return;
+                       }
+                       if(logMINOR)
+                               Logger.minor(this, "Queueing resend request for 
" + packetNumber);
+                       QueuedResendRequest qrr = new 
QueuedResendRequest(packetNumber);
+                       resendRequestQueue.add(qrr);
+               }
+       }

-    /**
-     * Is an ack request queued for this packet number?
-     */
-    private boolean queuedAckRequest(int packetNumber) {
-        return ackRequestQueue.containsKey(new Integer(packetNumber));
-    }
+       /**
+        * Queue an ack request
+        * @param packetNumber the packet serial number to queue a
+        * resend request for
+        * @throws UpdatableSortedLinkedListKilledException 
+        */
+       private void queueAckRequest(int packetNumber) throws 
UpdatableSortedLinkedListKilledException {
+               synchronized(ackRequestQueue) {
+                       // FIXME should we just remove the existing ack 
request? If we do, we get a better
+                       // estimate of RTT on lossy links... if we don't, lossy 
links will include the average
+                       // time to send a packet including all resends. The 
latter may be useful, and in fact
+                       // the former is unreliable...
+                       if(queuedAckRequest(packetNumber)) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Not queueing ack 
request for " + packetNumber + " - already queued");
+                               return;
+                       }
+                       if(logMINOR)
+                               Logger.minor(this, "Queueing ack request for " 
+ packetNumber + " on " + this);
+                       QueuedAckRequest qrr = new 
QueuedAckRequest(packetNumber, false);
+                       ackRequestQueue.add(qrr);
+               }
+       }

-    /**
-     * Is a resend request queued for this packet number?
-     */
-    private boolean queuedResendRequest(int packetNumber) {
-        return resendRequestQueue.containsKey(new Integer(packetNumber));
-    }
+       /**
+        * Is an ack request queued for this packet number?
+        */
+       private boolean queuedAckRequest(int packetNumber) {
+               return ackRequestQueue.containsKey(new Integer(packetNumber));
+       }

-    /**
-     * Called when we have received several packet acknowledgements.
-     * Synchronized for the same reason as the sender code is:
-     * So that we don't end up sending packets too late when overloaded,
-     * and get horrible problems such as asking to resend packets which
-     * haven't been sent yet.
-     */
-    public synchronized void acknowledgedPackets(int[] seqNos) {
-       AsyncMessageCallback[][] callbacks = new 
AsyncMessageCallback[seqNos.length][];
-       for(int i=0;i<seqNos.length;i++) {
-               int realSeqNo = seqNos[i];
-               if(logMINOR) Logger.minor(this, "Acknowledged packet: 
"+realSeqNo);
+       /**
+        * Is a resend request queued for this packet number?
+        */
+       private boolean queuedResendRequest(int packetNumber) {
+               return resendRequestQueue.containsKey(new 
Integer(packetNumber));
+       }
+
+       /**
+        * Called when we have received several packet acknowledgements.
+        * Synchronized for the same reason as the sender code is:
+        * So that we don't end up sending packets too late when overloaded,
+        * and get horrible problems such as asking to resend packets which
+        * haven't been sent yet.
+        */
+       public synchronized void acknowledgedPackets(int[] seqNos) {
+               AsyncMessageCallback[][] callbacks = new 
AsyncMessageCallback[seqNos.length][];
+               for(int i = 0; i < seqNos.length; i++) {
+                       int realSeqNo = seqNos[i];
+                       if(logMINOR)
+                               Logger.minor(this, "Acknowledged packet: " + 
realSeqNo);
+                       try {
+                               removeAckRequest(realSeqNo);
+                       } catch(UpdatableSortedLinkedListKilledException e) {
+                               // Ignore, we are processing an incoming packet
+                       }
+                       if(logMINOR)
+                               Logger.minor(this, "Removed ack request");
+                       callbacks[i] = 
sentPacketsContents.getCallbacks(realSeqNo);
+                       byte[] buf = sentPacketsContents.get(realSeqNo);
+                       long timeAdded = sentPacketsContents.getTime(realSeqNo);
+                       if(sentPacketsContents.remove(realSeqNo))
+                               if(buf.length > Node.PACKET_SIZE) {
+                                       PacketThrottle throttle = 
pn.getThrottle();
+                                       throttle.notifyOfPacketAcknowledged();
+                                       
throttle.setRoundTripTime(System.currentTimeMillis() - timeAdded);
+                               }
+               }
+               int cbCount = 0;
+               for(int i = 0; i < callbacks.length; i++) {
+                       AsyncMessageCallback[] cbs = callbacks[i];
+                       if(cbs != null)
+                               for(int j = 0; j < cbs.length; j++) {
+                                       cbs[j].acknowledged();
+                                       cbCount++;
+                               }
+               }
+               if(cbCount > 0 && logMINOR)
+                       Logger.minor(this, "Executed " + cbCount + " 
callbacks");
+       }
+
+       /**
+        * Called when we have received a packet acknowledgement.
+        * @param realSeqNo
+        */
+       public void acknowledgedPacket(int realSeqNo) {
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               AsyncMessageCallback[] callbacks;
+               if(logMINOR)
+                       Logger.minor(this, "Acknowledged packet: " + realSeqNo);
                try {
-                       removeAckRequest(realSeqNo);
-               } catch (UpdatableSortedLinkedListKilledException e) {
+                       synchronized(this) {
+                               removeAckRequest(realSeqNo);
+                       }
+               } catch(UpdatableSortedLinkedListKilledException e) {
                        // Ignore, we are processing an incoming packet
                }
-               if(logMINOR) Logger.minor(this, "Removed ack request");
-               callbacks[i] = sentPacketsContents.getCallbacks(realSeqNo);
+               if(logMINOR)
+                       Logger.minor(this, "Removed ack request");
+               callbacks = sentPacketsContents.getCallbacks(realSeqNo);
                byte[] buf = sentPacketsContents.get(realSeqNo);
                long timeAdded = sentPacketsContents.getTime(realSeqNo);
-               if(sentPacketsContents.remove(realSeqNo)) {
+               if(sentPacketsContents.remove(realSeqNo))
                        if(buf.length > Node.PACKET_SIZE) {
                                PacketThrottle throttle = pn.getThrottle();
                                throttle.notifyOfPacketAcknowledged();
                                
throttle.setRoundTripTime(System.currentTimeMillis() - timeAdded);
                        }
+               if(callbacks != null) {
+                       for(int i = 0; i < callbacks.length; i++)
+                               callbacks[i].acknowledged();
+                       if(logMINOR)
+                               Logger.minor(this, "Executed " + 
callbacks.length + " callbacks");
                }
        }
-       int cbCount = 0;
-       for(int i=0;i<callbacks.length;i++) {
-               AsyncMessageCallback[] cbs = callbacks[i];
-               if(cbs != null) {
-                       for(int j=0;j<cbs.length;j++) {
-                               cbs[j].acknowledged();
-                               cbCount++;
-                       }
-               }
-       }
-       if(cbCount > 0 && logMINOR)
-               Logger.minor(this, "Executed "+cbCount+" callbacks");
-    }
-    
+
        /**
-     * Called when we have received a packet acknowledgement.
-     * @param realSeqNo
-     */
-    public void acknowledgedPacket(int realSeqNo) {
-       logMINOR = Logger.shouldLog(Logger.MINOR, this);
-        AsyncMessageCallback[] callbacks;
-        if(logMINOR) Logger.minor(this, "Acknowledged packet: "+realSeqNo);
-       try {
-               synchronized (this){
-                       removeAckRequest(realSeqNo);
+        * Remove an ack request from the queue by packet number.
+        * @throws UpdatableSortedLinkedListKilledException 
+        */
+       private void removeAckRequest(int seqNo) throws 
UpdatableSortedLinkedListKilledException {
+               QueuedAckRequest qr = (QueuedAckRequest) 
ackRequestQueue.removeByKey(seqNo);
+               if(qr != null)
+                       qr.onAcked();
+               else
+                       Logger.normal(this, "Removing ack request twice? Null 
on " + seqNo + " from " + pn.getPeer() + " (" + TimeUtil.formatTime((int) 
pn.averagePingTime(), 2, true) + " ping avg)");
+       }
+
+       /**
+        * Resend (off-thread but ASAP) a packet.
+        * @param seqNumber The serial number of the packet to be
+        * resent.
+        */
+       public void resendPacket(int seqNumber) {
+               byte[] resendData = sentPacketsContents.get(seqNumber);
+               if(resendData != null) {
+                       if(resendData.length > Node.PACKET_SIZE)
+                               pn.getThrottle().notifyOfPacketLost();
+                       synchronized(packetsToResend) {
+                               packetsToResend.add(seqNumber);
+                       }
+                       pn.node.ps.wakeUp();
+               } else {
+                       String msg = "Asking me to resend packet " + seqNumber +
+                               " which we haven't sent yet or which they have 
already acked (next=" + nextPacketNumber + ')';
+                       // Might just be late, but could indicate something 
serious.
+                       if(isDeprecated) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Other side wants us 
to resend packet " + seqNumber + " for " + this + " - we cannot do this because 
we are deprecated");
+                       } else
+                               Logger.normal(this, msg);
                }
-       } catch (UpdatableSortedLinkedListKilledException e) {
-               // Ignore, we are processing an incoming packet
        }
-       if(logMINOR) Logger.minor(this, "Removed ack request");
-        callbacks = sentPacketsContents.getCallbacks(realSeqNo);
-        byte[] buf = sentPacketsContents.get(realSeqNo);
-        long timeAdded = sentPacketsContents.getTime(realSeqNo);
-        if(sentPacketsContents.remove(realSeqNo)) {
-               if(buf.length > Node.PACKET_SIZE) {
-                       PacketThrottle throttle = pn.getThrottle();
-                       throttle.notifyOfPacketAcknowledged();
-                       throttle.setRoundTripTime(System.currentTimeMillis() - 
timeAdded);
-               }
-        }
-        if(callbacks != null) {
-            for(int i=0;i<callbacks.length;i++)
-                callbacks[i].acknowledged();
-            if(logMINOR) Logger.minor(this, "Executed "+callbacks.length+" 
callbacks");
-        }
-    }

-    /**
-     * Remove an ack request from the queue by packet number.
-     * @throws UpdatableSortedLinkedListKilledException 
-     */
-    private void removeAckRequest(int seqNo) throws 
UpdatableSortedLinkedListKilledException {
-        QueuedAckRequest qr = 
(QueuedAckRequest)ackRequestQueue.removeByKey(seqNo);
-       if(qr != null) qr.onAcked();
-       else
-               Logger.normal(this, "Removing ack request twice? Null on 
"+seqNo+" from "+pn.getPeer()+" ("+TimeUtil.formatTime((int) 
pn.averagePingTime(), 2, true)+" ping avg)");
-    }
-
-    /**
-     * Resend (off-thread but ASAP) a packet.
-     * @param seqNumber The serial number of the packet to be
-     * resent.
-     */
-    public void resendPacket(int seqNumber) {
-        byte[] resendData = sentPacketsContents.get(seqNumber);
-        if(resendData != null) {
-               if(resendData.length > Node.PACKET_SIZE)
-                       pn.getThrottle().notifyOfPacketLost();
-            synchronized(packetsToResend) {
-                packetsToResend.add(seqNumber);
-            }
-            pn.node.ps.wakeUp();
-        } else {
-               synchronized(this) {
-                       String msg = "Asking me to resend packet "+seqNumber+
-                               " which we haven't sent yet or which they have 
already acked (next="+nextPacketNumber+ ')';
-                       // Might just be late, but could indicate something 
serious.
-                       if(isDeprecated) {
-                               if(logMINOR)
-                                       Logger.minor(this, "Other side wants us 
to resend packet "+seqNumber+" for "+this+" - we cannot do this because we are 
deprecated");
-                       } else {
-                               Logger.normal(this, msg);
-                       }
-               }
-        }
-    }
-
-    /**
-     * Called when we receive an AckRequest.
-     * @param packetNumber The packet that the other side wants
-     * us to re-ack.
-     */
-    public synchronized void receivedAckRequest(int packetNumber) {
-        if(queuedAck(packetNumber)) {
-            // Already going to send an ack
-            // Don't speed it up though; wasteful
-        } else {
-            if(packetNumbersReceived.contains(packetNumber)) {
-                // We have received it, so send them an ack
-                queueAck(packetNumber);
-            } else {
-                // We have not received it, so get them to resend it
-                try {
+       /**
+        * Called when we receive an AckRequest.
+        * @param packetNumber The packet that the other side wants
+        * us to re-ack.
+        */
+       public synchronized void receivedAckRequest(int packetNumber) {
+               if(queuedAck(packetNumber)) {
+                       // Already going to send an ack
+                       // Don't speed it up though; wasteful
+               } else
+                       if(packetNumbersReceived.contains(packetNumber))
+                               // We have received it, so send them an ack
+                               queueAck(packetNumber);
+                       else {
+                               // We have not received it, so get them to 
resend it
+                               try {
                                        queueResendRequest(packetNumber);
-                               } catch 
(UpdatableSortedLinkedListKilledException e) {
+                               } 
catch(UpdatableSortedLinkedListKilledException e) {
                                        // Ignore, we are decoding, not sending.
                                }
-                highestSeenIncomingSerialNumber = 
Math.max(highestSeenIncomingSerialNumber, packetNumber);
-            }
-        }
-    }
+                               highestSeenIncomingSerialNumber = 
Math.max(highestSeenIncomingSerialNumber, packetNumber);
+                       }
+       }

-    /**
-     * Is there a queued ack with the given packet number?
-     * FIXME: have a hashtable? The others do, but probably it
-     * isn't necessary. We should be consistent about this -
-     * either take it out of UpdatableSortedLinkedListWithForeignIndex,
-     * or add one here.
-     */
-    private boolean queuedAck(int packetNumber) {
-        synchronized(ackQueue) {
-               Iterator it = ackQueue.iterator();
-               while (it.hasNext()) {
-                       QueuedAck qa = (QueuedAck) it.next();
-                       if(qa.packetNumber == packetNumber) return true;
-               }
-        }
-        return false;
-    }
+       /**
+        * Is there a queued ack with the given packet number?
+        * FIXME: have a hashtable? The others do, but probably it
+        * isn't necessary. We should be consistent about this -
+        * either take it out of UpdatableSortedLinkedListWithForeignIndex,
+        * or add one here.
+        */
+       private boolean queuedAck(int packetNumber) {
+               synchronized(ackQueue) {
+                       Iterator it = ackQueue.iterator();
+                       while(it.hasNext()) {
+                               QueuedAck qa = (QueuedAck) it.next();
+                               if(qa.packetNumber == packetNumber)
+                                       return true;
+                       }
+               }
+               return false;
+       }

-    /**
-     * Destination forgot a packet.
-     * This is normal if we are the secondary key.
-     * @param seqNumber The packet number lost.
-     */
-    public synchronized void destForgotPacket(int seqNumber) {
-        if(isDeprecated) {
-            Logger.normal(this, "Destination forgot packet: "+seqNumber);
-        } else {
-            Logger.error(this, "Destination forgot packet: "+seqNumber);
-        }
-        try {
-                       removeResendRequest(seqNumber);
-               } catch (UpdatableSortedLinkedListKilledException e) {
-                       // Ignore
+       /**
+        * Destination forgot a packet.
+        * This is normal if we are the secondary key.
+        * @param seqNumber The packet number lost.
+        */
+       public void destForgotPacket(int seqNumber) {
+               if(isDeprecated)
+                       Logger.normal(this, "Destination forgot packet: " + 
seqNumber);
+               else
+                       Logger.error(this, "Destination forgot packet: " + 
seqNumber);
+               synchronized(this) {
+                       try {
+                               removeResendRequest(seqNumber);
+                       } catch(UpdatableSortedLinkedListKilledException e) {
+                               // Ignore
+                       }
                }
-    }
+       }

-    /**
-     * @return A packet number for a new outgoing packet.
-     * This method will block until one is available if
-     * necessary.
-     * @throws KeyChangedException if the thread is interrupted when waiting
-     */
-    public int allocateOutgoingPacketNumber() throws KeyChangedException, 
NotConnectedException {
-        int packetNumber;
-        if(!pn.isConnected()) throw new NotConnectedException();
-        synchronized(this) {
-            if(isDeprecated) throw new KeyChangedException();
-            packetNumber = nextPacketNumber++;
-            if(logMINOR) Logger.minor(this, "Allocated "+packetNumber+" in 
allocateOutgoingPacketNumber for "+this);
-        }
-        while(true) {
-            try {
-                sentPacketsContents.lock(packetNumber);
-                return packetNumber;
-            } catch (InterruptedException e) {
-               synchronized(this) {
-                       if(isDeprecated) throw new KeyChangedException();
-               }
-            }
-        }
-    }
+       /**
+        * @return A packet number for a new outgoing packet.
+        * This method will block until one is available if
+        * necessary.
+        * @throws KeyChangedException if the thread is interrupted when waiting
+        */
+       public int allocateOutgoingPacketNumber() throws KeyChangedException, 
NotConnectedException {
+               int packetNumber;
+               if(!pn.isConnected())
+                       throw new NotConnectedException();
+               synchronized(this) {
+                       if(isDeprecated)
+                               throw new KeyChangedException();
+                       packetNumber = nextPacketNumber++;
+                       if(logMINOR)
+                               Logger.minor(this, "Allocated " + packetNumber 
+ " in allocateOutgoingPacketNumber for " + this);
+               }
+               while(true) {
+                       try {
+                               sentPacketsContents.lock(packetNumber);
+                               return packetNumber;
+                       } catch(InterruptedException e) {
+                               synchronized(this) {
+                                       if(isDeprecated)
+                                               throw new KeyChangedException();
+                               }
+                       }
+               }
+       }

-    /**
-     * @return A packet number for a new outgoing packet.
-     * This method will not block, and will throw an exception
-     * if it would need to block.
-     * @throws KeyChangedException if the thread is interrupted when waiting
-     */
-    public int allocateOutgoingPacketNumberNeverBlock() throws 
KeyChangedException, NotConnectedException, WouldBlockException {
-        int packetNumber;
-        if(!pn.isConnected()) throw new NotConnectedException();
-        synchronized(this) {
-            packetNumber = nextPacketNumber;
-            if(isDeprecated) throw new KeyChangedException();
-            sentPacketsContents.lockNeverBlock(packetNumber);
-            nextPacketNumber = packetNumber+1;
-            if(logMINOR) Logger.minor(this, "Allocated "+packetNumber+" in 
allocateOutgoingPacketNumberNeverBlock for "+this);
-            return packetNumber;
-        }
-    }
+       /**
+        * @return A packet number for a new outgoing packet.
+        * This method will not block, and will throw an exception
+        * if it would need to block.
+        * @throws KeyChangedException if the thread is interrupted when waiting
+        */
+       public int allocateOutgoingPacketNumberNeverBlock() throws 
KeyChangedException, NotConnectedException, WouldBlockException {
+               int packetNumber;
+               if(!pn.isConnected())
+                       throw new NotConnectedException();
+               synchronized(this) {
+                       packetNumber = nextPacketNumber;
+                       if(isDeprecated)
+                               throw new KeyChangedException();
+                       sentPacketsContents.lockNeverBlock(packetNumber);
+                       nextPacketNumber = packetNumber + 1;
+                       if(logMINOR)
+                               Logger.minor(this, "Allocated " + packetNumber 
+ " in allocateOutgoingPacketNumberNeverBlock for " + this);
+                       return packetNumber;
+               }
+       }

-    public int[] grabForgotten() {
-       if(logMINOR) Logger.minor(this, "Grabbing forgotten packet numbers");
-        int[] acks;
-        synchronized(forgottenQueue) {
-            // Grab the acks and tell them they are sent
-            int length = forgottenQueue.size();
-            acks = new int[length];
-            int i=0;
-            
-            Iterator it = forgottenQueue.iterator();
-            while (it.hasNext()) {
-                QueuedForgotten ack = (QueuedForgotten) it.next();
-                acks[i++] = ack.packetNumber;
-                if(logMINOR) Logger.minor(this, "Grabbing ack 
"+ack.packetNumber+" from "+this);
-                it.remove();   // sent
-            }
-        }
-        return acks;
-    }
+       public int[] grabForgotten() {
+               if(logMINOR)
+                       Logger.minor(this, "Grabbing forgotten packet numbers");
+               int[] acks;
+               synchronized(forgottenQueue) {
+                       // Grab the acks and tell them they are sent
+                       int length = forgottenQueue.size();
+                       acks = new int[length];
+                       int i = 0;

+                       Iterator it = forgottenQueue.iterator();
+                       while(it.hasNext()) {
+                               QueuedForgotten ack = (QueuedForgotten) 
it.next();
+                               acks[i++] = ack.packetNumber;
+                               if(logMINOR)
+                                       Logger.minor(this, "Grabbing ack " + 
ack.packetNumber + " from " + this);
+                               it.remove();    // sent
+                       }
+               }
+               return acks;
+       }
+
        public void requeueForgot(int[] forgotPackets, int start, int length) {
                synchronized(forgottenQueue) { // It doesn't do anything else 
does it? REDFLAG
-                       for(int i=start;i<start+length;i++) {
+                       for(int i = start; i < start + length; i++) {
                                queueForgotten(i, false);
                        }
                }
        }

-    
-    /**
-     * Grab all the currently queued acks to be sent to this node.
-     * @return An array of packet numbers that we need to acknowledge.
-     */
-    public int[] grabAcks() {
-       if(logMINOR) Logger.minor(this, "Grabbing acks");
-        int[] acks;
-        synchronized(ackQueue) {
-            // Grab the acks and tell them they are sent
-            int length = ackQueue.size();
-            acks = new int[length];
-            int i=0;
-            Iterator it = ackQueue.iterator();
-            while (it.hasNext()) {
-                QueuedAck ack = (QueuedAck) it.next();
-                acks[i++] = ack.packetNumber;
-                if(logMINOR) Logger.minor(this, "Grabbing ack 
"+ack.packetNumber+" from "+this);
-                it.remove();   // sent
-            }
-        }
-        return acks;
-    }
+       /**
+        * Grab all the currently queued acks to be sent to this node.
+        * @return An array of packet numbers that we need to acknowledge.
+        */
+       public int[] grabAcks() {
+               if(logMINOR)
+                       Logger.minor(this, "Grabbing acks");
+               int[] acks;
+               synchronized(ackQueue) {
+                       // Grab the acks and tell them they are sent
+                       int length = ackQueue.size();
+                       acks = new int[length];
+                       int i = 0;
+                       Iterator it = ackQueue.iterator();
+                       while(it.hasNext()) {
+                               QueuedAck ack = (QueuedAck) it.next();
+                               acks[i++] = ack.packetNumber;
+                               if(logMINOR)
+                                       Logger.minor(this, "Grabbing ack " + 
ack.packetNumber + " from " + this);
+                               it.remove();    // sent
+                       }
+               }
+               return acks;
+       }

-    /**
-     * Grab all the currently queued resend requests.
-     * @return An array of the packet numbers of all the packets we want to be 
resent.
-     * @throws NotConnectedException If the peer is no longer connected.
-     */
-    public int[] grabResendRequests() throws NotConnectedException {
-        UpdatableSortedLinkedListItem[] items;
-        int[] packetNumbers;
-        int realLength;
-        long now = System.currentTimeMillis();
-        try {
-        synchronized(resendRequestQueue) {
-            items = resendRequestQueue.toArray();
-            int length = items.length;
-            packetNumbers = new int[length];
-            realLength = 0;
-            for(int i=0;i<length;i++) {
-                QueuedResendRequest qrr = (QueuedResendRequest)items[i];
-                if(packetNumbersReceived.contains(qrr.packetNumber)) {
-                       if(logMINOR) Logger.minor(this, "Have already seen 
"+qrr.packetNumber+", removing from resend list");
-                       resendRequestQueue.remove(qrr);
-                       continue;
-                }
-                if(qrr.activeTime <= now) {
-                    packetNumbers[realLength++] = qrr.packetNumber;
-                    if(logMINOR) Logger.minor(this, "Grabbing resend request: 
"+qrr.packetNumber+" from "+this);
-                    qrr.sent();
-                } else {
-                       if(logMINOR) Logger.minor(this, "Rejecting resend 
request: "+qrr.packetNumber+" - in future by "+(qrr.activeTime-now)+"ms for 
"+this);
-                }
-            }
-        }
-        } catch (UpdatableSortedLinkedListKilledException e) {
-               throw new NotConnectedException();
-        }
-        int[] trimmedPacketNumbers = new int[realLength];
-        System.arraycopy(packetNumbers, 0, trimmedPacketNumbers, 0, 
realLength);
-        return trimmedPacketNumbers;
-    }
+       /**
+        * Grab all the currently queued resend requests.
+        * @return An array of the packet numbers of all the packets we want to 
be resent.
+        * @throws NotConnectedException If the peer is no longer connected.
+        */
+       public int[] grabResendRequests() throws NotConnectedException {
+               UpdatableSortedLinkedListItem[] items;
+               int[] packetNumbers;
+               int realLength;
+               long now = System.currentTimeMillis();
+               try {
+                       synchronized(resendRequestQueue) {
+                               items = resendRequestQueue.toArray();
+                               int length = items.length;
+                               packetNumbers = new int[length];
+                               realLength = 0;
+                               for(int i = 0; i < length; i++) {
+                                       QueuedResendRequest qrr = 
(QueuedResendRequest) items[i];
+                                       
if(packetNumbersReceived.contains(qrr.packetNumber)) {
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Have already seen " + qrr.packetNumber + ", removing from resend list");
+                                               resendRequestQueue.remove(qrr);
+                                               continue;
+                                       }
+                                       if(qrr.activeTime <= now) {
+                                               packetNumbers[realLength++] = 
qrr.packetNumber;
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Grabbing resend request: " + qrr.packetNumber + " from " + this);
+                                               qrr.sent();
+                                       } else
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Rejecting resend request: " + qrr.packetNumber + " - in future by " + 
(qrr.activeTime - now) + "ms for " + this);
+                               }
+                       }
+               } catch(UpdatableSortedLinkedListKilledException e) {
+                       throw new NotConnectedException();
+               }
+               int[] trimmedPacketNumbers = new int[realLength];
+               System.arraycopy(packetNumbers, 0, trimmedPacketNumbers, 0, 
realLength);
+               return trimmedPacketNumbers;
+       }

-    public int[] grabAckRequests() throws NotConnectedException {
-        UpdatableSortedLinkedListItem[] items;
-        int[] packetNumbers;
-        int realLength;
-        if(logMINOR) Logger.minor(this, "Grabbing ack requests");
-        try {
-        synchronized(ackRequestQueue) {
-            long now = System.currentTimeMillis();
-            items = ackRequestQueue.toArray();
-            int length = items.length;
-            packetNumbers = new int[length];
-            realLength = 0;
-            for(int i=0;i<length;i++) {
-                QueuedAckRequest qr = (QueuedAckRequest)items[i];
-                int packetNumber = qr.packetNumber;
-                if(qr.activeTime <= now) {
-                    if(sentPacketsContents.get(packetNumber) == null) {
-                       if(logMINOR) Logger.minor(this, "Asking to ack packet 
which has already been acked: "+packetNumber+" on "+this+".grabAckRequests");
-                        ackRequestQueue.remove(qr);
-                        continue;
-                    }
-                    if(now - qr.createdTime > 2*60*1000) {
-                       Logger.error(this, "Packet "+qr.packetNumber+" sent 
over "+(now - qr.createdTime)+"ms ago and still not acked on "+this+" for "+pn);
-                    }
-                    packetNumbers[realLength++] = packetNumber;
-                    if(logMINOR) Logger.minor(this, "Grabbing ack request 
"+packetNumber+" ("+realLength+") from "+this);
-                    qr.sent();
-                } else {
-                       if(logMINOR) Logger.minor(this, "Ignoring ack request 
"+packetNumber+" ("+realLength+") - will become active in 
"+(qr.activeTime-now)+"ms on "+this+" - "+qr);
-                }
-            }
-        }
-        } catch (UpdatableSortedLinkedListKilledException e) {
-               throw new NotConnectedException();
-        }
-        if(logMINOR) Logger.minor(this, "realLength now "+realLength);
-        int[] trimmedPacketNumbers = new int[realLength];
-        System.arraycopy(packetNumbers, 0, trimmedPacketNumbers, 0, 
realLength);
-        if(logMINOR) Logger.minor(this, "Returning 
"+trimmedPacketNumbers.length+" ackRequests");
-        return trimmedPacketNumbers;
-    }
+       public int[] grabAckRequests() throws NotConnectedException {
+               UpdatableSortedLinkedListItem[] items;
+               int[] packetNumbers;
+               int realLength;
+               if(logMINOR)
+                       Logger.minor(this, "Grabbing ack requests");
+               try {
+                       synchronized(ackRequestQueue) {
+                               long now = System.currentTimeMillis();
+                               items = ackRequestQueue.toArray();
+                               int length = items.length;
+                               packetNumbers = new int[length];
+                               realLength = 0;
+                               for(int i = 0; i < length; i++) {
+                                       QueuedAckRequest qr = 
(QueuedAckRequest) items[i];
+                                       int packetNumber = qr.packetNumber;
+                                       if(qr.activeTime <= now) {
+                                               
if(sentPacketsContents.get(packetNumber) == null) {
+                                                       if(logMINOR)
+                                                               
Logger.minor(this, "Asking to ack packet which has already been acked: " + 
packetNumber + " on " + this + ".grabAckRequests");
+                                                       
ackRequestQueue.remove(qr);
+                                                       continue;
+                                               }
+                                               if(now - qr.createdTime > 2 * 
60 * 1000)
+                                                       Logger.error(this, 
"Packet " + qr.packetNumber + " sent over " + (now - qr.createdTime) + "ms ago 
and still not acked on " + this + " for " + pn);
+                                               packetNumbers[realLength++] = 
packetNumber;
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Grabbing ack request " + packetNumber + " (" + realLength + ") from " + this);
+                                               qr.sent();
+                                       } else
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Ignoring ack request " + packetNumber + " (" + realLength + ") - will become 
active in " + (qr.activeTime - now) + "ms on " + this + " - " + qr);
+                               }
+                       }
+               } catch(UpdatableSortedLinkedListKilledException e) {
+                       throw new NotConnectedException();
+               }
+               if(logMINOR)
+                       Logger.minor(this, "realLength now " + realLength);
+               int[] trimmedPacketNumbers = new int[realLength];
+               System.arraycopy(packetNumbers, 0, trimmedPacketNumbers, 0, 
realLength);
+               if(logMINOR)
+                       Logger.minor(this, "Returning " + 
trimmedPacketNumbers.length + " ackRequests");
+               return trimmedPacketNumbers;
+       }

-    /**
-     * @return The time at which we will have to send some
-     * notifications. Or Long.MAX_VALUE if there are none to send.
-     */
-    public long getNextUrgentTime() {
-        long earliestTime = Long.MAX_VALUE;
-        synchronized(ackQueue) {
-            if(!ackQueue.isEmpty()) {
-                QueuedAck qa = (QueuedAck) ackQueue.get(0);
-                earliestTime = qa.urgentTime;
-            }
-        }
-        synchronized(resendRequestQueue) {
-            if(!resendRequestQueue.isEmpty()) {
-                QueuedResendRequest qr = (QueuedResendRequest) 
resendRequestQueue.getLowest();
-                earliestTime = Math.min(earliestTime, qr.urgentTime);
-            }
-        }
-        synchronized(ackRequestQueue) {
-            if(!ackRequestQueue.isEmpty()) {
-                QueuedAckRequest qr = (QueuedAckRequest) 
ackRequestQueue.getLowest();
-                earliestTime = Math.min(earliestTime, qr.urgentTime);
-            }
-        }
-        return earliestTime;
-    }
+       /**
+        * @return The time at which we will have to send some
+        * notifications. Or Long.MAX_VALUE if there are none to send.
+        */
+       public long getNextUrgentTime() {
+               long earliestTime = Long.MAX_VALUE;
+               synchronized(ackQueue) {
+                       if(!ackQueue.isEmpty()) {
+                               QueuedAck qa = (QueuedAck) ackQueue.get(0);
+                               earliestTime = qa.urgentTime;
+                       }
+               }
+               synchronized(resendRequestQueue) {
+                       if(!resendRequestQueue.isEmpty()) {
+                               QueuedResendRequest qr = (QueuedResendRequest) 
resendRequestQueue.getLowest();
+                               earliestTime = Math.min(earliestTime, 
qr.urgentTime);
+                       }
+               }
+               synchronized(ackRequestQueue) {
+                       if(!ackRequestQueue.isEmpty()) {
+                               QueuedAckRequest qr = (QueuedAckRequest) 
ackRequestQueue.getLowest();
+                               earliestTime = Math.min(earliestTime, 
qr.urgentTime);
+                       }
+               }
+               return earliestTime;
+       }

-    /**
-     * @return The last sent new packet number.
-     */
-    public int getLastOutgoingSeqNumber() {
-        synchronized(this) {
-            return nextPacketNumber-1;
-        }
-    }
+       /**
+        * @return The last sent new packet number.
+        */
+       public int getLastOutgoingSeqNumber() {
+               synchronized(this) {
+                       return nextPacketNumber - 1;
+               }
+       }

-    /**
-     * Report a packet has been sent
-     * @param data The data we have just sent (payload only, decrypted). 
-     * @param seqNumber The packet number.
-     * @throws NotConnectedException 
-     */
-    public void sentPacket(byte[] data, int seqNumber, AsyncMessageCallback[] 
callbacks, short priority) throws NotConnectedException {
-        if(callbacks != null) {
-            for(int i=0;i<callbacks.length;i++) {
-                if(callbacks[i] == null)
-                    throw new NullPointerException();
-            }
-        }
-        sentPacketsContents.add(seqNumber, data, callbacks, priority);
-        try {
+       /**
+        * Report a packet has been sent
+        * @param data The data we have just sent (payload only, decrypted). 
+        * @param seqNumber The packet number.
+        * @throws NotConnectedException 
+        */
+       public void sentPacket(byte[] data, int seqNumber, 
AsyncMessageCallback[] callbacks, short priority) throws NotConnectedException {
+               if(callbacks != null)
+                       for(int i = 0; i < callbacks.length; i++) {
+                               if(callbacks[i] == null)
+                                       throw new NullPointerException();
+                       }
+               sentPacketsContents.add(seqNumber, data, callbacks, priority);
+               try {
                        queueAckRequest(seqNumber);
-               } catch (UpdatableSortedLinkedListKilledException e) {
+               } catch(UpdatableSortedLinkedListKilledException e) {
                        throw new NotConnectedException();
                }
-    }
+       }

-    /**
-     * Clear the KeyTracker. Deprecate it, clear all resend, ack, request-ack 
etc queues.
-     * Return the messages we still had in flight. The caller will then either 
add them to
-     * another KeyTracker, or call their callbacks to indicate failure.
-     */
-    private LimitedRangeIntByteArrayMapElement[] clear() {
-       if(logMINOR) Logger.minor(this, "Clearing "+this);
-        isDeprecated = true;
-        LimitedRangeIntByteArrayMapElement[] elements;
-        synchronized(sentPacketsContents) {
-            elements = sentPacketsContents.grabAll(); // will clear
-        }
-        synchronized(ackQueue) {
-            ackQueue.clear();
-        }
-        resendRequestQueue.kill();
-        ackRequestQueue.kill();
-        synchronized(packetsToResend) {
-            packetsToResend.clear();
-        }
-       packetNumbersReceived.clear();
-       return elements;
-    }
+       /**
+        * Clear the KeyTracker. Deprecate it, clear all resend, ack, 
request-ack etc queues.
+        * Return the messages we still had in flight. The caller will then 
either add them to
+        * another KeyTracker, or call their callbacks to indicate failure.
+        */
+       private LimitedRangeIntByteArrayMapElement[] clear() {
+               if(logMINOR)
+                       Logger.minor(this, "Clearing " + this);
+               isDeprecated = true;
+               LimitedRangeIntByteArrayMapElement[] elements;
+               synchronized(sentPacketsContents) {
+                       elements = sentPacketsContents.grabAll(); // will clear
+               }
+               synchronized(ackQueue) {
+                       ackQueue.clear();
+               }
+               resendRequestQueue.kill();
+               ackRequestQueue.kill();
+               synchronized(packetsToResend) {
+                       packetsToResend.clear();
+               }
+               packetNumbersReceived.clear();
+               return elements;
+       }

-    /**
-     * Completely deprecate the KeyTracker, in favour of a new one. 
-     * It will no longer be used for anything. The KeyTracker will be cleared 
and all outstanding packets
-     * moved to the new KeyTracker.
-     * 
-     * *** Must only be called if the KeyTracker is not to be kept. Otherwise, 
we may receive some packets twice. ***
-     */
-    public void completelyDeprecated(KeyTracker newTracker) {
-       if(logMINOR) Logger.minor(this, "Completely deprecated: "+this+" in 
favour of "+newTracker);
-       LimitedRangeIntByteArrayMapElement[] elements = clear();
-       if(elements.length == 0) return; // nothing more to do
-        MessageItem[] messages = new MessageItem[elements.length];
-        for(int i=0;i<elements.length;i++) {
-            LimitedRangeIntByteArrayMapElement element = elements[i];
-            byte[] buf = element.data;
-            AsyncMessageCallback[] callbacks = element.callbacks;
-            // Ignore packet#
-            if(logMINOR) Logger.minor(this, "Queueing resend of what was once 
"+element.packetNumber);
-            messages[i] = new MessageItem(buf, callbacks, true, 0, 
pn.resendByteCounter, element.priority);
-        }
-        pn.requeueMessageItems(messages, 0, messages.length, true);
-        
-        pn.node.ps.wakeUp();
-    }
+       /**
+        * Completely deprecate the KeyTracker, in favour of a new one. 
+        * It will no longer be used for anything. The KeyTracker will be 
cleared and all outstanding packets
+        * moved to the new KeyTracker.
+        * 
+        * *** Must only be called if the KeyTracker is not to be kept. 
Otherwise, we may receive some packets twice. ***
+        */
+       public void completelyDeprecated(KeyTracker newTracker) {
+               if(logMINOR)
+                       Logger.minor(this, "Completely deprecated: " + this + " 
in favour of " + newTracker);
+               LimitedRangeIntByteArrayMapElement[] elements = clear();
+               if(elements.length == 0)
+                       return; // nothing more to do
+               MessageItem[] messages = new MessageItem[elements.length];
+               for(int i = 0; i < elements.length; i++) {
+                       LimitedRangeIntByteArrayMapElement element = 
elements[i];
+                       byte[] buf = element.data;
+                       AsyncMessageCallback[] callbacks = element.callbacks;
+                       // Ignore packet#
+                       if(logMINOR)
+                               Logger.minor(this, "Queueing resend of what was 
once " + element.packetNumber);
+                       messages[i] = new MessageItem(buf, callbacks, true, 0, 
pn.resendByteCounter, element.priority);
+               }
+               pn.requeueMessageItems(messages, 0, messages.length, true);

-    /**
-     * Called when the node appears to have been disconnected.
-     * Dump all sent messages.
-     */
-    public void disconnected() {
-        // Clear everything, call the callbacks
-       LimitedRangeIntByteArrayMapElement[] elements = clear();
-        for(int i=0;i<elements.length;i++) {
-            LimitedRangeIntByteArrayMapElement element = elements[i];
-            AsyncMessageCallback[] callbacks = element.callbacks;
-            if(callbacks != null) {
-                for(int j=0;j<callbacks.length;j++)
-                    callbacks[j].disconnected();
-            }
-        }
-    }
+               pn.node.ps.wakeUp();
+       }

-    /**
-     * Fill rpiTemp with ResendPacketItems of packets that need to be
-     * resent.
-     * @return An array of integers which contains the packet numbers
-     * to be resent (the RPI's are put into rpiTemp), or null if there
-     * are no packets to resend.
-     * 
-     * Not a very nice API, but it saves a load of allocations, and at
-     * least it's documented!
-     */
-    public int[] grabResendPackets(Vector rpiTemp, int[] numbers) {
-       rpiTemp.clear();
-        long now = System.currentTimeMillis();
-        long fourRTTs = twoRTTs();
-        int count=0;
-        synchronized(packetsToResend) {
-            int len = packetsToResend.size();
-            if(numbers.length < len)
-               numbers = new int[len * 2];
-            for(Iterator it=packetsToResend.iterator();it.hasNext();) {
-                int packetNo = ((Integer)it.next()).intValue();
-                long resentTime = sentPacketsContents.getReaddedTime(packetNo);
-                if(now - resentTime > fourRTTs) {
-                       // Either never resent, or resent at least 4 RTTs ago
-                       numbers[count++] = packetNo;
-                       it.remove();
-                }
-            }
-            packetsToResend.clear();
-        }
-        for(int i=0;i<count;i++) {
-            int packetNo = numbers[i];
-            byte[] buf = sentPacketsContents.get(packetNo);
-            if(buf == null) {
-               if(logMINOR) Logger.minor(this, "Contents null for "+packetNo+" 
in grabResendPackets on "+this);
-                continue; // acked already?
-            }
-            AsyncMessageCallback[] callbacks = 
sentPacketsContents.getCallbacks(packetNo);
-            short priority = sentPacketsContents.getPriority(packetNo, 
DMT.PRIORITY_BULK_DATA);
-            rpiTemp.add(new ResendPacketItem(buf, packetNo, this, callbacks, 
priority));
-        }
-        if(rpiTemp.isEmpty()) return null;
-        return numbers;
-    }
+       /**
+        * Called when the node appears to have been disconnected.
+        * Dump all sent messages.
+        */
+       public void disconnected() {
+               // Clear everything, call the callbacks
+               LimitedRangeIntByteArrayMapElement[] elements = clear();
+               for(int i = 0; i < elements.length; i++) {
+                       LimitedRangeIntByteArrayMapElement element = 
elements[i];
+                       AsyncMessageCallback[] callbacks = element.callbacks;
+                       if(callbacks != null)
+                               for(int j = 0; j < callbacks.length; j++)
+                                       callbacks[j].disconnected();
+               }
+       }

+       /**
+        * Fill rpiTemp with ResendPacketItems of packets that need to be
+        * resent.
+        * @return An array of integers which contains the packet numbers
+        * to be resent (the RPI's are put into rpiTemp), or null if there
+        * are no packets to resend.
+        * 
+        * Not a very nice API, but it saves a load of allocations, and at
+        * least it's documented!
+        */
+       public int[] grabResendPackets(Vector rpiTemp, int[] numbers) {
+               rpiTemp.clear();
+               long now = System.currentTimeMillis();
+               long fourRTTs = twoRTTs();
+               int count = 0;
+               synchronized(packetsToResend) {
+                       int len = packetsToResend.size();
+                       if(numbers.length < len)
+                               numbers = new int[len * 2];
+                       for(Iterator it = packetsToResend.iterator(); 
it.hasNext();) {
+                               int packetNo = ((Integer) it.next()).intValue();
+                               long resentTime = 
sentPacketsContents.getReaddedTime(packetNo);
+                               if(now - resentTime > fourRTTs) {
+                                       // Either never resent, or resent at 
least 4 RTTs ago
+                                       numbers[count++] = packetNo;
+                                       it.remove();
+                               }
+                       }
+                       packetsToResend.clear();
+               }
+               for(int i = 0; i < count; i++) {
+                       int packetNo = numbers[i];
+                       byte[] buf = sentPacketsContents.get(packetNo);
+                       if(buf == null) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Contents null for " 
+ packetNo + " in grabResendPackets on " + this);
+                               continue; // acked already?
+                       }
+                       AsyncMessageCallback[] callbacks = 
sentPacketsContents.getCallbacks(packetNo);
+                       short priority = 
sentPacketsContents.getPriority(packetNo, DMT.PRIORITY_BULK_DATA);
+                       rpiTemp.add(new ResendPacketItem(buf, packetNo, this, 
callbacks, priority));
+               }
+               if(rpiTemp.isEmpty())
+                       return null;
+               return numbers;
+       }
+
        public boolean isDeprecated() {
                return this.isDeprecated;
        }
@@ -1033,5 +1064,4 @@
        public synchronized long timeLastDecodedPacket() {
                return timeLastDecodedPacket;
        }
-       
 }

Modified: branches/db4o/freenet/src/freenet/node/Node.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/Node.java    2008-09-24 20:42:20 UTC 
(rev 22823)
+++ branches/db4o/freenet/src/freenet/node/Node.java    2008-09-24 22:16:49 UTC 
(rev 22824)
@@ -1500,63 +1500,6 @@

                final String suffix = "-" + getDarknetPortNumber();
                String datastoreDir = nodeConfig.getString("storeDir");
-               // FIXME: temporary cludge for backward compat.
-               File tmpFile = new File("datastore");
-               if(".".equals(datastoreDir) && !tmpFile.exists()) {
-                       System.out.println("Your node seems to be using the old 
directory, we will move it: !!DO NOT RESTART!!");
-                       Logger.normal(this, "Your node seems to be using the 
old directory, we will move it: !!DO NOT RESTART!!");
-                       boolean done = false;
-                       try {
-                               if(tmpFile.mkdir()) {
-                                       File chkStoreCache = new 
File("chk"+suffix+".cache");
-                                       File chkStoreCacheNew = new 
File("datastore/chk"+suffix+".cache");
-                                       
if(!chkStoreCache.renameTo(chkStoreCacheNew))
-                                               throw new IOException();
-                                       File chkStoreStore = new 
File("chk"+suffix+".store");
-                                       File chkStoreStoreNew = new 
File("datastore/chk"+suffix+".store");
-                                       
if(!chkStoreStore.renameTo(chkStoreStoreNew))
-                                               throw new IOException();
-                                       
-                                       File sskStoreCache = new 
File("ssk"+suffix+".cache");
-                                       File sskStoreCacheNew = new 
File("datastore/ssk"+suffix+".cache");
-                                       
if(!sskStoreCache.renameTo(sskStoreCacheNew))
-                                               throw new IOException();
-                                       File sskStoreStore = new 
File("ssk"+suffix+".store");
-                                       File sskStoreStoreNew = new 
File("datastore/ssk"+suffix+".store");
-                                       
if(!sskStoreStore.renameTo(sskStoreStoreNew))
-                                               throw new IOException();
-                                       
-                                       File pubkeyStoreCache = new 
File("pubkey"+suffix+".cache");
-                                       File pubkeyStoreCacheNew = new 
File("datastore/pubkey"+suffix+".cache");
-                                       
if(!pubkeyStoreCache.renameTo(pubkeyStoreCacheNew))
-                                               throw new IOException();
-                                       File pubkeyStoreStore = new 
File("pubkey"+suffix+".store");
-                                       File pubkeyStoreStoreNew = new 
File("datastore/pubkey"+suffix+".store");
-                                       
if(!pubkeyStoreStore.renameTo(pubkeyStoreStoreNew))
-                                               throw new IOException();
-                                       
-                                       File databaseStoreDir = new 
File("database"+suffix);
-                                       File databaseStoreDirNew = new 
File("datastore/database"+suffix);
-                                       
if(!databaseStoreDir.renameTo(databaseStoreDirNew))
-                                               throw new IOException();
-                                       done = true;
-                               }
-                       } catch (Throwable e) {
-                               e.printStackTrace();
-                               done = false;
-                       }
-                       
-                       if(done) {
-                               datastoreDir = "datastore/";
-                               nodeConfig.fixOldDefault("storeDir", 
datastoreDir);
-                               Logger.normal(this, "The migration is complete, 
cool :)");
-                               System.out.println("The migration is complete, 
cool :)");
-                       } else {
-                               Logger.error(this, "Something went wrong :( 
please report the bug!");
-                               System.err.println("Something went wrong :( 
please report the bug!");
-                       }
-               }
-               
                storeDir = new File(datastoreDir);
                if(!((storeDir.exists() && storeDir.isDirectory()) || 
(storeDir.mkdir()))) {
                        String msg = "Could not find or create datastore 
directory";

Modified: branches/db4o/freenet/src/freenet/node/NodeClientCore.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/NodeClientCore.java  2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/NodeClientCore.java  2008-09-24 
22:16:49 UTC (rev 22824)
@@ -1,5 +1,6 @@
 package freenet.node;

+import freenet.config.NodeNeedRestartException;
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
@@ -130,7 +131,6 @@
        public static int maxBackgroundUSKFetchers;     // Client stuff that 
needs to be configged - FIXME
        static final int MAX_ARCHIVE_HANDLERS = 200; // don't take up much 
RAM... FIXME
        static final long MAX_CACHED_ARCHIVE_DATA = 32 * 1024 * 1024; // make a 
fixed fraction of the store by default? FIXME
-       static final long MAX_ARCHIVE_SIZE = 2 * 1024 * 1024; // ??? FIXME
        static final long MAX_ARCHIVED_FILE_SIZE = 1024 * 1024; // arbitrary... 
FIXME
        static final int MAX_CACHED_ELEMENTS = 256 * 1024; // equally 
arbitrary! FIXME hopefully we can cache many of these though
        /** Each FEC item can take a fair amount of RAM, since it's fully 
activated with all the buckets, potentially 256
@@ -355,7 +355,21 @@
                        });
                
setUploadAllowedDirs(nodeConfig.getStringArr("uploadAllowedDirs"));

-               archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS, 
MAX_CACHED_ARCHIVE_DATA, MAX_ARCHIVE_SIZE, MAX_ARCHIVED_FILE_SIZE, 
MAX_CACHED_ELEMENTS, tempBucketFactory);
+               nodeConfig.register("maxArchiveSize", "5MiB", sortOrder++, 
true, false, "NodeClientCore.maxArchiveSize", 
"NodeClientCore.maxArchiveSizeLong", new LongCallback() {
+
+                       @Override
+                       public Long get() {
+                               return archiveManager.getMaxArchiveSize();
+                       }
+
+                       @Override
+                       public void set(Long val) throws 
InvalidConfigValueException, NodeNeedRestartException {
+                               if(val == get()) return;
+                               archiveManager.setMaxArchiveSize(val);
+                       }
+               });
+               
+               archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS, 
MAX_CACHED_ARCHIVE_DATA, nodeConfig.getLong("maxArchiveSize"), 
MAX_ARCHIVED_FILE_SIZE, MAX_CACHED_ELEMENTS, tempBucketFactory);
                Logger.normal(this, "Initializing USK Manager");
                System.out.println("Initializing USK Manager");
                uskManager.init(container, clientContext);

Modified: branches/db4o/freenet/src/freenet/node/PeerManager.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/PeerManager.java     2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/PeerManager.java     2008-09-24 
22:16:49 UTC (rev 22824)
@@ -4,7 +4,6 @@
 package freenet.node;

 import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
@@ -14,7 +13,6 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
-import java.io.Writer;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1068,6 +1066,7 @@
                return sb.toString();
        }
        private final Object writePeersSync = new Object();
+       private final Object writePeerFileSync = new Object();

        void writePeers() {
                node.ps.queueTimedJob(new Runnable() {
@@ -1077,25 +1076,83 @@
                        }
                }, 0);
        }
+       
+       protected StringBuilder getDarknetPeersString() {
+               StringBuilder sb = new StringBuilder();
+               PeerNode[] peers;
+               synchronized(this) {
+                       peers = myPeers;
+               }
+               for(PeerNode pn : peers) {
+                       if(pn instanceof DarknetPeerNode)
+                               sb.append(pn.exportDiskFieldSet());
+               }
+               
+               return sb;
+       }
+       
+       protected StringBuilder getOpennetPeersString() {
+               StringBuilder sb = new StringBuilder();
+               PeerNode[] peers;
+               synchronized(this) {
+                       peers = myPeers;
+               }
+               for(PeerNode pn : peers) {
+                       if(pn instanceof OpennetPeerNode)
+                               sb.append(pn.exportDiskFieldSet());
+               }
+               
+               return sb;
+       }
+       
+       protected StringBuilder getOldOpennetPeersString(OpennetManager om) {
+               StringBuilder sb = new StringBuilder();
+               PeerNode[] peers;
+               synchronized(this) {
+                       peers = om.getOldPeers();
+               }
+               for(PeerNode pn : peers) {
+                       if(pn instanceof OpennetPeerNode)
+                               sb.append(pn.exportDiskFieldSet());
+               }
+               
+               return sb;
+       }

        private void writePeersInner() {
+               StringBuilder darknet = null;
+               StringBuilder opennet = null;
+               StringBuilder oldOpennetPeers = null;
+               String oldOpennetPeersFilename = null;
+               
                synchronized(writePeersSync) {
                        if(darkFilename != null)
-                               writePeersInner(darkFilename, 
getDarknetPeers());
+                               darknet = getDarknetPeersString();
                        OpennetManager om = node.getOpennet();
                        if(om != null) {
                                if(openFilename != null)
-                                       writePeersInner(openFilename, 
getOpennetPeers());
-                               writePeersInner(om.getOldPeersFilename(), 
om.getOldPeers());
+                                       opennet = getOpennetPeersString();
+                               oldOpennetPeersFilename = 
om.getOldPeersFilename();
+                               oldOpennetPeers = getOldOpennetPeersString(om);
                        }
                }
+               
+               synchronized(writePeerFileSync) {
+                       if(darknet != null)
+                               writePeersInner(darkFilename, darknet);
+                       if(oldOpennetPeers != null) {
+                               if(opennet != null)
+                                       writePeersInner(openFilename, opennet);
+                               writePeersInner(oldOpennetPeersFilename, 
oldOpennetPeers);
+                       }
+               }
        }

        /**
         * Write the peers file to disk
         */
-       private void writePeersInner(String filename, PeerNode[] peers) {
-               synchronized(writePeersSync) {
+       private void writePeersInner(String filename, StringBuilder sb) {
+               synchronized(writePeerFileSync) {
                        FileOutputStream fos = null;
                        String f = filename + ".bak";
                        try {
@@ -1112,13 +1169,11 @@
                                Closer.close(w);
                                throw new Error("Impossible: JVM doesn't 
support UTF-8: " + e2, e2);
                        }
-                       BufferedWriter bw = new BufferedWriter(w);
                        try {
-                               boolean succeeded = writePeers(bw, peers);
-                               bw.close();
-                               bw = null;
-                               if(!succeeded)
-                                       return;
+                               w.write(sb.toString());
+                               w.flush();
+                               w.close();
+                               w = null;

                                File fnam = new File(filename);
                                FileUtil.renameTo(new File(f), fnam);
@@ -1131,38 +1186,12 @@
                                Logger.error(this, "Cannot write file: " + e, 
e);
                                return; // don't overwrite old file!
                        } finally {
-                               Closer.close(bw);
+                               Closer.close(w);
                                Closer.close(fos);
                        }
                }
        }

-       public boolean writePeers(Writer bw) {
-               if(!writePeers(bw, getDarknetPeers()))
-                       return false;
-               if(!writePeers(bw, getOpennetPeers()))
-                       return false;
-               return true;
-       }
-
-       public boolean writePeers(Writer bw, PeerNode[] peers) {
-               for(int i = 0; i < peers.length; i++) {
-                       try {
-                               peers[i].write(bw);
-                               bw.flush();
-                       } catch(IOException e) {
-                               try {
-                                       bw.close();
-                               } catch(IOException e1) {
-                                       Logger.error(this, "Cannot close file!: 
" + e1, e1);
-                               }
-                               Logger.error(this, "Cannot write peers to disk: 
" + e, e);
-                               return false;
-                       }
-               }
-               return true;
-       }
-
        /**
         * Update the numbers needed by our PeerManagerUserAlert on the UAM.
         * Also run the node's onConnectedPeers() method if applicable

Modified: branches/db4o/freenet/src/freenet/node/PeerNode.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/PeerNode.java        2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/PeerNode.java        2008-09-24 
22:16:49 UTC (rev 22824)
@@ -2564,6 +2564,17 @@
                        fs.put("metadata", meta);
                fs.writeTo(w);
        }
+       
+       /**
+        * (both metadata + normal fieldset but atomically)
+        */
+       public synchronized SimpleFieldSet exportDiskFieldSet() {
+               SimpleFieldSet fs = exportFieldSet();
+               SimpleFieldSet meta = exportMetadataFieldSet();
+               if(!meta.isEmpty())
+                       fs.put("metadata", meta);
+               return fs;
+       }

        /**
        * Export metadata about the node as a SimpleFieldSet

Modified: branches/db4o/freenet/src/freenet/node/TestnetHandler.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/TestnetHandler.java  2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/TestnetHandler.java  2008-09-24 
22:16:49 UTC (rev 22824)
@@ -182,8 +182,6 @@
                                                fs = 
node.exportOpennetPublicFieldSet();
                                                fs.writeTo(bw);
                                        }
-                                       bw.write("\n\nMy peers:\n");
-                                       node.peers.writePeers(bw);
                                        bw.close();
                                }else {
                                        Logger.error(this, "Unknown testnet 
command: "+command);

Modified: 
branches/db4o/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java   
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java   
2008-09-24 22:16:49 UTC (rev 22824)
@@ -14,6 +14,7 @@
 import freenet.support.SimpleFieldSet;
 import freenet.support.io.Closer;
 import freenet.support.io.LineReadingInputStream;
+import freenet.support.io.TooLongException;

 public class FCPConnectionInputHandler implements Runnable {

@@ -31,6 +32,8 @@
            freenet.support.Logger.OSThread.logPID(this);
                try {
                        realRun();
+               } catch (TooLongException e) {
+                       Logger.normal(this, "Caught"+e.getMessage(),e);
                } catch (IOException e) {
                        if(Logger.shouldLog(Logger.MINOR, this))
                                Logger.minor(this, "Caught "+e, e);

Modified: branches/db4o/freenet/src/freenet/support/DoublyLinkedList.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/DoublyLinkedList.java     
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/DoublyLinkedList.java     
2008-09-24 22:16:49 UTC (rev 22824)
@@ -6,31 +6,31 @@
  * Framework for managing a doubly linked list.
  * @author tavin
  */
-public interface DoublyLinkedList<T> {
+public interface DoublyLinkedList<T extends DoublyLinkedList.Item<T>> {
     public abstract DoublyLinkedList<T> clone();

     /**
      * List element
      */
-    public interface Item<T> {
+    public interface Item<T extends Item<T>> {
                /**
                 * Get next {@link Item}. May or may not return
                 * <code>null</code> if this is the last <code>Item</code>.
                 * 
                 * @see DoublyLinkedList#hasNext()
                 */
-        DoublyLinkedList.Item<T> getNext();
+        T getNext();
         /** Set next {@link Item} */
-        DoublyLinkedList.Item<T> setNext(DoublyLinkedList.Item<T> i);
+        T setNext(T i);
         /**
         * Get previous {@link Item}. May or may not return <code>null</code>
         * if this is the first <code>Item</code>.
         * 
         * @see DoublyLinkedList#hasNext()
         */
-        Item<T> getPrev();
+        T getPrev();
         /** Get previous {@link Item} */
-        Item<T> setPrev(DoublyLinkedList.Item<T> i);
+        T setPrev(T i);

         /** Return the contained list. <strong>For sanity checking 
only.</strong> */
         DoublyLinkedList<T> getParent();
@@ -45,23 +45,23 @@
     /** Check if this list is empty. @return <code>true</code> if this list is 
empty, <code>false</code> otherwise. */
     boolean isEmpty();
     /** Get a {@link Enumeration} of {@link DoublyLinkedList.Item}. */
-    Enumeration elements();   // for consistency w/ typical Java API
+    Enumeration<T> elements();   // for consistency w/ typical Java API

     /**
      * Returns the first item.
      * @return  the item at the head of the list, or <code>null</code> if empty
      */
-    Item head();
+    T head();
     /**
      * Returns the last item.
      * @return  the item at the tail of the list, or <code>null</code> if empty
      */
-    Item tail();
+    T tail();

     /**
      * Puts the item before the first item.
      */
-    void unshift(DoublyLinkedList.Item<T> i);
+    void unshift(T i);
     /**
      * Put all items in the specified list before the first item.
      */
@@ -69,7 +69,7 @@
     /**
      * Removes and returns the first item.
      */
-    Item shift();
+    T shift();
     /**
      * Remove <tt>n</tt> elements from head and return them as a 
<code>DoublyLinkedList</code>.
      */
@@ -78,7 +78,7 @@
     /**
      * Puts the item after the last item.
      */
-    void push(DoublyLinkedList.Item<T> i);
+    void push(T i);
     /**
      * Puts all items in the specified list after the last item.
      */
@@ -86,41 +86,41 @@
     /**
      * Removes and returns the last item.
      */
-    Item pop();
+    T pop();
     /**
      * Remove <tt>n</tt> elements from tail and return them as a 
<code>DoublyLinkedList</code>.
      */
     DoublyLinkedList pop(int n);

-    /** @return <code>true</code> if <code>i</code> has next item. (ie. not 
the last item); <code>false</code> otherwise */ 
-    boolean hasNext(DoublyLinkedList.Item<T> i);
+    /** @return <code>true</code> if <code>i</code> has next item. (ie. not 
the last item); <code>false</code> otherwise */
+    boolean hasNext(T i);
     /** @return <code>true</code> if <code>i</code> has previous item. (ie. 
not the first item); <code>false</code> otherwise */
-    boolean hasPrev(DoublyLinkedList.Item<T> i);
+    boolean hasPrev(T i);

     /** @return next item of <code>i</code>. If this is the last element, 
return <code>null</code> */
-    Item next(DoublyLinkedList.Item<T> i);
+    T next(T i);
     /** @return previous item of <code>i</code>. If this is the first element, 
return <code>null</code> */
-    Item prev(DoublyLinkedList.Item<T> i);
-    /** Remove and return a element 
+    T prev(T i);
+    /** Remove and return a element
      * @return  this item, or <code>null</code> if the item was not in the list
      */
-    Item remove(DoublyLinkedList.Item<T> i);
+    T remove(T i);
     /**
      * Inserts item <code>j</code> before item <code>i</code>.
      */
-    void insertPrev(DoublyLinkedList.Item<T> i, DoublyLinkedList.Item<T> j);
+    void insertPrev(T i, T j);
     /**
      * Inserts the entire {@link DoublyLinkedList} <code>l</code> before item 
<code>i</code>.
      */
-    void insertPrev(DoublyLinkedList.Item<T> i, DoublyLinkedList<T> l);  
+    void insertPrev(T i, DoublyLinkedList<T> l);
     /**
      * Inserts item <code>j</code> after item <code>i</code.
      */
-    void insertNext(DoublyLinkedList.Item<T> i, DoublyLinkedList.Item<T> j);   
 
+    void insertNext(T i, T j);
     /**
      * Inserts the entire {@link DoublyLinkedList} <code>l</code> after item 
<code>i</code>.
      */
-    void insertNext(DoublyLinkedList.Item<T> i, DoublyLinkedList<T> l);
+    void insertNext(T i, DoublyLinkedList<T> l);
 }



Modified: branches/db4o/freenet/src/freenet/support/DoublyLinkedListImpl.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/DoublyLinkedListImpl.java 
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/DoublyLinkedListImpl.java 
2008-09-24 22:16:49 UTC (rev 22824)
@@ -10,10 +10,10 @@
  * TODO: there are still some unimplemented methods
  *       -- it remains to be seen if they are needed at all
  */
-public class DoublyLinkedListImpl<T> implements DoublyLinkedList<T>{
+public class DoublyLinkedListImpl<T extends DoublyLinkedListImpl.Item<T>> 
implements DoublyLinkedList<T> {

     protected int size;
-    protected DoublyLinkedListImpl.Item<T> _headptr, _tailptr;
+    protected T _headptr, _tailptr;

        @Override
     public final DoublyLinkedListImpl<T> clone() {
@@ -24,14 +24,14 @@
      * A new list with no items.
      */
     public DoublyLinkedListImpl() {
-        _headptr = new Item<T>();
-        _tailptr = new Item<T>();
+        _headptr = (T) new Item();
+               _tailptr = (T) new Item();
         _headptr.setParent(this);
         _tailptr.setParent(this);
         clear();
     }

-    protected DoublyLinkedListImpl(DoublyLinkedListImpl.Item<T> _h, 
DoublyLinkedListImpl.Item<T> _t, int size) {
+    protected DoublyLinkedListImpl(T _h, T _t, int size) {
         _headptr  = _h;
         _tailptr  = _t;
         _headptr.setParent(this);
@@ -43,13 +43,13 @@
     /**
      * @param impl
      */
-    protected DoublyLinkedListImpl(DoublyLinkedListImpl impl) {
+    protected DoublyLinkedListImpl(DoublyLinkedListImpl<T> impl) {
         this();
-        Enumeration<DoublyLinkedListImpl.Item<T>> e = impl.forwardElements();
+        Enumeration<T> e = impl.forwardElements();
         boolean checked = false;
         for(;e.hasMoreElements();) {
-            DoublyLinkedListImpl.Item<T> oi = e.nextElement();
-            DoublyLinkedList.Item<T> i = oi.clone();
+            T oi =  e.nextElement();
+                       T i = oi.clone();
             if(!checked) {
                 checked = true;
                 if(!i.getClass().getName().equals(oi.getClass().getName())) {
@@ -70,8 +70,8 @@
        // Help to detect removal after clear().
        // The check in remove() is enough, strictly,
        // as long as people don't add elements afterwards.
-       DoublyLinkedList.Item<T> pos = _headptr.next;
-       DoublyLinkedList.Item<T> opos = _headptr;
+       DoublyLinkedList.Item pos = _headptr.next;
+               DoublyLinkedList.Item opos = _headptr;
        while(true) {
                if(pos == _tailptr) break;
                if(pos == null) break;
@@ -104,22 +104,22 @@
      * {@inheritDoc}
      * @see #forwardElements()
      */
-    public final Enumeration<DoublyLinkedList.Item<T>> elements() {
+    public final Enumeration elements() {
         return forwardElements();
     }

     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> head() {
-        return size == 0 ? null : _headptr.next;
+    public final T head() {
+        return size == 0 ? null : (T) _headptr.next;
     }

     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> tail() {
-        return size == 0 ? null : _tailptr.prev;
+    public final T tail() {
+               return size == 0 ? null : (T) _tailptr.prev;
     }


@@ -127,8 +127,8 @@
     /**
      * {@inheritDoc}
      */
-    public final void unshift(DoublyLinkedList.Item<T> i) {
-        insertNext(_headptr, i);
+    public final void unshift(T i) {
+        insertNext((T) _headptr, i);
     }

     /**
@@ -141,8 +141,8 @@
     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> shift() {
-        return size == 0 ? null : remove(_headptr.next);
+    public final T shift() {
+               return size == 0 ? null : (T) remove((T) _headptr.next);
     }
     /**
      * {@inheritDoc}
@@ -152,13 +152,13 @@
         if (n > size) n = size;
         if (n < 1) return new DoublyLinkedListImpl<T>();

-        DoublyLinkedList.Item<T> i = _headptr;
+        T i = _headptr;
         for (int m=0; m<n; ++m)
             i = i.getNext();

-        DoublyLinkedList.Item<T> j = i.getNext();
-        Item<T> newheadptr = new Item<T>();
-        Item<T> newtailptr = new Item<T>();
+        T j = i.getNext();
+               T newheadptr = (T) new Item<T>();
+               T newtailptr = (T) new Item<T>();

         j.setPrev(newheadptr);
         newheadptr.setNext(j);
@@ -179,8 +179,8 @@
     /**
      * {@inheritDoc}
      */
-    public final void push(DoublyLinkedList.Item<T> i) {
-        insertPrev(_tailptr, i);
+    public final void push(T i) {
+        insertPrev((T) _tailptr, i);
     }
     /**
      * {@inheritDoc}
@@ -192,8 +192,8 @@
     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> pop() {
-        return size == 0 ? null : remove(_tailptr.prev);
+    public final T pop() {
+               return size == 0 ? null : (T) remove((T) _tailptr.prev);
     }
     /**
      * {@inheritDoc}
@@ -203,13 +203,13 @@
         if (n > size) n = size;
         if (n < 1) return new DoublyLinkedListImpl<T>();

-        DoublyLinkedList.Item<T> i = _tailptr;
+        T i = _tailptr;
         for (int m=0; m<n; ++m)
             i = i.getPrev();

-        DoublyLinkedList.Item<T> j = i.getPrev();
-        DoublyLinkedListImpl.Item<T> newtailptr = new Item<T>();
-        DoublyLinkedListImpl.Item<T> newheadptr = new Item<T>();
+        T j = i.getPrev();
+               T newtailptr = (T) new Item<T>();
+               T newheadptr = (T) new Item<T>();

         j.setNext(newtailptr);
         newtailptr.setPrev(j);
@@ -230,29 +230,29 @@
     /**
      * {@inheritDoc}
      */
-    public final boolean hasNext(DoublyLinkedList.Item<T> i) {
-        DoublyLinkedList.Item<T> next = i.getNext();
+    public final boolean hasNext(T i) {
+               DoublyLinkedList.Item next = i.getNext();
         return (next != null) && (next != _tailptr);
     }
     /**
      * {@inheritDoc}
      */
-    public final boolean hasPrev(DoublyLinkedList.Item<T> i) {
-        DoublyLinkedList.Item<T> prev = i.getPrev();
+    public final boolean hasPrev(T i) {
+               DoublyLinkedList.Item prev = i.getPrev();
         return (prev != null) && (prev != _headptr);
     }
     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> next(DoublyLinkedList.Item<T> i) {
-        DoublyLinkedList.Item<T> next = i.getNext();
-        return next == _tailptr ? null : next;
+    public final T next(T i) {
+               T next = i.getNext();
+        return next == _tailptr ? null : (T) next;
     }
     /**
      * {@inheritDoc}
      */
-    public final DoublyLinkedList.Item<T> prev(DoublyLinkedList.Item<T> i) {
-        DoublyLinkedList.Item<T> prev = i.getPrev();
+    public final T prev(T i) {
+               T prev = i.getPrev();
         return prev == _headptr ? null : prev;
     }

@@ -262,7 +262,7 @@
     /**
      * {@inheritDoc}
      */
-    public DoublyLinkedList.Item<T> remove(DoublyLinkedList.Item<T> i) {
+    public T remove(T i) {
        if (i.getParent() == null) return null; // not in list
        if(isEmpty()) {
                Logger.error(this, "Illegal ERROR: Removing from an empty 
list!!");
@@ -270,7 +270,7 @@
        }
        if (i.getParent() != this)
                throw new PromiscuousItemException(i, i.getParent());
-        DoublyLinkedList.Item<T> next = i.getNext(), prev = i.getPrev();
+        T next = i.getNext(), prev = i.getPrev();
         if ((next == null) && (prev == null)) return null;  // not in the list
         if ((next == null) || (prev == null))
                throw new NullPointerException("next="+next+", prev="+prev); // 
partially in the list?!
@@ -291,7 +291,7 @@
     /**
      * {@inheritDoc}
      */
-    public void insertPrev(DoublyLinkedList.Item<T> i, 
DoublyLinkedList.Item<T> j) {
+    public void insertPrev(T i, T j) {
        if (i.getParent() == null)
                throw new PromiscuousItemException(i, i.getParent()); // 
different trace to make easier debugging
        if (i.getParent() != this)
@@ -300,7 +300,7 @@
                throw new PromiscuousItemException(j, j.getParent());
         if ((j.getNext() != null) || (j.getPrev() != null))
             throw new PromiscuousItemException(j);
-        DoublyLinkedList.Item<T> prev = i.getPrev();
+        T prev = i.getPrev();
         if (prev == null)
             throw new VirginItemException(i);
         prev.setNext(j);
@@ -315,21 +315,21 @@
      * {@inheritDoc}
      * FIXME: unimplemented
      */
-    public void insertPrev(DoublyLinkedList.Item<T> i, DoublyLinkedList<T> l) {
+    public void insertPrev(T i, DoublyLinkedList<T> l) {
         throw new RuntimeException("function currently unimplemented because i 
am a lazy sod");
     }

     /**
      * {@inheritDoc}
      */
-    public void insertNext(DoublyLinkedList.Item<T> i, 
DoublyLinkedList.Item<T> j) {
+    public void insertNext(T i, T j) {
        if (i.getParent() != this)
                throw new PromiscuousItemException(i, i.getParent());
        if (j.getParent() != null)
                throw new PromiscuousItemException(j, i.getParent());
         if ((j.getNext() != null) || (j.getPrev() != null))
             throw new PromiscuousItemException(j);
-        DoublyLinkedList.Item next = i.getNext();
+        T next = i.getNext();
         if (next == null)
             throw new VirginItemException(i);
         next.setPrev(j);
@@ -344,7 +344,7 @@
      * {@inheritDoc}
      * FIXME: unimplemented
      */
-    public void insertNext(DoublyLinkedList.Item<T> i, DoublyLinkedList<T> l) {
+    public void insertNext(T i, DoublyLinkedList<T> l) {
         throw new RuntimeException("function currently unimplemented because i 
am a lazy sod");
     }

@@ -354,54 +354,54 @@
     /**
      * @return  an Enumeration of list elements from head to tail
      */
-    private Enumeration<DoublyLinkedList.Item<T>> forwardElements() {
+    private Enumeration<T> forwardElements() {
         return new ForwardWalker();
     }

     /**
      * @return  an Enumeration of list elements from tail to head
      */
-    protected Enumeration<DoublyLinkedList.Item<T>> reverseElements() {
-        return new ReverseWalker();
+    protected Enumeration<T> reverseElements() {
+               return new ReverseWalker();
     }

-    private class ForwardWalker<T extends DoublyLinkedListImpl.Item<T>> 
implements Enumeration<DoublyLinkedList.Item<T>> {
-        protected DoublyLinkedList.Item<T> next;
+    private class ForwardWalker implements Enumeration<T> {
+               protected DoublyLinkedList.Item next;
         protected ForwardWalker() {
             next = _headptr.getNext();
         }
-        protected ForwardWalker(DoublyLinkedList.Item<T> startAt,
+        protected ForwardWalker(DoublyLinkedList.Item startAt,
                                 boolean inclusive) {
             next = (inclusive ? startAt : startAt.getNext());
         }
         public final boolean hasMoreElements() {
             return next != _tailptr;
         }
-        public DoublyLinkedList.Item<T> nextElement() {
+        public T nextElement() {
             if (next == _tailptr)
                 throw new NoSuchElementException();
-            DoublyLinkedList.Item<T> result = next;
+            T result = (T) next;
             next = next.getNext();
             return result;
         }
     }

-    private class ReverseWalker<T extends DoublyLinkedList.Item<T>> implements 
Enumeration<DoublyLinkedList.Item<T>> {
-        protected DoublyLinkedList.Item<T> next;
+    private class ReverseWalker implements Enumeration<T> {
+               protected DoublyLinkedList.Item next;
         protected ReverseWalker() {
             next = _tailptr.getPrev();
         }
-        protected ReverseWalker(DoublyLinkedList.Item<T> startAt,
+        protected ReverseWalker(DoublyLinkedList.Item startAt,
                                 boolean inclusive) {
             next = (inclusive ? startAt : startAt.getPrev());
         }
         public final boolean hasMoreElements() {
             return next != _headptr;
         }
-        public DoublyLinkedList.Item<T> nextElement() {
+        public T nextElement() {
             if (next == _headptr)
                 throw new NoSuchElementException();
-            DoublyLinkedList.Item<T> result = next;
+            T result = (T) next;
            if(next == null) throw new IllegalStateException("next==null");
             next = next.getPrev();
             return result;
@@ -411,36 +411,36 @@

     //=== list element ====================================================

-    public static class Item<T> implements DoublyLinkedList.Item<T> {
-        private DoublyLinkedList.Item<T> prev;
-        private DoublyLinkedList.Item<T> next;
+    public static class Item<T extends Item<T>> implements 
DoublyLinkedList.Item<T> {
+               private T prev;
+               private T next;
         private DoublyLinkedList list;
                @Override
-        public DoublyLinkedList.Item<T> clone() {
+        public T clone() {
             if(getClass() != Item.class)
                 throw new RuntimeException("Must implement clone() for 
"+getClass());
-            return new Item<T>();
+            return (T) new Item();
         }
-        public final DoublyLinkedList.Item getNext() {
+        public final T getNext() {
             return next;
         }
-        public final DoublyLinkedList.Item setNext(DoublyLinkedList.Item<T> i) 
{
-            DoublyLinkedList.Item<T> old = next;
+        public final T setNext(T i) {
+                       T old = next;
             next = i;
             return old;
         }
-        public final DoublyLinkedList.Item getPrev() {
+        public final T getPrev() {
             return prev;
         }
-        public final DoublyLinkedList.Item setPrev(DoublyLinkedList.Item<T> i) 
{
-            DoublyLinkedList.Item<T> old = prev;
+        public final T setPrev(T i) {
+                       T old = prev;
             prev = i;
             return old;
         }
-               public DoublyLinkedList getParent() {
+               public DoublyLinkedList<T> getParent() {
                        return list;
                }
-               public DoublyLinkedList setParent(DoublyLinkedList<T> l) {
+               public DoublyLinkedList<T> setParent(DoublyLinkedList<T> l) {
                        DoublyLinkedList old = list;
                        list = l;
                        return old;

Modified: branches/db4o/freenet/src/freenet/support/HTMLNode.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/HTMLNode.java     2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/HTMLNode.java     2008-09-24 
22:16:49 UTC (rev 22824)
@@ -8,7 +8,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;

 public class HTMLNode implements XMLCharacterClasses {
@@ -44,9 +43,7 @@
        }

        public HTMLNode(String name, String[] attributeNames, String[] 
attributeValues, String content) {
-               
-               Matcher nameMatcher = namePattern.matcher(name);
-               if ((name == null) || (!"#".equals(name) && !"%".equals(name) 
&& !nameMatcher.matches())) {
+               if ((name == null) || (!"#".equals(name) && !"%".equals(name) 
&& !namePattern.matcher(name).matches())) {
                        throw new IllegalArgumentException("element name is not 
legal");
                }
                if ((attributeNames != null) && (attributeValues != null)) {
@@ -193,7 +190,9 @@
                }
                if (children.size() == 0) {
                        if ("textarea".equals(name) || ("div").equals(name) || 
("a").equals(name)) {
-                               
tagBuffer.append("></").append(name).append('>');
+                               tagBuffer.append("></");
+                               tagBuffer.append(name);
+                               tagBuffer.append('>');
                        } else {
                                tagBuffer.append(" />");
                        }
@@ -206,7 +205,9 @@
                                HTMLNode childNode = children.get(childIndex);
                                childNode.generate(tagBuffer);
                        }
-                       tagBuffer.append("</").append(name).append('>');
+                       tagBuffer.append("</");
+                       tagBuffer.append(name);
+                       tagBuffer.append('>');
                        if(("div").equals(name)|| ("form").equals(name)|| 
("input").equals(name)|| ("li").equals(name)|| ("option").equals(name)|| 
("script").equals(name)|| ("table").equals(name)|| ("tr").equals(name)|| 
("td").equals(name)) {
                                tagBuffer.append('\n');
                        }

Modified: branches/db4o/freenet/src/freenet/support/LRUHashtable.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/LRUHashtable.java 2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/LRUHashtable.java 2008-09-24 
22:16:49 UTC (rev 22824)
@@ -12,7 +12,7 @@
      * push is by far the most done operation, this should be an
      * overall improvement.
      */
-    private final DoublyLinkedListImpl<V> list = new DoublyLinkedListImpl<V>();
+    private final DoublyLinkedListImpl<QItem<K, V>> list = new 
DoublyLinkedListImpl<QItem<K, V>>();
     private final Hashtable<K, QItem<K, V>> hash = new Hashtable<K, QItem<K, 
V>>();

     /**
@@ -34,12 +34,12 @@
                Logger.minor(this, "Pushed "+insert+" ( "+key+ ' ' +value+" )");

         list.unshift(insert);
-    } 
+    }

     /**
      *  @return Least recently pushed key.
      */
-    public final synchronized Object popKey() {
+    public final synchronized K popKey() {
         if ( list.size() > 0 ) {
             return (   hash.remove(((QItem) list.pop()).obj)).obj;
         } else {
@@ -50,7 +50,7 @@
     /**
      * @return Least recently pushed value.
      */
-    public final synchronized Object popValue() {
+    public final synchronized V popValue() {
         if ( list.size() > 0 ) {
             return (   hash.remove(((QItem) list.pop()).obj)).value;
         } else {
@@ -58,12 +58,12 @@
         }
     }

-       public final synchronized Object peekValue() {
+       public final synchronized V peekValue() {
         if ( list.size() > 0 ) {
                if(hash == null) throw new NullPointerException();
-               QItem tail = (QItem) list.tail();
+               QItem<K,V> tail = (QItem<K,V>) list.tail();
                Object object = tail.obj;
-               QItem i = (QItem) hash.get(object);
+               QItem<K,V> i = (QItem<K,V>) hash.get(object);
                if(i == null) {
                        String obToString = "(toString() threw)";
                        try {
@@ -114,39 +114,39 @@
        return q.value;
     }

-    public Enumeration keys() {
+    public Enumeration<K> keys() {
         return new ItemEnumeration();
     }

-    public Enumeration values() {
+    public Enumeration<V> values() {
        return new ValuesEnumeration();
     }

-    private class ItemEnumeration implements Enumeration {
-        private Enumeration source = list.reverseElements();
+    private class ItemEnumeration implements Enumeration<K> {
+               private Enumeration<QItem<K, V>> source = 
list.reverseElements();

         public boolean hasMoreElements() {
             return source.hasMoreElements();
         }

-        public Object nextElement() {
-            return ((QItem) source.nextElement()).obj;
+        public K nextElement() {
+                       return source.nextElement().obj;
         }
     }

-    private class ValuesEnumeration implements Enumeration {
-        private Enumeration source = list.reverseElements();
+    private class ValuesEnumeration implements Enumeration<V> {
+               private Enumeration<QItem<K, V>> source = 
list.reverseElements();

         public boolean hasMoreElements() {
             return source.hasMoreElements();
         }

-        public Object nextElement() {
-            return ((QItem) source.nextElement()).value;
+        public V nextElement() {
+                       return (source.nextElement()).value;
         }
     }

-    public static class QItem<K, V> extends DoublyLinkedListImpl.Item<V> {
+    protected static class QItem<K, V> extends 
DoublyLinkedListImpl.Item<QItem<K, V>> {
         public K obj;
         public V value;

@@ -166,7 +166,7 @@
        }

        /**
-        * Note that unlike the java.util versions, this will not reallocate 
(hence it doesn't return), 
+        * Note that unlike the java.util versions, this will not reallocate 
(hence it doesn't return),
         * so pass in an appropriately big array, and make sure you hold the 
lock!
         * @param entries
         * @return

Modified: branches/db4o/freenet/src/freenet/support/LRUQueue.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/LRUQueue.java     2008-09-24 
20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/LRUQueue.java     2008-09-24 
22:16:49 UTC (rev 22824)
@@ -3,7 +3,7 @@
 import java.util.Enumeration;
 import java.util.Hashtable;

-public class LRUQueue {
+public class LRUQueue<T> {

     /*
      * I've just converted this to using the DLList and Hashtable
@@ -12,8 +12,8 @@
      * push is by far the most done operation, this should be an
      * overall improvement.
      */
-    private final DoublyLinkedListImpl list = new DoublyLinkedListImpl();
-    private final Hashtable hash = new Hashtable();
+    private final DoublyLinkedListImpl<QItem<T>> list = new 
DoublyLinkedListImpl<QItem<T>>();
+    private final Hashtable<T, QItem<T>> hash = new Hashtable<T, QItem<T>>();

     /**
      *       push()ing an object that is already in
@@ -21,10 +21,10 @@
      *       recently used position, but doesn't add
      *       a duplicate entry in the queue.
      */
-    public final synchronized void push(Object obj) {
-        QItem insert = (QItem)hash.get(obj);        
+    public final synchronized void push(T obj) {
+        QItem<T> insert = hash.get(obj);        
         if (insert == null) {
-            insert = new QItem(obj);
+            insert = new QItem<T>(obj);
             hash.put(obj,insert);
         } else {
             list.remove(insert);
@@ -36,10 +36,10 @@
     /**
      * push to bottom (least recently used position)
      */
-       public synchronized void pushLeast(Object obj) {
-        QItem insert = (QItem)hash.get(obj);        
+       public synchronized void pushLeast(T obj) {
+               QItem<T> insert = hash.get(obj);        
         if (insert == null) {
-            insert = new QItem(obj);
+            insert = new QItem<T>(obj);
             hash.put(obj,insert);
         } else {
             list.remove(insert);
@@ -51,9 +51,9 @@
     /**
      *  @return Least recently pushed Object.
      */
-    public final synchronized Object pop() {
+    public final synchronized T pop() {
         if ( list.size() > 0 ) {
-            return ((QItem)hash.remove(((QItem)list.pop()).obj)).obj;
+            return hash.remove(list.pop().obj).obj;
         } else {
             return null;
         }
@@ -63,8 +63,8 @@
         return list.size();
     }

-    public final synchronized boolean remove(Object obj) {
-       QItem i = (QItem)(hash.remove(obj));
+    public final synchronized boolean remove(T obj) {
+       QItem<T> i = hash.remove(obj);
        if(i != null) {
            list.remove(i);
            return true;
@@ -82,26 +82,26 @@
         return hash.containsKey(obj);
     }

-    public Enumeration elements() {
+    public Enumeration<T> elements() {
         return new ItemEnumeration();
     }

-    private class ItemEnumeration implements Enumeration {
-        private Enumeration source = list.reverseElements();
+    private class ItemEnumeration implements Enumeration<T> {
+        private Enumeration<QItem<T>> source = list.reverseElements();

         public boolean hasMoreElements() {
             return source.hasMoreElements();
         }

-        public Object nextElement() {
-            return ((QItem) source.nextElement()).obj;
+        public T nextElement() {
+                       return source.nextElement().obj;
         }
     }

-    private static class QItem extends DoublyLinkedListImpl.Item {
-        public Object obj;
+    private static class QItem<T> extends DoublyLinkedListImpl.Item<QItem<T>> {
+               public T obj;

-        public QItem(Object obj) {
+        public QItem(T obj) {
             this.obj = obj;
         }
     }
@@ -119,7 +119,7 @@
      * order.
         * @param array The array to fill in. If it is too small a new array of 
the same type will be allocated.
      */
-       public synchronized Object[] toArray(Object[] array) {
+       public synchronized <E> E[] toArray(E[] array) {
                return hash.keySet().toArray(array);
        }

@@ -131,8 +131,8 @@
        public synchronized Object[] toArrayOrdered() {
                Object[] array = new Object[list.size()];
                int x = 0;
-               for(Enumeration e = 
list.reverseElements();e.hasMoreElements();) {
-                       array[x++] = ((QItem)e.nextElement()).obj;
+               for(Enumeration<QItem<T>> e = 
list.reverseElements();e.hasMoreElements();) {
+                       array[x++] = e.nextElement().obj;
                }
                return array;
        }
@@ -146,14 +146,14 @@
         *            The array to fill in. If it is too small a new array of 
the
         *            same type will be allocated.
         */
-       public synchronized Object[] toArrayOrdered(Object[] array) {
+       public synchronized <E> E[] toArrayOrdered(E[] array) {
                array = toArray(array);
                int listSize = list.size();
                if(array.length != listSize)
                        throw new 
IllegalStateException("array.length="+array.length+" but list.size="+listSize);
                int x = 0;
-               for(Enumeration e = 
list.reverseElements();e.hasMoreElements();) {
-                       array[x++] = ((QItem)e.nextElement()).obj;
+               for(Enumeration<QItem<T>> e = 
list.reverseElements();e.hasMoreElements();) {
+                       array[x++] = (E) e.nextElement().obj;
                }
                return array;
        }

Modified: branches/db4o/freenet/src/freenet/support/io/ArrayBucket.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/ArrayBucket.java       
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/io/ArrayBucket.java       
2008-09-24 22:16:49 UTC (rev 22824)
@@ -1,5 +1,6 @@
 package freenet.support.io;

+import freenet.support.Logger;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -13,28 +14,40 @@
 import freenet.support.api.Bucket;

 /**
- * A bucket that stores data in the memory.
- * 
- * @author oskar
+ * A bucket that stores data in RAM
  */
 public class ArrayBucket implements Bucket {

        private final ArrayList<byte[]> data;
        private final String name;
        private volatile boolean readOnly;
-
-       public ArrayBucket() {
-               this("ArrayBucket");
+       
+       /** The maximum size of the bucket; -1 means no maxSize */
+       private final long maxSize;
+       private long size;
+       
+       private static boolean logDEBUG = false;
+       
+       public ArrayBucket(long maxSize) {
+               this("ArrayBucket", maxSize);
        }
+       
+       public ArrayBucket(byte[] finalData) {
+               this(finalData, finalData.length);
+               setReadOnly();
+       }

-       public ArrayBucket(byte[] initdata) {
-               this("ArrayBucket");
+       public ArrayBucket(byte[] initdata, long maxSize) {
+               this("ArrayBucket", maxSize);
                data.add(initdata);
        }

-       ArrayBucket(String name) {
+       ArrayBucket(String name, long maxSize) {
                data = new ArrayList<byte[]>();
                this.name = name;
+               this.maxSize = maxSize;
+               if(logDEBUG && maxSize < 0)
+                       Logger.minor(this, "Has been called with maxSize<0 !", 
new NullPointerException());
        }

        public synchronized OutputStream getOutputStream() throws IOException {
@@ -67,12 +80,7 @@
        }

        public synchronized long size() {
-               long currentSize = 0;
-               
-               for(byte[] buf : data)
-                       currentSize += buf.length;
-               
-               return currentSize;
+               return size;
        }

        public String getName() {
@@ -89,13 +97,23 @@
                @Override
                public synchronized void write(byte b[], int off, int len) {
                        if(readOnly) throw new IllegalStateException("Read 
only");
+                       long sizeIfWritten = size + len;
+                       if(logDEBUG && maxSize > -1 && maxSize < sizeIfWritten) 
// FIXME: should be IOE but how to do it?
+                               throw new IllegalArgumentException("The maxSize 
of the bucket is "+maxSize+
+                                       " and writing "+len+ " bytes to it 
would make it oversize!");
                        super.write(b, off, len);
+                       size = sizeIfWritten;
                }

                @Override
                public synchronized void write(int b) {
                        if(readOnly) throw new IllegalStateException("Read 
only");
+                       long sizeIfWritten = size + 1;
+                       if(logDEBUG && maxSize > -1 && maxSize < sizeIfWritten) 
// FIXME: should be IOE but how to do it?
+                               throw new IllegalArgumentException("The maxSize 
of the bucket is "+maxSize+
+                                       " and writing 1 byte to it would make 
it oversize!");
                        super.write(b);
+                       size = sizeIfWritten;
                }

                @Override
@@ -128,12 +146,12 @@
                                        return -1;
                                }
                        }
-                       int i = in.read();
-                       if (i == -1) {
+                       int x = in.read();
+                       if (x == -1) {
                                in = null;
                                return priv_read();
                        } else {
-                               return i;
+                               return x;
                        }
                }

@@ -155,12 +173,12 @@
                                        return -1;
                                }
                        }
-                       int i = in.read(b, off, len);
-                       if (i == -1) {
+                       int x = in.read(b, off, len);
+                       if (x == -1) {
                                in = null;
                                return priv_read(b, off, len);
                        } else {
-                               return i;
+                               return x;
                        }
                }

@@ -187,14 +205,16 @@
        }

        public synchronized void free() {
+               readOnly = true;
                data.clear();
+               size = 0;
                // Not much else we can do.
        }

        public synchronized byte[] toByteArray() {
                long sz = size();
-               int size = (int)sz;
-               byte[] buf = new byte[size];
+               int bufSize = (int)sz;
+               byte[] buf = new byte[bufSize];
                int index = 0;
                for(Iterator i=data.iterator();i.hasNext();) {
                        byte[] obuf = (byte[]) i.next();

Modified: branches/db4o/freenet/src/freenet/support/io/ArrayBucketFactory.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/ArrayBucketFactory.java        
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/io/ArrayBucketFactory.java        
2008-09-24 22:16:49 UTC (rev 22824)
@@ -11,11 +11,11 @@
 public class ArrayBucketFactory implements BucketFactory {

        public Bucket makeBucket(long size) throws IOException {
-               return new ArrayBucket();
+               return new ArrayBucket(size);
        }

        public void freeBucket(Bucket b) throws IOException {
-               // Do nothing
+               b.free();
        }

 }

Modified: 
branches/db4o/freenet/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/LineReadingInputStream.java    
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/io/LineReadingInputStream.java    
2008-09-24 22:16:49 UTC (rev 22824)
@@ -3,6 +3,7 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.support.io;

+import freenet.support.HexUtil;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,7 +33,7 @@

                byte[] buf = new byte[Math.max(Math.min(128, maxLength), 
Math.min(1024, bufferSize))];
                int ctr = 0;
-               mark((maxLength+1)*2); // Might be more than maxLengh if we use 
utf8
+               mark(Integer.MAX_VALUE); // Might be more than maxLengh if we 
use utf8
                while(true) {
                        int x = read(buf, ctr, buf.length - ctr);
                        if(x == -1) {
@@ -41,9 +42,8 @@
                                return new String(buf, 0, ctr, utf ? "UTF-8" : 
"ISO-8859-1");
                        }
                        // REDFLAG this is definitely safe with the above 
charsets, it may not be safe with some wierd ones. 
-                       for(; ctr < buf.length; ctr++) {
-                               if(ctr >= maxLength)
-                                       throw new TooLongException();
+                       int end = ctr + x;
+                       for(; ctr < end; ctr++) {
                                if(buf[ctr] == '\n') {
                                        String toReturn = "";
                                        if(ctr != 0) {
@@ -54,10 +54,12 @@
                                        skip(ctr + 1);
                                        return toReturn;
                                }
+                               if(ctr >= maxLength)
+                                       throw new TooLongException("We reached 
maxLength="+maxLength+ " parsing\n "+HexUtil.bytesToHex(buf, 0, ctr) + "\n" + 
new String(buf, 0, ctr, utf ? "UTF-8" : "ISO-8859-1"));
                        }
-                       if(x > 0) {
+                       if((buf.length != maxLength) && (buf.length - ctr < 
bufferSize)) {
                                byte[] newBuf = new byte[Math.min(buf.length * 
2, maxLength)];
-                               System.arraycopy(buf, 0, newBuf, 0, buf.length);
+                               System.arraycopy(buf, 0, newBuf, 0, ctr);
                                buf = newBuf;
                        }
                }
@@ -84,7 +86,7 @@
                                return new String(buf, 0, ctr, utf ? "UTF-8" : 
"ISO-8859-1");
                        }
                        if(ctr >= maxLength)
-                               throw new TooLongException();
+                                       throw new TooLongException("We reached 
maxLength="+maxLength+ " parsing\n "+HexUtil.bytesToHex(buf, 0, ctr) + "\n" + 
new String(buf, 0, ctr, utf ? "UTF-8" : "ISO-8859-1"));
                        if(ctr >= buf.length) {
                                byte[] newBuf = new byte[Math.min(buf.length * 
2, maxLength)];
                                System.arraycopy(buf, 0, newBuf, 0, buf.length);

Modified: branches/db4o/freenet/src/freenet/support/io/TempBucketFactory.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/TempBucketFactory.java 
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/io/TempBucketFactory.java 
2008-09-24 22:16:49 UTC (rev 22824)
@@ -1,3 +1,6 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
 package freenet.support.io;

 import freenet.crypt.RandomSource;
@@ -6,32 +9,95 @@
 import freenet.support.api.Bucket;
 import freenet.support.api.BucketFactory;

-/*
- * This code is part of FProxy, an HTTP proxy server for Freenet. It is
- * distributed under the GNU Public Licence (GPL) version 2. See
- * http://www.gnu.org/ for further details of the GPL.
- */
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Random;

+import com.db4o.ObjectContainer;
+
 /**
  * Temporary Bucket Factory
- * 
- * @author giannij
  */
 public class TempBucketFactory implements BucketFactory {
+       public class TempBucket implements Bucket {
+               private Bucket currentBucket;
+               
+               public TempBucket(Bucket cur) {
+                       this.currentBucket = cur;
+               }
+               
+               public final void migrateToFileBucket() throws IOException {
+                       RAMBucket ramBucket = null;
+                       synchronized(this) {
+                               if(!isRAMBucket())
+                                       return;

+                               ramBucket = (RAMBucket) currentBucket;
+                               TempFileBucket tempFB = new 
TempFileBucket(filenameGenerator.makeRandomFilename(), filenameGenerator);
+                               BucketTools.copy(currentBucket, tempFB);
+                               currentBucket = tempFB;
+                       }
+                       ramBucket.free();
+               }
+               
+               public final synchronized boolean isRAMBucket() {
+                       return (currentBucket instanceof RAMBucket);
+               }
+
+               public synchronized OutputStream getOutputStream() throws 
IOException {
+                       return currentBucket.getOutputStream();
+               }
+
+               public synchronized InputStream getInputStream() throws 
IOException {
+                       return currentBucket.getInputStream();
+               }
+
+               public synchronized String getName() {
+                       return currentBucket.getName();
+               }
+
+               public synchronized long size() {
+                       return currentBucket.size();
+               }
+
+               public synchronized boolean isReadOnly() {
+                       return currentBucket.isReadOnly();
+               }
+
+               public synchronized void setReadOnly() {
+                       currentBucket.setReadOnly();
+               }
+
+               public synchronized void free() {
+                       currentBucket.free();
+               }
+
+               public Bucket createShadow() throws IOException {
+                       return currentBucket.createShadow();
+               }
+
+               public void removeFrom(ObjectContainer container) {
+                       currentBucket.removeFrom(container);
+                       container.delete(this);
+               }
+
+               public void storeTo(ObjectContainer container) {
+                       currentBucket.storeTo(container);
+                       container.set(this);
+               }
+       }
+
        private class RAMBucket extends ArrayBucket {
-               private final long size;
-               
                public RAMBucket(long size) {
-                       super("RAMBucket");
-                       this.size = size;
+                       super("RAMBucket", size);
+                       _hasTaken(size);
                }

                @Override
                public void free() {
                        super.free();
-                       _hasFreed(size);
+                       _hasFreed(size());
                }
        }

@@ -45,8 +111,8 @@
        public long maxRAMBucketSize;
        public long maxRamUsed;

-       final RandomSource strongPRNG;
-       final Random weakPRNG;
+       private final RandomSource strongPRNG;
+       private final Random weakPRNG;
        private volatile boolean reallyEncrypt;

        // Storage accounting disabled by default.
@@ -67,7 +133,11 @@
                return makeBucket(size, factor, defaultIncrement);
        }

-       protected synchronized void _hasFreed(long size) {
+       private synchronized void _hasTaken(long size) {
+               bytesInUse += size;
+       }
+       
+       private synchronized void _hasFreed(long size) {
                bytesInUse -= size;
        }

@@ -111,19 +181,21 @@
         *                If it is not possible to create a temp bucket due to 
an
         *                I/O error
         */
-       public Bucket makeBucket(long size, float factor, long increment) 
throws IOException {
+       public TempBucket makeBucket(long size, float factor, long increment) 
throws IOException {
                Bucket realBucket = null;
-               boolean isARAMBucket = false;
+               boolean useRAMBucket = false;

                synchronized(this) {
                        if((size > 0) && (size <= maxRAMBucketSize) && 
(bytesInUse <= maxRamUsed)) {
-                               bytesInUse += size;
-                               isARAMBucket = true;
+                               useRAMBucket = true;
                        }
                }

-               realBucket = (isARAMBucket ? new RAMBucket(size) : new 
TempFileBucket(filenameGenerator.makeRandomFilename(), filenameGenerator));
+               // Do we want a RAMBucket or a FileBucket?
+               realBucket = (useRAMBucket ? new RAMBucket(size) : new 
TempFileBucket(filenameGenerator.makeRandomFilename(), filenameGenerator));
+               // Do we want it to be encrypted?
+               realBucket = (!reallyEncrypt ? realBucket : new 
PaddedEphemerallyEncryptedBucket(realBucket, 1024, strongPRNG, weakPRNG));

-               return (!reallyEncrypt ? realBucket : new 
PaddedEphemerallyEncryptedBucket(realBucket, 1024, strongPRNG, weakPRNG));
+               return new TempBucket(realBucket);
        }
 }

Modified: branches/db4o/freenet/src/freenet/support/io/TooLongException.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/TooLongException.java  
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/src/freenet/support/io/TooLongException.java  
2008-09-24 22:16:49 UTC (rev 22824)
@@ -9,4 +9,7 @@
 public class TooLongException extends IOException {
        private static final long serialVersionUID = -1;

+       TooLongException(String s) {
+               super(s);
+       }
 }
\ No newline at end of file

Modified: 
branches/db4o/freenet/test/freenet/clients/http/filter/ContentFilterTest.java
===================================================================
--- 
branches/db4o/freenet/test/freenet/clients/http/filter/ContentFilterTest.java   
    2008-09-24 20:42:20 UTC (rev 22823)
+++ 
branches/db4o/freenet/test/freenet/clients/http/filter/ContentFilterTest.java   
    2008-09-24 22:16:49 UTC (rev 22824)
@@ -62,7 +62,8 @@
        private String HTMLFilter(String data) throws Exception {
                String typeName = "text/html";
                URI baseURI = new URI(BASE_URI);
+               byte[] dataToFilter = data.getBytes("UTF-8");

-               return ContentFilter.filter(new 
ArrayBucket(data.getBytes("UTF-8")), bf, typeName, baseURI, 
null).data.toString();
+               return ContentFilter.filter(new ArrayBucket(dataToFilter, -1), 
bf, typeName, baseURI, null).data.toString();
        }
 }

Modified: 
branches/db4o/freenet/test/freenet/support/compress/GzipCompressorTest.java
===================================================================
--- branches/db4o/freenet/test/freenet/support/compress/GzipCompressorTest.java 
2008-09-24 20:42:20 UTC (rev 22823)
+++ branches/db4o/freenet/test/freenet/support/compress/GzipCompressorTest.java 
2008-09-24 22:16:49 UTC (rev 22824)
@@ -111,7 +111,7 @@
        public void testCompressException() {

                byte[] uncompressedData = UNCOMPRESSED_DATA_1.getBytes();
-               Bucket inBucket = new ArrayBucket(uncompressedData);
+               Bucket inBucket = new ArrayBucket(uncompressedData, 
uncompressedData.length);
                BucketFactory factory = new ArrayBucketFactory();

                try {
@@ -133,7 +133,7 @@

                byte[] compressedData = doCompress(uncompressedData);

-               Bucket inBucket = new ArrayBucket(compressedData);
+               Bucket inBucket = new ArrayBucket(compressedData, 
uncompressedData.length);
                BucketFactory factory = new ArrayBucketFactory();

                try {
@@ -147,7 +147,7 @@

        private byte[] doBucketDecompress(byte[] compressedData) {

-               Bucket inBucket = new ArrayBucket(compressedData);
+               Bucket inBucket = new ArrayBucket(compressedData, 
compressedData.length);
                BucketFactory factory = new ArrayBucketFactory();
                Bucket outBucket = null;

@@ -179,7 +179,7 @@
        }

        private byte[] doCompress(byte[] uncompressedData) {
-               Bucket inBucket = new ArrayBucket(uncompressedData);
+               Bucket inBucket = new ArrayBucket(uncompressedData, 
uncompressedData.length);
                BucketFactory factory = new ArrayBucketFactory();
                Bucket outBucket = null;


Modified: 
branches/db4o/freenet/test/freenet/support/io/LineReadingInputStreamTest.java
===================================================================
--- 
branches/db4o/freenet/test/freenet/support/io/LineReadingInputStreamTest.java   
    2008-09-24 20:42:20 UTC (rev 22823)
+++ 
branches/db4o/freenet/test/freenet/support/io/LineReadingInputStreamTest.java   
    2008-09-24 22:16:49 UTC (rev 22824)
@@ -21,6 +21,9 @@
        };

        public static final String STRESSED_LINE = "\n\u0114\n";
+       public static final String NULL_LINE = "a\u0000\u0000\u0000\u0000\n";
+       public static final String LENGTH_CHECKING_LINE = "a\u0000a\n";
+       public static final int LENGTH_CHECKING_LINE_LF = 3;

        public static final int MAX_LENGTH = 128;
        public static final int BUFFER_SIZE = 128;
@@ -42,17 +45,28 @@
                assertNull(instance.readLineWithoutMarking(MAX_LENGTH, 
BUFFER_SIZE, false));

                // is it returning null?
-               is = new NullInputStream();
+               is = new MockInputStream();
                instance = new LineReadingInputStream(is);
                assertNull(instance.readLineWithoutMarking(0, BUFFER_SIZE, 
false));
+               assertNull(instance.readLineWithoutMarking(0, 0, false));

                // is it throwing?
-               is = new ByteArrayInputStream("aaa\na\n".getBytes());
+               is = new ByteArrayInputStream(LENGTH_CHECKING_LINE.getBytes());
                instance = new LineReadingInputStream(is);
                try {
-                       instance.readLineWithoutMarking(2, BUFFER_SIZE, true);
+                       instance.readLineWithoutMarking(LENGTH_CHECKING_LINE_LF 
- 1, BUFFER_SIZE, true);
                        fail();
                } catch (TooLongException e) {}
+               
+               // Same test shouldn't throw
+               is = new ByteArrayInputStream(LENGTH_CHECKING_LINE.getBytes());
+               instance = new LineReadingInputStream(is);
+               assertEquals(LENGTH_CHECKING_LINE.substring(0, 
LENGTH_CHECKING_LINE_LF), 
instance.readLineWithoutMarking(LENGTH_CHECKING_LINE_LF, BUFFER_SIZE, true));
+               
+               // is it handling nulls properly? @see #2501
+               is = new ByteArrayInputStream(NULL_LINE.getBytes());
+               instance = new LineReadingInputStream(is);
+               assertEquals(NULL_LINE.substring(0, 5), 
instance.readLineWithoutMarking(BUFFER_SIZE, 1, true));
        }

        public void testReadLine() throws Exception {
@@ -71,18 +85,29 @@
                }
                assertNull(instance.readLine(MAX_LENGTH, BUFFER_SIZE, false));

-               // is it returning null?
-               is = new NullInputStream();
+               // is it returning null? and blocking when it should be?
+               is = new MockInputStream();
                instance = new LineReadingInputStream(is);
                assertNull(instance.readLine(0, BUFFER_SIZE, false));
+               assertNull(instance.readLine(0, 0, false));

                // is it throwing?
-               is = new ByteArrayInputStream("aaa\na\n".getBytes());
+               is = new ByteArrayInputStream(LENGTH_CHECKING_LINE.getBytes());
                instance = new LineReadingInputStream(is);
                try {
-                       instance.readLine(2, BUFFER_SIZE, true);
+                       instance.readLine(LENGTH_CHECKING_LINE_LF - 1, 
BUFFER_SIZE, true);
                        fail();
                } catch (TooLongException e) {}
+               
+               // Same test shouldn't throw
+               is = new ByteArrayInputStream(LENGTH_CHECKING_LINE.getBytes());
+               instance = new LineReadingInputStream(is);
+               assertEquals(LENGTH_CHECKING_LINE.substring(0, 
LENGTH_CHECKING_LINE_LF), instance.readLine(LENGTH_CHECKING_LINE_LF, 
BUFFER_SIZE, true));
+               
+               // is it handling nulls properly? @see #2501
+               is = new ByteArrayInputStream(NULL_LINE.getBytes());
+               instance = new LineReadingInputStream(is);
+               assertEquals(NULL_LINE.substring(0, 5), 
instance.readLine(BUFFER_SIZE, 1, true));
        }

        public void testBothImplementation() throws Exception {

Copied: branches/db4o/freenet/test/freenet/support/io/MockInputStream.java 
(from rev 22001, trunk/freenet/test/freenet/support/io/MockInputStream.java)
===================================================================
--- branches/db4o/freenet/test/freenet/support/io/MockInputStream.java          
                (rev 0)
+++ branches/db4o/freenet/test/freenet/support/io/MockInputStream.java  
2008-09-24 22:16:49 UTC (rev 22824)
@@ -0,0 +1,20 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.support.io;
+
+import java.io.InputStream;
+
+public class MockInputStream extends InputStream {
+
+       public MockInputStream() {
+       }
+
+       public int read() {
+               return -1;
+       }
+
+       public int read(byte[] data, int offset, int len) {
+               return len;
+       }
+}
\ No newline at end of file


Reply via email to