Add and use memory-based post reply implementation.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 9 Feb 2013 02:36:15 +0000 (03:36 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 9 Feb 2013 02:36:15 +0000 (03:36 +0100)
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/database/Database.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/database/DatabaseException.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/database/PostDatabase.java
src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/database/PostReplyProvider.java
src/main/java/net/pterodactylus/sone/database/PostReplyStore.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java
src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java
src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/SonePlugin.java

index c684695..2d24690 100644 (file)
@@ -64,11 +64,11 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
 import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.database.Database;
+import net.pterodactylus.sone.database.DatabaseException;
 import net.pterodactylus.sone.database.PostBuilder;
-import net.pterodactylus.sone.database.PostDatabase;
 import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.PostReplyBuilder;
-import net.pterodactylus.sone.database.PostReplyBuilderFactory;
 import net.pterodactylus.sone.database.PostReplyProvider;
 import net.pterodactylus.sone.database.SoneProvider;
 import net.pterodactylus.sone.fcp.FcpInterface;
@@ -90,13 +90,10 @@ import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.service.AbstractService;
 import net.pterodactylus.util.thread.NamedThreadFactory;
 
-import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Ordering;
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
@@ -178,16 +175,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        private final Set<String> knownSones = new HashSet<String>();
 
        /** The post database. */
-       private final PostDatabase postDatabase;
-
-       /** The post reply builder factory. */
-       private final PostReplyBuilderFactory postReplyBuilderFactory;
-
-       /** All replies. */
-       private final Map<String, PostReply> replies = new HashMap<String, PostReply>();
-
-       /** All known replies. */
-       private final Set<String> knownReplies = new HashSet<String>();
+       private final Database database;
 
        /** All bookmarked posts. */
        /* synchronize access on itself. */
@@ -224,13 +212,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *            The WebOfTrust updater
         * @param eventBus
         *            The event bus
-        * @param postDatabase
-        *            The post database
-        * @param postReplyBuilderFactory
-        *            The post reply builder factory
+        * @param database
+        *            The database
         */
        @Inject
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, PostDatabase postDatabase, PostReplyBuilderFactory postReplyBuilderFactory) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -240,8 +226,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                this.updateChecker = new UpdateChecker(eventBus, freenetInterface);
                this.webOfTrustUpdater = webOfTrustUpdater;
                this.eventBus = eventBus;
-               this.postDatabase = postDatabase;
-               this.postReplyBuilderFactory = postReplyBuilderFactory;
+               this.database = database;
        }
 
        //
@@ -492,7 +477,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         * @return A new post builder
         */
        public PostBuilder postBuilder() {
-               return postDatabase.newPostBuilder();
+               return database.newPostBuilder();
        }
 
        /**
@@ -500,7 +485,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         */
        @Override
        public Optional<Post> getPost(String postId) {
-               return postDatabase.getPost(postId);
+               return database.getPost(postId);
        }
 
        /**
@@ -508,7 +493,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         */
        @Override
        public Collection<Post> getPosts(String soneId) {
-               return postDatabase.getPosts(soneId);
+               return database.getPosts(soneId);
        }
 
        /**
@@ -517,7 +502,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        @Override
        public Collection<Post> getDirectedPosts(final String recipientId) {
                checkNotNull(recipientId, "recipient must not be null");
-               return postDatabase.getDirectedPosts(recipientId);
+               return database.getDirectedPosts(recipientId);
        }
 
        /**
@@ -526,7 +511,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         * @return A new post reply builder
         */
        public PostReplyBuilder postReplyBuilder() {
-               return postReplyBuilderFactory.newPostReplyBuilder();
+               return database.newPostReplyBuilder();
        }
 
        /**
@@ -534,9 +519,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         */
        @Override
        public Optional<PostReply> getPostReply(String replyId) {
-               synchronized (replies) {
-                       return Optional.fromNullable(replies.get(replyId));
-               }
+               return database.getPostReply(replyId);
        }
 
        /**
@@ -544,19 +527,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         */
        @Override
        public List<PostReply> getReplies(final String postId) {
-               return Ordering.from(Reply.TIME_COMPARATOR).sortedCopy(FluentIterable.from(getSones()).transformAndConcat(new Function<Sone, Iterable<PostReply>>() {
-
-                       @Override
-                       public Iterable<PostReply> apply(Sone sone) {
-                               return sone.getReplies();
-                       }
-               }).filter(new Predicate<PostReply>() {
-
-                       @Override
-                       public boolean apply(PostReply reply) {
-                               return postId.equals(reply.getPostId());
-                       }
-               }));
+               return database.getReplies(postId);
        }
 
        /**
@@ -1017,7 +988,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                return;
                        }
                        /* find removed posts. */
