Add Sone downloader.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 14 Oct 2010 19:26:10 +0000 (21:26 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 14 Oct 2010 19:26:10 +0000 (21:26 +0200)
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/FreenetInterface.java
src/main/java/net/pterodactylus/sone/core/SoneDownloader.java [new file with mode: 0644]

index dfa94d6..3c3a659 100644 (file)
@@ -59,6 +59,9 @@ public class Core extends AbstractService {
        /** Interface to freenet. */
        private FreenetInterface freenetInterface;
 
+       /** The Sone downloader. */
+       private SoneDownloader soneDownloader;
+
        /** The local Sones. */
        private final Set<Sone> localSones = new HashSet<Sone>();
 
@@ -108,6 +111,7 @@ public class Core extends AbstractService {
         */
        public Core freenetInterface(FreenetInterface freenetInterface) {
                this.freenetInterface = freenetInterface;
+               soneDownloader = new SoneDownloader(freenetInterface);
                return this;
        }
 
@@ -150,6 +154,7 @@ public class Core extends AbstractService {
                        soneCache.put(sone.getId(), sone);
                        SoneInserter soneInserter = new SoneInserter(freenetInterface, sone);
                        soneInserter.start();
+                       soneDownloader.addSone(sone);
                        soneInserters.put(sone, soneInserter);
                }
        }
@@ -241,6 +246,7 @@ public class Core extends AbstractService {
         */
        @Override
        protected void serviceStop() {
+               soneDownloader.stop();
                /* stop all Sone inserters. */
                for (SoneInserter soneInserter : soneInserters.values()) {
                        soneInserter.stop();
index 05b1fba..dae6f68 100644 (file)
 
 package net.pterodactylus.sone.core;
 
+import java.net.MalformedURLException;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.service.AbstractService;
+
+import com.db4o.ObjectContainer;
+
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
 import freenet.client.HighLevelSimpleClient;
+import freenet.client.HighLevelSimpleClientImpl;
 import freenet.client.InsertException;
+import freenet.client.async.ClientContext;
+import freenet.client.async.USKCallback;
 import freenet.keys.FreenetURI;
+import freenet.keys.USK;
 import freenet.node.Node;
+import freenet.node.RequestStarter;
 
 /**
  * Contains all necessary functionality for interacting with the Freenet node.
@@ -41,12 +52,14 @@ public class FreenetInterface extends AbstractService {
        private static final Logger logger = Logging.getLogger(FreenetInterface.class);
 
        /** The node to interact with. */
-       @SuppressWarnings("unused")
        private final Node node;
 
        /** The high-level client to use for requests. */
        private final HighLevelSimpleClient client;
 
+       /** The USK callbacks. */
+       private final Map<String, USKCallback> soneUskCallbacks = new HashMap<String, USKCallback>();
+
        /**
         * Creates a new Freenet interface.
         *
@@ -116,4 +129,63 @@ public class FreenetInterface extends AbstractService {
                }
        }
 
+       /**
+        * Registers the USK for the given Sone and notifies the given
+        * {@link SoneDownloader} if an update was found.
+        *
+        * @param sone
+        *            The Sone to watch
+        * @param soneDownloader
+        *            The Sone download to notify on updates
+        */
+       public void registerUsk(final Sone sone, final SoneDownloader soneDownloader) {
+               try {
+                       logger.log(Level.FINE, "Registering Sone “%s” for USK updates at %s…", new Object[] { sone, sone.getRequestUri().setMetaString(new String[] { "sone.xml" }) });
+                       USKCallback uskCallback = new USKCallback() {
+
+                               @Override
+                               @SuppressWarnings("synthetic-access")
+                               public void onFoundEdition(long edition, USK key, ObjectContainer objectContainer, ClientContext clientContext, boolean metadata, short codec, byte[] data, boolean newKnownGood, boolean newSlotToo) {
+                                       logger.log(Level.FINE, "Found USK update for Sone “%s” at %s.", new Object[] { sone, key });
+                                       if (newKnownGood) {
+                                               sone.updateUris(key.getURI());
+                                       }
+                                       soneDownloader.fetchSone(sone);
+                               }
+
+                               @Override
+                               public short getPollingPriorityProgress() {
+                                       return RequestStarter.INTERACTIVE_PRIORITY_CLASS;
+                               }
+
+                               @Override
+                               public short getPollingPriorityNormal() {
+                                       return RequestStarter.INTERACTIVE_PRIORITY_CLASS;
+                               }
+                       };
+                       soneUskCallbacks.put(sone.getId(), uskCallback);
+                       node.clientCore.uskManager.subscribe(USK.create(sone.getRequestUri()), uskCallback, true, (HighLevelSimpleClientImpl) client);
+               } catch (MalformedURLException mue1) {
+                       logger.log(Level.WARNING, "Could not subscribe USK “" + sone.getRequestUri() + "”!", mue1);
+               }
+       }
+
+       /**
+        * Unsubscribes the request URI of the given Sone.
+        *
+        * @param sone
+        *            The Sone to unregister
+        */
+       public void unregisterUsk(Sone sone) {
+               USKCallback uskCallback = soneUskCallbacks.remove(sone.getId());
+               if (uskCallback == null) {
+                       return;
+               }
+               try {
+                       node.clientCore.uskManager.unsubscribe(USK.create(sone.getRequestUri()), uskCallback);
+               } catch (MalformedURLException mue1) {
+                       logger.log(Level.FINE, "Could not unsubscribe USK “" + sone.getRequestUri() + "”!", mue1);
+               }
+       }
+
 }
diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java
new file mode 100644 (file)
index 0000000..1a8f870
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Sone - SoneDownloader.java - Copyright © 2010 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.service.AbstractService;
+import freenet.client.FetchResult;
+
+/**
+ * The Sone downloader is responsible for download Sones as they are updated.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SoneDownloader extends AbstractService {
+
+       /** The logger. */
+       private static final Logger logger = Logging.getLogger(SoneDownloader.class);
+
+       /** The Freenet interface. */
+       private final FreenetInterface freenetInterface;
+
+       /** The sones to update. */
+       private final Set<Sone> sones = new HashSet<Sone>();
+
+       /**
+        * Creates a new Sone downloader.
+        *
+        * @param freenetInterface
+        *            The Freenet interface
+        */
+       public SoneDownloader(FreenetInterface freenetInterface) {
+               super("Sone Downloader");
+               this.freenetInterface = freenetInterface;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Adds the given Sone to the set of Sones that will be watched for updates.
+        *
+        * @param sone
+        *            The Sone to add
+        */
+       public void addSone(Sone sone) {
+               if (sones.add(sone)) {
+                       freenetInterface.registerUsk(sone, this);
+               }
+       }
+
+       /**
+        * Fetches the updated Sone. This method is a callback method for
+        * {@link FreenetInterface#registerUsk(Sone, SoneDownloader)}.
+        *
+        * @param sone
+        *            The Sone to fetch
+        */
+       public void fetchSone(Sone sone) {
+               logger.log(Level.FINE, "Starting fetch for Sone “%s” from %s…", new Object[] { sone, sone.getRequestUri().setMetaString(new String[] { "sone.xml" }) });
+               FetchResult fetchResult = freenetInterface.fetchUri(sone.getRequestUri().setMetaString(new String[] { "sone.xml" }));
+               logger.log(Level.FINEST, "Got %d bytes back.", fetchResult.size());
+       }
+
+       //
+       // SERVICE METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void serviceStop() {
+               for (Sone sone : sones) {
+                       freenetInterface.unregisterUsk(sone);
+               }
+       }
+
+}