X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=43561204030924d16aba23c63b154bcff1fe9347;hb=fd560d013c7de7b8c70b65156a41d4a6bc9aeb06;hp=94908cba1b95571a2a451b57def86358609df56c;hpb=cce88f819ae0ef84b5a86c6a826f88ce0208107f;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 94908cb..4356120 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -1,5 +1,5 @@ /* - * Sone - Core.java - Copyright © 2010–2012 David Roden + * Sone - Core.java - Copyright © 2010–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 @@ -32,6 +32,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,6 +56,8 @@ import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.PostBuilder; +import net.pterodactylus.sone.data.PostBuilderFactory; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; @@ -62,7 +66,7 @@ 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.data.impl.PostImpl; +import net.pterodactylus.sone.data.impl.PostReplyImpl; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; import net.pterodactylus.sone.freenet.wot.Identity; @@ -74,19 +78,20 @@ import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent; import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent; import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent; import net.pterodactylus.sone.main.SonePlugin; +import net.pterodactylus.sone.utils.IntegerRangePredicate; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.thread.NamedThreadFactory; -import net.pterodactylus.util.thread.Ticker; -import net.pterodactylus.util.validation.EqualityValidator; -import net.pterodactylus.util.validation.IntegerRangeValidator; -import net.pterodactylus.util.validation.OrValidator; +import com.google.common.base.Function; 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; @@ -167,6 +172,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider /** All known Sones. */ private final Set knownSones = new HashSet(); + /** The post builder. */ + private final PostBuilderFactory postBuilderFactory; + /** All posts. */ private final Map posts = new HashMap(); @@ -196,7 +204,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider private final Map temporaryImages = new HashMap(); /** Ticker for threads that mark own elements as known. */ - private final Ticker localElementTicker = new Ticker(); + private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1); /** The time the configuration was last touched. */ private volatile long lastConfigurationUpdate; @@ -214,9 +222,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * The WebOfTrust updater * @param eventBus * The event bus + * @param postBuilderFactory + * The post builder */ @Inject - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus) { + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, PostBuilderFactory postBuilderFactory) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -226,6 +236,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider this.updateChecker = new UpdateChecker(eventBus, freenetInterface); this.webOfTrustUpdater = webOfTrustUpdater; this.eventBus = eventBus; + this.postBuilderFactory = postBuilderFactory; } // @@ -510,35 +521,21 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * Returns the post with the given ID. + * Returns a post builder. * - * @param postId - * The ID of the post to get - * @return The post with the given ID, or a new post with the given ID + * @return A new post builder */ - public Post getPost(String postId) { - return getPost(postId, true); + public PostBuilder postBuilder() { + return postBuilderFactory.newPostBuilder(); } /** - * Returns the post with the given ID, optionally creating a new post. - * - * @param postId - * The ID of the post to get - * @param create - * {@code true} it create a new post if no post with the given ID - * exists, {@code false} to return {@code null} - * @return The post, or {@code null} if there is no such post + * {@inheritDoc} */ @Override - public Post getPost(String postId, boolean create) { + public Post getPost(String postId) { synchronized (posts) { - Post post = posts.get(postId); - if ((post == null) && create) { - post = new PostImpl(postId); - posts.put(postId, post); - } - return post; + return posts.get(postId); } } @@ -579,7 +576,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider synchronized (replies) { PostReply reply = replies.get(replyId); if (create && (reply == null)) { - reply = new PostReply(replyId); + reply = new PostReplyImpl(replyId); replies.put(replyId, reply); } return reply; @@ -593,18 +590,20 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * The post to get all replies for * @return All replies for the given post */ - public List getReplies(Post post) { - Set sones = getSones(); - List replies = new ArrayList(); - for (Sone sone : sones) { - for (PostReply reply : sone.getReplies()) { - if (reply.getPost().equals(post)) { - replies.add(reply); - } + public List getReplies(final Post post) { + return Ordering.from(Reply.TIME_COMPARATOR).sortedCopy(FluentIterable.from(getSones()).transformAndConcat(new Function>() { + + @Override + public Iterable apply(Sone sone) { + return sone.getReplies(); } - } - Collections.sort(replies, Reply.TIME_COMPARATOR); - return replies; + }).filter(new Predicate() { + + @Override + public boolean apply(PostReply reply) { + return post.equals(reply.getPost()); + } + })); } /** @@ -676,7 +675,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider Set posts = new HashSet(); synchronized (bookmarkedPosts) { for (String bookmarkedPostId : bookmarkedPosts) { - Post post = getPost(bookmarkedPostId, false); + Post post = getPost(bookmarkedPostId); if (post != null) { posts.add(post); } @@ -1109,16 +1108,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider List storedPosts = storedSone.getPosts(); synchronized (knownPosts) { for (Post post : sone.getPosts()) { - post.setSone(storedSone).setKnown(knownPosts.contains(post.getId())); - if (!storedPosts.contains(post)) { - if (post.getTime() < getSoneFollowingTime(sone)) { - knownPosts.add(post.getId()); - post.setKnown(true); - } else if (!knownPosts.contains(post.getId())) { - eventBus.post(new NewPostFoundEvent(post)); + PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); + postBuilder.copyPost(post).from(storedSone); + Post newPost = postBuilder.build().setKnown(knownPosts.contains(post.getId())); + if (!storedPosts.contains(newPost)) { + if (newPost.getTime() < getSoneFollowingTime(sone)) { + knownPosts.add(newPost.getId()); + newPost.setKnown(true); + } else if (!knownPosts.contains(newPost.getId())) { + eventBus.post(new NewPostFoundEvent(newPost)); } } - posts.put(post.getId(), post); + posts.put(newPost.getId(), newPost); } } } @@ -1311,11 +1312,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText); + PostBuilder postBuilder = postBuilder().withId(postId).from(sone).withTime(postTime).withText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { - post.setRecipient(getSone(postRecipientId)); + postBuilder.to(getSone(postRecipientId)); } - posts.add(post); + posts.add(postBuilder.build()); } /* load replies. */ @@ -1537,17 +1538,19 @@ 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; } - final Post post = new PostImpl(sone, time, text); + PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); + postBuilder.from(sone).randomId().withTime(time).withText(text.trim()); if (recipient != null) { - post.setRecipient(recipient); + postBuilder.to(recipient); } + final Post post = postBuilder.build(); synchronized (posts) { posts.put(post.getId(), post); } eventBus.post(new NewPostFoundEvent(post)); sone.addPost(post); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1556,7 +1559,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void run() { markPostKnown(post); } - }, "Mark " + post + " read."); + }, 10, TimeUnit.SECONDS); return post; } @@ -1679,7 +1682,7 @@ 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; } - final PostReply reply = new PostReply(sone, post, System.currentTimeMillis(), text); + final PostReply reply = new PostReplyImpl(sone, post, System.currentTimeMillis(), text.trim()); synchronized (replies) { replies.put(reply.getId(), reply); } @@ -1688,7 +1691,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } sone.addReply(reply); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1697,7 +1700,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void run() { markReplyKnown(reply); } - }, "Mark " + reply + " read."); + }, 10, TimeUnit.SECONDS); return reply; } @@ -1940,6 +1943,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider */ @Override public void serviceStop() { + localElementTicker.shutdownNow(); synchronized (sones) { for (Entry soneInserter : soneInserters.entrySet()) { soneInserter.getValue().stop(); @@ -2187,7 +2191,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider @SuppressWarnings("unchecked") private void loadConfiguration() { /* create options. */ - options.addIntegerOption("InsertionDelay", new DefaultOption(60, new IntegerRangeValidator(0, Integer.MAX_VALUE), new OptionWatcher() { + options.addIntegerOption("InsertionDelay", new DefaultOption(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new OptionWatcher() { @Override public void optionChanged(Option option, Integer oldValue, Integer newValue) { @@ -2195,13 +2199,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } })); - options.addIntegerOption("PostsPerPage", new DefaultOption(10, new IntegerRangeValidator(1, Integer.MAX_VALUE))); - options.addIntegerOption("ImagesPerPage", new DefaultOption(9, new IntegerRangeValidator(1, Integer.MAX_VALUE))); - options.addIntegerOption("CharactersPerPost", new DefaultOption(400, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); - options.addIntegerOption("PostCutOffLength", new DefaultOption(200, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); + options.addIntegerOption("PostsPerPage", new DefaultOption(10, new IntegerRangePredicate(1, Integer.MAX_VALUE))); + options.addIntegerOption("ImagesPerPage", new DefaultOption(9, new IntegerRangePredicate(1, Integer.MAX_VALUE))); + options.addIntegerOption("CharactersPerPost", new DefaultOption(400, Predicates. or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1)))); + options.addIntegerOption("PostCutOffLength", new DefaultOption(200, Predicates. or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1)))); options.addBooleanOption("RequireFullAccess", new DefaultOption(false)); - options.addIntegerOption("PositiveTrust", new DefaultOption(75, new IntegerRangeValidator(0, 100))); - options.addIntegerOption("NegativeTrust", new DefaultOption(-25, new IntegerRangeValidator(-100, 100))); + options.addIntegerOption("PositiveTrust", new DefaultOption(75, new IntegerRangePredicate(0, 100))); + options.addIntegerOption("NegativeTrust", new DefaultOption(-25, new IntegerRangePredicate(-100, 100))); options.addStringOption("TrustComment", new DefaultOption("Set from Sone Web Interface")); options.addBooleanOption("ActivateFcpInterface", new DefaultOption(false, new OptionWatcher() { @@ -2465,358 +2469,4 @@ public class Core extends AbstractService implements SoneProvider, PostProvider touchConfiguration(); } - /** - * Convenience interface for external classes that want to access the core’s - * configuration. - * - * @author David ‘Bombe’ Roden - */ - public static class Preferences { - - /** The wrapped options. */ - private final Options options; - - /** - * Creates a new preferences object wrapped around the given options. - * - * @param options - * The options to wrap - */ - public Preferences(Options options) { - this.options = options; - } - - /** - * Returns the insertion delay. - * - * @return The insertion delay - */ - public int getInsertionDelay() { - return options.getIntegerOption("InsertionDelay").get(); - } - - /** - * Validates the given insertion delay. - * - * @param insertionDelay - * The insertion delay to validate - * @return {@code true} if the given insertion delay was valid, - * {@code false} otherwise - */ - public boolean validateInsertionDelay(Integer insertionDelay) { - return options.getIntegerOption("InsertionDelay").validate(insertionDelay); - } - - /** - * Sets the insertion delay - * - * @param insertionDelay - * The new insertion delay, or {@code null} to restore it to - * the default value - * @return This preferences - */ - public Preferences setInsertionDelay(Integer insertionDelay) { - options.getIntegerOption("InsertionDelay").set(insertionDelay); - return this; - } - - /** - * Returns the number of posts to show per page. - * - * @return The number of posts to show per page - */ - public int getPostsPerPage() { - return options.getIntegerOption("PostsPerPage").get(); - } - - /** - * Validates the number of posts per page. - * - * @param postsPerPage - * The number of posts per page - * @return {@code true} if the number of posts per page was valid, - * {@code false} otherwise - */ - public boolean validatePostsPerPage(Integer postsPerPage) { - return options.getIntegerOption("PostsPerPage").validate(postsPerPage); - } - - /** - * Sets the number of posts to show per page. - * - * @param postsPerPage - * The number of posts to show per page - * @return This preferences object - */ - public Preferences setPostsPerPage(Integer postsPerPage) { - options.getIntegerOption("PostsPerPage").set(postsPerPage); - return this; - } - - /** - * Returns the number of images to show per page. - * - * @return The number of images to show per page - */ - public int getImagesPerPage() { - return options.getIntegerOption("ImagesPerPage").get(); - } - - /** - * Validates the number of images per page. - * - * @param imagesPerPage - * The number of images per page - * @return {@code true} if the number of images per page was valid, - * {@code false} otherwise - */ - public boolean validateImagesPerPage(Integer imagesPerPage) { - return options.getIntegerOption("ImagesPerPage").validate(imagesPerPage); - } - - /** - * Sets the number of images per page. - * - * @param imagesPerPage - * The number of images per page - * @return This preferences object - */ - public Preferences setImagesPerPage(Integer imagesPerPage) { - options.getIntegerOption("ImagesPerPage").set(imagesPerPage); - return this; - } - - /** - * Returns the number of characters per post, or -1 if the - * posts should not be cut off. - * - * @return The numbers of characters per post - */ - public int getCharactersPerPost() { - return options.getIntegerOption("CharactersPerPost").get(); - } - - /** - * Validates the number of characters per post. - * - * @param charactersPerPost - * The number of characters per post - * @return {@code true} if the number of characters per post was valid, - * {@code false} otherwise - */ - public boolean validateCharactersPerPost(Integer charactersPerPost) { - return options.getIntegerOption("CharactersPerPost").validate(charactersPerPost); - } - - /** - * Sets the number of characters per post. - * - * @param charactersPerPost - * The number of characters per post, or -1 to - * not cut off the posts - * @return This preferences objects - */ - public Preferences setCharactersPerPost(Integer charactersPerPost) { - options.getIntegerOption("CharactersPerPost").set(charactersPerPost); - return this; - } - - /** - * Returns the number of characters the shortened post should have. - * - * @return The number of characters of the snippet - */ - public int getPostCutOffLength() { - return options.getIntegerOption("PostCutOffLength").get(); - } - - /** - * Validates the number of characters after which to cut off the post. - * - * @param postCutOffLength - * The number of characters of the snippet - * @return {@code true} if the number of characters of the snippet is - * valid, {@code false} otherwise - */ - public boolean validatePostCutOffLength(Integer postCutOffLength) { - return options.getIntegerOption("PostCutOffLength").validate(postCutOffLength); - } - - /** - * Sets the number of characters the shortened post should have. - * - * @param postCutOffLength - * The number of characters of the snippet - * @return This preferences - */ - public Preferences setPostCutOffLength(Integer postCutOffLength) { - options.getIntegerOption("PostCutOffLength").set(postCutOffLength); - return this; - } - - /** - * Returns whether Sone requires full access to be even visible. - * - * @return {@code true} if Sone requires full access, {@code false} - * otherwise - */ - public boolean isRequireFullAccess() { - return options.getBooleanOption("RequireFullAccess").get(); - } - - /** - * Sets whether Sone requires full access to be even visible. - * - * @param requireFullAccess - * {@code true} if Sone requires full access, {@code false} - * otherwise - */ - public void setRequireFullAccess(Boolean requireFullAccess) { - options.getBooleanOption("RequireFullAccess").set(requireFullAccess); - } - - /** - * Returns the positive trust. - * - * @return The positive trust - */ - public int getPositiveTrust() { - return options.getIntegerOption("PositiveTrust").get(); - } - - /** - * Validates the positive trust. - * - * @param positiveTrust - * The positive trust to validate - * @return {@code true} if the positive trust was valid, {@code false} - * otherwise - */ - public boolean validatePositiveTrust(Integer positiveTrust) { - return options.getIntegerOption("PositiveTrust").validate(positiveTrust); - } - - /** - * Sets the positive trust. - * - * @param positiveTrust - * The new positive trust, or {@code null} to restore it to - * the default vlaue - * @return This preferences - */ - public Preferences setPositiveTrust(Integer positiveTrust) { - options.getIntegerOption("PositiveTrust").set(positiveTrust); - return this; - } - - /** - * Returns the negative trust. - * - * @return The negative trust - */ - public int getNegativeTrust() { - return options.getIntegerOption("NegativeTrust").get(); - } - - /** - * Validates the negative trust. - * - * @param negativeTrust - * The negative trust to validate - * @return {@code true} if the negative trust was valid, {@code false} - * otherwise - */ - public boolean validateNegativeTrust(Integer negativeTrust) { - return options.getIntegerOption("NegativeTrust").validate(negativeTrust); - } - - /** - * Sets the negative trust. - * - * @param negativeTrust - * The negative trust, or {@code null} to restore it to the - * default value - * @return The preferences - */ - public Preferences setNegativeTrust(Integer negativeTrust) { - options.getIntegerOption("NegativeTrust").set(negativeTrust); - return this; - } - - /** - * Returns the trust comment. This is the comment that is set in the web - * of trust when a trust value is assigned to an identity. - * - * @return The trust comment - */ - public String getTrustComment() { - return options.getStringOption("TrustComment").get(); - } - - /** - * Sets the trust comment. - * - * @param trustComment - * The trust comment, or {@code null} to restore it to the - * default value - * @return This preferences - */ - public Preferences setTrustComment(String trustComment) { - options.getStringOption("TrustComment").set(trustComment); - return this; - } - - /** - * Returns whether the {@link FcpInterface FCP interface} is currently - * active. - * - * @see FcpInterface#setActive(boolean) - * @return {@code true} if the FCP interface is currently active, - * {@code false} otherwise - */ - public boolean isFcpInterfaceActive() { - return options.getBooleanOption("ActivateFcpInterface").get(); - } - - /** - * Sets whether the {@link FcpInterface FCP interface} is currently - * active. - * - * @see FcpInterface#setActive(boolean) - * @param fcpInterfaceActive - * {@code true} to activate the FCP interface, {@code false} - * to deactivate the FCP interface - * @return This preferences object - */ - public Preferences setFcpInterfaceActive(boolean fcpInterfaceActive) { - options.getBooleanOption("ActivateFcpInterface").set(fcpInterfaceActive); - return this; - } - - /** - * Returns the action level for which full access to the FCP interface - * is required. - * - * @return The action level for which full access to the FCP interface - * is required - */ - public FullAccessRequired getFcpFullAccessRequired() { - return FullAccessRequired.values()[options.getIntegerOption("FcpFullAccessRequired").get()]; - } - - /** - * Sets the action level for which full access to the FCP interface is - * required - * - * @param fcpFullAccessRequired - * The action level - * @return This preferences - */ - public Preferences setFcpFullAccessRequired(FullAccessRequired fcpFullAccessRequired) { - options.getIntegerOption("FcpFullAccessRequired").set((fcpFullAccessRequired != null) ? fcpFullAccessRequired.ordinal() : null); - return this; - } - - } - }