-                       Collection<Post> existingPosts = postDatabase.getPosts(sone.getId());
+                       Collection<Post> existingPosts = database.getPosts(sone.getId());
                        for (Post oldPost : existingPosts) {
                                if (!sone.getPosts().contains(oldPost)) {
                                        eventBus.post(new PostRemovedEvent(oldPost));
@@ -1035,32 +1006,26 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                }
                        }
                        /* store posts. */
-                       postDatabase.storePosts(sone, sone.getPosts());
-                       synchronized (replies) {
-                               if (!soneRescueMode) {
-                                       for (PostReply reply : storedSone.get().getReplies()) {
-                                               replies.remove(reply.getId());
-                                               if (!sone.getReplies().contains(reply)) {
-                                                       eventBus.post(new PostReplyRemovedEvent(reply));
-                                               }
+                       database.storePosts(sone, sone.getPosts());
+                       if (!soneRescueMode) {
+                               for (PostReply reply : storedSone.get().getReplies()) {
+                                       if (!sone.getReplies().contains(reply)) {
+                                               eventBus.post(new PostReplyRemovedEvent(reply));
                                        }
                                }
-                               Set<PostReply> storedReplies = storedSone.get().getReplies();
-                               synchronized (knownReplies) {
-                                       for (PostReply reply : sone.getReplies()) {
-                                               reply.setKnown(knownReplies.contains(reply.getId()));
-                                               if (!storedReplies.contains(reply)) {
-                                                       if (reply.getTime() < getSoneFollowingTime(sone)) {
-                                                               knownReplies.add(reply.getId());
-                                                               reply.setKnown(true);
-                                                       } else if (!knownReplies.contains(reply.getId())) {
-                                                               eventBus.post(new NewPostReplyFoundEvent(reply));
-                                                       }
-                                               }
-                                               replies.put(reply.getId(), reply);
-                                       }
+                       }
+                       Set<PostReply> storedReplies = storedSone.get().getReplies();
+                       for (PostReply reply : sone.getReplies()) {
+                               if (storedReplies.contains(reply)) {
+                                       continue;
+                               }
+                               if (reply.getTime() < getSoneFollowingTime(sone)) {
+                                       reply.setKnown(true);
+                               } else if (!reply.isKnown()) {
+                                       eventBus.post(new NewPostReplyFoundEvent(reply));
                                }
                        }
+                       database.storePostReplies(sone, sone.getReplies());
                        synchronized (albums) {
                                synchronized (images) {
                                        for (Album album : storedSone.get().getAlbums()) {
@@ -1220,8 +1185,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                logger.log(Level.WARNING, "Invalid reply found, aborting load!");
                                return;
                        }
-                       PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder();
-                       postReplyBuilder.withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText);
+                       PostReplyBuilder postReplyBuilder = postReplyBuilder().withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText);
                        replies.add(postReplyBuilder.build());
                }
 
@@ -1349,19 +1313,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                knownSones.add(friend);
                        }
                }
-               postDatabase.storePosts(sone, posts);
+               database.storePosts(sone, posts);
                for (Post post : posts) {
                        post.setKnown(true);
                }
-               synchronized (this.replies) {
-                       for (PostReply postReply : replies) {
-                               this.replies.put(postReply.getId(), postReply);
-                       }
-               }
-               synchronized (knownReplies) {
-                       for (PostReply reply : replies) {
-                               knownReplies.add(reply.getId());
-                       }
+               database.storePostReplies(sone, replies);
+               for (PostReply reply : replies) {
+                       reply.setKnown(true);
                }
        }
 
@@ -1430,13 +1388,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
                        return null;
                }
-               PostBuilder postBuilder = postDatabase.newPostBuilder();
+               PostBuilder postBuilder = database.newPostBuilder();
                postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim());
                if (recipient.isPresent()) {
                        postBuilder.to(recipient.get().getId());
                }
                final Post post = postBuilder.build();
-               postDatabase.storePost(post);
+               database.storePost(post);
                eventBus.post(new NewPostFoundEvent(post));
                sone.addPost(post);
                touchConfiguration();
@@ -1464,7 +1422,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        logger.log(Level.WARNING, String.format("Tried to delete post of non-local Sone: %s", post.getSone()));
                        return;
                }
-               postDatabase.removePost(post);
+               database.removePost(post);
                eventBus.post(new PostRemovedEvent(post));
                markPostKnown(post);
                touchConfiguration();
@@ -1548,15 +1506,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone));
                        return null;
                }
