X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=9ccaa799868f05189150c5162fab46c01dcd9d9f;hp=b603eff1101a9135b10e77153082d4c8dbeae7ee;hb=10fa1478fef15624a873c0878ed017603742bcfe;hpb=b767ceb6adea75829f9eeb417d2a3f3e3f445ba9 diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index b603eff..9ccaa79 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; @@ -61,9 +63,14 @@ import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.PostBuilderFactory; +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.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; @@ -79,17 +86,19 @@ 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 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.common.primitives.Longs; import com.google.inject.Inject; import freenet.keys.FreenetURI; @@ -99,7 +108,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); @@ -147,7 +156,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider private volatile FcpInterface fcpInterface; /** The times Sones were followed. */ - private final Map soneFollowingTimes = new HashMap(); + private final Map soneFollowingTimes = new HashMap(); /** Locked local Sones. */ /* synchronize on itself. */ @@ -168,12 +177,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 +212,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 +230,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 +246,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; } // @@ -326,25 +347,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * Returns all Sones, remote and local. - * - * @return All Sones + * {@inheritDocs} */ - public Set getSones() { - return new HashSet(sones.values()); - } - - /** - * Returns the Sone with the given ID, regardless whether it’s local or - * remote. - * - * @param id - * The ID of the Sone to get - * @return The Sone with the given ID, or {@code null} if there is no such - * Sone - */ - public Sone getSone(String id) { - return getSone(id, true); + @Override + public Collection getSones() { + synchronized (sones) { + return Collections.unmodifiableCollection(sones.values()); + } } /** @@ -353,43 +362,20 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * * @param id * The ID of the Sone to get - * @param create - * {@code true} to create a new Sone if none exists, - * {@code false} to return {@code null} if a Sone with the given - * ID does not exist * @return The Sone with the given ID, or {@code null} if there is no such * Sone */ @Override - public Sone getSone(String id, boolean create) { + public Optional getSone(String id) { synchronized (sones) { - if (!sones.containsKey(id) && create) { - Sone sone = new Sone(id, false); - sones.put(id, sone); - } - return sones.get(id); + return Optional.fromNullable(sones.get(id)); } } /** - * Checks whether the core knows a Sone with the given ID. - * - * @param id - * The ID of the Sone - * @return {@code true} if there is a Sone with the given ID, {@code false} - * otherwise - */ - public boolean hasSone(String id) { - synchronized (sones) { - return sones.containsKey(id); - } - } - - /** - * Returns all local Sones. - * - * @return All local Sones + * {@inheritDocs} */ + @Override public Collection getLocalSones() { synchronized (sones) { return Collections2.filter(sones.values(), new Predicate() { @@ -428,10 +414,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * Returns all remote Sones. - * - * @return All remote Sones + * {@inheritDocs} */ + @Override public Collection getRemoteSones() { synchronized (sones) { return Collections2.filter(sones.values(), new Predicate() { @@ -487,10 +472,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider */ public long getSoneFollowingTime(Sone sone) { synchronized (soneFollowingTimes) { - if (soneFollowingTimes.containsKey(sone)) { - return soneFollowingTimes.get(sone); - } - return Long.MAX_VALUE; + return Optional.fromNullable(soneFollowingTimes.get(sone.getId())).or(Long.MAX_VALUE); } } @@ -511,101 +493,86 @@ 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 Optional 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 Optional.fromNullable(posts.get(postId)); } } /** - * Returns all posts that have the given Sone as recipient. - * - * @see Post#getRecipient() - * @param recipient - * The recipient of the posts - * @return All posts that have the given Sone as recipient + * {@inheritDocs} */ - public Set getDirectedPosts(Sone recipient) { - checkNotNull(recipient, "recipient must not be null"); - Set directedPosts = new HashSet(); + @Override + public Collection getPosts(String soneId) { + return postDatabase.getPosts(soneId); + } + + /** + * {@inheritDoc} + */ + @Override + public Collection getDirectedPosts(final String recipientId) { + checkNotNull(recipientId, "recipient must not be null"); synchronized (posts) { - for (Post post : posts.values()) { - if (recipient.equals(post.getRecipient())) { - directedPosts.add(post); + return Collections2.filter(posts.values(), new Predicate() { + + @Override + public boolean apply(Post post) { + return recipientId.equals(post.getRecipientId().orNull()); } - } + }); } - return directedPosts; } /** - * 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 PostReply getPostReply(String replyId, boolean create) { + public PostReplyBuilder postReplyBuilder() { + return postReplyBuilderFactory.newPostReplyBuilder(); + } + + /** + * {@inheritDoc} + */ + @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.getId().equals(reply.getPostId()); + } + })); } /** @@ -677,9 +644,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider Set posts = new HashSet(); synchronized (bookmarkedPosts) { for (String bookmarkedPostId : bookmarkedPosts) { - Post post = getPost(bookmarkedPostId, false); - if (post != null) { - posts.add(post); + Optional post = getPost(bookmarkedPostId); + if (!post.isPresent()) { + posts.add(post.get()); } } } @@ -824,10 +791,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); return null; } - sone.setLatestEdition(Optional.fromNullable(Longs.tryParse(ownIdentity.getProperty("Sone.LatestEdition"))).or(0L)); + sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)); sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); sone.setKnown(true); /* TODO - load posts ’n stuff */ + trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet())); sones.put(ownIdentity.getId(), sone); final SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, sone); soneInserters.put(sone, soneInserter); @@ -858,7 +826,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider sone.getOptions().addBooleanOption("ShowNotification/NewReplies", new DefaultOption(true)); sone.getOptions().addEnumOption("ShowCustomAvatars", new DefaultOption(ShowCustomAvatars.NEVER)); - followSone(sone, getSone("nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI")); + followSone(sone, "nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"); touchConfiguration(); return sone; } @@ -878,8 +846,8 @@ 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.setLatestEdition(Optional.fromNullable(Longs.tryParse(identity.getProperty("Sone.LatestEdition"))).or(0L)); + sone.setRequestUri(SoneUri.create(identity.getRequestUri())); + sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); if (newSone) { synchronized (knownSones) { newSone = !knownSones.contains(sone.getId()); @@ -889,7 +857,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider eventBus.post(new NewSoneFoundEvent(sone)); for (Sone localSone : getLocalSones()) { if (localSone.getOptions().getBooleanOption("AutoFollow").get()) { - followSone(localSone, sone); + followSone(localSone, sone.getId()); } } } @@ -919,39 +887,21 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void followSone(Sone sone, String soneId) { checkNotNull(sone, "sone must not be null"); checkNotNull(soneId, "soneId must not be null"); - Sone followedSone = getSone(soneId, true); - if (followedSone == null) { - logger.log(Level.INFO, String.format("Ignored Sone with invalid ID: %s", soneId)); - return; - } - followSone(sone, getSone(soneId)); - } - - /** - * Lets the given local Sone follow the other given Sone. If the given Sone - * was not followed by any local Sone before, this will mark all elements of - * the followed Sone as read that have been created before the current - * moment. - * - * @param sone - * The local Sone that should follow the other Sone - * @param followedSone - * The Sone that should be followed - */ - public void followSone(Sone sone, Sone followedSone) { - checkNotNull(sone, "sone must not be null"); - checkNotNull(followedSone, "followedSone must not be null"); - sone.addFriend(followedSone.getId()); + sone.addFriend(soneId); synchronized (soneFollowingTimes) { - if (!soneFollowingTimes.containsKey(followedSone)) { + if (!soneFollowingTimes.containsKey(soneId)) { long now = System.currentTimeMillis(); - soneFollowingTimes.put(followedSone, now); - for (Post post : followedSone.getPosts()) { + soneFollowingTimes.put(soneId, now); + Optional followedSone = getSone(soneId); + if (!followedSone.isPresent()) { + return; + } + for (Post post : followedSone.get().getPosts()) { if (post.getTime() < now) { markPostKnown(post); } } - for (PostReply reply : followedSone.getReplies()) { + for (PostReply reply : followedSone.get().getReplies()) { if (reply.getTime() < now) { markReplyKnown(reply); } @@ -972,30 +922,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void unfollowSone(Sone sone, String soneId) { checkNotNull(sone, "sone must not be null"); checkNotNull(soneId, "soneId must not be null"); - unfollowSone(sone, getSone(soneId, false)); - } - - /** - * Lets the given local Sone unfollow the other given Sone. If the given - * local Sone is the last local Sone that followed the given Sone, its - * following time will be removed. - * - * @param sone - * The local Sone that should unfollow another Sone - * @param unfollowedSone - * The Sone being unfollowed - */ - public void unfollowSone(Sone sone, Sone unfollowedSone) { - checkNotNull(sone, "sone must not be null"); - checkNotNull(unfollowedSone, "unfollowedSone must not be null"); - sone.removeFriend(unfollowedSone.getId()); + sone.removeFriend(soneId); boolean unfollowedSoneStillFollowed = false; for (Sone localSone : getLocalSones()) { - unfollowedSoneStillFollowed |= localSone.hasFriend(unfollowedSone.getId()); + unfollowedSoneStillFollowed |= localSone.hasFriend(soneId); } if (!unfollowedSoneStillFollowed) { synchronized (soneFollowingTimes) { - soneFollowingTimes.remove(unfollowedSone); + soneFollowingTimes.remove(soneId); } } touchConfiguration(); @@ -1092,25 +1026,25 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * of the age of the given Sone */ public void updateSone(Sone sone, boolean soneRescueMode) { - if (hasSone(sone.getId())) { - Sone storedSone = getSone(sone.getId()); - if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) { + Optional storedSone = getSone(sone.getId()); + if (storedSone.isPresent()) { + if (!soneRescueMode && !(sone.getTime() > storedSone.get().getTime())) { logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone)); return; } synchronized (posts) { if (!soneRescueMode) { - for (Post post : storedSone.getPosts()) { + for (Post post : storedSone.get().getPosts()) { posts.remove(post.getId()); if (!sone.getPosts().contains(post)) { eventBus.post(new PostRemovedEvent(post)); } } } - List storedPosts = storedSone.getPosts(); + List storedPosts = storedSone.get().getPosts(); synchronized (knownPosts) { for (Post post : sone.getPosts()) { - post.setSone(storedSone).setKnown(knownPosts.contains(post.getId())); + post.setKnown(knownPosts.contains(post.getId())); if (!storedPosts.contains(post)) { if (post.getTime() < getSoneFollowingTime(sone)) { knownPosts.add(post.getId()); @@ -1125,17 +1059,17 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } synchronized (replies) { if (!soneRescueMode) { - for (PostReply reply : storedSone.getReplies()) { + for (PostReply reply : storedSone.get().getReplies()) { replies.remove(reply.getId()); if (!sone.getReplies().contains(reply)) { eventBus.post(new PostReplyRemovedEvent(reply)); } } } - Set storedReplies = storedSone.getReplies(); + Set storedReplies = storedSone.get().getReplies(); synchronized (knownReplies) { for (PostReply reply : sone.getReplies()) { - reply.setSone(storedSone).setKnown(knownReplies.contains(reply.getId())); + reply.setKnown(knownReplies.contains(reply.getId())); if (!storedReplies.contains(reply)) { if (reply.getTime() < getSoneFollowingTime(sone)) { knownReplies.add(reply.getId()); @@ -1150,7 +1084,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } synchronized (albums) { synchronized (images) { - for (Album album : storedSone.getAlbums()) { + for (Album album : storedSone.get().getAlbums()) { albums.remove(album.getId()); for (Image image : album.getImages()) { images.remove(image.getId()); @@ -1164,36 +1098,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } } } - synchronized (storedSone) { - if (!soneRescueMode || (sone.getTime() > storedSone.getTime())) { - storedSone.setTime(sone.getTime()); - } - storedSone.setClient(sone.getClient()); - storedSone.setProfile(sone.getProfile()); - if (soneRescueMode) { - for (Post post : sone.getPosts()) { - storedSone.addPost(post); - } - for (PostReply reply : sone.getReplies()) { - storedSone.addReply(reply); - } - for (String likedPostId : sone.getLikedPostIds()) { - storedSone.addLikedPostId(likedPostId); - } - for (String likedReplyId : sone.getLikedReplyIds()) { - storedSone.addLikedReplyId(likedReplyId); - } - for (Album album : sone.getAlbums()) { - storedSone.addAlbum(album); - } - } else { - storedSone.setPosts(sone.getPosts()); - storedSone.setReplies(sone.getReplies()); - storedSone.setLikePostIds(sone.getLikedPostIds()); - storedSone.setLikeReplyIds(sone.getLikedReplyIds()); - storedSone.setAlbums(sone.getAlbums()); - } - storedSone.setLatestEdition(sone.getLatestEdition()); + synchronized (sones) { + sone.setOptions(storedSone.get().getOptions()); + sones.put(sone.getId(), sone); } } } @@ -1312,11 +1219,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.getId()).withTime(postTime).withText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { - post.setRecipient(getSone(postRecipientId)); + postBuilder.to(postRecipientId); } - posts.add(post); + posts.add(postBuilder.build()); } /* load replies. */ @@ -1334,7 +1241,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.getId()).to(postId).withTime(replyTime).withText(replyText); + replies.add(postReplyBuilder.build()); } /* load post likes. */ @@ -1461,11 +1370,21 @@ public class Core extends AbstractService implements SoneProvider, PostProvider knownSones.add(friend); } } + synchronized (this.posts) { + for (Post post : posts) { + this.posts.put(post.getId(), post); + } + } synchronized (knownPosts) { for (Post post : posts) { knownPosts.add(post.getId()); } } + synchronized (this.replies) { + for (PostReply postReply : replies) { + this.replies.put(postReply.getId(), postReply); + } + } synchronized (knownReplies) { for (PostReply reply : replies) { knownReplies.add(reply.getId()); @@ -1513,7 +1432,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * The text of the post * @return The created post */ - public Post createPost(Sone sone, Sone recipient, String text) { + public Post createPost(Sone sone, Optional recipient, String text) { return createPost(sone, recipient, System.currentTimeMillis(), text); } @@ -1531,24 +1450,26 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * The text of the post * @return The created post */ - public Post createPost(Sone sone, Sone recipient, long time, String text) { + public Post createPost(Sone sone, Optional recipient, 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 post for non-local Sone: %s", sone)); return null; } - final Post post = new PostImpl(sone, time, text.trim()); - if (recipient != null) { - post.setRecipient(recipient); + PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); + postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim()); + if (recipient.isPresent()) { + postBuilder.to(recipient.get().getId()); } + 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 +1478,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void run() { markPostKnown(post); } - }, "Mark " + post + " read."); + }, 10, TimeUnit.SECONDS); return post; } @@ -1657,30 +1578,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.getId()).to(post.getId()).currentTime().withText(text.trim()); + final PostReply reply = postReplyBuilder.build(); synchronized (replies) { replies.put(reply.getId(), reply); } @@ -1689,7 +1595,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 +1604,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void run() { markReplyKnown(reply); } - }, "Mark " + reply + " read."); + }, 10, TimeUnit.SECONDS); return reply; } @@ -1941,6 +1847,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(); @@ -1951,6 +1858,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider webOfTrustUpdater.stop(); updateChecker.stop(); soneDownloader.stop(); + soneDownloaders.shutdown(); identityManager.stop(); } @@ -2006,7 +1914,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider for (Post post : sone.getPosts()) { String postPrefix = sonePrefix + "/Posts/" + postCounter++; configuration.getStringValue(postPrefix + "/ID").setValue(post.getId()); - configuration.getStringValue(postPrefix + "/Recipient").setValue((post.getRecipient() != null) ? post.getRecipient().getId() : null); + configuration.getStringValue(postPrefix + "/Recipient").setValue(post.getRecipientId().orNull()); configuration.getLongValue(postPrefix + "/Time").setValue(post.getTime()); configuration.getStringValue(postPrefix + "/Text").setValue(post.getText()); } @@ -2017,7 +1925,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider for (PostReply reply : sone.getReplies()) { String replyPrefix = sonePrefix + "/Replies/" + replyCounter++; configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getId()); - configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPost().getId()); + configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPostId()); configuration.getLongValue(replyPrefix + "/Time").setValue(reply.getTime()); configuration.getStringValue(replyPrefix + "/Text").setValue(reply.getText()); } @@ -2135,8 +2043,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider /* save Sone following times. */ soneCounter = 0; synchronized (soneFollowingTimes) { - for (Entry soneFollowingTime : soneFollowingTimes.entrySet()) { - configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey().getId()); + for (Entry soneFollowingTime : soneFollowingTimes.entrySet()) { + configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey()); configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").setValue(soneFollowingTime.getValue()); ++soneCounter; } @@ -2185,7 +2093,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider /** * Loads the configuration. */ - @SuppressWarnings("unchecked") private void loadConfiguration() { /* create options. */ options.addIntegerOption("InsertionDelay", new DefaultOption(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new OptionWatcher() { @@ -2254,13 +2161,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider break; } long time = configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").getValue(Long.MAX_VALUE); - Sone followedSone = getSone(soneId); - if (followedSone == null) { - logger.log(Level.WARNING, String.format("Ignoring Sone with invalid ID: %s", soneId)); - } else { - synchronized (soneFollowingTimes) { - soneFollowingTimes.put(getSone(soneId), time); - } + synchronized (soneFollowingTimes) { + soneFollowingTimes.put(soneId, time); } ++soneCounter; } @@ -2319,23 +2221,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 @@ -2346,7 +2231,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider OwnIdentity ownIdentity = ownIdentityAddedEvent.ownIdentity(); logger.log(Level.FINEST, String.format("Adding OwnIdentity: %s", ownIdentity)); if (ownIdentity.hasContext("Sone")) { - trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet())); addLocalSone(ownIdentity); } } @@ -2394,7 +2278,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider public void run() { Sone sone = getRemoteSone(identity.getId(), false); sone.setIdentity(identity); - sone.setLatestEdition(Optional.fromNullable(Longs.tryParse(identity.getProperty("Sone.LatestEdition"))).or(sone.getLatestEdition())); + sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition())); soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } @@ -2425,14 +2309,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider /* some local identity still trusts this identity, don’t remove. */ return; } - Sone sone = getSone(identity.getId(), false); - if (sone == null) { + Optional sone = getSone(identity.getId()); + if (!sone.isPresent()) { /* TODO - we don’t have the Sone anymore. should this happen? */ return; } synchronized (posts) { synchronized (knownPosts) { - for (Post post : sone.getPosts()) { + for (Post post : sone.get().getPosts()) { posts.remove(post.getId()); eventBus.post(new PostRemovedEvent(post)); } @@ -2440,7 +2324,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } synchronized (replies) { synchronized (knownReplies) { - for (PostReply reply : sone.getReplies()) { + for (PostReply reply : sone.get().getReplies()) { replies.remove(reply.getId()); eventBus.post(new PostReplyRemovedEvent(reply)); } @@ -2449,7 +2333,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider synchronized (sones) { sones.remove(identity.getId()); } - eventBus.post(new SoneRemovedEvent(sone)); + eventBus.post(new SoneRemovedEvent(sone.get())); } /**