X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=bafe7a80406f5c730ff6876be6db07de5ec461c2;hp=565aec4679b93e9d98117cefe98833a68595fb7f;hb=d535f744751ba449e0f34fe3a42f1020988f14be;hpb=f187773b693959d19248b31d9b2091c37114b60b diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 565aec4..bafe7a8 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 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 @@ -17,55 +17,83 @@ package net.pterodactylus.sone.core; +import static com.google.common.base.Preconditions.checkArgument; +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; import java.util.Map; -import java.util.Set; 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; import net.pterodactylus.sone.core.Options.DefaultOption; import net.pterodactylus.sone.core.Options.Option; import net.pterodactylus.sone.core.Options.OptionWatcher; +import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; +import net.pterodactylus.sone.core.event.MarkPostKnownEvent; +import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; +import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; +import net.pterodactylus.sone.core.event.NewPostFoundEvent; +import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent; +import net.pterodactylus.sone.core.event.NewSoneFoundEvent; +import net.pterodactylus.sone.core.event.PostRemovedEvent; +import net.pterodactylus.sone.core.event.PostReplyRemovedEvent; +import net.pterodactylus.sone.core.event.SoneLockedEvent; +import net.pterodactylus.sone.core.event.SoneRemovedEvent; +import net.pterodactylus.sone.core.event.SoneUnlockedEvent; 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; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.data.TemporaryImage; -import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.IdentityListener; import net.pterodactylus.sone.freenet.wot.IdentityManager; import net.pterodactylus.sone.freenet.wot.OwnIdentity; -import net.pterodactylus.sone.freenet.wot.Trust; -import net.pterodactylus.sone.freenet.wot.WebOfTrustException; +import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent; +import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent; +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.Ticker; -import net.pterodactylus.util.validation.EqualityValidator; -import net.pterodactylus.util.validation.IntegerRangeValidator; -import net.pterodactylus.util.validation.OrValidator; -import net.pterodactylus.util.validation.Validation; -import net.pterodactylus.util.version.Version; +import net.pterodactylus.util.thread.NamedThreadFactory; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; + import freenet.keys.FreenetURI; /** @@ -73,19 +101,22 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener, ImageInsertListener { +public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider { /** The logger. */ private static final Logger logger = Logging.getLogger(Core.class); + /** The start time. */ + private final long startupTime = System.currentTimeMillis(); + /** The options. */ private final Options options = new Options(); /** The preferences. */ private final Preferences preferences = new Preferences(options); - /** The core listener manager. */ - private final CoreListenerManager coreListenerManager = new CoreListenerManager(this); + /** The event bus. */ + private final EventBus eventBus; /** The configuration. */ private Configuration configuration; @@ -106,70 +137,75 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis private final ImageInserter imageInserter; /** Sone downloader thread-pool. */ - private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10); + private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10, new NamedThreadFactory("Sone Downloader %2$d")); /** The update checker. */ private final UpdateChecker updateChecker; + /** The trust updater. */ + private final WebOfTrustUpdater webOfTrustUpdater; + /** The FCP interface. */ 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. */ private final Set lockedSones = new HashSet(); /** Sone inserters. */ - /* synchronize access on this on localSones. */ + /* synchronize access on this on sones. */ private final Map soneInserters = new HashMap(); /** Sone rescuers. */ - /* synchronize access on this on localSones. */ + /* synchronize access on this on sones. */ private final Map soneRescuers = new HashMap(); - /** All local Sones. */ - /* synchronize access on this on itself. */ - private Map localSones = new HashMap(); - - /** All remote Sones. */ + /** All Sones. */ /* synchronize access on this on itself. */ - private Map remoteSones = new HashMap(); + private final Map sones = new HashMap(); /** All known Sones. */ - private Set knownSones = new HashSet(); + private final Set knownSones = new HashSet(); + + /** The post builder. */ + private final PostBuilderFactory postBuilderFactory; /** All posts. */ - private Map posts = new HashMap(); + private final Map posts = new HashMap(); /** All known posts. */ - private Set knownPosts = new HashSet(); + private final Set knownPosts = new HashSet(); + + /** The post reply builder factory. */ + private final PostReplyBuilderFactory postReplyBuilderFactory; /** All replies. */ - private Map replies = new HashMap(); + private final Map replies = new HashMap(); /** All known replies. */ - private Set knownReplies = new HashSet(); + private final Set knownReplies = new HashSet(); /** All bookmarked posts. */ /* synchronize access on itself. */ - private Set bookmarkedPosts = new HashSet(); + private final Set bookmarkedPosts = new HashSet(); /** Trusted identities, sorted by own identities. */ - private Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); + private final Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); /** All known albums. */ - private Map albums = new HashMap(); + private final Map albums = new HashMap(); /** All known images. */ - private Map images = new HashMap(); + private final Map images = new HashMap(); /** All temporary images. */ - private Map temporaryImages = new HashMap(); + private final Map temporaryImages = new HashMap(); /** Ticker for threads that mark own elements as known. */ - private Ticker localElementTicker = new Ticker(); + private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1); /** The time the configuration was last touched. */ private volatile long lastConfigurationUpdate; @@ -183,46 +219,44 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The freenet interface * @param identityManager * The identity manager - */ - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager) { + * @param webOfTrustUpdater + * 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, PostBuilderFactory postBuilderFactory, PostReplyBuilderFactory postReplyBuilderFactory) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; this.identityManager = identityManager; this.soneDownloader = new SoneDownloader(this, freenetInterface); - this.imageInserter = new ImageInserter(this, freenetInterface); - this.updateChecker = new UpdateChecker(freenetInterface); + this.imageInserter = new ImageInserter(freenetInterface); + this.updateChecker = new UpdateChecker(eventBus, freenetInterface); + this.webOfTrustUpdater = webOfTrustUpdater; + this.eventBus = eventBus; + this.postBuilderFactory = postBuilderFactory; + this.postReplyBuilderFactory = postReplyBuilderFactory; } // - // LISTENER MANAGEMENT + // ACCESSORS // /** - * Adds a new core listener. + * Returns the time Sone was started. * - * @param coreListener - * The listener to add + * @return The startup time (in milliseconds since Jan 1, 1970 UTC) */ - public void addCoreListener(CoreListener coreListener) { - coreListenerManager.addListener(coreListener); + public long getStartupTime() { + return startupTime; } /** - * Removes a core listener. - * - * @param coreListener - * The listener to remove - */ - public void removeCoreListener(CoreListener coreListener) { - coreListenerManager.removeListener(coreListener); - } - - // - // ACCESSORS - // - - /** * Sets the configuration to use. This will automatically save the current * configuration to the given configuration. * @@ -279,8 +313,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone rescuer for the given Sone */ public SoneRescuer getSoneRescuer(Sone sone) { - Validation.begin().isNotNull("Sone", sone).check().is("Local Sone", isLocalSone(sone)).check(); - synchronized (localSones) { + checkNotNull(sone, "sone must not be null"); + checkArgument(sone.isLocal(), "sone must be local"); + synchronized (sones) { SoneRescuer soneRescuer = soneRescuers.get(sone); if (soneRescuer == null) { soneRescuer = new SoneRescuer(this, soneDownloader, sone); @@ -310,23 +345,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return All Sones */ public Set getSones() { - Set allSones = new HashSet(); - allSones.addAll(getLocalSones()); - allSones.addAll(getRemoteSones()); - return allSones; - } - - /** - * 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); + return new HashSet(sones.values()); } /** @@ -335,19 +354,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * * @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) { - if (isLocalSone(id)) { - return getLocalSone(id); + public Sone getSone(String id) { + synchronized (sones) { + return sones.get(id); } - return getRemoteSone(id, create); } /** @@ -359,33 +373,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * otherwise */ public boolean hasSone(String id) { - return isLocalSone(id) || isRemoteSone(id); - } - - /** - * Returns whether the given Sone is a local Sone. - * - * @param sone - * The Sone to check for its locality - * @return {@code true} if the given Sone is local, {@code false} otherwise - */ - public boolean isLocalSone(Sone sone) { - synchronized (localSones) { - return localSones.containsKey(sone.getId()); - } - } - - /** - * Returns whether the given ID is the ID of a local Sone. - * - * @param id - * The Sone ID to check for its locality - * @return {@code true} if the given ID is a local Sone, {@code false} - * otherwise - */ - public boolean isLocalSone(String id) { - synchronized (localSones) { - return localSones.containsKey(id); + synchronized (sones) { + return sones.containsKey(id); } } @@ -394,21 +383,16 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * * @return All local Sones */ - public Set getLocalSones() { - synchronized (localSones) { - return new HashSet(localSones.values()); - } - } + public Collection getLocalSones() { + synchronized (sones) { + return Collections2.filter(sones.values(), new Predicate() { - /** - * Returns the local Sone with the given ID. - * - * @param id - * The ID of the Sone to get - * @return The Sone with the given ID - */ - public Sone getLocalSone(String id) { - return getLocalSone(id, true); + @Override + public boolean apply(Sone sone) { + return sone.isLocal(); + } + }); + } } /** @@ -422,11 +406,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone with the given ID, or {@code null} */ public Sone getLocalSone(String id, boolean create) { - synchronized (localSones) { - Sone sone = localSones.get(id); + synchronized (sones) { + Sone sone = sones.get(id); if ((sone == null) && create) { - sone = new Sone(id); - localSones.put(id, sone); + sone = new Sone(id, true); + sones.put(id, sone); + } + if ((sone != null) && !sone.isLocal()) { + sone = new Sone(id, true); + sones.put(id, sone); } return sone; } @@ -437,9 +425,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * * @return All remote Sones */ - public Set getRemoteSones() { - synchronized (remoteSones) { - return new HashSet(remoteSones.values()); + public Collection getRemoteSones() { + synchronized (sones) { + return Collections2.filter(sones.values(), new Predicate() { + + @Override + public boolean apply(Sone sone) { + return !sone.isLocal(); + } + }); } } @@ -454,45 +448,17 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone with the given ID */ public Sone getRemoteSone(String id, boolean create) { - synchronized (remoteSones) { - Sone sone = remoteSones.get(id); + synchronized (sones) { + Sone sone = sones.get(id); if ((sone == null) && create && (id != null) && (id.length() == 43)) { - sone = new Sone(id); - remoteSones.put(id, sone); + sone = new Sone(id, false); + sones.put(id, sone); } return sone; } } /** - * Returns whether the given Sone is a remote Sone. - * - * @param sone - * The Sone to check - * @return {@code true} if the given Sone is a remote Sone, {@code false} - * otherwise - */ - public boolean isRemoteSone(Sone sone) { - synchronized (remoteSones) { - return remoteSones.containsKey(sone.getId()); - } - } - - /** - * Returns whether the Sone with the given ID is a remote Sone. - * - * @param id - * The ID of the Sone to check - * @return {@code true} if the Sone with the given ID is a remote Sone, - * {@code false} otherwise - */ - public boolean isRemoteSone(String id) { - synchronized (remoteSones) { - return remoteSones.containsKey(id); - } - } - - /** * Returns whether the given Sone has been modified. * * @param sone @@ -531,106 +497,70 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return {@code true} if the target Sone is trusted by the origin Sone */ public boolean isSoneTrusted(Sone origin, Sone target) { - Validation.begin().isNotNull("Origin", origin).isNotNull("Target", target).check().isInstanceOf("Origin’s OwnIdentity", origin.getIdentity(), OwnIdentity.class).check(); + 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()); } /** - * 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 Post(postId); - posts.put(postId, post); - } - return post; + return 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 + * {@inheritDoc} */ - public Set getDirectedPosts(Sone recipient) { - Validation.begin().isNotNull("Recipient", recipient).check(); - Set directedPosts = new HashSet(); + @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 (post.getRecipient() != null) && (post.getRecipient().getId().equals(recipientId)); } - } + }); } - return directedPosts; } - /** - * Returns the reply with the given ID. If there is no reply with the given - * ID yet, a new one is created. + * Returns a post reply builder. * - * @param replyId - * The ID of the reply to get - * @return The reply + * @return A new post reply builder */ - public PostReply getReply(String replyId) { - return getReply(replyId, true); + public PostReplyBuilder postReplyBuilder() { + return postReplyBuilderFactory.newPostReplyBuilder(); } /** - * 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. - * - * @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 + * {@inheritDoc} */ - public PostReply getReply(String replyId, boolean create) { + @Override + public PostReply getPostReply(String replyId) { synchronized (replies) { - PostReply reply = replies.get(replyId); - if (create && (reply == null)) { - reply = new PostReply(replyId); - replies.put(replyId, reply); - } - return reply; + return 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} */ + @Override public List getReplies(Post post) { Set sones = getSones(); List replies = new ArrayList(); @@ -714,7 +644,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis 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); } @@ -821,7 +751,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void lockSone(Sone sone) { synchronized (lockedSones) { if (lockedSones.add(sone)) { - coreListenerManager.fireSoneLocked(sone); + eventBus.post(new SoneLockedEvent(sone)); } } } @@ -836,7 +766,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void unlockSone(Sone sone) { synchronized (lockedSones) { if (lockedSones.remove(sone)) { - coreListenerManager.fireSoneUnlocked(sone); + eventBus.post(new SoneUnlockedEvent(sone)); } } } @@ -853,21 +783,20 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Given OwnIdentity is null!"); return null; } - synchronized (localSones) { + synchronized (sones) { final Sone sone; try { - sone = getLocalSone(ownIdentity.getId()).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); + sone = getLocalSone(ownIdentity.getId(), true).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); } catch (MalformedURLException mue1) { - logger.log(Level.SEVERE, "Could not convert the Identity’s URIs to Freenet URIs: " + ownIdentity.getInsertUri() + ", " + ownIdentity.getRequestUri(), mue1); + 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(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)); sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); sone.setKnown(true); /* TODO - load posts ’n stuff */ - localSones.put(ownIdentity.getId(), sone); - final SoneInserter soneInserter = new SoneInserter(this, freenetInterface, sone); - soneInserter.addSoneInsertListener(this); + sones.put(ownIdentity.getId(), sone); + final SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, sone); soneInserters.put(sone, soneInserter); sone.setStatus(SoneStatus.idle); loadSone(sone); @@ -884,10 +813,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The created Sone */ public Sone createSone(OwnIdentity ownIdentity) { - try { - ownIdentity.addContext("Sone"); - } catch (WebOfTrustException wote1) { - logger.log(Level.SEVERE, "Could not add “Sone” context to own identity: " + ownIdentity, wote1); + if (!webOfTrustUpdater.addContextWait(ownIdentity, "Sone")) { + logger.log(Level.SEVERE, String.format("Could not add “Sone” context to own identity: %s", ownIdentity)); return null; } Sone sone = addLocalSone(ownIdentity); @@ -898,7 +825,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis 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; } @@ -915,10 +842,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Given Identity is null!"); return null; } - synchronized (remoteSones) { + 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) { @@ -926,10 +853,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } sone.setKnown(!newSone); if (newSone) { - coreListenerManager.fireNewSoneFound(sone); + eventBus.post(new NewSoneFoundEvent(sone)); for (Sone localSone : getLocalSones()) { if (localSone.getOptions().getBooleanOption("AutoFollow").get()) { - followSone(localSone, sone); + followSone(localSone, sone.getId()); } } } @@ -957,33 +884,17 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the Sone to follow */ public void followSone(Sone sone, String soneId) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Sone ID", soneId).check(); - 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) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Followed Sone", followedSone).check(); - sone.addFriend(followedSone.getId()); + checkNotNull(sone, "sone must not be null"); + checkNotNull(soneId, "soneId must not be null"); + sone.addFriend(soneId); synchronized (soneFollowingTimes) { - if (!soneFollowingTimes.containsKey(followedSone)) { + if (!soneFollowingTimes.containsKey(soneId)) { long now = System.currentTimeMillis(); - soneFollowingTimes.put(followedSone, now); + soneFollowingTimes.put(soneId, now); + Sone followedSone = getSone(soneId); + if (followedSone == null) { + return; + } for (Post post : followedSone.getPosts()) { if (post.getTime() < now) { markPostKnown(post); @@ -1008,55 +919,22 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the Sone being unfollowed */ public void unfollowSone(Sone sone, String soneId) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Sone ID", soneId).check(); - 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) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Unfollowed Sone", unfollowedSone).check(); - sone.removeFriend(unfollowedSone.getId()); + checkNotNull(sone, "sone must not be null"); + checkNotNull(soneId, "soneId must not be null"); + 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(); } /** - * Retrieves the trust relationship from the origin to the target. If the - * trust relationship can not be retrieved, {@code null} is returned. - * - * @see Identity#getTrust(OwnIdentity) - * @param origin - * The origin of the trust tree - * @param target - * The target of the trust - * @return The trust relationship - */ - public Trust getTrust(Sone origin, Sone target) { - if (!isLocalSone(origin)) { - logger.log(Level.WARNING, "Tried to get trust from remote Sone: %s", origin); - return null; - } - return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity()); - } - - /** * Sets the trust value of the given origin Sone for the target Sone. * * @param origin @@ -1067,12 +945,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The trust value (from {@code -100} to {@code 100}) */ public void setTrust(Sone origin, Sone target, int trustValue) { - Validation.begin().isNotNull("Trust Origin", origin).check().isInstanceOf("Trust Origin", origin.getIdentity(), OwnIdentity.class).isNotNull("Trust Target", target).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check(); - try { - ((OwnIdentity) origin.getIdentity()).setTrust(target.getIdentity(), trustValue, preferences.getTrustComment()); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not set trust for Sone: " + target, wote1); - } + checkNotNull(origin, "origin must not be null"); + checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); + checkNotNull(target, "target must not be null"); + checkArgument((trustValue >= -100) && (trustValue <= 100), "trustValue must be within [-100, 100]"); + webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), trustValue, preferences.getTrustComment()); } /** @@ -1084,12 +961,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The trust target */ public void removeTrust(Sone origin, Sone target) { - Validation.begin().isNotNull("Trust Origin", origin).isNotNull("Trust Target", target).check().isInstanceOf("Trust Origin Identity", origin.getIdentity(), OwnIdentity.class).check(); - try { - ((OwnIdentity) origin.getIdentity()).removeTrust(target.getIdentity()); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not remove trust for Sone: " + target, wote1); - } + checkNotNull(origin, "origin must not be null"); + checkNotNull(target, "target must not be null"); + checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); + webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), null, null); } /** @@ -1153,7 +1028,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis if (hasSone(sone.getId())) { Sone storedSone = getSone(sone.getId()); if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) { - logger.log(Level.FINE, "Downloaded Sone %s is not newer than stored Sone %s.", new Object[] { sone, storedSone }); + logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone)); return; } synchronized (posts) { @@ -1161,23 +1036,25 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis for (Post post : storedSone.getPosts()) { posts.remove(post.getId()); if (!sone.getPosts().contains(post)) { - coreListenerManager.firePostRemoved(post); + eventBus.post(new PostRemovedEvent(post)); } } } 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()); - } else if (!knownPosts.contains(post.getId())) { - sone.setKnown(false); - coreListenerManager.fireNewPostFound(post); + 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(post.getId(), post); + posts.put(newPost.getId(), newPost); } } } @@ -1186,20 +1063,20 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis for (PostReply reply : storedSone.getReplies()) { replies.remove(reply.getId()); if (!sone.getReplies().contains(reply)) { - coreListenerManager.fireReplyRemoved(reply); + eventBus.post(new PostReplyRemovedEvent(reply)); } } } Set storedReplies = storedSone.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()); + reply.setKnown(true); } else if (!knownReplies.contains(reply.getId())) { - reply.setKnown(false); - coreListenerManager.fireNewReplyFound(reply); + eventBus.post(new NewPostReplyFoundEvent(reply)); } } replies.put(reply.getId(), reply); @@ -1258,33 +1135,28 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** * Deletes the given Sone. This will remove the Sone from the - * {@link #getLocalSone(String) local Sones}, stops its {@link SoneInserter} - * and remove the context from its identity. + * {@link #getLocalSones() local Sones}, stop its {@link SoneInserter} and + * remove the context from its identity. * * @param sone * The Sone to delete */ public void deleteSone(Sone sone) { if (!(sone.getIdentity() instanceof OwnIdentity)) { - logger.log(Level.WARNING, "Tried to delete Sone of non-own identity: %s", sone); + logger.log(Level.WARNING, String.format("Tried to delete Sone of non-own identity: %s", sone)); return; } - synchronized (localSones) { - if (!localSones.containsKey(sone.getId())) { - logger.log(Level.WARNING, "Tried to delete non-local Sone: %s", sone); + synchronized (sones) { + if (!getLocalSones().contains(sone)) { + logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); return; } - localSones.remove(sone.getId()); + sones.remove(sone.getId()); SoneInserter soneInserter = soneInserters.remove(sone); - soneInserter.removeSoneInsertListener(this); soneInserter.stop(); } - try { - ((OwnIdentity) sone.getIdentity()).removeContext("Sone"); - ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition"); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not remove context and properties from Sone: " + sone, wote1); - } + webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone"); + webOfTrustUpdater.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition"); try { configuration.getLongValue("Sone/" + sone.getId() + "/Time").setValue(null); } catch (ConfigurationException ce1) { @@ -1294,7 +1166,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** * Marks the given Sone as known. If the Sone was not {@link Post#isKnown() - * known} before, a {@link CoreListener#markSoneKnown(Sone)} event is fired. + * known} before, a {@link MarkSoneKnownEvent} is fired. * * @param sone * The Sone to mark as known @@ -1305,7 +1177,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (knownSones) { knownSones.add(sone.getId()); } - coreListenerManager.fireMarkSoneKnown(sone); + eventBus.post(new MarkSoneKnownEvent(sone)); touchConfiguration(); } } @@ -1318,8 +1190,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The Sone to load and update */ public void loadSone(Sone sone) { - if (!isLocalSone(sone)) { - logger.log(Level.FINE, "Tried to load non-local Sone: %s", sone); + if (!sone.isLocal()) { + logger.log(Level.FINE, String.format("Tried to load non-local Sone: %s", sone)); return; } @@ -1375,11 +1247,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText); + PostBuilder postBuilder = postBuilderFactory.newPostBuilder().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. */ @@ -1397,7 +1269,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Invalid reply found, aborting load!"); return; } - replies.add(getReply(replyId).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. */ @@ -1451,12 +1325,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis if (albumParentId != null) { Album parentAlbum = getAlbum(albumParentId, false); if (parentAlbum == null) { - logger.log(Level.WARNING, "Invalid parent album ID: " + albumParentId); + logger.log(Level.WARNING, String.format("Invalid parent album ID: %s", albumParentId)); return; } parentAlbum.addAlbum(album); } else { - topLevelAlbums.add(album); + if (!topLevelAlbums.contains(album)) { + topLevelAlbums.add(album); + } } } @@ -1593,21 +1469,25 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The created post */ public Post createPost(Sone sone, Sone recipient, long time, String text) { - if (!isLocalSone(sone)) { - logger.log(Level.FINE, "Tried to create post for non-local Sone: %s", sone); + 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 Post(sone, time, text); + PostBuilder postBuilder = postBuilderFactory.newPostBuilder(); + postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim()); if (recipient != null) { - post.setRecipient(recipient); + postBuilder.to(recipient.getId()); } + final Post post = postBuilder.build(); synchronized (posts) { posts.put(post.getId(), post); } - coreListenerManager.fireNewPostFound(post); + eventBus.post(new NewPostFoundEvent(post)); sone.addPost(post); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1616,7 +1496,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void run() { markPostKnown(post); } - }, "Mark " + post + " read."); + }, 10, TimeUnit.SECONDS); return post; } @@ -1627,15 +1507,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The post to delete */ public void deletePost(Post post) { - if (!isLocalSone(post.getSone())) { - logger.log(Level.WARNING, "Tried to delete post of non-local Sone: %s", post.getSone()); + if (!post.getSone().isLocal()) { + 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()); } - coreListenerManager.firePostRemoved(post); + eventBus.post(new PostRemovedEvent(post)); markPostKnown(post); touchConfiguration(); } @@ -1650,11 +1530,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void markPostKnown(Post post) { post.setKnown(true); synchronized (knownPosts) { - coreListenerManager.fireMarkPostKnown(post); + eventBus.post(new MarkPostKnownEvent(post)); if (knownPosts.add(post.getId())) { touchConfiguration(); } } + for (PostReply reply : getReplies(post)) { + markReplyKnown(reply); + } } /** @@ -1713,37 +1596,24 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @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) { - if (!isLocalSone(sone)) { - logger.log(Level.FINE, "Tried to create reply for non-local Sone: %s", sone); + 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 PostReply(sone, post, System.currentTimeMillis(), text); + 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); } synchronized (knownReplies) { - coreListenerManager.fireNewReplyFound(reply); + eventBus.post(new NewPostReplyFoundEvent(reply)); } sone.addReply(reply); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1752,7 +1622,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void run() { markReplyKnown(reply); } - }, "Mark " + reply + " read."); + }, 10, TimeUnit.SECONDS); return reply; } @@ -1764,8 +1634,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ public void deleteReply(PostReply reply) { Sone sone = reply.getSone(); - if (!isLocalSone(sone)) { - logger.log(Level.FINE, "Tried to delete non-local reply: %s", reply); + if (!sone.isLocal()) { + logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply)); return; } synchronized (replies) { @@ -1789,7 +1659,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void markReplyKnown(PostReply reply) { reply.setKnown(true); synchronized (knownReplies) { - coreListenerManager.fireMarkReplyKnown(reply); + eventBus.post(new MarkPostReplyKnownEvent(reply)); if (knownReplies.add(reply.getId())) { touchConfiguration(); } @@ -1839,7 +1709,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The album to remove */ public void deleteAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().is("Local Sone", isLocalSone(album.getSone())).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().isLocal(), "album’s Sone must be a local Sone"); if (!album.isEmpty()) { return; } @@ -1851,7 +1722,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (albums) { albums.remove(album.getId()); } - saveSone(album.getSone()); + touchConfiguration(); } /** @@ -1866,7 +1737,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The newly created image */ public Image createImage(Sone sone, Album album, TemporaryImage temporaryImage) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Album", album).isNotNull("Temporary Image", temporaryImage).check().is("Local Sone", isLocalSone(sone)).check().isEqual("Owner and Album Owner", sone, album.getSone()).check(); + checkNotNull(sone, "sone must not be null"); + checkNotNull(album, "album must not be null"); + checkNotNull(temporaryImage, "temporaryImage must not be null"); + checkArgument(sone.isLocal(), "sone must be a local Sone"); + checkArgument(sone.equals(album.getSone()), "album must belong to the given Sone"); Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis()); album.addImage(image); synchronized (images) { @@ -1885,13 +1760,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The image to delete */ public void deleteImage(Image image) { - Validation.begin().isNotNull("Image", image).check().is("Local Sone", isLocalSone(image.getSone())).check(); + checkNotNull(image, "image must not be null"); + checkArgument(image.getSone().isLocal(), "image must belong to a local Sone"); deleteTemporaryImage(image.getId()); image.getAlbum().removeImage(image); synchronized (images) { images.remove(image.getId()); } - saveSone(image.getSone()); + touchConfiguration(); } /** @@ -1919,7 +1795,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The temporary image to delete */ public void deleteTemporaryImage(TemporaryImage temporaryImage) { - Validation.begin().isNotNull("Temporary Image", temporaryImage).check(); + checkNotNull(temporaryImage, "temporaryImage must not be null"); deleteTemporaryImage(temporaryImage.getId()); } @@ -1930,7 +1806,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the temporary image to delete */ public void deleteTemporaryImage(String imageId) { - Validation.begin().isNotNull("Temporary Image ID", imageId).check(); + checkNotNull(imageId, "imageId must not be null"); synchronized (temporaryImages) { temporaryImages.remove(imageId); } @@ -1959,8 +1835,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis @Override public void serviceStart() { loadConfiguration(); - updateChecker.addUpdateListener(this); updateChecker.start(); + identityManager.start(); + webOfTrustUpdater.init(); + webOfTrustUpdater.start(); } /** @@ -1987,15 +1865,18 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ @Override public void serviceStop() { - synchronized (localSones) { - for (SoneInserter soneInserter : soneInserters.values()) { - soneInserter.removeSoneInsertListener(this); - soneInserter.stop(); + localElementTicker.shutdownNow(); + synchronized (sones) { + for (Entry soneInserter : soneInserters.entrySet()) { + soneInserter.getValue().stop(); + saveSone(soneInserter.getKey()); } } + saveConfiguration(); + webOfTrustUpdater.stop(); updateChecker.stop(); - updateChecker.removeUpdateListener(this); soneDownloader.stop(); + identityManager.stop(); } // @@ -2010,16 +1891,16 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The Sone to save */ private synchronized void saveSone(Sone sone) { - if (!isLocalSone(sone)) { - logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone); + if (!sone.isLocal()) { + logger.log(Level.FINE, String.format("Tried to save non-local Sone: %s", sone)); return; } if (!(sone.getIdentity() instanceof OwnIdentity)) { - logger.log(Level.WARNING, "Local Sone without OwnIdentity found, refusing to save: %s", sone); + logger.log(Level.WARNING, String.format("Local Sone without OwnIdentity found, refusing to save: %s", sone)); return; } - logger.log(Level.INFO, "Saving Sone: %s", sone); + logger.log(Level.INFO, String.format("Saving Sone: %s", sone)); try { /* save Sone into configuration. */ String sonePrefix = "Sone/" + sone.getId(); @@ -2132,13 +2013,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis configuration.save(); - ((OwnIdentity) sone.getIdentity()).setProperty("Sone.LatestEdition", String.valueOf(sone.getLatestEdition())); + webOfTrustUpdater.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition())); - logger.log(Level.INFO, "Sone %s saved.", sone); + logger.log(Level.INFO, String.format("Sone %s saved.", sone)); } catch (ConfigurationException ce1) { - logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not set WoT property for Sone: " + sone, wote1); + logger.log(Level.WARNING, String.format("Could not save Sone: %s", sone), ce1); } } @@ -2159,6 +2038,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis configuration.getIntValue("Option/ConfigurationVersion").setValue(0); configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal()); configuration.getIntValue("Option/PostsPerPage").setValue(options.getIntegerOption("PostsPerPage").getReal()); + configuration.getIntValue("Option/ImagesPerPage").setValue(options.getIntegerOption("ImagesPerPage").getReal()); configuration.getIntValue("Option/CharactersPerPost").setValue(options.getIntegerOption("CharactersPerPost").getReal()); configuration.getIntValue("Option/PostCutOffLength").setValue(options.getIntegerOption("PostCutOffLength").getReal()); configuration.getBooleanValue("Option/RequireFullAccess").setValue(options.getBooleanOption("RequireFullAccess").getReal()); @@ -2167,9 +2047,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis configuration.getStringValue("Option/TrustComment").setValue(options.getStringOption("TrustComment").getReal()); configuration.getBooleanValue("Option/ActivateFcpInterface").setValue(options.getBooleanOption("ActivateFcpInterface").getReal()); configuration.getIntValue("Option/FcpFullAccessRequired").setValue(options.getIntegerOption("FcpFullAccessRequired").getReal()); - configuration.getBooleanValue("Option/SoneRescueMode").setValue(options.getBooleanOption("SoneRescueMode").getReal()); - configuration.getBooleanValue("Option/ClearOnNextRestart").setValue(options.getBooleanOption("ClearOnNextRestart").getReal()); - configuration.getBooleanValue("Option/ReallyClearOnNextRestart").setValue(options.getBooleanOption("ReallyClearOnNextRestart").getReal()); /* save known Sones. */ int soneCounter = 0; @@ -2183,8 +2060,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /* 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; } @@ -2236,7 +2113,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis @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) { @@ -2244,12 +2121,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } })); - options.addIntegerOption("PostsPerPage", new DefaultOption(10, 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() { @@ -2268,23 +2146,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } })); - options.addBooleanOption("SoneRescueMode", new DefaultOption(false)); - options.addBooleanOption("ClearOnNextRestart", new DefaultOption(false)); - options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption(false)); - - /* read options from configuration. */ - options.getBooleanOption("ClearOnNextRestart").set(configuration.getBooleanValue("Option/ClearOnNextRestart").getValue(null)); - options.getBooleanOption("ReallyClearOnNextRestart").set(configuration.getBooleanValue("Option/ReallyClearOnNextRestart").getValue(null)); - boolean clearConfiguration = options.getBooleanOption("ClearOnNextRestart").get() && options.getBooleanOption("ReallyClearOnNextRestart").get(); - options.getBooleanOption("ClearOnNextRestart").set(null); - options.getBooleanOption("ReallyClearOnNextRestart").set(null); - if (clearConfiguration) { - /* stop loading the configuration. */ - return; - } loadConfigurationValue("InsertionDelay"); loadConfigurationValue("PostsPerPage"); + loadConfigurationValue("ImagesPerPage"); loadConfigurationValue("CharactersPerPost"); loadConfigurationValue("PostCutOffLength"); options.getBooleanOption("RequireFullAccess").set(configuration.getBooleanValue("Option/RequireFullAccess").getValue(null)); @@ -2293,7 +2158,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null)); options.getBooleanOption("ActivateFcpInterface").set(configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null)); options.getIntegerOption("FcpFullAccessRequired").set(configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null)); - options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null)); /* load known Sones. */ int soneCounter = 0; @@ -2320,7 +2184,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, String.format("Ignoring Sone with invalid ID: %s", soneId)); } else { synchronized (soneFollowingTimes) { - soneFollowingTimes.put(getSone(soneId), time); + soneFollowingTimes.put(soneId, time); } } ++soneCounter; @@ -2375,37 +2239,20 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis try { options.getIntegerOption(optionName).set(configuration.getIntValue("Option/" + optionName).getValue(null)); } catch (IllegalArgumentException iae1) { - logger.log(Level.WARNING, "Invalid value for " + optionName + " in configuration, using default."); + logger.log(Level.WARNING, String.format("Invalid value for %s in configuration, using default.", optionName)); } } /** - * Generate a Sone URI from the given URI and latest edition. + * Notifies the core that a new {@link OwnIdentity} was added. * - * @param uriString - * The URI to derive the Sone URI from - * @return The derived URI + * @param ownIdentityAddedEvent + * The event */ - private 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, "Could not create Sone URI from URI: " + uriString, mue1); - return null; - } - } - - // - // INTERFACE IdentityListener - // - - /** - * {@inheritDoc} - */ - @Override - public void ownIdentityAdded(OwnIdentity ownIdentity) { - logger.log(Level.FINEST, "Adding OwnIdentity: " + ownIdentity); + @Subscribe + public void ownIdentityAdded(OwnIdentityAddedEvent ownIdentityAddedEvent) { + 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); @@ -2413,30 +2260,42 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /** - * {@inheritDoc} + * Notifies the core that an {@link OwnIdentity} was removed. + * + * @param ownIdentityRemovedEvent + * The event */ - @Override - public void ownIdentityRemoved(OwnIdentity ownIdentity) { - logger.log(Level.FINEST, "Removing OwnIdentity: " + ownIdentity); + @Subscribe + public void ownIdentityRemoved(OwnIdentityRemovedEvent ownIdentityRemovedEvent) { + OwnIdentity ownIdentity = ownIdentityRemovedEvent.ownIdentity(); + logger.log(Level.FINEST, String.format("Removing OwnIdentity: %s", ownIdentity)); trustedIdentities.remove(ownIdentity); } /** - * {@inheritDoc} + * Notifies the core that a new {@link Identity} was added. + * + * @param identityAddedEvent + * The event */ - @Override - public void identityAdded(OwnIdentity ownIdentity, Identity identity) { - logger.log(Level.FINEST, "Adding Identity: " + identity); - trustedIdentities.get(ownIdentity).add(identity); + @Subscribe + 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); addRemoteSone(identity); } /** - * {@inheritDoc} + * Notifies the core that an {@link Identity} was updated. + * + * @param identityUpdatedEvent + * The event */ - @Override - public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) { - new Thread(new Runnable() { + @Subscribe + public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) { + final Identity identity = identityUpdatedEvent.identity(); + soneDownloaders.execute(new Runnable() { @Override @SuppressWarnings("synthetic-access") @@ -2447,14 +2306,19 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } - }).start(); + }); } /** - * {@inheritDoc} + * Notifies the core that an {@link Identity} was removed. + * + * @param identityRemovedEvent + * The event */ - @Override - public void identityRemoved(OwnIdentity ownIdentity, Identity identity) { + @Subscribe + public void identityRemoved(IdentityRemovedEvent identityRemovedEvent) { + OwnIdentity ownIdentity = identityRemovedEvent.ownIdentity(); + Identity identity = identityRemovedEvent.identity(); trustedIdentities.get(ownIdentity).remove(identity); boolean foundIdentity = false; for (Entry> trustedIdentity : trustedIdentities.entrySet()) { @@ -2469,7 +2333,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /* some local identity still trusts this identity, don’t remove. */ return; } - Sone sone = getSone(identity.getId(), false); + Sone sone = getSone(identity.getId()); if (sone == null) { /* TODO - we don’t have the Sone anymore. should this happen? */ return; @@ -2478,7 +2342,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (knownPosts) { for (Post post : sone.getPosts()) { posts.remove(post.getId()); - coreListenerManager.firePostRemoved(post); + eventBus.post(new PostRemovedEvent(post)); } } } @@ -2486,470 +2350,28 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (knownReplies) { for (PostReply reply : sone.getReplies()) { replies.remove(reply.getId()); - coreListenerManager.fireReplyRemoved(reply); + eventBus.post(new PostReplyRemovedEvent(reply)); } } } - synchronized (remoteSones) { - remoteSones.remove(identity.getId()); + synchronized (sones) { + sones.remove(identity.getId()); } - coreListenerManager.fireSoneRemoved(sone); - } - - // - // INTERFACE UpdateListener - // - - /** - * {@inheritDoc} - */ - @Override - public void updateFound(Version version, long releaseTime, long latestEdition) { - coreListenerManager.fireUpdateFound(version, releaseTime, latestEdition); - } - - // - // INTERFACE ImageInsertListener - // - - /** - * {@inheritDoc} - */ - @Override - public void insertStarted(Sone sone) { - coreListenerManager.fireSoneInserting(sone); - } - - /** - * {@inheritDoc} - */ - @Override - public void insertFinished(Sone sone, long insertDuration) { - coreListenerManager.fireSoneInserted(sone, insertDuration); - } - - /** - * {@inheritDoc} - */ - @Override - public void insertAborted(Sone sone, Throwable cause) { - coreListenerManager.fireSoneInsertAborted(sone, cause); - } - - // - // SONEINSERTLISTENER METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public void imageInsertStarted(Image image) { - logger.log(Level.WARNING, "Image insert started for " + image); - coreListenerManager.fireImageInsertStarted(image); + eventBus.post(new SoneRemovedEvent(sone)); } /** - * {@inheritDoc} - */ - @Override - public void imageInsertAborted(Image image) { - logger.log(Level.WARNING, "Image insert aborted for " + image); - coreListenerManager.fireImageInsertAborted(image); - } - - /** - * {@inheritDoc} - */ - @Override - public void imageInsertFinished(Image image, FreenetURI key) { - logger.log(Level.WARNING, "Image insert finished for " + image + ": " + key); - image.setKey(key.toString()); - deleteTemporaryImage(image.getId()); - saveSone(image.getSone()); - coreListenerManager.fireImageInsertFinished(image); - } - - /** - * {@inheritDoc} + * Deletes the temporary image. + * + * @param imageInsertFinishedEvent + * The event */ - @Override - public void imageInsertFailed(Image image, Throwable cause) { - logger.log(Level.WARNING, "Image insert failed for " + image, cause); - coreListenerManager.fireImageInsertFailed(image, cause); - } - - /** - * 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 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; - } - - /** - * Returns whether Sone should clear its settings on the next restart. - * In order to be effective, {@link #isReallyClearOnNextRestart()} needs - * to return {@code true} as well! - * - * @return {@code true} if Sone should clear its settings on the next - * restart, {@code false} otherwise - */ - public boolean isClearOnNextRestart() { - return options.getBooleanOption("ClearOnNextRestart").get(); - } - - /** - * Sets whether Sone will clear its settings on the next restart. - * - * @param clearOnNextRestart - * {@code true} if Sone should clear its settings on the next - * restart, {@code false} otherwise - * @return This preferences - */ - public Preferences setClearOnNextRestart(Boolean clearOnNextRestart) { - options.getBooleanOption("ClearOnNextRestart").set(clearOnNextRestart); - return this; - } - - /** - * Returns whether Sone should really clear its settings on next - * restart. This is a confirmation option that needs to be set in - * addition to {@link #isClearOnNextRestart()} in order to clear Sone’s - * settings on the next restart. - * - * @return {@code true} if Sone should really clear its settings on the - * next restart, {@code false} otherwise - */ - public boolean isReallyClearOnNextRestart() { - return options.getBooleanOption("ReallyClearOnNextRestart").get(); - } - - /** - * Sets whether Sone should really clear its settings on the next - * restart. - * - * @param reallyClearOnNextRestart - * {@code true} if Sone should really clear its settings on - * the next restart, {@code false} otherwise - * @return This preferences - */ - public Preferences setReallyClearOnNextRestart(Boolean reallyClearOnNextRestart) { - options.getBooleanOption("ReallyClearOnNextRestart").set(reallyClearOnNextRestart); - return this; - } - + @Subscribe + public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) { + logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", imageInsertFinishedEvent.image(), imageInsertFinishedEvent.resultingUri())); + imageInsertFinishedEvent.image().setKey(imageInsertFinishedEvent.resultingUri().toString()); + deleteTemporaryImage(imageInsertFinishedEvent.image().getId()); + touchConfiguration(); } }