-               PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder();
+               PostReplyBuilder postReplyBuilder = postReplyBuilder();
                postReplyBuilder.randomId().from(sone.getId()).to(post.getId()).currentTime().withText(text.trim());
                final PostReply reply = postReplyBuilder.build();
-               synchronized (replies) {
-                       replies.put(reply.getId(), reply);
-               }
-               synchronized (knownReplies) {
-                       eventBus.post(new NewPostReplyFoundEvent(reply));
-               }
+               database.storePostReply(reply);
+               eventBus.post(new NewPostReplyFoundEvent(reply));
                sone.addReply(reply);
                touchConfiguration();
                localElementTicker.schedule(new Runnable() {
@@ -1584,13 +1538,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply));
                        return;
                }
-               synchronized (replies) {
-                       replies.remove(reply.getId());
-               }
-               synchronized (knownReplies) {
-                       markReplyKnown(reply);
-                       knownReplies.remove(reply.getId());
-               }
+               database.removePostReply(reply);
+               markReplyKnown(reply);
                sone.removeReply(reply);
                touchConfiguration();
        }
@@ -1603,12 +1552,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *            The reply to mark as known
         */
        public void markReplyKnown(PostReply reply) {
+               boolean previouslyKnown = reply.isKnown();
                reply.setKnown(true);
-               synchronized (knownReplies) {
-                       eventBus.post(new MarkPostReplyKnownEvent(reply));
-                       if (knownReplies.add(reply.getId())) {
-                               touchConfiguration();
-                       }
+               eventBus.post(new MarkPostReplyKnownEvent(reply));
+               if (!previouslyKnown) {
+                       touchConfiguration();
                }
        }
 
@@ -1785,6 +1733,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                identityManager.start();
                webOfTrustUpdater.init();
                webOfTrustUpdater.start();
+               database.start();
        }
 
        /**
@@ -1819,6 +1768,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        }
                }
                saveConfiguration();
+               database.stop();
                webOfTrustUpdater.stop();
                updateChecker.stop();
                soneDownloader.stop();
@@ -2016,16 +1966,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        }
 
                        /* save known posts. */
-                       postDatabase.saveKnownPosts(configuration, "KnownPosts/");
-
-                       /* save known replies. */
-                       int replyCounter = 0;
-                       synchronized (knownReplies) {
-                               for (String knownReplyId : knownReplies) {
-                                       configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId);
-                               }
-                               configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null);
-                       }
+                       database.save();
 
                        /* save bookmarked posts. */
                        int bookmarkedPostCounter = 0;
