Author: toad
Date: 2006-03-25 16:36:47 +0000 (Sat, 25 Mar 2006)
New Revision: 8312

Added:
   trunk/freenet/src/freenet/client/async/USKInserter.java
   trunk/freenet/src/freenet/keys/InsertableUSK.java
Modified:
   trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
   trunk/freenet/src/freenet/client/InserterContext.java
   trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
   trunk/freenet/src/freenet/client/async/ClientPutState.java
   trunk/freenet/src/freenet/client/async/ClientPutter.java
   trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java
   trunk/freenet/src/freenet/client/async/MultiPutCompletionCallback.java
   trunk/freenet/src/freenet/client/async/PutCompletionCallback.java
   trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java
   trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
   trunk/freenet/src/freenet/client/async/SingleFileInserter.java
   trunk/freenet/src/freenet/client/async/SplitFileInserter.java
   trunk/freenet/src/freenet/client/async/SplitFileInserterSegment.java
   trunk/freenet/src/freenet/client/async/USKFetcher.java
   trunk/freenet/src/freenet/client/async/USKManager.java
   trunk/freenet/src/freenet/keys/InsertableClientSSK.java
   trunk/freenet/src/freenet/keys/USK.java
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/Version.java
Log:
571:
USK insert support (over FCP).
Also some USK bugfixes.

Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-03-25 16:36:47 UTC (rev 8312)
@@ -145,11 +145,13 @@
                                SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES, 
NON_SPLITFILE_RETRIES,
                                FETCH_SPLITFILES, FOLLOW_REDIRECTS, 
LOCAL_REQUESTS_ONLY,
                                MAX_SPLITFILE_BLOCKS_PER_SEGMENT, 
MAX_SPLITFILE_CHECK_BLOCKS_PER_SEGMENT,
-                               random, archiveManager, bucketFactory, 
globalEventProducer, cacheLocalRequests, node.uskManager);
+                               random, archiveManager, bucketFactory, 
globalEventProducer, 
+                               cacheLocalRequests, node.uskManager);
        }

        public InserterContext getInserterContext() {
                return new InserterContext(bucketFactory, random, 
INSERT_RETRIES, CONSECUTIVE_RNFS_ASSUME_SUCCESS,
-                               SPLITFILE_INSERT_THREADS, 
SPLITFILE_BLOCKS_PER_SEGMENT, SPLITFILE_CHECK_BLOCKS_PER_SEGMENT, 
globalEventProducer, cacheLocalRequests);
+                               SPLITFILE_INSERT_THREADS, 
SPLITFILE_BLOCKS_PER_SEGMENT, SPLITFILE_CHECK_BLOCKS_PER_SEGMENT, 
+                               globalEventProducer, cacheLocalRequests, 
node.uskManager);
        }
 }

Modified: trunk/freenet/src/freenet/client/InserterContext.java
===================================================================
--- trunk/freenet/src/freenet/client/InserterContext.java       2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/InserterContext.java       2006-03-25 
16:36:47 UTC (rev 8312)
@@ -1,5 +1,6 @@
 package freenet.client;

+import freenet.client.async.USKManager;
 import freenet.client.events.ClientEventProducer;
 import freenet.client.events.SimpleEventProducer;
 import freenet.crypt.RandomSource;
@@ -21,11 +22,13 @@
        public final ClientEventProducer eventProducer;
        /** Interesting tradeoff, see comments at top of Node.java. */
        public final boolean cacheLocalRequests;
