X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=5103b7d527afe1b22082cdaf4f50f6de229cc857;hp=862cc789778cfd6e255e44013e1a5bd602340dee;hb=c2e868714435ac7c75d77d1911d0dfb00393d051;hpb=c30ad3a3fcfc66d0c1757a50afe413d9942d7793 diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 862cc78..5103b7d 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -56,11 +55,7 @@ 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; @@ -68,6 +63,13 @@ 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.database.Database; +import net.pterodactylus.sone.database.DatabaseException; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.PostReplyProvider; +import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; import net.pterodactylus.sone.freenet.wot.Identity; @@ -87,9 +89,14 @@ import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.thread.NamedThreadFactory; +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.HashMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; @@ -170,30 +177,15 @@ 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(); - - /** All known replies. */ - private final Set knownReplies = new HashSet(); + /** The post database. */ + private final Database database; /** All bookmarked posts. */ /* synchronize access on itself. */ private final Set bookmarkedPosts = new HashSet(); /** Trusted identities, sorted by own identities. */ - private final Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); + private final Multimap trustedIdentities = Multimaps.synchronizedSetMultimap(HashMultimap.create()); /** All known albums. */ private final Map albums = new HashMap(); @@ -223,13 +215,11 @@ 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 + * @param database + * The database */ @Inject - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, PostBuilderFactory postBuilderFactory, PostReplyBuilderFactory postReplyBuilderFactory) { + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -239,8 +229,7 @@ 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; + this.database = database; } // @@ -340,25 +329,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } /** - * Returns all Sones, remote and local. - * - * @return All Sones - */ - 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 + * {@inheritDocs} */ - public Sone getSone(String id) { - return getSone(id, true); + @Override + public Collection getSones() { + synchronized (sones) { + return ImmutableSet.copyOf(sones.values()); + } } /** @@ -367,52 +344,29 @@ 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() { + return FluentIterable.from(sones.values()).filter(new Predicate() { @Override public boolean apply(Sone sone) { return sone.isLocal(); } - }); + }).toSet(); } } @@ -442,19 +396,18 @@ 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() { + return FluentIterable.from(sones.values()).filter(new Predicate() { @Override public boolean apply(Sone sone) { return !sone.isLocal(); } - }); + }).toSet(); } } @@ -501,10 +454,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); } } @@ -521,7 +471,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, checkNotNull(origin, "origin must not be null"); checkNotNull(target, "target must not be null"); checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin’s identity must be an OwnIdentity"); - return trustedIdentities.containsKey(origin.getIdentity()) && trustedIdentities.get(origin.getIdentity()).contains(target.getIdentity()); + return trustedIdentities.containsEntry(origin.getIdentity(), target.getIdentity()); } /** @@ -530,38 +480,32 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return A new post builder */ public PostBuilder postBuilder() { - return postBuilderFactory.newPostBuilder(); + return database.newPostBuilder(); } /** * {@inheritDoc} */ @Override - public Post getPost(String postId) { - synchronized (posts) { - return posts.get(postId); - } + public Optional getPost(String postId) { + return database.getPost(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 - */ - public Set getDirectedPosts(Sone recipient) { - checkNotNull(recipient, "recipient must not be null"); - Set directedPosts = new HashSet(); - synchronized (posts) { - for (Post post : posts.values()) { - if (recipient.equals(post.getRecipient())) { - directedPosts.add(post); - } - } - } - return directedPosts; + * {@inheritDocs} + */ + @Override + public Collection getPosts(String soneId) { + return database.getPosts(soneId); + } + + /** + * {@inheritDoc} + */ + @Override + public Collection getDirectedPosts(final String recipientId) { + checkNotNull(recipientId, "recipient must not be null"); + return database.getDirectedPosts(recipientId); } /** @@ -570,35 +514,23 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return A new post reply builder */ public PostReplyBuilder postReplyBuilder() { - return postReplyBuilderFactory.newPostReplyBuilder(); + return database.newPostReplyBuilder(); } /** * {@inheritDoc} */ @Override - public PostReply getPostReply(String replyId) { - synchronized (replies) { - return replies.get(replyId); - } + public Optional getPostReply(String replyId) { + return database.getPostReply(replyId); } /** * {@inheritDoc} */ @Override - 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); - } - } - } - Collections.sort(replies, Reply.TIME_COMPARATOR); - return replies; + public List getReplies(final String postId) { + return database.getReplies(postId); } /** @@ -670,9 +602,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, Set posts = new HashSet(); synchronized (bookmarkedPosts) { for (String bookmarkedPostId : bookmarkedPosts) { - Post post = getPost(bookmarkedPostId); - if (post != null) { - posts.add(post); + Optional post = getPost(bookmarkedPostId); + if (post.isPresent()) { + posts.add(post.get()); } } } @@ -809,6 +741,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, "Given OwnIdentity is null!"); return null; } + logger.info(String.format("Adding Sone from OwnIdentity: %s", ownIdentity)); synchronized (sones) { final Sone sone; try { @@ -869,7 +802,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return null; } synchronized (sones) { - final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity); + final Sone sone = getRemoteSone(identity.getId(), true); + if (sone.isLocal()) { + return sone; + } + sone.setIdentity(identity); boolean newSone = sone.getRequestUri() == null; sone.setRequestUri(SoneUri.create(identity.getRequestUri())); sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); @@ -917,16 +854,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, if (!soneFollowingTimes.containsKey(soneId)) { long now = System.currentTimeMillis(); soneFollowingTimes.put(soneId, now); - Sone followedSone = getSone(soneId); - if (followedSone == null) { + Optional followedSone = getSone(soneId); + if (!followedSone.isPresent()) { return; } - for (Post post : followedSone.getPosts()) { + 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); } @@ -1051,73 +988,60 @@ 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()) { - posts.remove(post.getId()); - if (!sone.getPosts().contains(post)) { - eventBus.post(new PostRemovedEvent(post)); - } - } + /* find removed posts. */ + Collection existingPosts = database.getPosts(sone.getId()); + for (Post oldPost : existingPosts) { + if (!sone.getPosts().contains(oldPost)) { + eventBus.post(new PostRemovedEvent(oldPost)); } - List storedPosts = storedSone.getPosts(); - synchronized (knownPosts) { - for (Post post : sone.getPosts()) { - PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); - postBuilder.copyPost(post).from(storedSone.getId()); - 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(newPost.getId(), newPost); - } + } + /* find new posts. */ + for (Post newPost : sone.getPosts()) { + if (existingPosts.contains(newPost)) { + continue; + } + if (newPost.getTime() < getSoneFollowingTime(sone)) { + newPost.setKnown(true); + } else if (!newPost.isKnown()) { + eventBus.post(new NewPostFoundEvent(newPost)); } } - synchronized (replies) { - if (!soneRescueMode) { - for (PostReply reply : storedSone.getReplies()) { - replies.remove(reply.getId()); - if (!sone.getReplies().contains(reply)) { - eventBus.post(new PostReplyRemovedEvent(reply)); - } + /* store posts. */ + database.storePosts(sone, sone.getPosts()); + if (!soneRescueMode) { + for (PostReply reply : storedSone.get().getReplies()) { + if (!sone.getReplies().contains(reply)) { + eventBus.post(new PostReplyRemovedEvent(reply)); } } - Set storedReplies = storedSone.getReplies(); - synchronized (knownReplies) { - for (PostReply reply : sone.getReplies()) { - reply.setKnown(knownReplies.contains(reply.getId())); - if (!storedReplies.contains(reply)) { - if (reply.getTime() < getSoneFollowingTime(sone)) { - knownReplies.add(reply.getId()); - reply.setKnown(true); - } else if (!knownReplies.contains(reply.getId())) { - eventBus.post(new NewPostReplyFoundEvent(reply)); - } - } - replies.put(reply.getId(), reply); - } + } + Set storedReplies = storedSone.get().getReplies(); + for (PostReply reply : sone.getReplies()) { + if (storedReplies.contains(reply)) { + continue; + } + if (reply.getTime() < getSoneFollowingTime(sone)) { + reply.setKnown(true); + } else if (!reply.isKnown()) { + eventBus.post(new NewPostReplyFoundEvent(reply)); } } + database.storePostReplies(sone, sone.getReplies()); synchronized (albums) { synchronized (images) { - for (Album album : storedSone.getAlbums()) { + for (Album album : storedSone.get().getRootAlbum().getAlbums()) { albums.remove(album.getId()); for (Image image : album.getImages()) { images.remove(image.getId()); } } - for (Album album : sone.getAlbums()) { + for (Album album : sone.getRootAlbum().getAlbums()) { albums.put(album.getId(), album); for (Image image : album.getImages()) { images.put(image.getId(), image); @@ -1125,36 +1049,15 @@ 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()); + synchronized (sones) { + sone.setOptions(storedSone.get().getOptions()); + sone.setKnown(storedSone.get().isKnown()); + sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle); + if (sone.isLocal()) { + soneInserters.get(storedSone.get()).setSone(sone); + touchConfiguration(); } - storedSone.setLatestEdition(sone.getLatestEdition()); + sones.put(sone.getId(), sone); } } } @@ -1220,6 +1123,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.FINE, String.format("Tried to load non-local Sone: %s", sone)); return; } + logger.info(String.format("Loading local Sone: %s", sone)); /* initialize options. */ sone.getOptions().addBooleanOption("AutoFollow", new DefaultOption(false)); @@ -1273,7 +1177,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - PostBuilder postBuilder = postBuilderFactory.newPostBuilder().withId(postId).from(sone.getId()).withTime(postTime).withText(postText); + PostBuilder postBuilder = postBuilder().withId(postId).from(sone.getId()).withTime(postTime).withText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { postBuilder.to(postRecipientId); } @@ -1295,8 +1199,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, "Invalid reply found, aborting load!"); return; } - PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder(); - postReplyBuilder.withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText); + PostReplyBuilder postReplyBuilder = postReplyBuilder().withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText); replies.add(postReplyBuilder.build()); } @@ -1416,7 +1319,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, for (String friendId : friends) { followSone(sone, friendId); } - sone.setAlbums(topLevelAlbums); + for (Album album : sone.getRootAlbum().getAlbums()) { + sone.getRootAlbum().removeAlbum(album); + } + for (Album album : topLevelAlbums) { + sone.getRootAlbum().addAlbum(album); + } soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); } synchronized (knownSones) { @@ -1424,16 +1332,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, knownSones.add(friend); } } - synchronized (knownPosts) { - for (Post post : posts) { - knownPosts.add(post.getId()); - } + database.storePosts(sone, posts); + for (Post post : posts) { + post.setKnown(true); } - synchronized (knownReplies) { - for (PostReply reply : replies) { - knownReplies.add(reply.getId()); - } + database.storePostReplies(sone, replies); + for (PostReply reply : replies) { + reply.setKnown(true); } + + logger.info(String.format("Sone loaded successfully: %s", sone)); } /** @@ -1476,7 +1384,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); } @@ -1494,22 +1402,20 @@ 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; } - PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); + PostBuilder postBuilder = database.newPostBuilder(); postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim()); - if (recipient != null) { - postBuilder.to(recipient.getId()); + if (recipient.isPresent()) { + postBuilder.to(recipient.get().getId()); } final Post post = postBuilder.build(); - synchronized (posts) { - posts.put(post.getId(), post); - } + database.storePost(post); eventBus.post(new NewPostFoundEvent(post)); sone.addPost(post); touchConfiguration(); @@ -1537,10 +1443,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, String.format("Tried to delete post of non-local Sone: %s", post.getSone())); return; } - post.getSone().removePost(post); - synchronized (posts) { - posts.remove(post.getId()); - } + database.removePost(post); eventBus.post(new PostRemovedEvent(post)); markPostKnown(post); touchConfiguration(); @@ -1555,13 +1458,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ public void markPostKnown(Post post) { post.setKnown(true); - synchronized (knownPosts) { - eventBus.post(new MarkPostKnownEvent(post)); - if (knownPosts.add(post.getId())) { - touchConfiguration(); - } - } - for (PostReply reply : getReplies(post)) { + eventBus.post(new MarkPostKnownEvent(post)); + touchConfiguration(); + for (PostReply reply : getReplies(post.getId())) { markReplyKnown(reply); } } @@ -1628,15 +1527,11 @@ 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; } - PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder(); + PostReplyBuilder postReplyBuilder = postReplyBuilder(); postReplyBuilder.randomId().from(sone.getId()).to(post.getId()).currentTime().withText(text.trim()); final PostReply reply = postReplyBuilder.build(); - synchronized (replies) { - replies.put(reply.getId(), reply); - } - synchronized (knownReplies) { - eventBus.post(new NewPostReplyFoundEvent(reply)); - } + database.storePostReply(reply); + eventBus.post(new NewPostReplyFoundEvent(reply)); sone.addReply(reply); touchConfiguration(); localElementTicker.schedule(new Runnable() { @@ -1664,13 +1559,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply)); return; } - synchronized (replies) { - replies.remove(reply.getId()); - } - synchronized (knownReplies) { - markReplyKnown(reply); - knownReplies.remove(reply.getId()); - } + database.removePostReply(reply); + markReplyKnown(reply); sone.removeReply(reply); touchConfiguration(); } @@ -1683,12 +1573,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * The reply to mark as known */ public void markReplyKnown(PostReply reply) { + boolean previouslyKnown = reply.isKnown(); reply.setKnown(true); - synchronized (knownReplies) { - eventBus.post(new MarkPostReplyKnownEvent(reply)); - if (knownReplies.add(reply.getId())) { - touchConfiguration(); - } + eventBus.post(new MarkPostReplyKnownEvent(reply)); + if (!previouslyKnown) { + touchConfiguration(); } } @@ -1700,7 +1589,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The new album */ public Album createAlbum(Sone sone) { - return createAlbum(sone, null); + return createAlbum(sone, sone.getRootAlbum()); } /** @@ -1719,11 +1608,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, albums.put(album.getId(), album); } album.setSone(sone); - if (parent != null) { - parent.addAlbum(album); - } else { - sone.addAlbum(album); - } + parent.addAlbum(album); return album; } @@ -1740,11 +1625,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, if (!album.isEmpty()) { return; } - if (album.getParent() == null) { - album.getSone().removeAlbum(album); - } else { - album.getParent().removeAlbum(album); - } + album.getParent().removeAlbum(album); synchronized (albums) { albums.remove(album.getId()); } @@ -1865,6 +1746,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, identityManager.start(); webOfTrustUpdater.init(); webOfTrustUpdater.start(); + database.start(); } /** @@ -1899,9 +1781,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } } saveConfiguration(); + database.stop(); webOfTrustUpdater.stop(); updateChecker.stop(); soneDownloader.stop(); + soneDownloaders.shutdown(); identityManager.stop(); } @@ -1957,7 +1841,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()); } @@ -1968,7 +1852,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()); } @@ -1996,7 +1880,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = sone.getAllAlbums(); + List albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); int albumCounter = 0; for (Album album : albums) { @@ -2004,7 +1888,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId()); configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle()); configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription()); - configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent() == null ? null : album.getParent().getId()); + configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId()); configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId()); } configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null); @@ -2095,22 +1979,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } /* save known posts. */ - int postCounter = 0; - synchronized (knownPosts) { - for (String knownPostId : knownPosts) { - configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(knownPostId); - } - configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null); - } - - /* save known replies. */ - int replyCounter = 0; - synchronized (knownReplies) { - for (String knownReplyId : knownReplies) { - configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId); - } - configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null); - } + database.save(); /* save bookmarked posts. */ int bookmarkedPostCounter = 0; @@ -2126,6 +1995,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } catch (ConfigurationException ce1) { logger.log(Level.SEVERE, "Could not store configuration!", ce1); + } catch (DatabaseException de1) { + logger.log(Level.SEVERE, "Could not save database!", de1); } finally { synchronized (configuration) { storingConfiguration = false; @@ -2136,7 +2007,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() { @@ -2205,41 +2075,12 @@ 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(soneId, time); - } + synchronized (soneFollowingTimes) { + soneFollowingTimes.put(soneId, time); } ++soneCounter; } - /* load known posts. */ - int postCounter = 0; - while (true) { - String knownPostId = configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").getValue(null); - if (knownPostId == null) { - break; - } - synchronized (knownPosts) { - knownPosts.add(knownPostId); - } - } - - /* load known replies. */ - int replyCounter = 0; - while (true) { - String knownReplyId = configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").getValue(null); - if (knownReplyId == null) { - break; - } - synchronized (knownReplies) { - knownReplies.add(knownReplyId); - } - } - /* load bookmarked posts. */ int bookmarkedPostCounter = 0; while (true) { @@ -2280,7 +2121,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); } } @@ -2295,7 +2135,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public void ownIdentityRemoved(OwnIdentityRemovedEvent ownIdentityRemovedEvent) { OwnIdentity ownIdentity = ownIdentityRemovedEvent.ownIdentity(); logger.log(Level.FINEST, String.format("Removing OwnIdentity: %s", ownIdentity)); - trustedIdentities.remove(ownIdentity); + trustedIdentities.removeAll(ownIdentity); } /** @@ -2308,7 +2148,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public void identityAdded(IdentityAddedEvent identityAddedEvent) { Identity identity = identityAddedEvent.identity(); logger.log(Level.FINEST, String.format("Adding Identity: %s", identity)); - trustedIdentities.get(identityAddedEvent.ownIdentity()).add(identity); + trustedIdentities.put(identityAddedEvent.ownIdentity(), identity); addRemoteSone(identity); } @@ -2327,6 +2167,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @SuppressWarnings("synthetic-access") public void run() { Sone sone = getRemoteSone(identity.getId(), false); + if (sone.isLocal()) { + return; + } sone.setIdentity(identity); sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition())); soneDownloader.addSone(sone); @@ -2345,9 +2188,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public void identityRemoved(IdentityRemovedEvent identityRemovedEvent) { OwnIdentity ownIdentity = identityRemovedEvent.ownIdentity(); Identity identity = identityRemovedEvent.identity(); - trustedIdentities.get(ownIdentity).remove(identity); + trustedIdentities.remove(ownIdentity, identity); boolean foundIdentity = false; - for (Entry> trustedIdentity : trustedIdentities.entrySet()) { + for (Entry> trustedIdentity : trustedIdentities.asMap().entrySet()) { if (trustedIdentity.getKey().equals(ownIdentity)) { continue; } @@ -2359,31 +2202,23 @@ 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()) { - posts.remove(post.getId()); - eventBus.post(new PostRemovedEvent(post)); - } - } + database.removePosts(sone.get()); + for (Post post : sone.get().getPosts()) { + eventBus.post(new PostRemovedEvent(post)); } - synchronized (replies) { - synchronized (knownReplies) { - for (PostReply reply : sone.getReplies()) { - replies.remove(reply.getId()); - eventBus.post(new PostReplyRemovedEvent(reply)); - } - } + database.removePostReplies(sone.get()); + for (PostReply reply : sone.get().getReplies()) { + eventBus.post(new PostReplyRemovedEvent(reply)); } synchronized (sones) { sones.remove(identity.getId()); } - eventBus.post(new SoneRemovedEvent(sone)); + eventBus.post(new SoneRemovedEvent(sone.get())); } /**