Author: xor
Date: 2008-11-11 20:49:33 +0000 (Tue, 11 Nov 2008)
New Revision: 23491

Modified:
   trunk/plugins/WoT/introduction/IntroductionPuzzle.java
   trunk/plugins/WoT/introduction/IntroductionServer.java
Log:
Implement puzzle solution downloading in the IntroductionServer.

Modified: trunk/plugins/WoT/introduction/IntroductionPuzzle.java
===================================================================
--- trunk/plugins/WoT/introduction/IntroductionPuzzle.java      2008-11-11 
19:45:03 UTC (rev 23490)
+++ trunk/plugins/WoT/introduction/IntroductionPuzzle.java      2008-11-11 
20:49:33 UTC (rev 23491)
@@ -5,8 +5,10 @@
  */
 package plugins.WoT.introduction;
 
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.MalformedURLException;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
@@ -51,6 +53,8 @@
        public static final String INTRODUCTION_CONTEXT = "introduction";
        public static final int MINIMAL_SOLUTION_LENGTH = 5;
        
+       private static final SimpleDateFormat mDateFormat = new 
SimpleDateFormat("yyyy-MM-dd");
+       
        /* Included in XML: */
        
        private final String mMimeType;
@@ -115,6 +119,31 @@
                mIndex = myIndex;
        }
        
+       public static ObjectSet<IntroductionPuzzle> 
getByInserter(ObjectContainer db, OwnIdentity i) {
+               Query q = db.query();
+               q.constrain(IntroductionPuzzle.class);
+               q.descend("mInserter").constrain(i);
+               return q.execute();
+       }
+       
+       public static IntroductionPuzzle getBySolutionURI(ObjectContainer db, 
FreenetURI uri) throws ParseException {
+               String[] tokens = uri.getDocName().split("|");
+               String id = tokens[1];
+               Date date = mDateFormat.parse(tokens[2]);
+               int index = Integer.parseInt(tokens[3]);
+               
+               Query q = db.query();
+               q.constrain(IntroductionPuzzle.class);
+               q.descend("mInserter").descend("id").constrain(id);
+               q.descend("mDateOfInsertion").constrain(date);
+               q.descend("mIndex").constrain(index);
+               ObjectSet<IntroductionPuzzle> result = q.execute();
+               
+               assert(result.size() == 1);
+               
+               return (result.hasNext() ? result.next() : null);
+       }
+       
        public String getMimeType() {
                return mMimeType;
        }
@@ -128,7 +157,7 @@
                
                /* FIXME: I did not really understand the javadoc of 
FreenetURI. Please verify that the following code actually creates an URI
                 * which looks like the one I specified in the javadoc above 
this function. Thanks. */
-               String dayOfInsertion = new 
SimpleDateFormat("yyyy-MM-dd").format(mDateOfInsertion);
+               String dayOfInsertion = mDateFormat.format(mDateOfInsertion);
                FreenetURI baseURI = 
((OwnIdentity)mInserter).getInsertURI().setKeyType("KSK");
                baseURI = baseURI.setDocName(WoT.WOT_CONTEXT + "/" + 
INTRODUCTION_CONTEXT);
                return baseURI.setMetaString(new String[] {dayOfInsertion + "|" 
+ mIndex + ".xml"} );
@@ -189,6 +218,7 @@
                
q.descend("mValidUntilTime").constrain(System.currentTimeMillis()).smaller();
                ObjectSet<IntroductionPuzzle> result = q.execute();
                
+               Logger.debug(IntroductionPuzzle.class, "Deleting " + 
result.size() + " old puzzles.");
                for(IntroductionPuzzle p : result)
                        db.delete(p);
                
@@ -232,6 +262,14 @@
                serializer.transform(domSource, resultStream);
        }
        