+       public final USKManager uskManager;

        public InserterContext(BucketFactory bf, RandomSource random,
                        int maxRetries, int rnfsToSuccess, int maxThreads, int 
splitfileSegmentDataBlocks, int splitfileSegmentCheckBlocks,
-                       ClientEventProducer eventProducer, boolean 
cacheLocalRequests) {
+                       ClientEventProducer eventProducer, boolean 
cacheLocalRequests, USKManager uskManager) {
                this.bf = bf;
+               this.uskManager = uskManager;
                this.random = random;
                dontCompress = false;
                splitfileAlgorithm = Metadata.SPLITFILE_ONION_STANDARD;
@@ -39,6 +42,7 @@
        }

        public InserterContext(InserterContext ctx) {
+               this.uskManager = ctx.uskManager;
                this.bf = ctx.bf;
                this.random = ctx.random;
                this.dontCompress = ctx.dontCompress;
@@ -53,6 +57,7 @@
        }

        public InserterContext(InserterContext ctx, SimpleEventProducer 
producer) {
+               this.uskManager = ctx.uskManager;
                this.bf = ctx.bf;
                this.random = ctx.random;
                this.dontCompress = ctx.dontCompress;

Modified: trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java   
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java   
2006-03-25 16:36:47 UTC (rev 8312)
@@ -84,8 +84,7 @@
        }

        public boolean ignoreStore() {
-               // TODO Auto-generated method stub
-               return false;
+               return ctx.ignoreStore;
        }

        public synchronized void cancel() {

Modified: trunk/freenet/src/freenet/client/async/ClientPutState.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientPutState.java  2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/ClientPutState.java  2006-03-25 
16:36:47 UTC (rev 8312)
@@ -1,5 +1,7 @@
 package freenet.client.async;

+import freenet.client.InserterException;
+
 /**
  * ClientPutState
  * 
@@ -11,4 +13,5 @@

        public abstract void cancel();

+       public abstract void schedule() throws InserterException;
 }

Modified: trunk/freenet/src/freenet/client/async/ClientPutter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientPutter.java    2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/ClientPutter.java    2006-03-25 
16:36:47 UTC (rev 8312)
@@ -6,6 +6,7 @@
 import freenet.client.InserterException;
 import freenet.client.Metadata;
 import freenet.client.events.SplitfileProgressEvent;
+import freenet.keys.BaseClientKey;
 import freenet.keys.ClientKey;
 import freenet.keys.FreenetURI;
 import freenet.support.Bucket;
@@ -78,7 +79,7 @@
                client.onFailure(e, this);
        }

-       public void onEncode(ClientKey key, ClientPutState state) {
+       public void onEncode(BaseClientKey key, ClientPutState state) {
                this.uri = key.getURI();
                client.onGeneratedURI(uri, this);
        }

Modified: trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java  
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java  
2006-03-25 16:36:47 UTC (rev 8312)
@@ -32,16 +32,18 @@
        private final SortedVectorByNumber[] priorities;
        // we have one for inserts and one for requests
        final boolean isInsertScheduler;
+       final boolean isSSKScheduler;
        final RandomSource random;
        private final HashMap allRequestsByClientRequest;
        private final RequestStarter starter;
        private final Node node;

-       public ClientRequestScheduler(boolean forInserts, RandomSource random, 
RequestStarter starter, Node node) {
+       public ClientRequestScheduler(boolean forInserts, boolean forSSKs, 
RandomSource random, RequestStarter starter, Node node) {
                this.starter = starter;
                this.random = random;
                this.node = node;
                this.isInsertScheduler = forInserts;
+               this.isSSKScheduler = forSSKs;
                priorities = new 
SortedVectorByNumber[RequestStarter.NUMBER_OF_PRIORITY_CLASSES];
                allRequestsByClientRequest = new HashMap();
        }

Modified: trunk/freenet/src/freenet/client/async/MultiPutCompletionCallback.java
===================================================================
--- trunk/freenet/src/freenet/client/async/MultiPutCompletionCallback.java      
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/MultiPutCompletionCallback.java      
2006-03-25 16:36:47 UTC (rev 8312)
@@ -5,6 +5,7 @@

 import freenet.client.InserterException;
 import freenet.client.Metadata;
+import freenet.keys.BaseClientKey;
 import freenet.keys.ClientKey;
 import freenet.support.Logger;

@@ -90,7 +91,7 @@
                return parent;
        }

-       public void onEncode(ClientKey key, ClientPutState state) {
+       public void onEncode(BaseClientKey key, ClientPutState state) {
                synchronized(this) {
                        if(state != generator) return;
                }
@@ -134,4 +135,8 @@
                cb.onBlockSetFinished(this);
        }

+       public void schedule() throws InserterException {
+               // Do nothing
+       }
+
 }

Modified: trunk/freenet/src/freenet/client/async/PutCompletionCallback.java
===================================================================
--- trunk/freenet/src/freenet/client/async/PutCompletionCallback.java   
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/PutCompletionCallback.java   
2006-03-25 16:36:47 UTC (rev 8312)
@@ -2,7 +2,9 @@

 import freenet.client.InserterException;
 import freenet.client.Metadata;
+import freenet.keys.BaseClientKey;
 import freenet.keys.ClientKey;
+import freenet.keys.USK;

 /**
  * Callback called when part of a put request completes.
@@ -13,7 +15,7 @@

        public void onFailure(InserterException e, ClientPutState state);

-       public void onEncode(ClientKey key, ClientPutState state);
+       public void onEncode(BaseClientKey usk, ClientPutState state);

        public void onTransition(ClientPutState oldState, ClientPutState 
newState);


Modified: trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java    
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java    
2006-03-25 16:36:47 UTC (rev 8312)
@@ -13,6 +13,7 @@
 import freenet.client.InserterException;
 import freenet.client.Metadata;
 import freenet.client.events.SplitfileProgressEvent;
+import freenet.keys.BaseClientKey;
 import freenet.keys.ClientKey;
 import freenet.keys.FreenetURI;
 import freenet.support.Bucket;
@@ -84,7 +85,7 @@
                        fail(e);
                }

-               public void onEncode(ClientKey key, ClientPutState state) {
+               public void onEncode(BaseClientKey key, ClientPutState state) {
                        if(metadata == null) {
                                // Don't have metadata yet
                                // Do have key
@@ -355,7 +356,7 @@
                fail(e);
        }

-       public void onEncode(ClientKey key, ClientPutState state) {
+       public void onEncode(BaseClientKey key, ClientPutState state) {
                this.finalURI = key.getURI();
                Logger.minor(this, "Got metadata key: "+finalURI);
                cb.onGeneratedURI(finalURI, this);

Modified: trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-03-25 16:36:47 UTC (rev 8312)
@@ -522,7 +522,7 @@
        }

        private static ClientGetState uskCreate(ClientGetter parent, 
GetCompletionCallback cb, ClientMetadata clientMetadata, USK usk, LinkedList 
metaStrings, FetcherContext ctx, ArchiveContext actx, int maxRetries, int 
recursionLevel, boolean dontTellClientGet, Object token, boolean isEssential, 
Bucket returnBucket) throws FetchException {
-               if(usk.suggestedEdition > 0) {
+               if(usk.suggestedEdition >= 0) {
                        // Return the latest known version but at least 
suggestedEdition.
                        long edition = ctx.uskManager.lookup(usk);
                        if(edition <= usk.suggestedEdition) {
@@ -583,7 +583,7 @@
                        this.returnBucket = returnBucket;
                }

-               public void onFoundEdition(long l, USK usk) {
+               public void onFoundEdition(long l, USK newUSK) {
                        ClientSSK key = usk.getSSK(l);
                        try {
                                if(l == Math.abs(usk.suggestedEdition)) {
@@ -592,7 +592,7 @@
                                                        token, false, 
returnBucket);
                                        sf.schedule();
                                } else {
-                                       cb.onFailure(new 
FetchException(FetchException.PERMANENT_REDIRECT, 
usk.copy(l).getURI().addMetaStrings(metaStrings)), null);
+                                       cb.onFailure(new 
FetchException(FetchException.PERMANENT_REDIRECT, 
newUSK.getURI().addMetaStrings(metaStrings)), null);
                                }
                        } catch (FetchException e) {
                                cb.onFailure(e, null);

Modified: trunk/freenet/src/freenet/client/async/SingleFileInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SingleFileInserter.java      
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/SingleFileInserter.java      
2006-03-25 16:36:47 UTC (rev 8312)
@@ -1,6 +1,7 @@
 package freenet.client.async;

 import java.io.IOException;
+import java.net.MalformedURLException;

 import freenet.client.InsertBlock;
 import freenet.client.InserterContext;
@@ -8,6 +9,7 @@
 import freenet.client.Metadata;
 import freenet.client.events.FinishedCompressionEvent;
 import freenet.client.events.StartedCompressionEvent;
+import freenet.keys.BaseClientKey;
 import freenet.keys.CHKBlock;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
@@ -96,12 +98,12 @@

                long origSize = data.size();
                String type = block.desiredURI.getKeyType().toUpperCase();
-               if(type.equals("SSK") || type.equals("KSK")) {
+               if(type.equals("SSK") || type.equals("KSK") || 
type.equals("USK")) {
                        blockSize = SSKBlock.DATA_LENGTH;
                } else if(type.equals("CHK")) {
                        blockSize = CHKBlock.DATA_LENGTH;
                } else {
-                       throw new 
InserterException(InserterException.INVALID_URI);
+                       throw new 
InserterException(InserterException.INVALID_URI, "Unknown key type: "+type, 
null);
                }

                Compressor bestCodec = null;
@@ -162,7 +164,8 @@
                if((block.clientMetadata == null || 
block.clientMetadata.isTrivial())) {
                        if(data.size() < blockSize) {
                                // Just insert it
-                               SingleBlockInserter bi = new 
SingleBlockInserter(parent, data, codecNumber, block.desiredURI, ctx, cb, 
metadata, (int)block.getData().size(), -1, getCHKOnly, true);
+                               ClientPutState bi =
+                                       createInserter(parent, data, 
codecNumber, block.desiredURI, ctx, cb, metadata, (int)block.getData().size(), 
-1, getCHKOnly, true);
                                cb.onTransition(this, bi);
                                bi.schedule();
                                cb.onBlockSetFinished(this);
@@ -189,7 +192,7 @@
                                } catch (IOException e) {
                                        throw new 
InserterException(InserterException.BUCKET_ERROR, e, null);
                                }
-                               SingleBlockInserter metaPutter = new 
SingleBlockInserter(parent, metadataBucket, (short) -1, block.desiredURI, ctx, 
mcb, true, (int)origSize, -1, getCHKOnly, true);
+                               ClientPutState metaPutter = 
createInserter(parent, metadataBucket, (short) -1, block.desiredURI, ctx, mcb, 
true, (int)origSize, -1, getCHKOnly, true);
                                mcb.addURIGenerator(metaPutter);
                                mcb.add(dataPutter);
                                cb.onTransition(this, mcb);
@@ -219,6 +222,22 @@
                return;
        }

+       private ClientPutState createInserter(BaseClientPutter parent, Bucket 
data, short compressionCodec, FreenetURI uri, 
+                       InserterContext ctx, PutCompletionCallback cb, boolean 
isMetadata, int sourceLength, int token, boolean getCHKOnly, 
+                       boolean addToParent) throws InserterException {
+               if(uri.getKeyType().equals("USK")) {
+                       try {
+                               return new USKInserter(parent, data, 
compressionCodec, uri, ctx, cb, isMetadata, sourceLength, token, 
+                                       getCHKOnly, addToParent);
+                       } catch (MalformedURLException e) {
+                               throw new 
InserterException(InserterException.INVALID_URI, e, null);
+                       }
+               } else {
+                       return new SingleBlockInserter(parent, data, 
compressionCodec, uri, ctx, cb, isMetadata, sourceLength, token, 
+                               getCHKOnly, addToParent);
+               }
+       }
+
        /**
         * When we get the metadata, start inserting it to our target key.
         * When we have inserted both the metadata and the splitfile,
@@ -323,7 +342,7 @@
                        return parent;
                }

-               public void onEncode(ClientKey key, ClientPutState state) {
+               public void onEncode(BaseClientKey key, ClientPutState state) {
                        if(state == metadataPutter)
                                cb.onEncode(key, this);
                }
@@ -346,6 +365,10 @@
                        }
                        cb.onBlockSetFinished(this);
                }
+
+               public void schedule() throws InserterException {
+                       // Do nothing
+               }

        }

@@ -356,4 +379,8 @@
        public void cancel() {
                cancelled = true;
        }
+
+       public void schedule() throws InserterException {
+               start();
+       }
 }

Modified: trunk/freenet/src/freenet/client/async/SplitFileInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SplitFileInserter.java       
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/SplitFileInserter.java       
2006-03-25 16:36:47 UTC (rev 8312)
@@ -268,4 +268,8 @@
                        segments[i].cancel();
        }

+       public void schedule() throws InserterException {
+               start();
+       }
+
 }

Modified: trunk/freenet/src/freenet/client/async/SplitFileInserterSegment.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SplitFileInserterSegment.java        
2006-03-25 15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/SplitFileInserterSegment.java        
2006-03-25 16:36:47 UTC (rev 8312)
@@ -7,6 +7,7 @@
 import freenet.client.InserterContext;
 import freenet.client.InserterException;
 import freenet.client.Metadata;
+import freenet.keys.BaseClientKey;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
 import freenet.keys.FreenetURI;
@@ -117,7 +118,7 @@
                parent.segmentFinished(this);
        }

-       public void onEncode(ClientKey key, ClientPutState state) {
+       public void onEncode(BaseClientKey key, ClientPutState state) {
                SingleBlockInserter sbi = (SingleBlockInserter)state;
                int x = sbi.token;
                FreenetURI uri = key.getURI();

Modified: trunk/freenet/src/freenet/client/async/USKFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/USKFetcher.java      2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/USKFetcher.java      2006-03-25 
16:36:47 UTC (rev 8312)
@@ -72,7 +72,7 @@

        /** Cancelled? */
        private boolean cancelled;
-
+       
        final ClientRequester parent;

        public synchronized boolean addCallback(USKFetcherCallback cb) {
@@ -169,6 +169,8 @@
        /** Keep going forever? */
        private final boolean backgroundPoll;

+       private boolean started = false;
+       
        USKFetcher(USK origUSK, USKManager manager, FetcherContext ctx, 
ClientRequester parent, int minFailures, boolean pollForever) {
                this.parent = parent;
                this.origUSK = origUSK;
@@ -187,13 +189,15 @@
                Logger.minor(this, "DNF: "+att);
                boolean finished = false;
                synchronized(this) {
+                       if(completed || cancelled) return;
                        lastFetchedEdition = Math.max(lastFetchedEdition, 
att.number);
                        runningAttempts.remove(att);
                        if(runningAttempts.isEmpty()) {
                                long curLatest = uskManager.lookup(origUSK);
                                Logger.minor(this, "latest: "+curLatest+", last 
fetched: "+lastFetchedEdition+", curLatest+MIN_FAILURES: 
"+(curLatest+minFailures));
-                               if(curLatest + minFailures >= 
lastFetchedEdition) {
+                               if(started) {
                                        finished = true;
+                                       completed = true;
                                }
                        } else 
                                Logger.minor(this, "Remaining: 
"+runningAttempts.size());
@@ -206,6 +210,7 @@
        private void finishSuccess() {
                if(backgroundPoll) {
                        synchronized(this) {
+                               started = false; // don't finish before have 
rescheduled
                                long valAtEnd = uskManager.lookup(origUSK);
                                if(valAtEnd > valueAtSchedule) {
                                        // Have advanced.
@@ -255,6 +260,7 @@
        void onSuccess(USKAttempt att, boolean dontUpdate) {
                LinkedList l = null;
                synchronized(this) {
+                       if(completed || cancelled) return;
                        runningAttempts.remove(att);
                        long curLatest = att.number;
                        if(!dontUpdate)
@@ -353,6 +359,7 @@
                        for(long i=startPoint;i<startPoint+minFailures;i++)
                                add(i);
                        attempts = (USKAttempt[]) runningAttempts.toArray(new 
USKAttempt[runningAttempts.size()]);
+                       started = true;
                }
                if(!cancelled)
                        for(int i=0;i<attempts.length;i++)

Added: trunk/freenet/src/freenet/client/async/USKInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/USKInserter.java     2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/USKInserter.java     2006-03-25 
16:36:47 UTC (rev 8312)
@@ -0,0 +1,195 @@
+package freenet.client.async;
+
+import java.net.MalformedURLException;
+
+import freenet.client.InserterContext;
+import freenet.client.InserterException;
+import freenet.client.Metadata;
+import freenet.keys.BaseClientKey;
+import freenet.keys.FreenetURI;
+import freenet.keys.InsertableUSK;
+import freenet.keys.USK;
+import freenet.support.Bucket;
+import freenet.support.Logger;
+
+/**
+ * Insert a USK. The algorithm is simply to do a thorough search for the 
latest edition, and insert at the
+ * following slot. Thereafter, if we get a collision, increment our slot; if 
we get more than 5 consecutive
+ * collisions, search for the latest slot again.
+ */
+public class USKInserter implements ClientPutState, USKFetcherCallback, 
PutCompletionCallback {
+
+       // Stuff to be passed on to the SingleBlockInserter
+       final BaseClientPutter parent;
+       final Bucket data;
+       final short compressionCodec;
+       final InserterContext ctx;
+       final PutCompletionCallback cb;
+       final boolean isMetadata;
+       final int sourceLength;
+       final int token;
+       final boolean getCHKOnly;
+       
+       final InsertableUSK privUSK;
+       final USK pubUSK;
+       /** Scanning for latest slot */
+       private USKFetcher fetcher;
+       /** Insert the actual SSK */
+       private SingleBlockInserter sbi;
+       private long edition;
+       /** Number of collisions while trying to insert so far */
+       private int consecutiveCollisions = 0;
+       private boolean finished;
+       /** After attempting inserts on this many slots, go back to the Fetcher 
*/
+       private static final long MAX_TRIED_SLOTS = 10;
+       
+       public void schedule() throws InserterException {
+               // Caller calls schedule()
+               // schedule() calls scheduleFetcher()
+               // scheduleFetcher() creates a Fetcher (set up to tell us about 
author-errors as well as valid inserts)
+               // (and starts it)
+               // when this completes, onFoundEdition() calls scheduleInsert()
+               // scheduleInsert() starts a SingleBlockInserter
+               // if that succeeds, we complete
+               // if that fails, we increment our index and try again (in the 
callback)
+               // if that continues to fail 5 times, we go back to 
scheduleFetcher()
+               scheduleFetcher();
+       }
+
+       /**
+        * Schedule a Fetcher to find us the latest inserted key of the USK.
+        * The Fetcher must be insert-mode, in other words, it must know that 
we want the latest edition,
+        * including author errors and so on.
+        */
+       private void scheduleFetcher() {
+               Logger.minor(this, "scheduling fetcher for "+pubUSK.getURI());
+               synchronized(this) {
+                       if(finished) return;
+                       fetcher = 
ctx.uskManager.getFetcherForInsertDontSchedule(pubUSK, this);
+               }
+               fetcher.schedule();
+       }
+
+       public void onFoundEdition(long l, USK key) {
+               edition = Math.max(l, edition);
+               consecutiveCollisions = 0;
+               fetcher = null;
+               scheduleInsert();
+       }
+
+       private void scheduleInsert() {
+               synchronized(this) {
+                       if(finished) return;
+                       long edNo = Math.max(edition, 
ctx.uskManager.lookup(pubUSK))+1;
+                       edition = edNo;
+                       Logger.minor(this, "scheduling insert for 
"+pubUSK.getURI()+" "+edition);
+                       try {
+                               sbi = new SingleBlockInserter(parent, data, 
compressionCodec, privUSK.getInsertableSSK(edition).getInsertURI(),
+                                               ctx, this, isMetadata, 
sourceLength, token, getCHKOnly, false);
+                       } catch (InserterException e) {
+                               cb.onFailure(e, this);
+                               return;
+                       }
+               }
+               try {
+                       sbi.schedule();
+               } catch (InserterException e) {
+                       cb.onFailure(e, this);
+               }
+       }
+
+       public void onSuccess(ClientPutState state) {
+               cb.onEncode(pubUSK.copy(edition), this);
+               cb.onSuccess(this);
+               synchronized(this) {
+                       finished = true;
+                       sbi = null;
+               }
+               FreenetURI targetURI = pubUSK.copy(edition).getURI();
+               FreenetURI realURI = ((SingleBlockInserter)state).getURI();
+               if(!targetURI.equals(realURI))
+                       Logger.error(this, "URI should be "+targetURI+" 
actually is "+realURI);
+               else
+                       Logger.minor(this, "URI should be "+targetURI+" 
actually is "+realURI);
+               // FINISHED!!!! Yay!!!
+       }
+
+       public void onFailure(InserterException e, ClientPutState state) {
+               sbi = null;
+               if(e.getMode() == InserterException.COLLISION) {
+                       // Try the next slot
+                       edition++;
+                       if(consecutiveCollisions++ > MAX_TRIED_SLOTS)
+                               scheduleFetcher();
+                       else
+                               scheduleInsert();
+               } else {
+                       cb.onFailure(e, state);
+               }
+       }
+
+       public USKInserter(BaseClientPutter parent, Bucket data, short 
compressionCodec, FreenetURI uri, 
+                       InserterContext ctx, PutCompletionCallback cb, boolean 
isMetadata, int sourceLength, int token, 
+                       boolean getCHKOnly, boolean addToParent) throws 
MalformedURLException {
+               edition = -1;
+               this.parent = parent;
+               this.data = data;
+               this.compressionCodec = compressionCodec;
+               this.ctx = ctx;
+               this.cb = cb;
+               this.isMetadata = isMetadata;
+               this.sourceLength = sourceLength;
+               this.token = token;
+               this.getCHKOnly = getCHKOnly;
+               if(addToParent) {
+                       parent.addBlock();
+                       parent.addMustSucceedBlocks(1);
+                       parent.notifyClients();
+               }
+               privUSK = InsertableUSK.create(uri);
+               pubUSK = privUSK.getUSK();
+       }
+
+       public BaseClientPutter getParent() {
+               return parent;
+       }
+
+       public synchronized void cancel() {
+               finished = true;
+               if(fetcher != null)
+                       fetcher.cancel();
+               if(sbi != null)
+                       sbi.cancel();
+               cb.onFailure(new 
InserterException(InserterException.CANCELLED), this);
+       }
+
+       public void onFailure() {
+               Logger.error(this, "Fetcher failed", new Exception("debug"));
+               scheduleInsert();
+       }
+
+       public void onCancelled() {
+               if(finished) return;
+               Logger.error(this, "Unexpected onCancelled()", new 
Exception("error"));
+               cancel();
+       }
+
+       public void onEncode(BaseClientKey key, ClientPutState state) {
+               // Ignore
+       }
+
+       public void onTransition(ClientPutState oldState, ClientPutState 
newState) {
+               // Shouldn't happen
+               Logger.error(this, "Got 
onTransition("+oldState+","+newState+")");
+       }
+
+       public void onMetadata(Metadata m, ClientPutState state) {
+               // Shouldn't happen
+               Logger.error(this, "Got onMetadata("+m+","+state+")");
+       }
+
+       public void onBlockSetFinished(ClientPutState state) {
+               // Ignore
+       }
+
+}

Modified: trunk/freenet/src/freenet/client/async/USKManager.java
===================================================================
--- trunk/freenet/src/freenet/client/async/USKManager.java      2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/client/async/USKManager.java      2006-03-25 
16:36:47 UTC (rev 8312)
@@ -91,13 +91,20 @@
                return f;
        }

+       public USKFetcher getFetcherForInsertDontSchedule(USK usk, 
USKFetcherCallback cb) {
+               USKFetcher f = new USKFetcher(usk, this, 
backgroundFetchContext, 
+                               new USKFetcherWrapper(usk, chkRequestScheduler, 
sskRequestScheduler), 3, false);
+               f.addCallback(cb);
+               return f;
+       }
+
        public void startTemporaryBackgroundFetcher(USK usk) {
                USK clear = usk.clearCopy();
                USKFetcher sched = null;
                synchronized(this) {
                        USKFetcher f = (USKFetcher) 
backgroundFetchersByClearUSK.get(clear);
                        if(f == null) {
-                               f = new USKFetcher(usk, this, 
backgroundFetchContext, new USKFetcherWrapper(usk, chkRequestScheduler, 
sskRequestScheduler), 10, false);
+                               f = new USKFetcher(usk, this, 
backgroundFetchContext, new USKFetcherWrapper(usk, chkRequestScheduler, 
sskRequestScheduler), 10, true);
                                sched = f;
                                backgroundFetchersByClearUSK.put(clear, f);
                        }
@@ -157,7 +164,7 @@
                        if(runBackgroundFetch) {
                                USKFetcher f = (USKFetcher) 
backgroundFetchersByClearUSK.get(clear);
                                if(f == null) {
-                                       f = new USKFetcher(origUSK, this, 
backgroundFetchContext, new USKFetcherWrapper(origUSK, chkRequestScheduler, 
sskRequestScheduler), 10, false);
+                                       f = new USKFetcher(origUSK, this, 
backgroundFetchContext, new USKFetcherWrapper(origUSK, chkRequestScheduler, 
sskRequestScheduler), 10, true);
                                        sched = f;
                                        backgroundFetchersByClearUSK.put(clear, 
f);
                                }

Modified: trunk/freenet/src/freenet/keys/InsertableClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableClientSSK.java     2006-03-25 
15:24:17 UTC (rev 8311)
+++ trunk/freenet/src/freenet/keys/InsertableClientSSK.java     2006-03-25 
16:36:47 UTC (rev 8312)
@@ -39,6 +39,8 @@
                        throw new MalformedURLException();
                if(uri.getDocName() == null || uri.getDocName().length() == 0)
                        throw new MalformedURLException("SSK URIs must have a 
document name (to avoid ambiguity)");
+               if(uri.getExtra() != null)
+                       throw new MalformedURLException("Insertable SSK URIs 
must NOT have ,extra - inserting from a pubkey rather than the privkey 
perhaps?");
                DSAGroup g = Global.DSAgroupBigA;
                DSAPrivateKey privKey = new DSAPrivateKey(new 
NativeBigInteger(1, uri.getKeyVal()));
                DSAPublicKey pubKey = new DSAPublicKey(g, privKey);

Added: trunk/freenet/src/freenet/keys/InsertableUSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableUSK.java   2006-03-25 15:24:17 UTC 
(rev 8311)
+++ trunk/freenet/src/freenet/keys/InsertableUSK.java   2006-03-25 16:36:47 UTC 
(rev 8312)
@@ -0,0 +1,83 @@
+package freenet.keys;
+
+import java.net.MalformedURLException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import net.i2p.util.NativeBigInteger;
+
+import freenet.crypt.DSAGroup;
+import freenet.crypt.DSAPrivateKey;
+import freenet.crypt.DSAPublicKey;
+import freenet.crypt.Global;
+import freenet.support.Logger;
+
+/**
+ * An insertable USK.
+ * 
+ * Changes from an ordinary USK:
+ * - It has a private key
+ * - getURI() doesn't include ,extra
+ * - constructor from URI doesn't need or want ,extra
+ * - It has a getUSK() method which gets the public USK
+ */
+public class InsertableUSK extends USK {
+       
+       public final DSAPrivateKey privKey;
+       public final DSAGroup group;
+       
+       public static InsertableUSK create(FreenetURI uri) throws 
MalformedURLException {
+               if(!uri.getKeyType().equalsIgnoreCase("USK"))
+                       throw new MalformedURLException();
+               if(uri.getDocName() == null || uri.getDocName().length() == 0)
+                       throw new MalformedURLException("USK URIs must have a 
document name (to avoid ambiguity)");
+               DSAGroup g = Global.DSAgroupBigA;
+               DSAPrivateKey privKey = new DSAPrivateKey(new 
NativeBigInteger(1, uri.getKeyVal()));
+               DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
+               MessageDigest md;
+               try {
+                       md = MessageDigest.getInstance("SHA-256");
+               } catch (NoSuchAlgorithmException e) {
+                       throw new Error(e);
+               }
+               md.update(pubKey.asBytes());
+               return new InsertableUSK(uri.getDocName(), md.digest(), 
uri.getCryptoKey(), privKey, g, uri.getSuggestedEdition());
+       }
+       
+       InsertableUSK(String docName, byte[] pubKeyHash, byte[] cryptoKey, 
DSAPrivateKey key, DSAGroup group, long suggestedEdition) throws 
MalformedURLException {
+               super(pubKeyHash, cryptoKey, docName, suggestedEdition);
+               if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
+                       throw new MalformedURLException("Decryption key wrong 
length: "+cryptoKey.length+" should be "+ClientSSK.CRYPTO_KEY_LENGTH);
+               this.privKey = key;
+               this.group = group;
+       }
+       
+       public FreenetURI getURI() {
+               return new FreenetURI(pubKeyHash, cryptoKey, null, siteName, 
suggestedEdition);
+       }
+
+       public USK getUSK() {
+               return new USK(pubKeyHash, cryptoKey, siteName, 
suggestedEdition);
+       }
+
+       public InsertableClientSSK getInsertableSSK(long ver) {
+               try {
+                       return new InsertableClientSSK(siteName + SEPARATOR + 
ver, pubKeyHash, 
+                                       new DSAPublicKey(group, privKey), 
privKey, cryptoKey);
+               } catch (MalformedURLException e) {
+                       Logger.error(this, "Caught "+e+" should not be possible 
in USK.getSSK", e);
+                       throw new Error(e);
+               }
+       }
+
+       public InsertableUSK privCopy(long edition) {
+               if(edition == suggestedEdition) return this;
+               try {
+                       return new InsertableUSK(siteName, pubKeyHash, 
cryptoKey, privKey, group, edition);
+               } catch (MalformedURLException e) {
+                       throw new Error(e);
+               }
+       }
+
+
+}

Modified: trunk/freenet/src/freenet/keys/USK.java
===================================================================
--- trunk/freenet/src/freenet/keys/USK.java     2006-03-25 15:24:17 UTC (rev 
8311)
+++ trunk/freenet/src/freenet/keys/USK.java     2006-03-25 16:36:47 UTC (rev 
8312)
@@ -21,7 +21,7 @@
         * I chose "-", because it makes it ludicrously easy to go from the USK 
form to the
         * SSK form, and we don't need to go vice versa.
         */
-       private static final String SEPARATOR = "-";
+       static protected final String SEPARATOR = "-";
        /** Public key hash */
        public final byte[] pubKeyHash;
        /** Encryption key */
@@ -52,7 +52,7 @@
                this(uri.getRoutingKey(), uri.getCryptoKey(), uri.getExtra(), 
uri.getDocName(), uri.getSuggestedEdition());
        }

-       private USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2, 
long suggestedEdition2) {
+       protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2, 
long suggestedEdition2) {
                this.pubKeyHash = pubKeyHash2;
                this.cryptoKey = cryptoKey2;
                this.siteName = siteName2;

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-03-25 15:24:17 UTC (rev 
8311)
+++ trunk/freenet/src/freenet/node/Node.java    2006-03-25 16:36:47 UTC (rev 
8312)
@@ -851,28 +851,28 @@

                archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS, 
MAX_CACHED_ARCHIVE_DATA, MAX_ARCHIVE_SIZE, MAX_ARCHIVED_FILE_SIZE, 
MAX_CACHED_ELEMENTS, random, tempFilenameGenerator);
                chkRequestThrottle = new RequestThrottle(5000, 2.0F);
-               chkRequestStarter = new RequestStarter(this, 
chkRequestThrottle, "Request starter ("+portNumber+")");
-               chkFetchScheduler = new ClientRequestScheduler(false, random, 
chkRequestStarter, this);
+               chkRequestStarter = new RequestStarter(this, 
chkRequestThrottle, "CHK Request starter ("+portNumber+")");
+               chkFetchScheduler = new ClientRequestScheduler(false, false, 
random, chkRequestStarter, this);
                chkRequestStarter.setScheduler(chkFetchScheduler);
                chkRequestStarter.start();
                //insertThrottle = new ChainedRequestThrottle(10000, 2.0F, 
requestThrottle);
                // FIXME reenable the above
                chkInsertThrottle = new RequestThrottle(10000, 2.0F);
-               chkInsertStarter = new RequestStarter(this, chkInsertThrottle, 
"Insert starter ("+portNumber+")");
-               chkPutScheduler = new ClientRequestScheduler(true, random, 
chkInsertStarter, this);
+               chkInsertStarter = new RequestStarter(this, chkInsertThrottle, 
"CHK Insert starter ("+portNumber+")");
+               chkPutScheduler = new ClientRequestScheduler(true, false, 
random, chkInsertStarter, this);
                chkInsertStarter.setScheduler(chkPutScheduler);
                chkInsertStarter.start();

                sskRequestThrottle = new RequestThrottle(5000, 2.0F);
-               sskRequestStarter = new RequestStarter(this, 
sskRequestThrottle, "Request starter ("+portNumber+")");
-               sskFetchScheduler = new ClientRequestScheduler(false, random, 
sskRequestStarter, this);
+               sskRequestStarter = new RequestStarter(this, 
sskRequestThrottle, "SSK Request starter ("+portNumber+")");
+               sskFetchScheduler = new ClientRequestScheduler(false, true, 
random, sskRequestStarter, this);
                sskRequestStarter.setScheduler(sskFetchScheduler);
                sskRequestStarter.start();
                //insertThrottle = new ChainedRequestThrottle(10000, 2.0F, 
requestThrottle);
                // FIXME reenable the above
                sskInsertThrottle = new RequestThrottle(10000, 2.0F);
-               sskInsertStarter = new RequestStarter(this, sskInsertThrottle, 
"Insert starter ("+portNumber+")");
-               sskPutScheduler = new ClientRequestScheduler(true, random, 
sskInsertStarter, this);
+               sskInsertStarter = new RequestStarter(this, sskInsertThrottle, 
"SSK Insert starter ("+portNumber+")");
+               sskPutScheduler = new ClientRequestScheduler(true, true, 
random, sskInsertStarter, this);
                sskInsertStarter.setScheduler(sskPutScheduler);
                sskInsertStarter.start();


Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-03-25 15:24:17 UTC (rev 
8311)
+++ trunk/freenet/src/freenet/node/Version.java 2006-03-25 16:36:47 UTC (rev 
8312)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       private static final int buildNumber = 570;
+       private static final int buildNumber = 571;

        /** Oldest build of Fred we will talk to */
        private static final int lastGoodBuild = 507;


Reply via email to