Fix logging of exception.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
index 26d21ed..e3fdd19 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Sone - Core.java - Copyright © 2010 David Roden
+ * Sone - Core.java - Copyright © 2010–2012 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
@@ -46,6 +46,7 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
 import net.pterodactylus.sone.data.TemporaryImage;
+import net.pterodactylus.sone.data.impl.PostImpl;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
 import net.pterodactylus.sone.freenet.wot.Identity;
@@ -53,7 +54,6 @@ 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.main.SonePlugin;
 import net.pterodactylus.util.config.Configuration;
 import net.pterodactylus.util.config.ConfigurationException;
@@ -111,6 +111,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        /** The update checker. */
        private final UpdateChecker updateChecker;
 
+       /** The trust updater. */
+       private final WebOfTrustUpdater webOfTrustUpdater;
+
        /** The FCP interface. */
        private volatile FcpInterface fcpInterface;
 
@@ -131,45 +134,45 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
 
        /** All local Sones. */
        /* synchronize access on this on itself. */
-       private Map<String, Sone> localSones = new HashMap<String, Sone>();
+       private final Map<String, Sone> localSones = new HashMap<String, Sone>();
 
        /** All remote Sones. */
        /* synchronize access on this on itself. */
-       private Map<String, Sone> remoteSones = new HashMap<String, Sone>();
+       private final Map<String, Sone> remoteSones = new HashMap<String, Sone>();
 
        /** All known Sones. */
-       private Set<String> knownSones = new HashSet<String>();
+       private final Set<String> knownSones = new HashSet<String>();
 
        /** All posts. */
-       private Map<String, Post> posts = new HashMap<String, Post>();
+       private final Map<String, Post> posts = new HashMap<String, Post>();
 
        /** All known posts. */
-       private Set<String> knownPosts = new HashSet<String>();
+       private final Set<String> knownPosts = new HashSet<String>();
 
        /** All replies. */
-       private Map<String, PostReply> replies = new HashMap<String, PostReply>();
+       private final Map<String, PostReply> replies = new HashMap<String, PostReply>();
 
        /** All known replies. */
-       private Set<String> knownReplies = new HashSet<String>();
+       private final Set<String> knownReplies = new HashSet<String>();
 
        /** All bookmarked posts. */
        /* synchronize access on itself. */
-       private Set<String> bookmarkedPosts = new HashSet<String>();
+       private final Set<String> bookmarkedPosts = new HashSet<String>();
 
        /** Trusted identities, sorted by own identities. */
-       private Map<OwnIdentity, Set<Identity>> trustedIdentities = Collections.synchronizedMap(new HashMap<OwnIdentity, Set<Identity>>());
+       private final Map<OwnIdentity, Set<Identity>> trustedIdentities = Collections.synchronizedMap(new HashMap<OwnIdentity, Set<Identity>>());
 
        /** All known albums. */
-       private Map<String, Album> albums = new HashMap<String, Album>();
+       private final Map<String, Album> albums = new HashMap<String, Album>();
 
        /** All known images. */
-       private Map<String, Image> images = new HashMap<String, Image>();
+       private final Map<String, Image> images = new HashMap<String, Image>();
 
        /** All temporary images. */
-       private Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
+       private final Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
 
        /** Ticker for threads that mark own elements as known. */
-       private Ticker localElementTicker = new Ticker();
+       private final Ticker localElementTicker = new Ticker();
 
        /** The time the configuration was last touched. */
        private volatile long lastConfigurationUpdate;
@@ -183,8 +186,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         *            The freenet interface
         * @param identityManager
         *            The identity manager
+        * @param webOfTrustUpdater
+        *            The WebOfTrust updater
         */
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -192,6 +197,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                this.soneDownloader = new SoneDownloader(this, freenetInterface);
                this.imageInserter = new ImageInserter(this, freenetInterface);
                this.updateChecker = new UpdateChecker(freenetInterface);
+               this.webOfTrustUpdater = webOfTrustUpdater;
        }
 
        //
@@ -561,7 +567,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (posts) {
                        Post post = posts.get(postId);
                        if ((post == null) && create) {
-                               post = new Post(postId);
+                               post = new PostImpl(postId);
                                posts.put(postId, post);
                        }
                        return post;
@@ -884,10 +890,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, String.format("Could not add “Sone” context to own identity: %s", 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);
@@ -1053,6 +1057,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        logger.log(Level.WARNING, String.format("Tried to get trust from remote Sone: %s", origin));
                        return null;
                }
+               webOfTrustUpdater.getTrust((OwnIdentity) origin.getIdentity(), target.getIdentity());
                return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity());
        }
 