@@ -2041,6 +1982,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
 
                } catch (ConfigurationException ce1) {
                        logger.log(Level.SEVERE, "Could not store configuration!", ce1);
+               } catch (DatabaseException de1) {
+                       logger.log(Level.SEVERE, "Could not save database!", de1);
                } finally {
                        synchronized (configuration) {
                                storingConfiguration = false;
@@ -2125,21 +2068,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        ++soneCounter;
                }
 
-               /* load known posts. */
-               postDatabase.loadKnownPosts(configuration, "KnownPosts/");
-
-               /* load known replies. */
-               int replyCounter = 0;
-               while (true) {
-                       String knownReplyId = configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").getValue(null);
-                       if (knownReplyId == null) {
-                               break;
-                       }
-                       synchronized (knownReplies) {
-                               knownReplies.add(knownReplyId);
-                       }
-               }
-
                /* load bookmarked posts. */
                int bookmarkedPostCounter = 0;
                while (true) {
@@ -2263,17 +2191,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        /* TODO - we don’t have the Sone anymore. should this happen? */
                        return;
                }
-               postDatabase.removePosts(sone.get());
+               database.removePosts(sone.get());
                for (Post post : sone.get().getPosts()) {
                        eventBus.post(new PostRemovedEvent(post));
                }
-               synchronized (replies) {
-                       synchronized (knownReplies) {
-                               for (PostReply reply : sone.get().getReplies()) {
-                                       replies.remove(reply.getId());
-                                       eventBus.post(new PostReplyRemovedEvent(reply));
-                               }
-                       }
+               database.removePostReplies(sone.get());
+               for (PostReply reply : sone.get().getReplies()) {
+                       eventBus.post(new PostReplyRemovedEvent(reply));
                }
                synchronized (sones) {
                        sones.remove(identity.getId());
diff --git a/src/main/java/net/pterodactylus/sone/database/Database.java b/src/main/java/net/pterodactylus/sone/database/Database.java
new file mode 100644 (file)
index 0000000..c93c061
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sone - Database.java - Copyright © 2013 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.database;
+
+import com.google.common.util.concurrent.Service;
+
+/**
+ * Database for Sone data. This interface combines the various provider, store,
+ * and builder factory interfaces into a single interface and adds some methods
+ * necessary for lifecycle management.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface Database extends Service, PostDatabase, PostReplyDatabase {
+
+       /**
+        * Saves the database.
+        *
+        * @throws DatabaseException
+        *             if an error occurs while saving
+        */
+       public void save() throws DatabaseException;
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/database/DatabaseException.java b/src/main/java/net/pterodactylus/sone/database/DatabaseException.java
new file mode 100644 (file)
index 0000000..43d3e77
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Sone - DatabaseException.java - Copyright © 2013 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.database;
+
+/**
+ * Exception that signals a database error.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DatabaseException extends Exception {
+
+       /**
+        * Creates a new database exception.
+        */
+       public DatabaseException() {
+               super();
+       }
+
+       /**
+        * Creates a new database exception.
+        *
+        * @param message
+        *            The message of the exception
+        */
+       public DatabaseException(String message) {
+               super(message);
+       }
+
+       /**
+        * Creates a new database exception.
+        *
+        * @param cause
+        *            The cause of the exception
+        */
+       public DatabaseException(Throwable cause) {
+               super(cause);
+       }
+
+       /**
+        * Creates a new database exception.
+        *
+        * @param message
+        *            The message of the exception
+        * @param cause
+        *            The cause of the exception
+        */
+       public DatabaseException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+}
index 1f9a2a8..40e6290 100644 (file)
@@ -17,9 +17,6 @@
 
 package net.pterodactylus.sone.database;
 
-import net.pterodactylus.util.config.Configuration;
-import net.pterodactylus.util.config.ConfigurationException;
-
 /**
  * Combines a {@link PostProvider}, a {@link PostBuilderFactory}, and a
  * {@link PostStore} into a complete post database.
@@ -28,31 +25,6 @@ import net.pterodactylus.util.config.ConfigurationException;
  */
 public interface PostDatabase extends PostProvider, PostBuilderFactory, PostStore {
 
-       /*
-        * these methods have to be here until the database knows how to save its
-        * own stuff. all the configuration-specific stuff will have to leave!
-        */
-
-       /**
-        * Loads the knows posts.
-        *
-        * @param configuration
-        *            The configuration to load the known posts from
-        * @param prefix
-        *            The prefix for the configuration keys
-        */
-       public void loadKnownPosts(Configuration configuration, String prefix);
-
-       /**
-        * Saves the knows posts.
-        *
-        * @param configuration
-        *            The configuration to save the known posts to
-        * @param prefix
-        *            The prefix for the configuration keys
-        * @throws ConfigurationException
-        *             if a value can not be stored in the configuration
-        */
-       public void saveKnownPosts(Configuration configuration, String prefix) throws ConfigurationException;
+       /* nothing here. */
 
 }
diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java b/src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java
new file mode 100644 (file)
index 0000000..c9a809f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sone - PostReplyDatabase.java - Copyright © 2013 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.database;
+
+/**
+ * Combines a {@link PostReplyProvider}, a {@link PostReplyBuilderFactory}, and
+ * a {@link PostReplyStore} into a complete post reply database.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface PostReplyDatabase extends PostReplyProvider, PostReplyBuilderFactory, PostReplyStore {
+
+       /* nothing here. */
+
+}
index 950012f..e186e5b 100644 (file)
@@ -19,7 +19,6 @@ package net.pterodactylus.sone.database;
 
 import java.util.List;
 
-import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.PostReply;
 
 import com.google.common.base.Optional;
diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyStore.java b/src/main/java/net/pterodactylus/sone/database/PostReplyStore.java
new file mode 100644 (file)
index 0000000..a3cefb3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Sone - PostReplyStore.java - Copyright © 2013 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.database;
+
+import java.util.Collection;
+
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
+
+/**
+ * Defines a store for {@link PostReply post replies}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface PostReplyStore {
+
+       /**
+        * Stores the given post reply.
+        *
+        * @param postReply
+        *            The post reply
+        */
+       public void storePostReply(PostReply postReply);
+
+       /**
+        * Stores the given post replies as exclusive collection of post replies for
+        * the given Sone. This will remove all other post replies from this Sone!
+        *
+        * @param sone
+        *            The Sone to store all post replies for
+        * @param postReplies
+        *            The post replies of the Sone
+        * @throws IllegalArgumentException
+        *             if one of the replies does not belong to the given Sone
+        */
+       public void storePostReplies(Sone sone, Collection<PostReply> postReplies) throws IllegalArgumentException;
+
+       /**
+        * Removes the given post reply from this store.
+        *
+        * @param postReply
+        *            The post reply to remove
+        */
+       public void removePostReply(PostReply postReply);
+
+       /**
+        * Removes all post replies of the given Sone.
+        *
+        * @param sone
+        *            The Sone to remove all post replies for
+        */
+       public void removePostReplies(Sone sone);
+
+}
index eed00ea..35d3c37 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Sone - MemoryPostDatabase.java - Copyright © 2013 David Roden
+ * Sone - MemoryDatabase.java - Copyright © 2013 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
@@ -19,26 +19,37 @@ package net.pterodactylus.sone.database.memory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.impl.AbstractPostBuilder;
+import net.pterodactylus.sone.data.impl.AbstractPostReplyBuilder;
+import net.pterodactylus.sone.database.Database;
+import net.pterodactylus.sone.database.DatabaseException;
 import net.pterodactylus.sone.database.PostBuilder;
 import net.pterodactylus.sone.database.PostDatabase;
