Author: bombe
Date: 2006-05-28 22:27:14 +0000 (Sun, 28 May 2006)
New Revision: 8906

Added:
   trunk/freenet/src/freenet/clients/http/PluginToadlet.java
   trunk/freenet/src/freenet/plugin/
   trunk/freenet/src/freenet/plugin/HttpPlugin.java
   trunk/freenet/src/freenet/plugin/Plugin.java
   trunk/freenet/src/freenet/plugin/PluginManager.java
   trunk/freenet/src/freenet/plugin/TestHttpPlugin.java
   trunk/freenet/src/freenet/plugin/TestPlugin.java
Modified:
   trunk/freenet/src/freenet/clients/http/BookmarkManager.java
   trunk/freenet/src/freenet/clients/http/FProxyToadlet.java
   trunk/freenet/src/freenet/clients/http/RedirectException.java
   trunk/freenet/src/freenet/clients/http/Spider.java
   trunk/freenet/src/freenet/clients/http/Toadlet.java
   trunk/freenet/src/freenet/clients/http/ToadletContext.java
   trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
   trunk/freenet/src/freenet/clients/http/WelcomeToadlet.java
   trunk/freenet/src/freenet/node/Node.java
Log:
move bookmark config from welcome toadlet to bookmark manager
change parameter name in Toadlet.writePermanentRedirect()
extend RedirectException with new uri as parameter
retrofit toad's spider to new plugin system
add convenience method to ToadletContext/Impl
add new plugin manager to node

Modified: trunk/freenet/src/freenet/clients/http/BookmarkManager.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/BookmarkManager.java 2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/BookmarkManager.java 2006-05-28 
22:27:14 UTC (rev 8906)
@@ -10,9 +10,16 @@
 import freenet.config.StringArrCallback;
 import freenet.config.StringArrOption;
 import freenet.config.InvalidConfigValueException;
+import freenet.config.SubConfig;
 import freenet.client.async.USKCallback;

 public class BookmarkManager {
+       private static final String[] DEFAULT_TESTNET_BOOKMARKS = {
+               "USK at 
60I8H8HinpgZSOuTSD66AVlIFAy-xsppFr0YCzCar7c,NzdivUGCGOdlgngOGRbbKDNfSCnjI0FXjHLzJM4xkJ4,AQABAAE/index/4/=INDEX.7-freesite"
+       };
+       private static final String[] DEFAULT_DARKNET_BOOKMARKS = {
+               "USK at 
PFeLTa1si2Ml5sDeUy7eDhPso6TPdmw-2gWfQ4Jg02w,3ocfrqgUMVWA2PeorZx40TW0c-FiIOL-TWKQHoDbVdE,AQABAAE/Index/-1/=Darknet
 Index"
+       };
        Vector bookmarks;
        Node node;
        USKUpdatedCallback uskcb;
@@ -71,10 +78,27 @@
                }
        }