@@ -1068,11 +1073,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        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, String.format("Could not set trust for Sone: %s", target), wote1);
-               }
+               webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), trustValue, preferences.getTrustComment());
        }
 
        /**
@@ -1085,11 +1086,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        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, String.format("Could not remove trust for Sone: %s", target), wote1);
-               }
+               webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), null, null);
        }
 
        /**
@@ -1279,12 +1276,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        soneInserter.removeSoneInsertListener(this);
                        soneInserter.stop();
                }
-               try {
-                       ((OwnIdentity) sone.getIdentity()).removeContext("Sone");
-                       ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition");
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not remove context and properties from Sone: %s", 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) {
@@ -1456,7 +1449,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                                }
                                parentAlbum.addAlbum(album);
                        } else {
-                               topLevelAlbums.add(album);
+                               if (!topLevelAlbums.contains(album)) {
+                                       topLevelAlbums.add(album);
+                               }
                        }
                }
 
@@ -1597,7 +1592,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        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);
+               final Post post = new PostImpl(sone, time, text);
                if (recipient != null) {
                        post.setRecipient(recipient);
                }
@@ -1854,7 +1849,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (albums) {
                        albums.remove(album.getId());
                }
-               saveSone(album.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1894,7 +1889,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                synchronized (images) {
                        images.remove(image.getId());
                }
-               saveSone(image.getSone());
+               touchConfiguration();
        }
 
        /**
@@ -1964,6 +1959,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                loadConfiguration();
                updateChecker.addUpdateListener(this);
                updateChecker.start();
+               webOfTrustUpdater.start();
        }
 
        /**
@@ -1996,6 +1992,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                                soneInserter.stop();
                        }
                }
+               webOfTrustUpdater.stop();
                updateChecker.stop();
                updateChecker.removeUpdateListener(this);
                soneDownloader.stop();
@@ -2135,13 +2132,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, String.format("Sone %s saved.", sone));
                } catch (ConfigurationException ce1) {
                        logger.log(Level.WARNING, String.format("Could not save Sone: %s", sone), ce1);
-               } catch (WebOfTrustException wote1) {
-                       logger.log(Level.WARNING, String.format("Could not set WoT property for Sone: %s", sone), wote1);
                }
        }
 
@@ -2162,6 +2157,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());
@@ -2170,9 +2166,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;
@@ -2248,6 +2241,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
 
                }));
                options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
+               options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
                options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
                options.addIntegerOption("PostCutOffLength", new DefaultOption<Integer>(200, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
                options.addBooleanOption("RequireFullAccess", new DefaultOption<Boolean>(false));
@@ -2271,23 +2265,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        }
 
                }));
-               options.addBooleanOption("SoneRescueMode", new DefaultOption<Boolean>(false));
-               options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
-               options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption<Boolean>(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));
@@ -2296,7 +2277,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;
@@ -2394,7 +2374,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]);
                        return uri;
                } catch (MalformedURLException mue1) {
-                       logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString, mue1));
+                       logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1);
                        return null;
                }
        }
@@ -2569,7 +2549,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", image, key));
                image.setKey(key.toString());
                deleteTemporaryImage(image.getId());
-               saveSone(image.getSone());
+               touchConfiguration();
                coreListenerManager.fireImageInsertFinished(image);
        }
 
@@ -2671,6 +2651,39 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                }
 
                /**
+                * Returns the number of images to show per page.
+                *
+                * @return The number of images to show per page
+                */
+               public int getImagesPerPage() {
+                       return options.getIntegerOption("ImagesPerPage").get();
+               }
+
+               /**
+                * Validates the number of images per page.
+                *
+                * @param imagesPerPage
+                *            The number of images per page
+                * @return {@code true} if the number of images per page was valid,
+                *         {@code false} otherwise
+                */
+               public boolean validateImagesPerPage(Integer imagesPerPage) {
+                       return options.getIntegerOption("ImagesPerPage").validate(imagesPerPage);
+               }
+
+               /**
+                * Sets the number of images per page.
+                *
+                * @param imagesPerPage
+                *            The number of images per page
+                * @return This preferences object
+                */
+               public Preferences setImagesPerPage(Integer imagesPerPage) {
+                       options.getIntegerOption("ImagesPerPage").set(imagesPerPage);
+                       return this;
+               }
+
+               /**
                 * Returns the number of characters per post, or <code>-1</code> if the
                 * posts should not be cut off.
                 *
@@ -2901,58 +2914,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                        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;
-               }
-
        }
 
 }