X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdatabase%2Fmemory%2FMemoryDatabase.java;h=c611eea01d4b7c24650ed65983ab5d532456a8e6;hp=67ea6710798633be7af108f0ddda902216822894;hb=d0170947a5968260c73b9b985466ede0f7c12336;hpb=6c45481d2b225a7614bf7624cdb3e9fbddae1a94 diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java index 67ea671..c611eea 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -19,11 +19,15 @@ package net.pterodactylus.sone.database.memory; import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.FluentIterable.from; +import static java.util.Collections.unmodifiableCollection; import static net.pterodactylus.sone.data.Reply.TIME_COMPARATOR; +import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; +import static net.pterodactylus.sone.data.Sone.toAllAlbums; +import static net.pterodactylus.sone.data.Sone.toAllImages; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -47,23 +51,27 @@ import net.pterodactylus.sone.database.ImageBuilder; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostDatabase; import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneBuilder; 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.base.Predicate; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.SortedSetMultimap; import com.google.common.collect.TreeMultimap; import com.google.common.util.concurrent.AbstractService; import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Memory-based {@link PostDatabase} implementation. * * @author David ‘Bombe’ Roden */ +@Singleton public class MemoryDatabase extends AbstractService implements Database { /** The lock. */ @@ -74,6 +82,9 @@ public class MemoryDatabase extends AbstractService implements Database { /** The configuration. */ private final Configuration configuration; + private final ConfigurationLoader configurationLoader; + + private final Map allSones = new HashMap(); /** All posts by their ID. */ private final Map allPosts = new HashMap(); @@ -81,9 +92,6 @@ public class MemoryDatabase extends AbstractService implements Database { /** All posts by their Sones. */ private final Multimap sonePosts = HashMultimap.create(); - /** All posts by their recipient. */ - private final Multimap recipientPosts = HashMultimap.create(); - /** Whether posts are known. */ private final Set knownPosts = new HashSet(); @@ -99,21 +107,16 @@ public class MemoryDatabase extends AbstractService implements Database { } }, TIME_COMPARATOR); - /** Replies by post. */ - private final SortedSetMultimap postReplies = TreeMultimap.create(new Comparator() { - - @Override - public int compare(String leftString, String rightString) { - return leftString.compareTo(rightString); - } - }, TIME_COMPARATOR); - /** Whether post replies are known. */ private final Set knownPostReplies = new HashSet(); private final Map allAlbums = new HashMap(); + private final Multimap soneAlbums = HashMultimap.create(); private final Map allImages = new HashMap(); + private final Multimap soneImages = HashMultimap.create(); + + private final MemoryBookmarkDatabase memoryBookmarkDatabase; /** * Creates a new memory database. @@ -127,6 +130,9 @@ public class MemoryDatabase extends AbstractService implements Database { public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) { this.soneProvider = soneProvider; this.configuration = configuration; + this.configurationLoader = new ConfigurationLoader(configuration); + memoryBookmarkDatabase = + new MemoryBookmarkDatabase(this, configurationLoader); } // @@ -152,6 +158,7 @@ public class MemoryDatabase extends AbstractService implements Database { /** {@inheritDocs} */ @Override protected void doStart() { + memoryBookmarkDatabase.start(); loadKnownPosts(); loadKnownPostReplies(); notifyStarted(); @@ -161,6 +168,7 @@ public class MemoryDatabase extends AbstractService implements Database { @Override protected void doStop() { try { + memoryBookmarkDatabase.stop(); save(); notifyStopped(); } catch (DatabaseException de1) { @@ -168,6 +176,109 @@ public class MemoryDatabase extends AbstractService implements Database { } } + @Override + public SoneBuilder newSoneBuilder() { + return new MemorySoneBuilder(); + } + + @Override + public void storeSone(Sone sone) { + lock.writeLock().lock(); + try { + removeSone(sone); + + allSones.put(sone.getId(), sone); + sonePosts.putAll(sone.getId(), sone.getPosts()); + for (Post post : sone.getPosts()) { + allPosts.put(post.getId(), post); + } + sonePostReplies.putAll(sone.getId(), sone.getReplies()); + for (PostReply postReply : sone.getReplies()) { + allPostReplies.put(postReply.getId(), postReply); + } + soneAlbums.putAll(sone.getId(), toAllAlbums.apply(sone)); + for (Album album : toAllAlbums.apply(sone)) { + allAlbums.put(album.getId(), album); + } + soneImages.putAll(sone.getId(), toAllImages.apply(sone)); + for (Image image : toAllImages.apply(sone)) { + allImages.put(image.getId(), image); + } + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void removeSone(Sone sone) { + lock.writeLock().lock(); + try { + allSones.remove(sone.getId()); + Collection removedPosts = sonePosts.removeAll(sone.getId()); + for (Post removedPost : removedPosts) { + allPosts.remove(removedPost.getId()); + } + Collection removedPostReplies = + sonePostReplies.removeAll(sone.getId()); + for (PostReply removedPostReply : removedPostReplies) { + allPostReplies.remove(removedPostReply.getId()); + } + Collection removedAlbums = + soneAlbums.removeAll(sone.getId()); + for (Album removedAlbum : removedAlbums) { + allAlbums.remove(removedAlbum.getId()); + } + Collection removedImages = + soneImages.removeAll(sone.getId()); + for (Image removedImage : removedImages) { + allImages.remove(removedImage.getId()); + } + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public Optional getSone(String soneId) { + lock.readLock().lock(); + try { + return fromNullable(allSones.get(soneId)); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getSones() { + lock.readLock().lock(); + try { + return new HashSet(allSones.values()); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getLocalSones() { + lock.readLock().lock(); + try { + return from(allSones.values()).filter(LOCAL_SONE_FILTER).toSet(); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getRemoteSones() { + lock.readLock().lock(); + try { + return from(allSones.values()) + .filter(not(LOCAL_SONE_FILTER)) .toSet(); + } finally { + lock.readLock().unlock(); + } + } + // // POSTPROVIDER METHODS // @@ -191,11 +302,15 @@ public class MemoryDatabase extends AbstractService implements Database { /** {@inheritDocs} */ @Override - public Collection getDirectedPosts(String recipientId) { + public Collection getDirectedPosts(final String recipientId) { lock.readLock().lock(); try { - Collection posts = recipientPosts.get(recipientId); - return (posts == null) ? Collections.emptySet() : new HashSet(posts); + return from(sonePosts.values()).filter(new Predicate() { + @Override + public boolean apply(Post post) { + return post.getRecipientId().asSet().contains(recipientId); + } + }).toSet(); } finally { lock.readLock().unlock(); } @@ -223,9 +338,6 @@ public class MemoryDatabase extends AbstractService implements Database { try { allPosts.put(post.getId(), post); getPostsFrom(post.getSone().getId()).add(post); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).add(post); - } } finally { lock.writeLock().unlock(); } @@ -239,69 +351,12 @@ public class MemoryDatabase extends AbstractService implements Database { try { allPosts.remove(post.getId()); getPostsFrom(post.getSone().getId()).remove(post); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } post.getSone().removePost(post); } finally { lock.writeLock().unlock(); } } - /** {@inheritDocs} */ - @Override - public void storePosts(Sone sone, Collection posts) throws IllegalArgumentException { - checkNotNull(sone, "sone must not be null"); - /* verify that all posts are from the same Sone. */ - for (Post post : posts) { - if (!sone.equals(post.getSone())) { - throw new IllegalArgumentException(String.format("Post from different Sone found: %s", post)); - } - } - - lock.writeLock().lock(); - try { - /* remove all posts by the Sone. */ - Collection oldPosts = getPostsFrom(sone.getId()); - for (Post post : oldPosts) { - allPosts.remove(post.getId()); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } - } - - /* add new posts. */ - getPostsFrom(sone.getId()).addAll(posts); - for (Post post : posts) { - allPosts.put(post.getId(), post); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).add(post); - } - } - } finally { - lock.writeLock().unlock(); - } - } - - /** {@inheritDocs} */ - @Override - public void removePosts(Sone sone) { - checkNotNull(sone, "sone must not be null"); - lock.writeLock().lock(); - try { - /* remove all posts by the Sone. */ - getPostsFrom(sone.getId()).clear(); - for (Post post : sone.getPosts()) { - allPosts.remove(post.getId()); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } - } - } finally { - lock.writeLock().unlock(); - } - } - // // POSTREPLYPROVIDER METHODS // @@ -319,13 +374,16 @@ public class MemoryDatabase extends AbstractService implements Database { /** {@inheritDocs} */ @Override - public List getReplies(String postId) { + public List getReplies(final String postId) { lock.readLock().lock(); try { - if (!postReplies.containsKey(postId)) { - return Collections.emptyList(); - } - return new ArrayList(postReplies.get(postId)); + return from(allPostReplies.values()) + .filter(new Predicate() { + @Override + public boolean apply(PostReply postReply) { + return postReply.getPostId().equals(postId); + } + }).toSortedList(TIME_COMPARATOR); } finally { lock.readLock().unlock(); } @@ -351,34 +409,6 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allPostReplies.put(postReply.getId(), postReply); - postReplies.put(postReply.getPostId(), postReply); - } finally { - lock.writeLock().unlock(); - } - } - - /** {@inheritDocs} */ - @Override - public void storePostReplies(Sone sone, Collection 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 { - /* remove all post replies of the Sone. */ - for (PostReply postReply : getRepliesFrom(sone.getId())) { - removePostReply(postReply); - } - for (PostReply postReply : postReplies) { - allPostReplies.put(postReply.getId(), postReply); - sonePostReplies.put(postReply.getSone().getId(), postReply); - this.postReplies.put(postReply.getPostId(), postReply); - } } finally { lock.writeLock().unlock(); } @@ -390,24 +420,6 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allPostReplies.remove(postReply.getId()); - if (postReplies.containsKey(postReply.getPostId())) { - postReplies.get(postReply.getPostId()).remove(postReply); - } - } finally { - 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(); } @@ -445,6 +457,7 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allAlbums.put(album.getId(), album); + soneAlbums.put(album.getSone().getId(), album); } finally { lock.writeLock().unlock(); } @@ -455,6 +468,7 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allAlbums.remove(album.getId()); + soneAlbums.remove(album.getSone().getId(), album); } finally { lock.writeLock().unlock(); } @@ -492,6 +506,7 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allImages.put(image.getId(), image); + soneImages.put(image.getSone().getId(), image); } finally { lock.writeLock().unlock(); } @@ -502,11 +517,32 @@ public class MemoryDatabase extends AbstractService implements Database { lock.writeLock().lock(); try { allImages.remove(image.getId()); + soneImages.remove(image.getSone().getId(), image); } finally { lock.writeLock().unlock(); } } + @Override + public void bookmarkPost(Post post) { + memoryBookmarkDatabase.bookmarkPost(post); + } + + @Override + public void unbookmarkPost(Post post) { + memoryBookmarkDatabase.unbookmarkPost(post); + } + + @Override + public boolean isPostBookmarked(Post post) { + return memoryBookmarkDatabase.isPostBookmarked(post); + } + + @Override + public Set getBookmarkedPosts() { + return memoryBookmarkDatabase.getBookmarkedPosts(); + } + // // PACKAGE-PRIVATE METHODS // @@ -607,35 +643,13 @@ public class MemoryDatabase extends AbstractService implements Database { } } - /** - * Gets all posts that are directed the given Sone, creating a new collection - * if there is none yet. - * - * @param recipientId - * The ID of the Sone to get the posts for - * @return All posts - */ - private Collection getPostsTo(String recipientId) { - lock.readLock().lock(); - try { - return recipientPosts.get(recipientId); - } finally { - lock.readLock().unlock(); - } - } - /** Loads the known posts. */ private void loadKnownPosts() { + Set knownPosts = configurationLoader.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); - } + this.knownPosts.clear(); + this.knownPosts.addAll(knownPosts); } finally { lock.writeLock().unlock(); } @@ -662,34 +676,13 @@ public class MemoryDatabase extends AbstractService implements Database { } } - /** - * Returns all replies by the given Sone. - * - * @param id - * The ID of the Sone - * @return The post replies of the Sone, sorted by time (newest first) - */ - private Collection getRepliesFrom(String id) { - lock.readLock().lock(); - try { - return Collections.unmodifiableCollection(sonePostReplies.get(id)); - } finally { - lock.readLock().unlock(); - } - } - /** Loads the known post replies. */ private void loadKnownPostReplies() { + Set knownPostReplies = configurationLoader.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); - } + this.knownPostReplies.clear(); + this.knownPostReplies.addAll(knownPostReplies); } finally { lock.writeLock().unlock(); }