-       BookmarkManager(Node n) {
+       public BookmarkManager(Node n) {
                this.bookmarks = new Vector();
                this.node = n;
                this.uskcb = new USKUpdatedCallback();
+               SubConfig sc = null;
+               try {
+                       sc = new SubConfig("fproxy", n.config);
+               } catch (IllegalArgumentException iae1) {
+                       sc = n.config.get("fproxy");
+               }
+               sc.register("bookmarks", n.isTestnetEnabled() ? 
DEFAULT_TESTNET_BOOKMARKS : DEFAULT_DARKNET_BOOKMARKS, 0, false, "List of 
bookmarks", "A list of bookmarked freesites", makeCB());
+               
+               String[] initialbookmarks = sc.getStringArr("bookmarks");
+               for (int i = 0; i < initialbookmarks.length; i++) {
+                       try {
+                               addBookmark(new Bookmark(initialbookmarks[i]));
+                       } catch (MalformedURLException mue) {
+                               // just ignore that one
+                       }
+               }
+
        }

        public BookmarkCallback makeCB() {
@@ -116,6 +140,7 @@
                        try {
                                USK u = USK.create(b.key);
                                this.node.uskManager.subscribe(u, this.uskcb, 
true);
+                               node.config.store();
                        } catch (MalformedURLException mue) {

                        }
@@ -127,6 +152,7 @@
                        try {
                                USK u = USK.create(b.key);
                                this.node.uskManager.subscribe(u, this.uskcb, 
true);
+                               node.config.store();
                        } catch (MalformedURLException mue) {

                        }


Property changes on: trunk/freenet/src/freenet/clients/http/BookmarkManager.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/FProxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FProxyToadlet.java   2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/FProxyToadlet.java   2006-05-28 
22:27:14 UTC (rev 8906)
@@ -323,7 +323,12 @@

        public static void maybeCreateFProxyEtc(Node node, Config config) 
throws IOException, InvalidConfigValueException {

-               SubConfig fproxyConfig = new SubConfig("fproxy", config);
+               SubConfig fproxyConfig = null;
+               try {
+                       fproxyConfig = new SubConfig("fproxy", config);
+               } catch (IllegalArgumentException iae1) {
+                       fproxyConfig = config.get("fproxy");
+               }

                try {
                        SimpleToadletServer server = new 
SimpleToadletServer(fproxyConfig, node);
@@ -343,6 +348,9 @@
                        WelcomeToadlet welcometoadlet = new 
WelcomeToadlet(client, node, fproxyConfig);
                        server.register(welcometoadlet, "/welcome/", true);

+                       PluginToadlet pluginToadlet = new PluginToadlet(client, 
node.pluginManager2);
+                       server.register(pluginToadlet, "/plugin/", true);
+                       
                        ConfigToadlet configtoadlet = new ConfigToadlet(client, 
config);
                        server.register(configtoadlet, "/config/", true);



Property changes on: trunk/freenet/src/freenet/clients/http/FProxyToadlet.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/clients/http/PluginToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/PluginToadlet.java   2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/PluginToadlet.java   2006-05-28 
22:27:14 UTC (rev 8906)
@@ -0,0 +1,226 @@
+/**
+ * 
+ */
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.plugin.HttpPlugin;
+import freenet.plugin.Plugin;
+import freenet.plugin.PluginManager;
+import freenet.support.HTMLEncoder;
+
+/**
+ * Toadlet for the plugin manager.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public class PluginToadlet extends Toadlet {
+
+       /** The plugin manager backing this toadlet. */
+       private final PluginManager pluginManager;
+
+       /**
+        * Creates a new toadlet.
+        * 
+        * @param client
+        *            The high-level client to use
+        * @param pluginManager
+        *            The plugin manager to use
+        */
+       protected PluginToadlet(HighLevelSimpleClient client, PluginManager 
pluginManager) {
+               super(client);
+               this.pluginManager = pluginManager;
+       }
+
+       /**
+        * Currently this toadlet only supports GET.
+        * 
+        * @see freenet.clients.http.Toadlet#supportedMethods()
+        * @return "GET"
+        */
+       public String supportedMethods() {
+               return "GET";
+       }
+
+       /**
+        * Handles a GET request.
+        * 
+        * @see freenet.clients.http.Toadlet#handleGet(java.net.URI,
+        *      freenet.clients.http.ToadletContext)
+        * @param uri
+        *            The URI that was requested
+        * @param ctx
+        *            The context of this toadlet
+        */
+       public void handleGet(URI uri, ToadletContext ctx) throws 
ToadletContextClosedException, IOException, RedirectException {
+               HTTPRequest httpRequest = new HTTPRequest(uri, null, ctx);
+
+               String uriPath = uri.getPath();
+               String pluginName = uriPath.substring(uriPath.lastIndexOf('/') 
+ 1);
+
+               if (pluginName.length() > 0) {
+                       Plugin plugin = findPlugin(pluginName);
+                       if (plugin != null) {
+                               if (plugin instanceof HttpPlugin) {
+                                       ((HttpPlugin) 
plugin).handleGet(httpRequest, ctx);
+                               } else {
+                                       writeReply(ctx, 220, "text/html; 
charset=utf-8", "OK", createBox(ctx, "Plugin has no web interface", "The plugin 
does not have a web interface, so there is nothing to show.").toString());
+                               }
+                               return;
+                       }
+                       writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
createBox(ctx, "Plugin not found", "The requested plugin could not be 
found.").toString());
+               }
+
+               String action = httpRequest.getParam("action");
+               if (action.length() == 0) {
+                       writePermanentRedirect(ctx, "Plugin list", 
"?action=list");
+                       return;
+               }
+
+               StringBuffer replyBuffer = new StringBuffer();
+               if ("list".equals(action)) {
+                       replyBuffer.append(listPlugins(ctx));
+               } else if ("add".equals(action)) {
+                       pluginName = httpRequest.getParam("pluginName");
+                       boolean added = false;
+                       try {
+                               pluginManager.addPlugin(pluginName);
+                               added = true;
+                       } catch (IllegalArgumentException iae1) {
+                       }
+                       if (added) {
+                               writePermanentRedirect(ctx, "Plugin list", 
"?action=list");
+                               return;
+                       }
+                       replyBuffer.append(createBox(ctx, "Plugin was not 
loaded", "The plugin you requested could not be loaded. Please verify the name 
of the plugin&rsquo;s class and the URL, if you gave one."));
+               } else if ("reload".equals(action)) {
+                       pluginName = httpRequest.getParam("pluginName");
+                       Plugin plugin = findPlugin(pluginName);
+                       plugin.stopPlugin();
+                       plugin.startPlugin();
+                       writePermanentRedirect(ctx, "Plugin list", 
"?action=list");
+               } else if ("unload".equals(action)) {
+                       pluginName = httpRequest.getParam("pluginName");
+                       Plugin plugin = findPlugin(pluginName);
+                       pluginManager.removePlugin(plugin);
+                       writePermanentRedirect(ctx, "Plugin list", 
"?action=list");
+               }
+               writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
replyBuffer.toString());
+       }
+
+       /**
+        * Searches the currently installed plugins for the plugin with the
+        * specified internal name.
+        * 
+        * @param internalPluginName
+        *            The internal name of the wanted plugin
+        * @return The wanted plugin, or <code>null</code> if no plugin could be
+        *         found
+        */
+       private Plugin findPlugin(String internalPluginName) {
+               Plugin[] plugins = pluginManager.getPlugins();
+               for (int pluginIndex = 0, pluginCount = plugins.length; 
pluginIndex < pluginCount; pluginIndex++) {
+                       Plugin plugin = plugins[pluginIndex];
+                       String pluginName = plugin.getClass().getName() + "@" + 
pluginIndex;
+                       if (pluginName.equals(internalPluginName)) {
+                               return plugin;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Creates a complete HTML page containing a list of all plugins.
+        * 
+        * @param context
+        *            The toadlet context
+        * @return A StringBuffer containing the HTML page
+        */
+       private StringBuffer listPlugins(ToadletContext context) {
+               Plugin[] plugins = pluginManager.getPlugins();
+               PageMaker pageMaker = context.getPageMaker();
+               StringBuffer outputBuffer = new StringBuffer();
+               pageMaker.makeHead(outputBuffer, "List of Plugins", true);
+
+               outputBuffer.append("<div id=\"infobox\">");
+               outputBuffer.append("<div id=\"infobox-header\">Plugin 
list</div>");
+               outputBuffer.append("<div id=\"infobox-content\">Plugin 
list</div>");
+               outputBuffer.append("<table id=\"plugintable\">");
+               outputBuffer.append("<tr>");
+               outputBuffer.append("<th>Plugin Name</th>");
+               outputBuffer.append("<th>Internal Name</th>");
+               outputBuffer.append("<th colspan=\"3\" />");
+               outputBuffer.append("</tr>\n");
+               for (int pluginIndex = 0, pluginCount = plugins.length; 
pluginIndex < pluginCount; pluginIndex++) {
+                       Plugin plugin = plugins[pluginIndex];
+                       String internalName = plugin.getClass().getName() + "@" 
+ pluginIndex;
+                       outputBuffer.append("<tr>");
+                       
outputBuffer.append("<td>").append(HTMLEncoder.encode(plugin.getPluginName())).append("</td>");
+                       
outputBuffer.append("<td>").append(HTMLEncoder.encode(internalName)).append("</td>");
+                       if (plugin instanceof HttpPlugin) {
+                               outputBuffer.append("<td><form 
action=\"").append(HTMLEncoder.encode(internalName)).append("\" 
method=\"get\"><input type=\"submit\" value=\"Visit\" /></form></td>");
+                       } else {
+                               outputBuffer.append("<td/>");
+                       }
+                       outputBuffer.append("<td><form action=\"\" 
method=\"get\"><input type=\"hidden\" name=\"action\" value=\"reload\"><input 
type=\"hidden\" name=\"pluginName\" value=\"").append(internalName).append("\" 
/><input type=\"submit\" value=\"Reload\" /></form></td>");
+                       outputBuffer.append("<td><form action=\"\" 
method=\"get\"><input type=\"hidden\" name=\"action\" value=\"unload\"><input 
type=\"hidden\" name=\"pluginName\" value=\"").append(internalName).append("\" 
/><input type=\"submit\" value=\"Unload\" /></form></td>");
+                       outputBuffer.append("</tr>\n");
+               }
+               outputBuffer.append("</table>");
+               outputBuffer.append("</div>\n");
+               outputBuffer.append("</div>\n");
+
+               appendAddPluginBox(outputBuffer);
+
+               pageMaker.makeTail(outputBuffer);
+               return outputBuffer;
+       }
+
+       /**
+        * Creates an alert box with the specified title and message. A link to 
the
+        * plugin list is added after the message.
+        * 
+        * @param context
+        *            The toadlet context
+        * @param title
+        *            The title of the box
+        * @param message
+        *            The content of the box
+        * @return A StringBuffer containing the complete page
+        */
+       private StringBuffer createBox(ToadletContext context, String title, 
String message) {
+               PageMaker pageMaker = context.getPageMaker();
+               StringBuffer outputBuffer = new StringBuffer();
+               pageMaker.makeHead(outputBuffer, HTMLEncoder.encode(title));
+               outputBuffer.append("<div class=\"infobox infobox-alert\">");
+               outputBuffer.append("<div 
class=\"infobox-header\">").append(HTMLEncoder.encode(title)).append("</div>\n");
+               outputBuffer.append("<div 
class=\"infobox-content\">").append(HTMLEncoder.encode(message)).append("</div>\n");
+               outputBuffer.append("<div class=\"infobox-content\">Please <a 
href=\"?action=list\">return</a> to the list of plugins.</div>\n");
+               outputBuffer.append("</div>\n");
+               pageMaker.makeTail(outputBuffer);
+
+               return outputBuffer;
+       }
+
+       /**
+        * Appends the HTML code for the &ldquo;add plugin&rdquo; box to the 
given
+        * StringBuffer.
+        * 
+        * @param outputBuffer
+        *            The StringBuffer to append the HTML code to
+        */
+       private void appendAddPluginBox(StringBuffer outputBuffer) {
+               outputBuffer.append("<div id=\"pluginadder\">");
+               outputBuffer.append("<form action=\"?add\" method=\"get\">");
+               outputBuffer.append("<input type=\"hidden\" name=\"action\" 
value=\"add\" />");
+               outputBuffer.append("<input type=\"text\" size=\"40\" 
name=\"pluginName\" value=\"\" />&nbsp;");
+               outputBuffer.append("<input type=\"submit\" value=\"Load 
plugin\" />");
+               outputBuffer.append("</form>");
+               outputBuffer.append("</div>\n");
+       }
+
+}


Property changes on: trunk/freenet/src/freenet/clients/http/PluginToadlet.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/RedirectException.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/RedirectException.java       
2006-05-28 22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/RedirectException.java       
2006-05-28 22:27:14 UTC (rev 8906)
@@ -4,4 +4,13 @@

 class RedirectException extends Exception {
        URI newuri;
+       
+       public RedirectException() {
+               super();
+       }
+       
+       public RedirectException(URI newURI) {
+               this.newuri = newURI;
+       }
+
 }


Property changes on: 
trunk/freenet/src/freenet/clients/http/RedirectException.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/Spider.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Spider.java  2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/clients/http/Spider.java  2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -1,7 +1,6 @@
 package freenet.clients.http;

 import java.io.BufferedWriter;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
@@ -9,11 +8,16 @@
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.Vector;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;

 import freenet.client.ClientMetadata;
 import freenet.client.FetchException;
@@ -29,16 +33,20 @@
 import freenet.keys.FreenetURI;
 import freenet.node.Node;
 import freenet.node.RequestStarter;
+import freenet.plugin.HttpPlugin;
+import freenet.plugin.PluginManager;
 import freenet.support.Bucket;
+import freenet.support.HTMLEncoder;
 import freenet.support.Logger;
+import freenet.support.MultiValueTable;

 /**
  * Spider. Produces an index.
  */
-public class Spider implements ClientCallback, FoundURICallback {
-       
+public class Spider implements HttpPlugin, ClientCallback, FoundURICallback {
+
        long tProducedIndex;
-       
+
        // URIs visited, or fetching, or queued. Added once then forgotten 
about.
        private final HashSet visitedURIs = new HashSet();
        private final HashSet urisWithWords = new HashSet();
@@ -47,58 +55,46 @@
        private final LinkedList queuedURIList = new LinkedList();
        private final HashMap runningFetchesByURI = new HashMap();
        private final HashMap urisByWord = new HashMap();
+
        // Can have many; this limit only exists to save memory.
        private final int maxParallelRequests = 200;
-       private final Node node;
-       private final FetcherContext ctx;
+
+       private Node node;
+       private FetcherContext ctx;
        private final short PRIORITY_CLASS = 
RequestStarter.PREFETCH_PRIORITY_CLASS;
-       
-       public Spider(BookmarkManager bm, Node node) {
-               this.node = node;
-               this.ctx = node.makeClient((short)0).getFetcherContext();
-               ctx.maxSplitfileBlockRetries = 10;
-               ctx.maxNonSplitfileRetries = 10;
-               ctx.maxTempLength = 2*1024*1024;
-               ctx.maxOutputLength = 2*1024*1024;
-               FreenetURI[] initialURIs = bm.getBookmarkURIs();
-               for(int i=0;i<initialURIs.length;i++)
-                       queueURI(initialURIs[i]);
-               tProducedIndex = System.currentTimeMillis();
-               startSomeRequests();
-       }
+       private boolean stopped = true;

        private synchronized void queueURI(FreenetURI uri) {
-               if((!visitedURIs.contains(uri)) && queuedURISet.add(uri)) {
-                       Logger.minor(this, "Spider queueing URI: "+uri);
+               if ((!visitedURIs.contains(uri)) && queuedURISet.add(uri)) {
                        queuedURIList.addLast(uri);
                        visitedURIs.add(uri);
                }
        }

        private void startSomeRequests() {
-               Vector toStart = null;
-               synchronized(this) {
+               ArrayList toStart = null;
+               synchronized (this) {
+                       if (stopped) {
+                               return;
+                       }
                        int running = runningFetchesByURI.size();
                        int queued = queuedURIList.size();
-                       if(running == maxParallelRequests || queued == 0) 
return;
-                       if(toStart == null)
-                               toStart = new 
Vector(Math.min(maxParallelRequests-running, queued));
-                       for(int i=running;i<maxParallelRequests;i++) {
-                               if(queuedURIList.isEmpty()) break;
+                       if (running == maxParallelRequests || queued == 0)
+                               return;
+                       toStart = new ArrayList(Math.min(maxParallelRequests - 
running, queued));
+                       for (int i = running; i < maxParallelRequests; i++) {
+                               if (queuedURIList.isEmpty())
+                                       break;
                                FreenetURI uri = (FreenetURI) 
queuedURIList.removeFirst();
                                queuedURISet.remove(uri);
                                ClientGetter getter = makeGetter(uri);
                                toStart.add(getter);
                        }
-               }
-               if(toStart != null) {
-                       for(int i=0;i<toStart.size();i++) {
+                       for (int i = 0; i < toStart.size(); i++) {
                                ClientGetter g = (ClientGetter) toStart.get(i);
                                try {
-                                       Logger.minor(this, "Starting "+g+" for 
"+g.getURI());
+                                       runningFetchesByURI.put(g.getURI(), g);
                                        g.start();
-                                       Logger.minor(this, "Started "+g+" for 
"+g.getURI());
-                                       runningFetchesByURI.put(g.getURI(), g);
                                } catch (FetchException e) {
                                        onFailure(e, g);
                                }
@@ -107,29 +103,27 @@
        }

        private ClientGetter makeGetter(FreenetURI uri) {
-               Logger.minor(this, "Starting getter for "+uri);
                ClientGetter g = new ClientGetter(this, node.chkFetchScheduler, 
node.sskFetchScheduler, uri, ctx, PRIORITY_CLASS, this, null);
                return g;
        }

        public void onSuccess(FetchResult result, ClientGetter state) {
                FreenetURI uri = state.getURI();
-               synchronized(this) {
+               synchronized (this) {
                        runningFetchesByURI.remove(uri);
                }
-               Logger.minor(this, "Success: "+uri);
                startSomeRequests();
                ClientMetadata cm = result.getMetadata();
                Bucket data = result.asBucket();
                String mimeType = cm.getMIMEType();
                try {
-                       ContentFilter.filter(data, ctx.bucketFactory, mimeType, 
new URI("http://127.0.0.1:8888/"+uri.toString(false)), this);
+                       ContentFilter.filter(data, ctx.bucketFactory, mimeType, 
new URI("http://127.0.0.1:8888/"; + uri.toString(false)), this);
                } catch (UnsafeContentTypeException e) {
                        return; // Ignore
                } catch (IOException e) {
-                       Logger.error(this, "Bucket error?: "+e, e);
+                       Logger.error(this, "Bucket error?: " + e, e);
                } catch (URISyntaxException e) {
-                       Logger.error(this, "Internal error: "+e, e);
+                       Logger.error(this, "Internal error: " + e, e);
                } finally {
                        data.free();
                }
@@ -137,13 +131,14 @@

        public void onFailure(FetchException e, ClientGetter state) {
                FreenetURI uri = state.getURI();
-               Logger.minor(this, "Failed: "+uri);
-               synchronized(this) {
+               synchronized (this) {
                        failedURIs.add(uri);
                        runningFetchesByURI.remove(uri);
                }
-               if(e.newURI != null)
+               if (e.newURI != null)
                        queueURI(e.newURI);
+               else
+                       queueURI(uri);
                startSomeRequests();
        }

@@ -169,13 +164,14 @@
                try {
                        uri = new FreenetURI(baseURI.getPath());
                } catch (MalformedURLException e) {
-                       Logger.error(this, "Caught "+e, e);
+                       Logger.error(this, "Caught " + e, e);
                        return;
                }
                String[] words = s.split("[^A-Za-z0-9]");
-               for(int i=0;i<words.length;i++) {
+               for (int i = 0; i < words.length; i++) {
                        String word = words[i];
-                       if(word == null || word.length() == 0) continue;
+                       if (word == null || word.length() == 0)
+                               continue;
                        word = word.toLowerCase();
                        addWord(word, uri);
                }
@@ -184,24 +180,23 @@
        private synchronized void addWord(String word, FreenetURI uri) {
                FreenetURI[] uris = (FreenetURI[]) urisByWord.get(word);
                urisWithWords.add(uri);
-               if(uris == null) {
+               if (uris == null) {
                        urisByWord.put(word, new FreenetURI[] { uri });
                } else {
-                       for(int i=0;i<uris.length;i++) {
-                               if(uris[i].equals(uri))
+                       for (int i = 0; i < uris.length; i++) {
+                               if (uris[i].equals(uri))
                                        return;
                        }
-                       FreenetURI[] newURIs = new FreenetURI[uris.length+1];
+                       FreenetURI[] newURIs = new FreenetURI[uris.length + 1];
                        System.arraycopy(uris, 0, newURIs, 0, uris.length);
                        newURIs[uris.length] = uri;
                        urisByWord.put(word, newURIs);
                }
-               Logger.minor(this, "Added word: "+word+" for "+uri);
-               if(tProducedIndex + 10*1000 < System.currentTimeMillis()) {
+               if (tProducedIndex + 10 * 1000 < System.currentTimeMillis()) {
                        try {
                                produceIndex();
                        } catch (IOException e) {
-                               Logger.error(this, "Caught "+e+" while creating 
index", e);
+                               Logger.error(this, "Caught " + e + " while 
creating index", e);
                        }
                        tProducedIndex = System.currentTimeMillis();
                }
@@ -217,27 +212,27 @@
                        throw new Error(e);
                }
                BufferedWriter bw = new BufferedWriter(osw);
-               if(urisByWord.isEmpty() || urisWithWords.isEmpty()) {
-                       Logger.minor(this, "No URIs with words");
+               if (urisByWord.isEmpty() || urisWithWords.isEmpty()) {
+                       System.out.println("No URIs with words");
                        return;
                }
                String[] words = (String[]) urisByWord.keySet().toArray(new 
String[urisByWord.size()]);
                Arrays.sort(words);
                FreenetURI[] uris = (FreenetURI[]) urisWithWords.toArray(new 
FreenetURI[urisWithWords.size()]);
                HashMap urisToNumbers = new HashMap();
-               for(int i=0;i<uris.length;i++) {
+               for (int i = 0; i < uris.length; i++) {
                        urisToNumbers.put(uris[i], new Integer(i));
-                       bw.write("!" + uris[i].toString(false)+"\n");
+                       bw.write("!" + uris[i].toString(false) + "\n");
                }
-               for(int i=0;i<words.length;i++) {
+               for (int i = 0; i < words.length; i++) {
                        StringBuffer s = new StringBuffer();
                        s.append('?');
                        s.append(words[i]);
                        FreenetURI[] urisForWord = (FreenetURI[]) 
urisByWord.get(words[i]);
-                       for(int j=0;j<urisForWord.length;j++) {
+                       for (int j = 0; j < urisForWord.length; j++) {
                                FreenetURI uri = urisForWord[j];
                                Integer x = (Integer) urisToNumbers.get(uri);
-                               if(x == null)
+                               if (x == null)
                                        Logger.error(this, "Eh?");
                                else {
                                        s.append(' ');
@@ -249,5 +244,159 @@
                }
                bw.close();
        }
+
+       /**
+        * @see 
freenet.plugin.HttpPlugin#handleGet(freenet.clients.http.HTTPRequest, 
freenet.clients.http.ToadletContext)
+        */
+       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
+               String action = request.getParam("action");
+               PageMaker pageMaker = context.getPageMaker();
+               if ((action == null) || (action.length() == 0)) {
+                       MultiValueTable responseHeaders = new MultiValueTable();
+                       responseHeaders.put("Location", "?action=list");
+                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
+                       return;
+               } else if ("list".equals(action)) {
+                       StringBuffer responseBuffer = new StringBuffer();
+                       pageMaker.makeHead(responseBuffer, "The Definitive 
Spider");
+                       /* create copies for multi-threaded use */
+                       Map runningFetches = new HashMap(runningFetchesByURI);
+                       List queued = new ArrayList(queuedURIList);
+                       Set visited = new HashSet(visitedURIs);
+                       Set failed = new HashSet(failedURIs);
+                       
responseBuffer.append(createNavbar(runningFetches.size(), queued.size(), 
visited.size(), failed.size()));
+                       responseBuffer.append(createAddBox());
+                       responseBuffer.append(createList("Running Fetches", 
"running", runningFetches.keySet()));
+                       responseBuffer.append(createList("Queued URIs", 
"queued", queued));
+                       responseBuffer.append(createList("Visited URIs", 
"visited", visited));
+                       responseBuffer.append(createList("Failed URIs", 
"failed", failed));
+                       pageMaker.makeTail(responseBuffer);
+                       MultiValueTable responseHeaders = new MultiValueTable();
+                       byte[] responseBytes = 
responseBuffer.toString().getBytes("utf-8");
+                       context.sendReplyHeaders(200, "OK", responseHeaders, 
"text/html; charset=utf-8", responseBytes.length);
+                       context.writeData(responseBytes);
+               } else if ("add".equals(action)) {
+                       String uriParam = request.getParam("key");
+                       try {
+                               FreenetURI uri = new FreenetURI(uriParam);
+                               synchronized (this) {
+                                       failedURIs.remove(uri);
+                                       visitedURIs.remove(uri);
+                               }
+                               queueURI(uri);
+                               startSomeRequests();
+                       } catch (MalformedURLException mue1) {
+                               sendSimpleResponse(context, "URL invalid", "The 
given URI is not valid. Please <a href=\"?action=list\">return</a> and try 
again.");
+                               return;
+                       }
+                       MultiValueTable responseHeaders = new MultiValueTable();
+                       responseHeaders.put("Location", "?action=list");
+                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
+                       return;
+               }
+       }
+
+       /**
+        * @see 
freenet.plugin.HttpPlugin#handlePost(freenet.clients.http.HTTPRequest, 
freenet.clients.http.ToadletContext)
+        */
+       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException {
+       }

+       private void sendSimpleResponse(ToadletContext context, String title, 
String message) throws ToadletContextClosedException, IOException {
+               PageMaker pageMaker = context.getPageMaker();
+               StringBuffer outputBuffer = new StringBuffer();
+               pageMaker.makeHead(outputBuffer, title);
+               outputBuffer.append("<div class=\"infobox infobox-alert\">");
+               outputBuffer.append("<div 
class=\"infobox-header\">").append(HTMLEncoder.encode(title)).append("</div>\n");
+               outputBuffer.append("<div 
class=\"infobox-content\">").append(HTMLEncoder.encode(message)).append("</div>\n");
+               outputBuffer.append("</div>\n");
+               byte[] responseBytes = 
outputBuffer.toString().getBytes("utf-8");
+               context.sendReplyHeaders(200, "OK", new MultiValueTable(), 
"text/html; charset=utf-8", responseBytes.length);
+               context.writeData(responseBytes);
+       }
+       
+       private StringBuffer createAddBox() {
+               StringBuffer outputBuffer = new StringBuffer();
+               outputBuffer.append("<div class=\"infobox\">");
+               outputBuffer.append("<div class=\"infobox-header\">Add a 
URI</div>");
+               outputBuffer.append("<div class=\"infobox-content\"><form 
action=\"\" method=\"get\">");
+               outputBuffer.append("<input type=\"hidden\" name=\"action\" 
value=\"add\" />");
+               outputBuffer.append("<input type=\"text\" size=\"40\" 
name=\"key\" value=\"\" />");
+               outputBuffer.append("<input type=\"submit\" value=\"Add URI\" 
/>");
+               outputBuffer.append("</form></div>\n");
+               outputBuffer.append("</div>\n");
+               return outputBuffer;
+       }
+
+       private StringBuffer createNavbar(int running, int queued, int visited, 
int failed) {
+               StringBuffer outputBuffer = new StringBuffer();
+               outputBuffer.append("<div class=\"infobox navbar\">");
+               outputBuffer.append("<div class=\"infobox-content\"><ul>");
+               outputBuffer.append("<li><a href=\"#running\">Running 
(").append(running).append(")</a></li>");
+               outputBuffer.append("<li><a href=\"#queued\">Queued 
(").append(queued).append(")</a></li>");
+               outputBuffer.append("<li><a href=\"#visited\">Visited 
(").append(visited).append(")</a></li>");
+               outputBuffer.append("<li><a href=\"#failed\">Failed 
(").append(failed).append(")</a></li>");
+               outputBuffer.append("</ul></div>\n");
+               outputBuffer.append("</div>\n");
+               return outputBuffer;
+       }
+
+       private StringBuffer createList(String listName, String anchorName, 
Collection collection) {
+               StringBuffer outputBuffer = new StringBuffer();
+               outputBuffer.append("<a 
name=\"").append(HTMLEncoder.encode(anchorName)).append("\"></a>");
+               outputBuffer.append("<div class=\"infobox\">");
+               outputBuffer.append("<div 
class=\"infobox-header\">").append(HTMLEncoder.encode(listName)).append(" 
(").append(collection.size()).append(")</div>\n");
+               outputBuffer.append("<div class=\"infobox-content\">");
+               Iterator collectionItems = collection.iterator();
+               while (collectionItems.hasNext()) {
+                       FreenetURI uri = (FreenetURI) collectionItems.next();
+                       
outputBuffer.append(HTMLEncoder.encode(uri.toString())).append("<br/>\n");
+               }
+               outputBuffer.append("</div>\n");
+               outputBuffer.append("</div>\n");
+               return outputBuffer;
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#getPluginName()
+        */
+       public String getPluginName() {
+               return "The Definitive Spider";
+       }
+
+       /**
+        * @see 
freenet.plugin.Plugin#setPluginManager(freenet.plugin.PluginManager)
+        */
+       public void setPluginManager(PluginManager pluginManager) {
+               this.node = pluginManager.getNode();
+               this.ctx = node.makeClient((short) 0).getFetcherContext();
+               ctx.maxSplitfileBlockRetries = 10;
+               ctx.maxNonSplitfileRetries = 10;
+               ctx.maxTempLength = 2 * 1024 * 1024;
+               ctx.maxOutputLength = 2 * 1024 * 1024;
+               tProducedIndex = System.currentTimeMillis();
+       }
+
+
+       /**
+        * @see freenet.plugin.Plugin#startPlugin()
+        */
+       public void startPlugin() {
+               FreenetURI[] initialURIs = 
node.bookmarkManager.getBookmarkURIs();
+               for (int i = 0; i < initialURIs.length; i++)
+                       queueURI(initialURIs[i]);
+               stopped = false;
+               startSomeRequests();
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#stopPlugin()
+        */
+       public void stopPlugin() {
+               synchronized (this) {
+                       stopped = true;
+                       queuedURIList.clear();
+               }
+       }
+
 }


Property changes on: trunk/freenet/src/freenet/clients/http/Spider.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/Toadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -141,14 +141,14 @@
                ctx.writeData(buf, 0, buf.length);
        }

-       protected void writePermanentRedirect(ToadletContext ctx, String msg, 
String string) throws ToadletContextClosedException, IOException {
+       protected void writePermanentRedirect(ToadletContext ctx, String msg, 
String location) throws ToadletContextClosedException, IOException {
                MultiValueTable mvt = new MultiValueTable();
-               mvt.put("Location", string);
+               mvt.put("Location", location);
                if(msg == null) msg = "";
                else msg = HTMLEncoder.encode(msg);
                String redirDoc =
                        
"<html><head><title>"+msg+"</title></head><body><h1>Permanent redirect: "+
-                       msg+"</h1><a href=\""+string+"\">Click 
here</a></body></html>";
+                       msg+"</h1><a href=\""+location+"\">Click 
here</a></body></html>";
                byte[] buf;
                try {
                        buf = redirDoc.getBytes("UTF-8");


Property changes on: trunk/freenet/src/freenet/clients/http/Toadlet.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/ToadletContext.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-05-28 
22:27:14 UTC (rev 8906)
@@ -28,6 +28,18 @@
        void writeData(byte[] data, int offset, int length) throws 
ToadletContextClosedException, IOException;

        /**
+        * Convenience method that simply calls {@link #writeData(byte[], int, 
int)}.
+        * 
+        * @param data
+        *            The data to write
+        * @throws ToadletContextClosedException
+        *             if the context has already been closed
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       void writeData(byte[] data) throws ToadletContextClosedException, 
IOException;
+
+       /**
         * Write data from a bucket. You must send reply headers first.
         */
        void writeData(Bucket data) throws ToadletContextClosedException, 
IOException;


Property changes on: trunk/freenet/src/freenet/clients/http/ToadletContext.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-05-28 22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-05-28 22:27:14 UTC (rev 8906)
@@ -361,6 +361,10 @@
                sockOutputStream.write(data, offset, length);
        }

+       public void writeData(byte[] data) throws 
ToadletContextClosedException, IOException {
+               writeData(data, 0, data.length);
+       }
+       
        public void writeData(Bucket data) throws 
ToadletContextClosedException, IOException {
                if(closed) throw new ToadletContextClosedException();
                BucketTools.copyTo(data, sockOutputStream, Long.MAX_VALUE);


Property changes on: 
trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/clients/http/WelcomeToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/WelcomeToadlet.java  2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/clients/http/WelcomeToadlet.java  2006-05-28 
22:27:14 UTC (rev 8906)
@@ -13,7 +13,6 @@
 import freenet.config.SubConfig;
 import freenet.keys.FreenetURI;
 import freenet.node.Node;
-import freenet.node.NodeStarter;
 import freenet.node.Version;
 import freenet.node.useralerts.UserAlert;
 import freenet.support.Bucket;
@@ -21,12 +20,6 @@
 import freenet.support.Logger;

 public class WelcomeToadlet extends Toadlet {
-       private static final String[] DEFAULT_TESTNET_BOOKMARKS = {
-               "USK at 
60I8H8HinpgZSOuTSD66AVlIFAy-xsppFr0YCzCar7c,NzdivUGCGOdlgngOGRbbKDNfSCnjI0FXjHLzJM4xkJ4,AQABAAE/index/4/=INDEX.7-freesite"
-       };
-       private static final String[] DEFAULT_DARKNET_BOOKMARKS = {
-               "USK at 
PFeLTa1si2Ml5sDeUy7eDhPso6TPdmw-2gWfQ4Jg02w,3ocfrqgUMVWA2PeorZx40TW0c-FiIOL-TWKQHoDbVdE,AQABAAE/Index/-1/=Darknet
 Index"
-       };
        private final static int MODE_ADD = 1;
        private final static int MODE_EDIT = 2;
        Node node;
@@ -37,19 +30,7 @@
                super(client);
                this.node = n;
                this.config = sc;
-               this.bookmarks = new BookmarkManager(n);
-               node.bookmarkManager = bookmarks;
-               
-               sc.register("bookmarks", n.isTestnetEnabled() ? 
DEFAULT_TESTNET_BOOKMARKS : DEFAULT_DARKNET_BOOKMARKS, 0, false, "List of 
bookmarks", "A list of bookmarked freesites", this.bookmarks.makeCB());
-               
-               String[] initialbookmarks = sc.getStringArr("bookmarks");
-               for (int i = 0; i < initialbookmarks.length; i++) {
-                       try {
-                               bookmarks.addBookmark(new 
Bookmark(initialbookmarks[i]));
-                       } catch (MalformedURLException mue) {
-                               // just ignore that one
-                       }
-               }
+               this.bookmarks = node.bookmarkManager;
        }

        public void handlePost(URI uri, Bucket data, ToadletContext ctx) throws 
ToadletContextClosedException, IOException {
@@ -135,7 +116,6 @@
                } else if (request.isParameterSet("addbookmark")) {
                        try {
                                bookmarks.addBookmark(new 
Bookmark(request.getParam("key"), request.getParam("name")));
-                               node.config.store();
                        } catch (MalformedURLException mue) {
                                this.sendBookmarkEditPage(ctx, MODE_ADD, null, 
request.getParam("key"), request.getParam("name"), "Given key does not appear 
to be a valid Freenet key.");
                                return;
@@ -153,7 +133,6 @@

                                if 
(request.isParameterSet("delete_"+b.hashCode())) {
                                        bookmarks.removeBookmark(b);
-                                       node.config.store();
                                } else if 
(request.isParameterSet("edit_"+b.hashCode())) {
                                        this.sendBookmarkEditPage(ctx, b);
                                        return;
@@ -163,7 +142,6 @@
                                                Bookmark newbkmk = new 
Bookmark(request.getParam("key"), request.getParam("name"));
                                                bookmarks.removeBookmark(b);
                                                bookmarks.addBookmark(newbkmk);
-                                               node.config.store();
                                        } catch (MalformedURLException mue) {
                                                this.sendBookmarkEditPage(ctx, 
MODE_EDIT, b, request.getParam("key"), request.getParam("name"), "Given key 
does not appear to be a valid freenet key.");
                                                return;


Property changes on: trunk/freenet/src/freenet/clients/http/WelcomeToadlet.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-05-28 22:22:42 UTC (rev 
8905)
+++ trunk/freenet/src/freenet/node/Node.java    2006-05-28 22:27:14 UTC (rev 
8906)
@@ -578,6 +578,7 @@

     // Things that's needed to keep track of
     public final PluginManager pluginManager;
+    public freenet.plugin.PluginManager pluginManager2;

     // Client stuff that needs to be configged - FIXME
     static final int MAX_ARCHIVE_HANDLERS = 200; // don't take up much RAM... 
FIXME
@@ -1369,6 +1370,10 @@
                } catch (InvalidConfigValueException e) {
                        throw new NodeInitException(EXIT_COULD_NOT_START_FCP, 
"Could not start FCP: "+e);
                }
+               
+               bookmarkManager = new BookmarkManager(this);
+               pluginManager2 = new freenet.plugin.PluginManager(this);
+               

         // FProxy
         // FIXME this is a hack, the real way to do this is plugins
@@ -1380,8 +1385,7 @@
                } catch (InvalidConfigValueException e) {
                        throw new 
NodeInitException(EXIT_COULD_NOT_START_FPROXY, "Could not start FProxy: "+e);   
              
                }
-               
-        
+
         // Node Updater
                try{
                        nodeUpdater = NodeUpdater.maybeCreate(this, config);
@@ -1407,11 +1411,6 @@
                if(testnetHandler != null)
                        testnetHandler.start();

-               // Spider. FIXME.
-               
-               //if(testnetEnabled)
-               //      new Spider(bookmarkManager, this);
-               
         persistentTempBucketFactory.completedInit();

         redetectAddress();


Property changes on: trunk/freenet/src/freenet/node/Node.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/plugin/HttpPlugin.java
===================================================================
--- trunk/freenet/src/freenet/plugin/HttpPlugin.java    2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/plugin/HttpPlugin.java    2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -0,0 +1,45 @@
+package freenet.plugin;
+
+import java.io.IOException;
+
+import freenet.clients.http.HTTPRequest;
+import freenet.clients.http.ToadletContext;
+import freenet.clients.http.ToadletContextClosedException;
+
+/**
+ * Interface for plugins that support HTTP interaction.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public interface HttpPlugin extends Plugin {
+
+       /**
+        * Handles the GET request.
+        * 
+        * @param request
+        *            The request used to interact with this plugin
+        * @param context
+        *            The context of the HTTP request
+        * @throws IOException
+        *             if an I/O error occurs
+        * @throws ToadletContextClosedException
+        *             if the context has already been closed.
+        */
+       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException;
+
+       /**
+        * Handles the POST request.
+        * 
+        * @param request
+        *            The request used to interact with this plugin
+        * @param context
+        *            The context of the HTTP request
+        * @throws IOException
+        *             if an I/O error occurs
+        * @throws ToadletContextClosedException
+        *             if the context has already been closed.
+        */
+       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException;
+
+}


Property changes on: trunk/freenet/src/freenet/plugin/HttpPlugin.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/plugin/Plugin.java
===================================================================
--- trunk/freenet/src/freenet/plugin/Plugin.java        2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/plugin/Plugin.java        2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -0,0 +1,37 @@
+package freenet.plugin;
+
+/**
+ * Interface for Fred plugins.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public interface Plugin {
+
+       /**
+        * Returns the name of the plugin.
+        * 
+        * @return The name of the plugin
+        */
+       public String getPluginName();
+
+       /**
+        * Sets the plugin manager that manages this plugin.
+        * 
+        * @param pluginManager
+        *            The plugin manager
+        */
+       public void setPluginManager(PluginManager pluginManager);
+
+       /**
+        * Starts the plugin. If the plugin needs threads they have to be 
started
+        * here.
+        */
+       public void startPlugin();
+
+       /**
+        * Stops the plugin.
+        */
+       public void stopPlugin();
+
+}


Property changes on: trunk/freenet/src/freenet/plugin/Plugin.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/plugin/PluginManager.java
===================================================================
--- trunk/freenet/src/freenet/plugin/PluginManager.java 2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/plugin/PluginManager.java 2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -0,0 +1,221 @@
+package freenet.plugin;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import freenet.config.InvalidConfigValueException;
+import freenet.config.StringArrCallback;
+import freenet.config.StringArrOption;
+import freenet.config.SubConfig;
+import freenet.node.Node;
+import freenet.support.Logger;
+
+/**
+ * Manages plugins.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public class PluginManager {
+
+       /** Object used for synchronization. */
+       private final Object syncObject = new Object();
+
+       /** The node. */
+       private final Node node;
+
+       /** The configuration of this plugin manager. */
+       private final SubConfig config;
+
+       /** Currently loaded plugins. */
+       private List plugins = new ArrayList();
+
+       /**
+        * Creates a new plugin manager.
+        * 
+        * @param node
+        *            The node
+        */
+       public PluginManager(Node node) {
+               this.node = node;
+
+               config = new SubConfig("pluginmanager2", node.config);
+               config.register("loadedPlugins", null, 9, true, "Plugins to 
load on start up", "A list of plugins that are started when the node starts", 
new StringArrCallback() {
+
+                       /**
+                        * Returns the current value of this option.
+                        * 
+                        * @see freenet.config.StringArrCallback#get()
+                        * @return The current value of this option
+                        */
+                       public String get() {
+                               StringBuffer optionValue = new StringBuffer();
+                               synchronized (syncObject) {
+                                       Iterator pluginIterator = 
plugins.iterator();
+                                       while (pluginIterator.hasNext()) {
+                                               Plugin plugin = (Plugin) 
pluginIterator.next();
+                                               if (optionValue.length() != 0) {
+                                                       
optionValue.append(StringArrOption.delimiter);
+                                               }
+                                               
optionValue.append(StringArrOption.encode(plugin.getClass().getName()));
+                                       }
+                               }
+                               return optionValue.toString();
+                       };
+
+                       /**
+                        * Sets the new value of this option.
+                        * 
+                        * @see 
freenet.config.StringArrCallback#set(java.lang.String)
+                        * @param val
+                        *            The new value
+                        * @throws InvalidConfigValueException
+                        *             if setting the value is not allowed, or 
the new value
+                        *             is not valid
+                        */
+                       public void set(String val) throws 
InvalidConfigValueException {
+                               throw new InvalidConfigValueException("Start or 
stop plugins to change this value.");
+                       };
+               });
+
+               String[] loadedPluginNames = 
config.getStringArr("loadedPlugins");
+               if (loadedPluginNames != null) {
+                       for (int pluginIndex = 0, pluginCount = 
loadedPluginNames.length; pluginIndex < pluginCount; pluginIndex++) {
+                               String pluginName = 
StringArrOption.decode(loadedPluginNames[pluginIndex]);
+                               try {
+                                       addPlugin(pluginName);
+                               } catch (IllegalArgumentException iae1) {
+                               }
+                       }
+               }
+
+               config.finishedInitialization();
+       }
+
+       /**
+        * Returns the node that created this plugin manager.
+        * 
+        * @return The node that created this plugin manager
+        */
+       public Node getNode() {
+               return node;
+       }
+
+       /**
+        * Returns all currently loaded plugins. The array is returned in no
+        * particular order.
+        * 
+        * @return All currently loaded plugins
+        */
+       public Plugin[] getPlugins() {
+               synchronized (syncObject) {
+                       return (Plugin[]) plugins.toArray(new 
Plugin[plugins.size()]);
+               }
+       }
+
+       /**
+        * Adds a plugin to the plugin manager. The name can contain a URL for 
a jar
+        * file from which the plugin is then loaded. If it does the URL and the
+        * plugin name are separated by a '@', e.g.
+        * 'plugin.TestPlugin at http://www.example.com/test.jar'. URLs can 
contain
+        * every protocol your VM understands.
+        * 
+        * @see URL
+        * @param pluginName
+        *            The name of the plugin
+        */
+       public void addPlugin(String pluginName) throws 
IllegalArgumentException {
+               Plugin newPlugin = createPlugin(pluginName);
+               if (newPlugin == null) {
+                       throw new IllegalArgumentException();
+               }
+               newPlugin.setPluginManager(this);
+               newPlugin.startPlugin();
+               synchronized (syncObject) {
+                       plugins.add(newPlugin);
+               }
+               saveConfig();
+       }
+
+       /**
+        * Remoes the plugin from the list of running plugins. The plugin is 
stopped
+        * before removing it.
+        * 
+        * @param plugin
+        *            The plugin to remove
+        */
+       public void removePlugin(Plugin plugin) {
+               plugin.stopPlugin();
+               synchronized (syncObject) {
+                       plugins.remove(plugin);
+               }
+               saveConfig();
+       }
+
+       /**
+        * Saves the configuration.
+        */
+       private void saveConfig() {
+               config.config.store();
+       }
+
+       /**
+        * Creates a plugin from a name. The name can contain a URL for a jar 
file
+        * from which the plugin is then loaded. If it does the URL and the 
plugin
+        * name are separated by a '@', e.g.
+        * 'plugin.TestPlugin at http://www.example.com/test.jar'. URLs can 
contain
+        * every protocol your VM understands.
+        * <p>
+        * <b>WARNING:</b> The code to load JAR files from URLs has <b>not</b>
+        * been tested.
+        * 
+        * @see URL
+        * @param pluginName
+        *            The name of the plugin
+        * @return The created plugin, or <code>null</code> if the plugin could
+        *         not be created
+        */
+       private Plugin createPlugin(String pluginName) {
+               int p = pluginName.indexOf('@');
+               String pluginSource = null;
+
+               /* split up */
+               if (p > -1) {
+                       pluginSource = pluginName.substring(p + 1);
+                       pluginName = pluginName.substring(0, p);
+               }
+
+               /* load jar file */
+               ClassLoader classLoader = getClass().getClassLoader();
+               if (pluginSource != null) {
+                       try {
+                               URL pluginSourceUrl = new URL(pluginSource);
+                               classLoader = new URLClassLoader(new URL[] { 
pluginSourceUrl });
+                       } catch (MalformedURLException mue1) {
+                               Logger.normal(this, "could not create class 
loader", mue1);
+                               return null;
+                       }
+               }
+
+               /* load class from class loader */
+               try {
+                       Class pluginClass = classLoader.loadClass(pluginName);
+                       if (Plugin.class.isAssignableFrom(pluginClass)) {
+                               Plugin plugin = (Plugin) 
pluginClass.newInstance();
+                               return plugin;
+                       }
+               } catch (ClassNotFoundException e) {
+                       Logger.normal(this, "could not find plugin class: " + 
pluginName, e);
+               } catch (InstantiationException e) {
+                       Logger.normal(this, "could not instantiate plugin 
class: " + pluginName, e);
+               } catch (IllegalAccessException e) {
+                       Logger.normal(this, "could not instantiate plugin 
class: " + pluginName, e);
+               }
+               return null;
+       }
+
+}


Property changes on: trunk/freenet/src/freenet/plugin/PluginManager.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/plugin/TestHttpPlugin.java
===================================================================
--- trunk/freenet/src/freenet/plugin/TestHttpPlugin.java        2006-05-28 
22:22:42 UTC (rev 8905)
+++ trunk/freenet/src/freenet/plugin/TestHttpPlugin.java        2006-05-28 
22:27:14 UTC (rev 8906)
@@ -0,0 +1,62 @@
+/**
+ * 
+ */
+package freenet.plugin;
+
+import java.io.IOException;
+
+import freenet.clients.http.HTTPRequest;
+import freenet.clients.http.ToadletContext;
+import freenet.clients.http.ToadletContextClosedException;
+import freenet.support.MultiValueTable;
+
+/**
+ * Test HTTP plugin. Outputs "Plugin works" to the browser.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public class TestHttpPlugin implements HttpPlugin {
+
+       /**
+        * @throws ToadletContextClosedException
+        * @see 
freenet.plugin.HttpPlugin#handleGet(freenet.clients.http.HTTPRequest)
+        */
+       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
+               byte[] messageBytes = "Plugin works.".getBytes("UTF-8");
+               context.sendReplyHeaders(200, "OK", new MultiValueTable(), 
"text/html; charset=utf-8", messageBytes.length);
+               context.writeData(messageBytes, 0, messageBytes.length);
+       }
+
+       /**
+        * @see 
freenet.plugin.HttpPlugin#handlePost(freenet.clients.http.HTTPRequest)
+        */
+       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#getPluginName()
+        */
+       public String getPluginName() {
+               return "Simple HTTP Test Plugin";
+       }
+
+       /**
+        * @see 
freenet.plugin.Plugin#setPluginManager(freenet.plugin.PluginManager)
+        */
+       public void setPluginManager(PluginManager pluginManager) {
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#startPlugin()
+        */
+       public void startPlugin() {
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#stopPlugin()
+        */
+       public void stopPlugin() {
+       }
+
+}


Property changes on: trunk/freenet/src/freenet/plugin/TestHttpPlugin.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/freenet/src/freenet/plugin/TestPlugin.java
===================================================================
--- trunk/freenet/src/freenet/plugin/TestPlugin.java    2006-05-28 22:22:42 UTC 
(rev 8905)
+++ trunk/freenet/src/freenet/plugin/TestPlugin.java    2006-05-28 22:27:14 UTC 
(rev 8906)
@@ -0,0 +1,39 @@
+/**
+ * 
+ */
+package freenet.plugin;
+
+/**
+ * Test plugin. Does absolutely nothing.
+ * 
+ * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
+ * @version $Id$
+ */
+public class TestPlugin implements Plugin {
+
+       /**
+        * @see freenet.plugin.Plugin#getPluginName()
+        */
+       public String getPluginName() {
+               return "Simple Test Plugin";
+       }
+
+       /**
+        * @see 
freenet.plugin.Plugin#setPluginManager(freenet.plugin.PluginManager)
+        */
+       public void setPluginManager(PluginManager pluginManager) {
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#startPlugin()
+        */
+       public void startPlugin() {
+       }
+
+       /**
+        * @see freenet.plugin.Plugin#stopPlugin()
+        */
+       public void stopPlugin() {
+       }
+
+}


Property changes on: trunk/freenet/src/freenet/plugin/TestPlugin.java
___________________________________________________________________
Name: svn:keywords
   + Id


Reply via email to