+       public static IntroductionPuzzle importFromXML(InputStream is) {
+               return null;
+       }
+       
+       public static Identity importSolutionFromXML(ObjectContainer db, 
InputStream is) {
+               return null;
+       }
+       
        public class PuzzleHandler extends DefaultHandler {
                private final Identity newInserter;
                private String newMimeType;

Modified: trunk/plugins/WoT/introduction/IntroductionServer.java
===================================================================
--- trunk/plugins/WoT/introduction/IntroductionServer.java      2008-11-11 
19:45:03 UTC (rev 23490)
+++ trunk/plugins/WoT/introduction/IntroductionServer.java      2008-11-11 
20:49:33 UTC (rev 23491)
@@ -8,7 +8,9 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerConfigurationException;
@@ -21,13 +23,22 @@
 import com.db4o.query.Query;
 
 import freenet.client.ClientMetadata;
+import freenet.client.FetchContext;
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
 import freenet.client.HighLevelSimpleClient;
 import freenet.client.InsertBlock;
 import freenet.client.InsertException;
+import freenet.client.async.BaseClientPutter;
+import freenet.client.async.ClientCallback;
+import freenet.client.async.ClientGetter;
 import freenet.keys.FreenetURI;
+import freenet.node.RequestStarter;
 import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.io.TempBucketFactory;
+import plugins.WoT.Identity;
+import plugins.WoT.IdentityParser;
 import plugins.WoT.OwnIdentity;
 import plugins.WoT.exceptions.InvalidParameterException;
 
@@ -36,7 +47,7 @@
  * 
  * @author xor
  */
-public class IntroductionServer implements Runnable {
+public class IntroductionServer implements Runnable, ClientCallback {
        
        private static final long THREAD_PERIOD = 30 * 60 * 1000; /* FIXME: 
tweak before release */
        private static final short PUZZLES_COUNT = 5; 
@@ -57,6 +68,8 @@
        private final TempBucketFactory mTBF;
 
        private final IntroductionPuzzleFactory[] mPuzzleFactories = new 
IntroductionPuzzleFactory[] { new CaptchaFactory1() };
+       
+       private final ArrayList<ClientGetter> mRequests = new 
ArrayList<ClientGetter>(PUZZLES_COUNT * 5); /* Just assume that there are 5 
identities */
 
        /**
         * Creates an IntroductionServer
@@ -84,6 +97,7 @@
                catch (InterruptedException e) {}
                
                while(isRunning) {
+                       Logger.debug(this, "Introduction server loop 
running...");
                        ObjectSet<OwnIdentity> identities = 
OwnIdentity.getAllOwnIdentities(db);
                        
                        IntroductionPuzzle.deleteOldPuzzles(db);
@@ -92,10 +106,12 @@
                                OwnIdentity identity = identities.next();
                                
if(identity.hasContext(IntroductionPuzzle.INTRODUCTION_CONTEXT)) {
                                        try {
-                                               managePuzzles(identity);
+                                               Logger.debug(this, "Managing 
puzzles of " + identity.getNickName());
                                                downloadSolutions(identity);
+                                               insertNewPuzzles(identity);
+                                               Logger.debug(this, "Managing 
puzzles finished.");
                                        } catch (Exception e) {
-                                               Logger.error(this, "Puzzle 
insert failed: " + e.getMessage(), e);
+                                               Logger.error(this, "Puzzle 
management failed for " + identity.getNickName(), e);
                                        }
                                }
                        }
@@ -107,12 +123,13 @@
                        catch (InterruptedException e)
                        {
                                mThread.interrupt();
+                               Logger.debug(this, "Introduction server loop 
interrupted.");
                        }
+                       Logger.debug(this, "Introduction server loop 
finished.");
                }
-
        }
        
-       public void terminate() {
+       public synchronized void terminate() {
                Logger.debug(this, "Stopping the introduction server...");
                isRunning = false;
                mThread.interrupt();
@@ -125,26 +142,37 @@
                }
                Logger.debug(this, "Stopped the introduction server.");
        }
-       
-       private void managePuzzles(OwnIdentity identity) {
-               Query q = db.query();
-               q.constrain(IntroductionPuzzle.class);
-               q.descend("mInserter").constrain(identity);
-               ObjectSet<IntroductionPuzzle> puzzles = q.execute();
                
-               Logger.debug(this, "Identity " + identity.getNickName() + " has 
" + puzzles.size() + " puzzles stored. Deleting expired ones ...");
+       private synchronized void downloadSolutions(OwnIdentity identity) 
throws FetchException {
+               ObjectSet<IntroductionPuzzle> puzzles = 
IntroductionPuzzle.getByInserter(db, identity);
                
+               Logger.debug(this, "Identity " + identity.getNickName() + " has 
" + puzzles.size() + " puzzles stored. Trying to fetch solutions ...");
+
+               /* TODO: We restart all requests in every iteration. Decide 
whether this makes sense or not, if not add code to re-use requests for
+                * puzzles which still exist.
+                * I think it makes sense to restart them because there are not 
many puzzles and therefore not many requests. */
+               for(int idx=0; idx < mRequests.size(); ++idx) {
+                       mRequests.get(idx).cancel();
+                       mRequests.remove(idx);
+               }
+               
                for(IntroductionPuzzle p : puzzles) {
-                       if(p.getValidUntilTime() < System.currentTimeMillis()) {
-                               db.delete(p);
-                               puzzles.remove(p);
-                       }
+                       FetchContext fetchContext = mClient.getFetchContext();
+                       fetchContext.maxSplitfileBlockRetries = -1; // retry 
forever
+                       fetchContext.maxNonSplitfileRetries = -1; // retry 
forever
+                       ClientGetter g = mClient.fetch(p.getSolutionURI(), -1, 
this, this, fetchContext);
+                       
g.setPriorityClass(RequestStarter.UPDATE_PRIORITY_CLASS); /* FIXME: decide 
which one to use */
+                       mRequests.add(g);
+                       Logger.debug(this, "Trying to fetch captcha solution  " 
+ p.getSolutionURI().toString());
                }
                
                db.commit();
+       }
+       
+       private synchronized void insertNewPuzzles(OwnIdentity identity) {
+               int puzzlesToInsert = PUZZLES_COUNT - 
IntroductionPuzzle.getByInserter(db, identity).size();
+               Logger.debug(this, "Trying to insert " + puzzlesToInsert + " 
puzzles from " + identity.getNickName());
                
-               int puzzlesToInsert = PUZZLES_COUNT - puzzles.size();
-               Logger.debug(this, "Trying to insert " + puzzlesToInsert + " 
puzzles from " + identity.getNickName());
                while(puzzlesToInsert > 0) {
                        try {
                                insertNewPuzzle(identity);
@@ -154,10 +182,11 @@
                        }
                        --puzzlesToInsert;
                }
+               
                Logger.debug(this, "Finished inserting puzzles from " + 
identity.getNickName());
        }
        
-       private void insertNewPuzzle(OwnIdentity identity) throws IOException, 
InsertException, TransformerException, ParserConfigurationException {
+       private synchronized void insertNewPuzzle(OwnIdentity identity) throws 
IOException, InsertException, TransformerException, 
ParserConfigurationException {
                Bucket tempB = mTBF.makeBucket(10 * 1024); /* TODO: set to a 
reasonable value */
                OutputStream os = tempB.getOutputStream();
                
@@ -186,7 +215,52 @@
                }
        }
        
-       private void downloadSolutions(OwnIdentity identity) {
+       /**
+        * Called when the node can't fetch a file OR when there is a newer 
edition.
+        * In our case, called when there is no solution to a puzzle in the 
network.
+        */
+       public synchronized void onFailure(FetchException e, ClientGetter 
state) {
+               Logger.normal(this, "Downloading puzzle solution " + 
state.getURI() + " failed: ", e);
+
+               mRequests.remove(state);
+       }
+
+       /**
+        * Called when a file is successfully fetched. We then add the identity 
which
+        * solved the puzzle.
+        */
+       public synchronized void onSuccess(FetchResult result, ClientGetter 
state) {
+               Logger.debug(this, "Fetched puzzle solution: " + 
state.getURI());
+
+               try {
+                       IntroductionPuzzle p = 
IntroductionPuzzle.getBySolutionURI(db, state.getURI());
+                       Identity newIdentity = p.importSolutionFromXML(db, 
result.asBucket().getInputStream());
+                       OwnIdentity puzzleOwner = (OwnIdentity)p.getInserter();
+                       
+                       puzzleOwner.setTrust(db, newIdentity, (byte)0, null); 
/* FIXME: is 0 the proper trust for newly imported identities? */
                
+                       state.cancel(); /* FIXME: is this necessary */ 
+                       mRequests.remove(state);
+               } catch (Exception e) {
+                       Logger.error(this, "Parsing failed for "+ 
state.getURI(), e);
+               }
        }
+       
+       /* Not needed functions from the ClientCallback inteface */
+       
+       // Only called by inserts
+       public void onSuccess(BaseClientPutter state) {}
+       
+       // Only called by inserts
+       public void onFailure(InsertException e, BaseClientPutter state) {}
+
+       // Only called by inserts
+       public void onFetchable(BaseClientPutter state) {}
+
+       // Only called by inserts
+       public void onGeneratedURI(FreenetURI uri, BaseClientPutter state) {}
+
+       /** Called when freenet.async thinks that the request should be 
serialized to
+        * disk, if it is a persistent request. */
+       public void onMajorProgress() {}
 }

_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs

Reply via email to