X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=70d9ea2c6dc01a086f3e5e0b32b342cf7a6ca68e;hb=93fdf9e218d808f65f618abbddce183191d8c15b;hp=581e2d50b6687d93ceec3807495ed4bb2aec0057;hpb=99888ce13cc17d49f5e217ab6f2c9ad5ef168792;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 581e2d5..70d9ea2 100644
--- a/src/main/java/net/pterodactylus/sone/core/Core.java
+++ b/src/main/java/net/pterodactylus/sone/core/Core.java
@@ -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,7 +56,11 @@ 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.PostReplyBuilder;
+import net.pterodactylus.sone.data.PostReplyBuilderFactory;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.data.Reply;
@@ -62,8 +68,6 @@ 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;
@@ -75,19 +79,21 @@ 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.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;
@@ -99,7 +105,7 @@ import freenet.keys.FreenetURI;
*
* @author David âBombeâ Roden
*/
-public class Core extends AbstractService implements SoneProvider, PostProvider {
+public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider {
/** The logger. */
private static final Logger logger = Logging.getLogger(Core.class);
@@ -168,12 +174,18 @@ 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();
/** All known posts. */
private final Set knownPosts = new HashSet();
+ /** The post reply builder factory. */
+ private final PostReplyBuilderFactory postReplyBuilderFactory;
+
/** All replies. */
private final Map replies = new HashMap();
@@ -197,7 +209,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;
@@ -215,9 +227,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
* The WebOfTrust updater
* @param eventBus
* The event bus
+ * @param postBuilderFactory
+ * The post builder
+ * @param postReplyBuilderFactory
+ * The post reply builder factory
*/
@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, PostReplyBuilderFactory postReplyBuilderFactory) {
super("Sone Core");
this.configuration = configuration;
this.freenetInterface = freenetInterface;
@@ -227,6 +243,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
this.updateChecker = new UpdateChecker(eventBus, freenetInterface);
this.webOfTrustUpdater = webOfTrustUpdater;
this.eventBus = eventBus;
+ this.postBuilderFactory = postBuilderFactory;
+ this.postReplyBuilderFactory = postReplyBuilderFactory;
}
//
@@ -511,35 +529,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);
}
}
@@ -565,47 +569,42 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
}
/**
- * Returns the reply with the given ID. If there is no reply with the given
- * ID yet, a new one is created, unless {@code create} is false in which
- * case {@code null} is returned.
+ * Returns a post reply builder.
*
- * @param replyId
- * The ID of the reply to get
- * @param create
- * {@code true} to always return a {@link Reply}, {@code false}
- * to return {@code null} if no reply can be found
- * @return The reply, or {@code null} if there is no such reply
+ * @return A new post reply builder
+ */
+ public PostReplyBuilder postReplyBuilder() {
+ return postReplyBuilderFactory.newPostReplyBuilder();
+ }
+
+ /**
+ * {@inheritDoc}
*/
- public PostReply getPostReply(String replyId, boolean create) {
+ @Override
+ public Optional getPostReply(String replyId) {
synchronized (replies) {
- PostReply reply = replies.get(replyId);
- if (create && (reply == null)) {
- reply = new PostReplyImpl(replyId);
- replies.put(replyId, reply);
- }
- return reply;
+ return Optional.fromNullable(replies.get(replyId));
}
}
/**
- * Returns all replies for the given post, order ascending by time.
- *
- * @param post
- * The post to get all replies for
- * @return All replies for the given post
+ * {@inheritDoc}
*/
- 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);
- }
+ @Override
+ 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());
+ }
+ }));
}
/**
@@ -677,7 +676,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);
}
@@ -878,7 +877,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
synchronized (sones) {
final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity);
boolean newSone = sone.getRequestUri() == null;
- sone.setRequestUri(getSoneUri(identity.getRequestUri()));
+ sone.setRequestUri(SoneUri.create(identity.getRequestUri()));
sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0));
if (newSone) {
synchronized (knownSones) {
@@ -1110,16 +1109,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);
}
}
}
@@ -1312,11 +1313,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. */
@@ -1334,7 +1335,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
logger.log(Level.WARNING, "Invalid reply found, aborting load!");
return;
}
- replies.add(getPostReply(replyId, true).setSone(sone).setPost(getPost(postId)).setTime(replyTime).setText(replyText));
+ PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder();
+ postReplyBuilder.withId(replyId).from(sone).to(postId).withTime(replyTime).withText(replyText);
+ replies.add(postReplyBuilder.build());
}
/* load post likes. */
@@ -1538,17 +1541,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.trim());
+ 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}
@@ -1557,7 +1562,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
public void run() {
markPostKnown(post);
}
- }, "Mark " + post + " read.");
+ }, 10, TimeUnit.SECONDS);
return post;
}
@@ -1657,30 +1662,15 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
* @return The created reply
*/
public PostReply createReply(Sone sone, Post post, String text) {
- return createReply(sone, post, System.currentTimeMillis(), text);
- }
-
- /**
- * Creates a new reply.
- *
- * @param sone
- * The Sone that creates the reply
- * @param post
- * The post that this reply refers to
- * @param time
- * The time of the reply
- * @param text
- * The text of the reply
- * @return The created reply
- */
- public PostReply createReply(Sone sone, Post post, long time, String text) {
checkNotNull(text, "text must not be null");
checkArgument(text.trim().length() > 0, "text must not be empty");
if (!sone.isLocal()) {
logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone));
return null;
}
- final PostReply reply = new PostReplyImpl(sone, post, System.currentTimeMillis(), text.trim());
+ PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder();
+ postReplyBuilder.randomId().from(sone).to(post.getId()).currentTime().withText(text.trim());
+ final PostReply reply = postReplyBuilder.build();
synchronized (replies) {
replies.put(reply.getId(), reply);
}
@@ -1689,7 +1679,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}
@@ -1698,7 +1688,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
public void run() {
markReplyKnown(reply);
}
- }, "Mark " + reply + " read.");
+ }, 10, TimeUnit.SECONDS);
return reply;
}
@@ -1941,6 +1931,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();
@@ -2188,7 +2179,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) {
@@ -2196,13 +2187,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() {
@@ -2319,23 +2310,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
}
/**
- * Generate a Sone URI from the given URI and latest edition.
- *
- * @param uriString
- * The URI to derive the Sone URI from
- * @return The derived URI
- */
- private static FreenetURI getSoneUri(String uriString) {
- try {
- FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]);
- return uri;
- } catch (MalformedURLException mue1) {
- logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1);
- return null;
- }
- }
-
- /**
* Notifies the core that a new {@link OwnIdentity} was added.
*
* @param ownIdentityAddedEvent