X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdatabase%2Fmemory%2FMemoryPostDatabase.java;h=fb661622a0513f3e384375f1d6fc94497a2de73b;hb=b10dc3573e2bf5c053d62c85b7ce19f9488d6a3c;hp=eed00eab1e3607a08b69a2fdb047f7230b9d72cc;hpb=268574fb0cd808a3a575843af256f8b98b643cb6;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostDatabase.java index eed00ea..fb66162 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostDatabase.java @@ -1,408 +1,194 @@ -/* - * Sone - MemoryPostDatabase.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 . - */ - package net.pterodactylus.sone.database.memory; -import static com.google.common.base.Preconditions.checkNotNull; - import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -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.Sone; -import net.pterodactylus.sone.data.impl.AbstractPostBuilder; -import net.pterodactylus.sone.database.PostBuilder; -import net.pterodactylus.sone.database.PostDatabase; -import net.pterodactylus.sone.database.SoneProvider; -import net.pterodactylus.util.config.Configuration; -import net.pterodactylus.util.config.ConfigurationException; +import net.pterodactylus.sone.database.DatabaseException; +import net.pterodactylus.sone.utils.Optionals; +import com.google.common.base.Function; import com.google.common.base.Optional; -import com.google.inject.Inject; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; /** - * Memory-based {@link PostDatabase} implementation. + * Groups {@link Post}-related database functions. * * @author David ‘Bombe’ Roden */ -public class MemoryPostDatabase implements PostDatabase { +class MemoryPostDatabase { - /** The lock. */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - /** The Sone provider. */ - private final SoneProvider soneProvider; - - /** All posts by their ID. */ - private final Map allPosts = new HashMap(); - - /** All posts by their Sones. */ - private final Map> sonePosts = new HashMap>(); - - /** All posts by their recipient. */ - private final Map> recipientPosts = new HashMap>(); - - /** Whether posts are known. */ + private final MemoryDatabase database; + private final ConfigurationLoader configurationLoader; + private final Multimap sonePosts = HashMultimap.create(); + private final Map postSones = new HashMap(); + private final Map postTimes = new HashMap(); + private final Map postRecipients = new HashMap(); + private final Multimap recipientPosts = HashMultimap.create(); + private final Map postTexts = new HashMap(); private final Set knownPosts = new HashSet(); - /** - * Creates a new memory database. - * - * @param soneProvider - * The Sone provider - */ - @Inject - public MemoryPostDatabase(SoneProvider soneProvider) { - this.soneProvider = soneProvider; + MemoryPostDatabase(MemoryDatabase database, ConfigurationLoader configurationLoader) { + this.database = database; + this.configurationLoader = configurationLoader; } - // - // POSTPROVIDER METHODS - // - - /** - * {@inheritDocs} - */ - @Override - public Optional getPost(String postId) { - lock.readLock().lock(); - try { - return Optional.fromNullable(allPosts.get(postId)); - } finally { - lock.readLock().unlock(); - } + void start() { + loadKnownPosts(); } - /** - * {@inheritDocs} - */ - @Override - public Collection getPosts(String soneId) { - return new HashSet(getPostsFrom(soneId)); + void stop() { + saveKnownPosts(); } - /** - * {@inheritDocs} - */ - @Override - public Collection getDirectedPosts(String recipientId) { + Optional getPost(String postId) { lock.readLock().lock(); try { - Collection posts = recipientPosts.get(recipientId); - return (posts == null) ? Collections. emptySet() : new HashSet(posts); + if (!postSones.containsKey(postId)) { + return Optional.absent(); + } + String sender = postSones.get(postId); + String recipientId = postRecipients.get(postId); + long time = postTimes.get(postId); + String text = postTexts.get(postId); + return Optional.of( + new MemoryPost(database, database, postId, sender, recipientId, time, text)); } finally { lock.readLock().unlock(); } } - // - // POSTBUILDERFACTORY METHODS - // - - /** - * {@inheritDocs} - */ - @Override - public PostBuilder newPostBuilder() { - return new MemoryPostBuilder(soneProvider); - } - - // - // POSTSTORE METHODS - // - - /** - * {@inheritDocs} - */ - @Override - public void storePost(Post post) { - checkNotNull(post, "post must not be null"); - lock.writeLock().lock(); + Collection getPostsFrom(String soneId) { + lock.readLock().lock(); try { - allPosts.put(post.getId(), post); - getPostsFrom(post.getSone().getId()).add(post); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).add(post); - } + return FluentIterable.from(sonePosts.get(soneId)) + .transform(postLoader) + .transform(Optionals.get()) + .toList(); } finally { - lock.writeLock().unlock(); + lock.readLock().unlock(); } } - /** - * {@inheritDocs} - */ - @Override - public void removePost(Post post) { - checkNotNull(post, "post must not be null"); - lock.writeLock().lock(); + Collection getDirectedPosts(String recipientId) { + lock.readLock().lock(); 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); + return FluentIterable.from(recipientPosts.get(recipientId)) + .transform(postLoader) + .transform(Optionals.get()) + .toList(); } finally { - lock.writeLock().unlock(); + lock.readLock().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)); - } - } - + void storePosts(String soneId, Collection posts) { lock.writeLock().lock(); try { - /* remove all posts by the Sone. */ - getPostsFrom(sone.getId()).clear(); + removePostsFor(soneId); for (Post post : posts) { - 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); - } + storePost(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(); + void storePost(Post post) { + String soneId = post.getSone().getId(); + final String postId = post.getId(); + sonePosts.put(soneId, postId); + postSones.put(postId, soneId); + postRecipients.put(postId, post.getRecipientId().orNull()); + if (post.getRecipientId().isPresent()) { + recipientPosts.put(post.getRecipientId().get(), postId); } + postTimes.put(postId, post.getTime()); + postTexts.put(postId, post.getText()); } - // - // POSTDATABASE METHODS - // - - /** - * {@inheritDocs} - */ - @Override - public void loadKnownPosts(Configuration configuration, String prefix) { + void removePostsFor(String soneId) { lock.writeLock().lock(); try { - int postCounter = 0; - while (true) { - String knownPostId = configuration.getStringValue(prefix + postCounter++ + "/ID").getValue(null); - if (knownPostId == null) { - break; - } - knownPosts.add(knownPostId); + for (String postId : sonePosts.removeAll(soneId)) { + removePost(postId); } } finally { lock.writeLock().unlock(); } } - /** - * {@inheritDocs} - */ - @Override - public void saveKnownPosts(Configuration configuration, String prefix) throws ConfigurationException { - lock.readLock().lock(); + void removePost(String postId) { + lock.writeLock().lock(); try { - int postCounter = 0; - for (String knownPostId : knownPosts) { - configuration.getStringValue(prefix + postCounter++ + "/ID").setValue(knownPostId); + postSones.remove(postId); + String recipient = postRecipients.remove(postId); + if (recipient != null) { + recipientPosts.remove(recipient, postId); } - configuration.getStringValue(prefix + postCounter + "/ID").setValue(null); + postTimes.remove(postId); + postTexts.remove(postId); } finally { - lock.readLock().unlock(); + lock.writeLock().unlock(); } } - // - // PACKAGE-PRIVATE METHODS - // - - /** - * Returns whether the given post is known. - * - * @param post - * The post - * @return {@code true} if the post is known, {@code false} otherwise - */ - boolean isPostKnown(Post post) { + boolean isPostKnown(String postId) { lock.readLock().lock(); try { - return knownPosts.contains(post.getId()); + return knownPosts.contains(postId); } finally { lock.readLock().unlock(); } } - /** - * Sets whether the given post is known. - * - * @param post - * The post - * @param known - * {@code true} if the post is known, {@code false} otherwise - */ - void setPostKnown(Post post, boolean known) { + void setPostKnown(String postId, boolean known) { lock.writeLock().lock(); try { if (known) { - knownPosts.add(post.getId()); + knownPosts.add(postId); } else { - knownPosts.remove(post.getId()); + knownPosts.remove(postId); } + saveKnownPosts(); } finally { lock.writeLock().unlock(); } } - // - // PRIVATE METHODS - // - - /** - * Gets all posts for the given Sone, creating a new collection if there is - * none yet. - * - * @param soneId - * The ID of the Sone to get the posts for - * @return All posts - */ - private Collection getPostsFrom(String soneId) { - Collection posts = null; - lock.readLock().lock(); - try { - posts = sonePosts.get(soneId); - } finally { - lock.readLock().unlock(); - } - if (posts != null) { - return posts; - } - - posts = new HashSet(); + private void loadKnownPosts() { + Set knownPosts = configurationLoader.loadKnownPosts(); lock.writeLock().lock(); try { - sonePosts.put(soneId, posts); + this.knownPosts.clear(); + this.knownPosts.addAll(knownPosts); } finally { lock.writeLock().unlock(); } - - return posts; } - /** - * 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) { - Collection posts = null; + private void saveKnownPosts() { lock.readLock().lock(); try { - posts = recipientPosts.get(recipientId); + configurationLoader.saveKnownPosts(knownPosts); } finally { lock.readLock().unlock(); } - if (posts != null) { - return posts; - } - - posts = new HashSet(); - lock.writeLock().lock(); - try { - recipientPosts.put(recipientId, posts); - } finally { - lock.writeLock().unlock(); - } - - return posts; } - /** - * {@link PostBuilder} implementation that creates a {@link MemoryPost}. - * - * @author David ‘Bombe’ Roden - */ - private class MemoryPostBuilder extends AbstractPostBuilder { - - /** - * Creates a new memory post builder. - * - * @param soneProvider - * The Sone provider - */ - public MemoryPostBuilder(SoneProvider soneProvider) { - super(soneProvider); - } - - /** - * {@inheritDocs} - */ + private Function> postLoader = new Function>() { @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.setKnown(isPostKnown(post)); - return post; + public Optional apply(String input) { + return getPost(input); } - - } + }; }