+import net.pterodactylus.sone.database.PostReplyBuilder;
 import net.pterodactylus.sone.database.SoneProvider;
 import net.pterodactylus.util.config.Configuration;
 import net.pterodactylus.util.config.ConfigurationException;
 
 import com.google.common.base.Optional;
+import com.google.common.util.concurrent.AbstractService;
 import com.google.inject.Inject;
 
 /**
@@ -46,7 +57,7 @@ import com.google.inject.Inject;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class MemoryPostDatabase implements PostDatabase {
+public class MemoryDatabase extends AbstractService implements Database {
 
        /** The lock. */
        private final ReadWriteLock lock = new ReentrantReadWriteLock();
@@ -54,6 +65,9 @@ public class MemoryPostDatabase implements PostDatabase {
        /** The Sone provider. */
        private final SoneProvider soneProvider;
 
+       /** The configuration. */
+       private final Configuration configuration;
+
        /** All posts by their ID. */
        private final Map<String, Post> allPosts = new HashMap<String, Post>();
 
@@ -66,15 +80,70 @@ public class MemoryPostDatabase implements PostDatabase {
        /** Whether posts are known. */
        private final Set<String> knownPosts = new HashSet<String>();
 
+       /** All post replies by their ID. */
+       private final Map<String, PostReply> allPostReplies = new HashMap<String, PostReply>();
+
+       /** Replies by post. */
+       private final Map<String, SortedSet<PostReply>> postReplies = new HashMap<String, SortedSet<PostReply>>();
+
+       /** Whether post replies are known. */
+       private final Set<String> knownPostReplies = new HashSet<String>();
+
        /**
         * Creates a new memory database.
         *
         * @param soneProvider
         *            The Sone provider
+        * @param configuration
+        *            The configuration for loading and saving elements
         */
        @Inject
-       public MemoryPostDatabase(SoneProvider soneProvider) {
+       public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) {
                this.soneProvider = soneProvider;
+               this.configuration = configuration;
+       }
+
+       //
+       // DATABASE METHODS
+       //
+
+       /**
+        * Saves the database.
+        *
+        * @throws DatabaseException
+        *             if an error occurs while saving
+        */
+       @Override
+       public void save() throws DatabaseException {
+               saveKnownPosts();
+               saveKnownPostReplies();
+       }
+
+       //
+       // SERVICE METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       protected void doStart() {
+               loadKnownPosts();
+               loadKnownPostReplies();
+               notifyStarted();
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       protected void doStop() {
+               try {
+                       save();
+                       notifyStopped();
+               } catch (DatabaseException de1) {
+                       notifyFailed(de1);
+               }
        }
 
        //
@@ -228,23 +297,98 @@ public class MemoryPostDatabase implements PostDatabase {
        }
 
        //
-       // POSTDATABASE METHODS
+       // POSTREPLYPROVIDER METHODS
        //
 
        /**
         * {@inheritDocs}
         */
        @Override
-       public void loadKnownPosts(Configuration configuration, String prefix) {
+       public Optional<PostReply> getPostReply(String id) {
+               lock.readLock().lock();
+               try {
+                       return Optional.fromNullable(allPostReplies.get(id));
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public List<PostReply> getReplies(String postId) {
+               lock.readLock().lock();
+               try {
+                       if (!postReplies.containsKey(postId)) {
+                               return Collections.emptyList();
+                       }
+                       return new ArrayList<PostReply>(postReplies.get(postId));
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
+
+       //
+       // POSTREPLYBUILDERFACTORY METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public PostReplyBuilder newPostReplyBuilder() {
+               return new MemoryPostReplyBuilder();
+       }
+
+       //
+       // POSTREPLYSTORE METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public void storePostReply(PostReply postReply) {
                lock.writeLock().lock();
                try {
-                       int postCounter = 0;
-                       while (true) {
-                               String knownPostId = configuration.getStringValue(prefix + postCounter++ + "/ID").getValue(null);
-                               if (knownPostId == null) {
-                                       break;
+                       allPostReplies.put(postReply.getId(), postReply);
+                       if (postReplies.containsKey(postReply.getPostId())) {
+                               postReplies.get(postReply.getPostId()).add(postReply);
+                       } else {
+                               TreeSet<PostReply> replies = new TreeSet<PostReply>(Reply.TIME_COMPARATOR);
+                               replies.add(postReply);
+                               postReplies.put(postReply.getPostId(), replies);
+                       }
+               } finally {
+                       lock.writeLock().unlock();
+               }
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public void storePostReplies(Sone sone, Collection<PostReply> postReplies) {
+               checkNotNull(sone, "sone must not be null");
+               /* verify that all posts are from the same Sone. */
+               for (PostReply postReply : postReplies) {
+                       if (!sone.equals(postReply.getSone())) {
+                               throw new IllegalArgumentException(String.format("PostReply from different Sone found: %s", postReply));
+                       }
+               }
+
+               lock.writeLock().lock();
+               try {
+                       for (PostReply postReply : postReplies) {
+                               allPostReplies.put(postReply.getId(), postReply);
+                               if (this.postReplies.containsKey(postReply.getPostId())) {
+                                       this.postReplies.get(postReply.getPostId()).add(postReply);
+                               } else {
+                                       TreeSet<PostReply> replies = new TreeSet<PostReply>(Reply.TIME_COMPARATOR);
+                                       replies.add(postReply);
+                                       this.postReplies.put(postReply.getPostId(), replies);
                                }
-                               knownPosts.add(knownPostId);
                        }
                } finally {
                        lock.writeLock().unlock();
@@ -255,16 +399,35 @@ public class MemoryPostDatabase implements PostDatabase {
         * {@inheritDocs}
         */
        @Override
-       public void saveKnownPosts(Configuration configuration, String prefix) throws ConfigurationException {
-               lock.readLock().lock();
+       public void removePostReply(PostReply postReply) {
+               lock.writeLock().lock();
                try {
-                       int postCounter = 0;
-                       for (String knownPostId : knownPosts) {
-                               configuration.getStringValue(prefix + postCounter++ + "/ID").setValue(knownPostId);
+                       allPostReplies.remove(postReply.getId());
+                       if (postReplies.containsKey(postReply.getPostId())) {
+                               postReplies.get(postReply.getPostId()).remove(postReply);
+                               if (postReplies.get(postReply.getPostId()).isEmpty()) {
+                                       postReplies.remove(postReply.getPostId());
+                               }
                        }
-                       configuration.getStringValue(prefix + postCounter + "/ID").setValue(null);
                } finally {
-                       lock.readLock().unlock();
+                       lock.writeLock().unlock();
+               }
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public void removePostReplies(Sone sone) {
+               checkNotNull(sone, "sone must not be null");
+
+               lock.writeLock().lock();
+               try {
+                       for (PostReply postReply : sone.getReplies()) {
+                               removePostReply(postReply);
+                       }
+               } finally {
+                       lock.writeLock().unlock();
                }
        }
 
@@ -309,6 +472,45 @@ public class MemoryPostDatabase implements PostDatabase {
                }
        }
 
+       /**
+        * Returns whether the given post reply is known.
+        *
+        * @param postReply
+        *            The post reply
+        * @return {@code true} if the given post reply is known, {@code false}
+        *         otherwise
+        */
+       boolean isPostReplyKnown(PostReply postReply) {
+               lock.readLock().lock();
+               try {
+                       return knownPostReplies.contains(postReply.getId());
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
+
+       /**
+        * Sets whether the given post reply is known.
+        *
+        * @param postReply
+        *            The post reply
+        * @param known
+        *            {@code true} if the post reply is known, {@code false}
+        *            otherwise
+        */
+       void setPostReplyKnown(PostReply postReply, boolean known) {
+               lock.writeLock().lock();
+               try {
+                       if (known) {
+                               knownPostReplies.add(postReply.getId());
+                       } else {
+                               knownPostReplies.remove(postReply.getId());
+                       }
+               } finally {
+                       lock.writeLock().unlock();
+               }
+       }
+
        //
        // PRIVATE METHODS
        //
@@ -376,6 +578,86 @@ public class MemoryPostDatabase implements PostDatabase {
        }
 
        /**
+        * Loads the known posts.
+        */
+       private void loadKnownPosts() {
+               lock.writeLock().lock();
+               try {
+                       int postCounter = 0;
+                       while (true) {
+                               String knownPostId = configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").getValue(null);
+                               if (knownPostId == null) {
+                                       break;
+                               }
+                               knownPosts.add(knownPostId);
+                       }
+               } finally {
+                       lock.writeLock().unlock();
+               }
+       }
+
+       /**
+        * Saves the known posts to the configuration.
+        *
+        * @throws DatabaseException
+        *             if a configuration error occurs
+        */
+       private void saveKnownPosts() throws DatabaseException {
+               lock.readLock().lock();
+               try {
+                       int postCounter = 0;
+                       for (String knownPostId : knownPosts) {
+                               configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(knownPostId);
+                       }
+                       configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null);
+               } catch (ConfigurationException ce1) {
+                       throw new DatabaseException("Could not save database.", ce1);
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
+
+       /**
+        * Loads the known post replies.
+        */
+       private void loadKnownPostReplies() {
+               lock.writeLock().lock();
+               try {
+                       int replyCounter = 0;
+                       while (true) {
+                               String knownReplyId = configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").getValue(null);
+                               if (knownReplyId == null) {
+                                       break;
+                               }
+                               knownPostReplies.add(knownReplyId);
+                       }
+               } finally {
+                       lock.writeLock().unlock();
+               }
+       }
+
+       /**
+        * Saves the known post replies to the configuration.
+        *
+        * @throws DatabaseException
+        *             if a configuration error occurs
+        */
+       private void saveKnownPostReplies() throws DatabaseException {
+               lock.readLock().lock();
+               try {
+                       int replyCounter = 0;
+                       for (String knownReplyId : knownPostReplies) {
+                               configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId);
+                       }
+                       configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null);
+               } catch (ConfigurationException ce1) {
+                       throw new DatabaseException("Could not save database.", ce1);
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
+
+       /**
         * {@link PostBuilder} implementation that creates a {@link MemoryPost}.
         *
         * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
@@ -398,11 +680,33 @@ public class MemoryPostDatabase implements PostDatabase {
                @Override
                public Post build() throws IllegalStateException {
                        validate();
-                       Post post = new MemoryPost(MemoryPostDatabase.this, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text);
+                       Post post = new MemoryPost(MemoryDatabase.this, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text);
                        post.setKnown(isPostKnown(post));
                        return post;
                }
 
        }
 
+       /**
+        * {@link PostReplyBuilder} implementation that creates
+        * {@link MemoryPostReply} objects.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       private class MemoryPostReplyBuilder extends AbstractPostReplyBuilder {
+
+               /**
+                * {@inheritDocs}
+                */
+               @Override
+               public PostReply build() throws IllegalStateException {
+                       validate();
+
+                       PostReply postReply = new MemoryPostReply(MemoryDatabase.this, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId);
+                       postReply.setKnown(isPostReplyKnown(postReply));
+                       return postReply;
+               }
+
+       }
+
 }
index 84f3714..22fa7e6 100644 (file)
@@ -34,7 +34,7 @@ import com.google.common.base.Optional;
 class MemoryPost implements Post {
 
        /** The post database. */
-       private final MemoryPostDatabase postDatabase;
+       private final MemoryDatabase postDatabase;
 
        /** The Sone provider. */
        private final SoneProvider soneProvider;
@@ -72,7 +72,7 @@ class MemoryPost implements Post {
         * @param text
         *            The text of the post
         */
-       public MemoryPost(MemoryPostDatabase postDatabase, SoneProvider soneProvider, String id, String soneId, String recipientId, long time, String text) {
+       public MemoryPost(MemoryDatabase postDatabase, SoneProvider soneProvider, String id, String soneId, String recipientId, long time, String text) {
                this.postDatabase = postDatabase;
                this.soneProvider = soneProvider;
                this.id = UUID.fromString(id);
diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java
new file mode 100644 (file)
index 0000000..a6686ca
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Sone - MemoryPostReply.java - Copyright © 2013 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.database.memory;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.database.SoneProvider;
+
+import com.google.common.base.Optional;
+
+/**
+ * Memory-based {@link PostReply} implementation.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+class MemoryPostReply implements PostReply {
+
+       /** The database. */
+       private final MemoryDatabase database;
+
+       /** The Sone provider. */
+       private final SoneProvider soneProvider;
+
+       /** The ID of the post reply. */
+       private final String id;
+
+       /** The ID of the owning Sone. */
+       private final String soneId;
+
+       /** The time of the post reply. */
+       private final long time;
+
+       /** The text of the post reply. */
+       private final String text;
+
+       /** The ID of the post this post reply refers to. */
+       private final String postId;
+
+       /**
+        * Creates a new memory-based {@link PostReply} implementation.
+        *
+        * @param database
+        *            The database
+        * @param soneProvider
+        *            The Sone provider
+        * @param id
+        *            The ID of the post reply
+        * @param soneId
+        *            The ID of the owning Sone
+        * @param time
+        *            The time of the post reply
+        * @param text
+        *            The text of the post reply
+        * @param postId
+        *            The ID of the post this post reply refers to
+        */
+       public MemoryPostReply(MemoryDatabase database, SoneProvider soneProvider, String id, String soneId, long time, String text, String postId) {
+               this.database = database;
+               this.soneProvider = soneProvider;
+               this.id = id;
+               this.soneId = soneId;
+               this.time = time;
+               this.text = text;
+               this.postId = postId;
+       }
+
+       //
+       // REPLY METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public String getId() {
+               return id;
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public Sone getSone() {
+               return soneProvider.getSone(soneId).get();
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public long getTime() {
+               return time;
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public String getText() {
+               return text;
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public boolean isKnown() {
+               return database.isPostReplyKnown(this);
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public PostReply setKnown(boolean known) {
+               database.setPostReplyKnown(this, known);
+               return this;
+       }
+
+       //
+       // POSTREPLY METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public String getPostId() {
+               return postId;
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public Optional<Post> getPost() {
+               return database.getPost(postId);
+       }
+
+       //
+       // OBJECT METHODS
+       //
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public int hashCode() {
+               return id.hashCode();
+       }
+
+       /**
+        * {@inheritDocs}
+        */
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof MemoryPostReply)) {
+                       return false;
+               }
+               MemoryPostReply memoryPostReply = (MemoryPostReply) object;
+               return memoryPostReply.id.equals(id);
+       }
+
+}
index f389da1..26e8bf2 100644 (file)
@@ -25,14 +25,12 @@ import java.util.logging.Logger;
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.core.FreenetInterface;
 import net.pterodactylus.sone.core.WebOfTrustUpdater;
-import net.pterodactylus.sone.data.impl.DefaultPostBuilderFactory;
-import net.pterodactylus.sone.data.impl.DefaultPostReplyBuilderFactory;
+import net.pterodactylus.sone.database.Database;
 import net.pterodactylus.sone.database.PostBuilderFactory;
-import net.pterodactylus.sone.database.PostDatabase;
 import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.PostReplyBuilderFactory;
 import net.pterodactylus.sone.database.SoneProvider;
-import net.pterodactylus.sone.database.memory.MemoryPostDatabase;
+import net.pterodactylus.sone.database.memory.MemoryDatabase;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
 import net.pterodactylus.sone.freenet.plugin.PluginConnector;
@@ -216,7 +214,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                        @Override
                        protected void configure() {
                                bind(Core.class).in(Singleton.class);
-                               bind(MemoryPostDatabase.class).in(Singleton.class);
+                               bind(MemoryDatabase.class).in(Singleton.class);
                                bind(EventBus.class).toInstance(eventBus);
                                bind(Configuration.class).toInstance(startConfiguration);
                                bind(FreenetInterface.class).in(Singleton.class);
@@ -227,11 +225,11 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                                bind(String.class).annotatedWith(Names.named("WebOfTrustContext")).toInstance("Sone");
                                bind(SonePlugin.class).toInstance(SonePlugin.this);
                                bind(FcpInterface.class).in(Singleton.class);
-                               bind(PostDatabase.class).to(MemoryPostDatabase.class);
-                               bind(PostBuilderFactory.class).to(MemoryPostDatabase.class);
-                               bind(PostReplyBuilderFactory.class).to(DefaultPostReplyBuilderFactory.class).in(Singleton.class);
+                               bind(Database.class).to(MemoryDatabase.class);
+                               bind(PostBuilderFactory.class).to(MemoryDatabase.class);
+                               bind(PostReplyBuilderFactory.class).to(MemoryDatabase.class);
                                bind(SoneProvider.class).to(Core.class).in(Singleton.class);
-                               bind(PostProvider.class).to(MemoryPostDatabase.class);
+                               bind(PostProvider.class).to(MemoryDatabase.class);
                                bindListener(Matchers.any(), new TypeListener() {
 
                                        @Override