From: David ‘Bombe’ Roden Date: Mon, 8 Oct 2012 12:14:38 +0000 (+0200) Subject: Merge branch 'release-0.8.3' X-Git-Tag: 0.8.3^0 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=61fea0173ef87542cae35247e6356ef36f2664d3;hp=50ce65f69e49ed10abeedaeb6615ffb37a0c0772 Merge branch 'release-0.8.3' --- diff --git a/pom.xml b/pom.xml index 9ea02ae..bcc9946 100644 --- a/pom.xml +++ b/pom.xml @@ -2,12 +2,12 @@ 4.0.0 net.pterodactylus sone - 0.8.2 + 0.8.3 net.pterodactylus utils - 0.12 + 0.12.2 junit diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 4001f46..c391582 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -53,8 +53,6 @@ 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.main.SonePlugin; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; @@ -79,6 +77,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** 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(); @@ -112,6 +113,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; @@ -184,8 +188,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; @@ -193,6 +199,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; } // @@ -224,6 +231,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis // /** + * Returns the time Sone was started. + * + * @return The startup time (in milliseconds since Jan 1, 1970 UTC) + */ + public long getStartupTime() { + return startupTime; + } + + /** * Sets the configuration to use. This will automatically save the current * configuration to the given configuration. * @@ -885,10 +901,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); @@ -1039,25 +1053,6 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /** - * 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, String.format("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 @@ -1069,11 +1064,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()); } /** @@ -1086,11 +1077,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); } /** @@ -1280,12 +1267,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) { @@ -1857,7 +1840,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (albums) { albums.remove(album.getId()); } - saveSone(album.getSone()); + touchConfiguration(); } /** @@ -1897,7 +1880,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (images) { images.remove(image.getId()); } - saveSone(image.getSone()); + touchConfiguration(); } /** @@ -1967,6 +1950,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis loadConfiguration(); updateChecker.addUpdateListener(this); updateChecker.start(); + webOfTrustUpdater.start(); } /** @@ -1994,11 +1978,14 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis @Override public void serviceStop() { synchronized (localSones) { - for (SoneInserter soneInserter : soneInserters.values()) { - soneInserter.removeSoneInsertListener(this); - soneInserter.stop(); + for (Entry soneInserter : soneInserters.entrySet()) { + soneInserter.getValue().removeSoneInsertListener(this); + soneInserter.getValue().stop(); + saveSone(soneInserter.getKey()); } } + saveConfiguration(); + webOfTrustUpdater.stop(); updateChecker.stop(); updateChecker.removeUpdateListener(this); soneDownloader.stop(); @@ -2138,13 +2125,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); } } @@ -2174,9 +2159,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; @@ -2276,20 +2258,6 @@ 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"); @@ -2302,7 +2270,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; @@ -2395,12 +2362,12 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The URI to derive the Sone URI from * @return The derived URI */ - private FreenetURI getSoneUri(String uriString) { + private static FreenetURI getSoneUri(String uriString) { try { FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]); return uri; } catch (MalformedURLException mue1) { - logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString, mue1)); + logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1); return null; } } @@ -2575,7 +2542,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); } @@ -2940,58 +2907,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; - } - } } diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index af81d0f..0ea895a 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -329,6 +329,8 @@ public class Options { /** * Adds an {@link Enum} {@link Option}. * + * @param + * The enum type * @param name * The name of the option * @param enumOption @@ -350,6 +352,8 @@ public class Options { * options.<SomeEnum> getEnumOption("SomeEnumOption").get(); * * + * @param + * The enum type * @param name * The name of the option * @return The enum option, or {@code null} if there is no enum option with diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java index 003e276..3325cd5 100644 --- a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java +++ b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java @@ -49,7 +49,7 @@ public class UpdateChecker { private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/"; /** The current latest known edition. */ - private static final int LATEST_EDITION = 49; + private static final int LATEST_EDITION = 54; /** The Freenet interface. */ private final FreenetInterface freenetInterface; diff --git a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java new file mode 100644 index 0000000..bdfc1ae --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java @@ -0,0 +1,696 @@ +/* + * Sone - WebOfTrustUpdater.java - Copyright © 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.pterodactylus.sone.freenet.plugin.PluginException; +import net.pterodactylus.sone.freenet.wot.DefaultIdentity; +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; +import net.pterodactylus.sone.freenet.wot.Trust; +import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector; +import net.pterodactylus.sone.freenet.wot.WebOfTrustException; +import net.pterodactylus.util.logging.Logging; +import net.pterodactylus.util.service.AbstractService; +import net.pterodactylus.util.validation.Validation; + +/** + * Updates WebOfTrust identity data in a background thread because communicating + * with the WebOfTrust plugin can potentially last quite long. + * + * @author David ‘Bombe’ Roden + */ +public class WebOfTrustUpdater extends AbstractService { + + /** The logger. */ + private static final Logger logger = Logging.getLogger(WebOfTrustUpdater.class); + + /** Stop job. */ + @SuppressWarnings("synthetic-access") + private final WebOfTrustUpdateJob stopJob = new WebOfTrustUpdateJob(); + + /** The web of trust connector. */ + private final WebOfTrustConnector webOfTrustConnector; + + /** The queue for jobs. */ + private final BlockingQueue updateJobs = new LinkedBlockingQueue(); + + /** + * Creates a new trust updater. + * + * @param webOfTrustConnector + * The web of trust connector + */ + public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) { + super("Trust Updater"); + this.webOfTrustConnector = webOfTrustConnector; + } + + // + // ACTIONS + // + + /** + * Updates the trust relation between the truster and the trustee. This + * method will return immediately and perform a trust update in the + * background. + * + * @param truster + * The identity giving the trust + * @param trustee + * The identity receiving the trust + * @param score + * The new level of trust (from -100 to 100, may be {@code null} + * to remove the trust completely) + * @param comment + * The comment of the trust relation + */ + public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) { + SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment); + if (updateJobs.contains(setTrustJob)) { + updateJobs.remove(setTrustJob); + } + logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob); + try { + updateJobs.put(setTrustJob); + } catch (InterruptedException e) { + /* the queue is unbounded so it should never block. */ + } + } + + /** + * Adds the given context to the given own identity. + * + * @param ownIdentity + * The own identity to add the context to + * @param context + * The context to add + */ + public void addContext(OwnIdentity ownIdentity, String context) { + addContextWait(ownIdentity, context, false); + } + + /** + * Adds the given context to the given own identity, waiting for completion + * of the operation. + * + * @param ownIdentity + * The own identity to add the context to + * @param context + * The context to add + * @return {@code true} if the context was added successfully, {@code false} + * otherwise + */ + public boolean addContextWait(OwnIdentity ownIdentity, String context) { + return addContextWait(ownIdentity, context, true); + } + + /** + * Adds the given context to the given own identity, waiting for completion + * of the operation. + * + * @param ownIdentity + * The own identity to add the context to + * @param context + * The context to add + * @param wait + * {@code true} to wait for the end of the operation, + * {@code false} to return immediately + * @return {@code true} if the context was added successfully, {@code false} + * if the context was not added successfully, or if the job should + * not wait for completion + */ + private boolean addContextWait(OwnIdentity ownIdentity, String context, boolean wait) { + AddContextJob addContextJob = new AddContextJob(ownIdentity, context); + if (!updateJobs.contains(addContextJob)) { + logger.log(Level.FINER, "Adding Context Job: " + addContextJob); + try { + updateJobs.put(addContextJob); + } catch (InterruptedException ie1) { + /* the queue is unbounded so it should never block. */ + } + if (wait) { + return addContextJob.waitForCompletion(); + } + } else if (wait) { + for (WebOfTrustUpdateJob updateJob : updateJobs) { + if (updateJob.equals(addContextJob)) { + return updateJob.waitForCompletion(); + } + } + } + return false; + } + + /** + * Removes the given context from the given own identity. + * + * @param ownIdentity + * The own identity to remove the context from + * @param context + * The context to remove + */ + public void removeContext(OwnIdentity ownIdentity, String context) { + RemoveContextJob removeContextJob = new RemoveContextJob(ownIdentity, context); + if (!updateJobs.contains(removeContextJob)) { + logger.log(Level.FINER, "Adding Context Job: " + removeContextJob); + try { + updateJobs.put(removeContextJob); + } catch (InterruptedException ie1) { + /* the queue is unbounded so it should never block. */ + } + } + } + + /** + * Sets a property on the given own identity. + * + * @param ownIdentity + * The own identity to set the property on + * @param propertyName + * The name of the property to set + * @param propertyValue + * The value of the property to set + */ + public void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue) { + SetPropertyJob setPropertyJob = new SetPropertyJob(ownIdentity, propertyName, propertyValue); + if (updateJobs.contains(setPropertyJob)) { + updateJobs.remove(setPropertyJob); + } + logger.log(Level.FINER, "Adding Property Job: " + setPropertyJob); + try { + updateJobs.put(setPropertyJob); + } catch (InterruptedException e) { + /* the queue is unbounded so it should never block. */ + } + } + + /** + * Removes a property from the given own identity. + * + * @param ownIdentity + * The own identity to remove the property from + * @param propertyName + * The name of the property to remove + */ + public void removeProperty(OwnIdentity ownIdentity, String propertyName) { + setProperty(ownIdentity, propertyName, null); + } + + // + // SERVICE METHODS + // + + /** + * {@inheritDoc} + */ + @Override + protected void serviceRun() { + while (!shouldStop()) { + try { + WebOfTrustUpdateJob updateJob = updateJobs.take(); + if (shouldStop() || (updateJob == stopJob)) { + break; + } + logger.log(Level.FINE, "Running Trust Update Job: " + updateJob); + long startTime = System.currentTimeMillis(); + updateJob.run(); + long endTime = System.currentTimeMillis(); + logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms."); + } catch (InterruptedException ie1) { + /* happens, ignore, loop. */ + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void serviceStop() { + try { + updateJobs.put(stopJob); + } catch (InterruptedException ie1) { + /* the queue is unbounded so it should never block. */ + } + } + + /** + * Base class for WebOfTrust update jobs. + * + * @author David ‘Bombe’ Roden + */ + private class WebOfTrustUpdateJob { + + /** Object for synchronization. */ + @SuppressWarnings("hiding") + private final Object syncObject = new Object(); + + /** Whether the job has finished. */ + private boolean finished; + + /** Whether the job was successful. */ + private boolean success; + + // + // ACTIONS + // + + /** + * Performs the actual update operation. + *

+ * The implementation of this class does nothing. + */ + public void run() { + /* does nothing. */ + } + + /** + * Waits for completion of this job or stopping of the WebOfTrust + * updater. + * + * @return {@code true} if this job finished successfully, {@code false} + * otherwise + * + * @see WebOfTrustUpdater#stop() + */ + @SuppressWarnings("synthetic-access") + public boolean waitForCompletion() { + synchronized (syncObject) { + while (!finished && !shouldStop()) { + try { + syncObject.wait(); + } catch (InterruptedException ie1) { + /* we’re looping, ignore. */ + } + } + return success; + } + } + + // + // PROTECTED METHODS + // + + /** + * Signals that this job has finished. + * + * @param success + * {@code true} if this job finished successfully, + * {@code false} otherwise + */ + protected void finish(boolean success) { + synchronized (syncObject) { + finished = true; + this.success = success; + syncObject.notifyAll(); + } + } + + } + + /** + * Base class for WebOfTrust trust update jobs. + * + * @author David ‘Bombe’ Roden + */ + private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob { + + /** The identity giving the trust. */ + protected final OwnIdentity truster; + + /** The identity receiving the trust. */ + protected final Identity trustee; + + /** + * Creates a new trust update job. + * + * @param truster + * The identity giving the trust + * @param trustee + * The identity receiving the trust + */ + @SuppressWarnings("synthetic-access") + public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) { + this.truster = truster; + this.trustee = trustee; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if ((object == null) || !object.getClass().equals(getClass())) { + return false; + } + WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object; + return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee)); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId()); + } + + } + + /** + * Update job that sets the trust relation between two identities. + * + * @author David ‘Bombe’ Roden + */ + private class SetTrustJob extends WebOfTrustTrustUpdateJob { + + /** The score of the relation. */ + private final Integer score; + + /** The comment of the relation. */ + private final String comment; + + /** + * Creates a new set trust job. + * + * @param truster + * The identity giving the trust + * @param trustee + * The identity receiving the trust + * @param score + * The score of the trust (from -100 to 100, may be + * {@code null} to remote the trust relation completely) + * @param comment + * The comment of the trust relation + */ + public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) { + super(truster, trustee); + this.score = score; + this.comment = comment; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void run() { + try { + if (score != null) { + if (trustee instanceof DefaultIdentity) { + ((DefaultIdentity) trustee).setTrust(truster, new Trust(score, null, 0)); + } + webOfTrustConnector.setTrust(truster, trustee, score, comment); + } else { + if (trustee instanceof DefaultIdentity) { + ((DefaultIdentity) trustee).setTrust(truster, null); + } + webOfTrustConnector.removeTrust(truster, trustee); + } + finish(true); + } catch (WebOfTrustException wote1) { + logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1); + finish(false); + } + } + + } + + /** + * Base class for context updates of an {@link OwnIdentity}. + * + * @author David ‘Bombe’ Roden + */ + private class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob { + + /** The own identity whose contexts to manage. */ + protected final OwnIdentity ownIdentity; + + /** The context to update. */ + protected final String context; + + /** + * Creates a new context update job. + * + * @param ownIdentity + * The own identity to update + * @param context + * The context to update + */ + @SuppressWarnings("synthetic-access") + public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) { + Validation.begin().isNotNull("OwnIdentity", ownIdentity).isNotNull("Context", context).check(); + this.ownIdentity = ownIdentity; + this.context = context; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if ((object == null) || !object.getClass().equals(getClass())) { + return false; + } + WebOfTrustContextUpdateJob updateJob = (WebOfTrustContextUpdateJob) object; + return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context); + } + + } + + /** + * Job that adds a context to an {@link OwnIdentity}. + * + * @author David ‘Bombe’ Roden + */ + private class AddContextJob extends WebOfTrustContextUpdateJob { + + /** + * Creates a new add-context job. + * + * @param ownIdentity + * The own identity whose contexts to manage + * @param context + * The context to add + */ + public AddContextJob(OwnIdentity ownIdentity, String context) { + super(ownIdentity, context); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void run() { + try { + webOfTrustConnector.addContext(ownIdentity, context); + ownIdentity.addContext(context); + finish(true); + } catch (PluginException pe1) { + logger.log(Level.WARNING, String.format("Could not add Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1); + finish(false); + } + } + + } + + /** + * Job that removes a context from an {@link OwnIdentity}. + * + * @author David ‘Bombe’ Roden + */ + private class RemoveContextJob extends WebOfTrustContextUpdateJob { + + /** + * Creates a new remove-context job. + * + * @param ownIdentity + * The own identity whose contexts to manage + * @param context + * The context to remove + */ + public RemoveContextJob(OwnIdentity ownIdentity, String context) { + super(ownIdentity, context); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void run() { + try { + webOfTrustConnector.removeContext(ownIdentity, context); + ownIdentity.removeContext(context); + finish(true); + } catch (PluginException pe1) { + logger.log(Level.WARNING, String.format("Could not remove Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1); + finish(false); + } + } + + } + + /** + * Base class for update jobs that deal with properties. + * + * @author David ‘Bombe’ Roden + */ + private class WebOfTrustPropertyUpdateJob extends WebOfTrustUpdateJob { + + /** The own identity to update properties on. */ + protected final OwnIdentity ownIdentity; + + /** The name of the property to update. */ + protected final String propertyName; + + /** + * Creates a new property update job. + * + * @param ownIdentity + * The own identity to update the property on + * @param propertyName + * The name of the property to update + */ + @SuppressWarnings("synthetic-access") + public WebOfTrustPropertyUpdateJob(OwnIdentity ownIdentity, String propertyName) { + this.ownIdentity = ownIdentity; + this.propertyName = propertyName; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if ((object == null) || !object.getClass().equals(getClass())) { + return false; + } + WebOfTrustPropertyUpdateJob updateJob = (WebOfTrustPropertyUpdateJob) object; + return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName); + } + + } + + /** + * WebOfTrust update job that sets a property on an {@link OwnIdentity}. + * + * @author David ‘Bombe’ Roden + */ + private class SetPropertyJob extends WebOfTrustPropertyUpdateJob { + + /** The value of the property to set. */ + private final String propertyValue; + + /** + * Creates a new set-property job. + * + * @param ownIdentity + * The own identity to set the property on + * @param propertyName + * The name of the property to set + * @param propertyValue + * The value of the property to set + */ + public SetPropertyJob(OwnIdentity ownIdentity, String propertyName, String propertyValue) { + super(ownIdentity, propertyName); + this.propertyValue = propertyValue; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void run() { + try { + if (propertyValue == null) { + webOfTrustConnector.removeProperty(ownIdentity, propertyName); + ownIdentity.removeProperty(propertyName); + } else { + webOfTrustConnector.setProperty(ownIdentity, propertyName, propertyValue); + ownIdentity.setProperty(propertyName, propertyValue); + } + finish(true); + } catch (PluginException pe1) { + logger.log(Level.WARNING, String.format("Could not set Property “%2$s” to “%3$s” on Own Identity %1$s!", ownIdentity, propertyName, propertyValue), pe1); + finish(false); + } + } + + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index ac6e41a..99d65ec 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -791,7 +791,9 @@ public class Sone implements Fingerprintable, Comparable { */ public void addAlbum(Album album) { Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); - albums.add(album); + if (!albums.contains(album)) { + albums.add(album); + } } /** diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java index 490a05c..1944570 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java @@ -110,7 +110,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * The text to encode * @return The encoded text */ - protected String encodeString(String text) { + protected static String encodeString(String text) { return text.replaceAll("\\\\", "\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r"); } @@ -233,7 +233,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * such as if the Sone is followed by the local Sone * @return The simple field set containing the given Sone */ - protected SimpleFieldSet encodeSone(Sone sone, String prefix, Sone localSone) { + protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Sone localSone) { SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder(); soneBuilder.put(prefix + "Name", sone.getName()); @@ -264,7 +264,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * {@code null}) * @return The simple field set containing the given Sones */ - protected SimpleFieldSet encodeSones(Collection sones, String prefix) { + protected static SimpleFieldSet encodeSones(Collection sones, String prefix) { SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder(); int soneIndex = 0; @@ -352,7 +352,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * {@code null}) * @return The simple field set containing the replies */ - protected SimpleFieldSet encodeReplies(Collection replies, String prefix) { + protected static SimpleFieldSet encodeReplies(Collection replies, String prefix) { SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder(); int replyIndex = 0; @@ -379,7 +379,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * {@code null}) * @return The simple field set containing the likes */ - protected SimpleFieldSet encodeLikes(Collection likes, String prefix) { + protected static SimpleFieldSet encodeLikes(Collection likes, String prefix) { SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder(); int likeIndex = 0; diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java index 128d46a..b3222be 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java +++ b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java @@ -197,7 +197,7 @@ public class FcpInterface { * @throws PluginNotFoundException * if the plugin can not be found */ - private void sendReply(PluginReplySender pluginReplySender, String identifier, Response response) throws PluginNotFoundException { + private static void sendReply(PluginReplySender pluginReplySender, String identifier, Response response) throws PluginNotFoundException { SimpleFieldSet replyParameters = response.getReplyParameters(); if (identifier != null) { replyParameters.putOverwrite("Identifier", identifier); diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java index 6e13a32..b73fb1d 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java @@ -44,7 +44,7 @@ public abstract class AbstractCommand implements Command { * if there is no value for the given key in the simple field * set, or the value can not be converted to a String */ - protected String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException { + protected static String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException { try { return simpleFieldSet.getString(key); } catch (FSParseException fspe1) { @@ -64,7 +64,7 @@ public abstract class AbstractCommand implements Command { * if there is no value for the given key in the simple field * set, or the value can not be converted to an int */ - protected int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException { + protected static int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException { try { return simpleFieldSet.getInt(key); } catch (FSParseException fspe1) { @@ -84,7 +84,7 @@ public abstract class AbstractCommand implements Command { * The default value * @return The int value */ - protected int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) { + protected static int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) { return simpleFieldSet.getInt(key, defaultValue); } @@ -100,7 +100,7 @@ public abstract class AbstractCommand implements Command { * if there is no value for the given key in the simple field * set, or the value can not be converted to a boolean */ - protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException { + protected static boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException { try { return simpleFieldSet.getBoolean(key); } catch (FSParseException fspe1) { @@ -120,7 +120,7 @@ public abstract class AbstractCommand implements Command { * The default value * @return The boolean value */ - protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) { + protected static boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) { return simpleFieldSet.getBoolean(key, defaultValue); } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java index 6fe4101..2ef33c3 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java @@ -17,23 +17,12 @@ package net.pterodactylus.sone.freenet.wot; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.util.cache.CacheException; -import net.pterodactylus.util.cache.CacheItem; -import net.pterodactylus.util.cache.DefaultCacheItem; -import net.pterodactylus.util.cache.MemoryCache; -import net.pterodactylus.util.cache.ValueRetriever; -import net.pterodactylus.util.cache.WritableCache; -import net.pterodactylus.util.collection.TimedMap; -import net.pterodactylus.util.logging.Logging; /** * A Web of Trust identity. @@ -42,12 +31,6 @@ import net.pterodactylus.util.logging.Logging; */ public class DefaultIdentity implements Identity { - /** The logger. */ - private static final Logger logger = Logging.getLogger(DefaultIdentity.class); - - /** The web of trust connector. */ - private final WebOfTrustConnector webOfTrustConnector; - /** The ID of the identity. */ private final String id; @@ -64,26 +47,11 @@ public class DefaultIdentity implements Identity { private final Map properties = Collections.synchronizedMap(new HashMap()); /** Cached trust. */ - /* synchronize on itself. */ - private final WritableCache trustCache = new MemoryCache(new ValueRetriever() { - - @Override - @SuppressWarnings("synthetic-access") - public CacheItem retrieve(OwnIdentity ownIdentity) throws CacheException { - try { - return new DefaultCacheItem(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this)); - } catch (PluginException pe1) { - throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1); - } - } - - }, new TimedMap>(60 * 60 * 1000)); + private final Map trustCache = Collections.synchronizedMap(new HashMap()); /** * Creates a new identity. * - * @param webOfTrustConnector - * The web of trust connector * @param id * The ID of the identity * @param nickname @@ -91,8 +59,7 @@ public class DefaultIdentity implements Identity { * @param requestUri * The request URI of the identity */ - public DefaultIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri) { - this.webOfTrustConnector = webOfTrustConnector; + public DefaultIdentity(String id, String nickname, String requestUri) { this.id = id; this.nickname = nickname; this.requestUri = requestUri; @@ -135,47 +102,35 @@ public class DefaultIdentity implements Identity { } /** - * Sets the contexts of this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param contexts - * The contexts to set + * {@inheritDoc} */ - void setContextsPrivate(Set contexts) { - this.contexts.clear(); - this.contexts.addAll(contexts); + @Override + public boolean hasContext(String context) { + return contexts.contains(context); } /** * {@inheritDoc} */ @Override - public boolean hasContext(String context) { - return contexts.contains(context); + public void setContexts(Collection contexts) { + this.contexts.clear(); + this.contexts.addAll(contexts); } /** - * Adds the given context to this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param context - * The context to add + * {@inheritDoc} */ - void addContextPrivate(String context) { + @Override + public void addContext(String context) { contexts.add(context); } /** - * Removes the given context from this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param context - * The context to remove + * {@inheritDoc} */ - public void removeContextPrivate(String context) { + @Override + public void removeContext(String context) { contexts.remove(context); } @@ -184,64 +139,40 @@ public class DefaultIdentity implements Identity { */ @Override public Map getProperties() { - synchronized (properties) { - return Collections.unmodifiableMap(properties); - } + return Collections.unmodifiableMap(properties); } /** - * Sets all properties of this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param properties - * The new properties of this identity + * {@inheritDoc} */ - void setPropertiesPrivate(Map properties) { - synchronized (this.properties) { - this.properties.clear(); - this.properties.putAll(properties); - } + @Override + public void setProperties(Map properties) { + this.properties.clear(); + this.properties.putAll(properties); } /** - * Sets the property with the given name to the given value. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param name - * The name of the property - * @param value - * The value of the property + * {@inheritDoc} */ - void setPropertyPrivate(String name, String value) { - synchronized (properties) { - properties.put(name, value); - } + @Override + public String getProperty(String name) { + return properties.get(name); } /** * {@inheritDoc} */ @Override - public String getProperty(String name) { - synchronized (properties) { - return properties.get(name); - } + public void setProperty(String name, String value) { + properties.put(name, value); } /** - * Removes the property with the given name. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param name - * The name of the property to remove + * {@inheritDoc} */ - void removePropertyPrivate(String name) { - synchronized (properties) { - properties.remove(name); - } + @Override + public void removeProperty(String name) { + properties.remove(name); } /** @@ -249,28 +180,23 @@ public class DefaultIdentity implements Identity { */ @Override public Trust getTrust(OwnIdentity ownIdentity) { - try { - synchronized (trustCache) { - return trustCache.get(ownIdentity); - } - } catch (CacheException ce1) { - logger.log(Level.WARNING, String.format("Could not get trust for OwnIdentity: %s", ownIdentity), ce1); - return null; - } + return trustCache.get(ownIdentity); } /** - * Sets the trust received for this identity by the given own identity. - * - * @param ownIdentity - * The own identity that gives the trust - * @param trust - * The trust received for this identity + * {@inheritDoc} */ - void setTrustPrivate(OwnIdentity ownIdentity, Trust trust) { - synchronized (trustCache) { - trustCache.put(ownIdentity, trust); - } + @Override + public void setTrust(OwnIdentity ownIdentity, Trust trust) { + trustCache.put(ownIdentity, trust); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeTrust(OwnIdentity ownIdentity) { + trustCache.remove(ownIdentity); } // diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java index c461c8b..451dd5f 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java @@ -17,12 +17,6 @@ package net.pterodactylus.sone.freenet.wot; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import net.pterodactylus.util.validation.Validation; - /** * An own identity is an identity that the owner of the node has full control * over. @@ -31,17 +25,12 @@ import net.pterodactylus.util.validation.Validation; */ public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity { - /** The identity manager. */ - private final WebOfTrustConnector webOfTrustConnector; - /** The insert URI of the identity. */ private final String insertUri; /** * Creates a new own identity. * - * @param webOfTrustConnector - * The identity manager * @param id * The ID of the identity * @param nickname @@ -51,26 +40,22 @@ public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity { * @param insertUri * The insert URI of the identity */ - public DefaultOwnIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri, String insertUri) { - super(webOfTrustConnector, id, nickname, requestUri); - this.webOfTrustConnector = webOfTrustConnector; + public DefaultOwnIdentity(String id, String nickname, String requestUri, String insertUri) { + super(id, nickname, requestUri); this.insertUri = insertUri; } /** * Copy constructor for an own identity. * - * @param webOfTrustConnector - * The web of trust connector * @param ownIdentity * The own identity to copy */ - public DefaultOwnIdentity(WebOfTrustConnector webOfTrustConnector, OwnIdentity ownIdentity) { - super(webOfTrustConnector, ownIdentity.getId(), ownIdentity.getNickname(), ownIdentity.getRequestUri()); - this.webOfTrustConnector = webOfTrustConnector; + public DefaultOwnIdentity(OwnIdentity ownIdentity) { + super(ownIdentity.getId(), ownIdentity.getNickname(), ownIdentity.getRequestUri()); this.insertUri = ownIdentity.getInsertUri(); - setContextsPrivate(ownIdentity.getContexts()); - setPropertiesPrivate(ownIdentity.getProperties()); + setContexts(ownIdentity.getContexts()); + setProperties(ownIdentity.getProperties()); } // @@ -85,124 +70,4 @@ public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity { return insertUri; } - /** - * {@inheritDoc} - */ - @Override - public void addContext(String context) throws WebOfTrustException { - webOfTrustConnector.addContext(this, context); - addContextPrivate(context); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeContext(String context) throws WebOfTrustException { - webOfTrustConnector.removeContext(this, context); - removeContextPrivate(context); - } - - /** - * {@inheritDoc} - */ - @Override - public void setContexts(Set contexts) throws WebOfTrustException { - for (String context : getContexts()) { - if (!contexts.contains(context)) { - webOfTrustConnector.removeContext(this, context); - } - } - for (String context : contexts) { - if (!getContexts().contains(context)) { - webOfTrustConnector.addContext(this, context); - } - } - setContextsPrivate(contexts); - } - - /** - * {@inheritDoc} - */ - @Override - public void setProperty(String name, String value) throws WebOfTrustException { - webOfTrustConnector.setProperty(this, name, value); - setPropertyPrivate(name, value); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeProperty(String name) throws WebOfTrustException { - webOfTrustConnector.removeProperty(this, name); - removePropertyPrivate(name); - } - - /** - * {@inheritDoc} - */ - @Override - public void setProperties(Map properties) throws WebOfTrustException { - for (Entry oldProperty : getProperties().entrySet()) { - if (!properties.containsKey(oldProperty.getKey())) { - webOfTrustConnector.removeProperty(this, oldProperty.getKey()); - } else { - webOfTrustConnector.setProperty(this, oldProperty.getKey(), properties.get(oldProperty.getKey())); - } - } - for (Entry newProperty : properties.entrySet()) { - if (!getProperties().containsKey(newProperty.getKey())) { - webOfTrustConnector.setProperty(this, newProperty.getKey(), newProperty.getValue()); - } - } - setPropertiesPrivate(properties); - } - - /** - * {@inheritDoc} - */ - @Override - public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException { - Validation.begin().isNotNull("Trust Target", target).isNotNull("Trust Comment", comment).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check(); - webOfTrustConnector.setTrust(this, target, trustValue, comment); - if (target instanceof DefaultIdentity) { - ((DefaultIdentity) target).setTrustPrivate(this, new Trust(trustValue, trustValue, 0)); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void removeTrust(Identity target) throws WebOfTrustException { - Validation.begin().isNotNull("Trust Target", target).check(); - webOfTrustConnector.removeTrust(this, target); - if (target instanceof DefaultIdentity) { - ((DefaultIdentity) target).setTrustPrivate(this, new Trust(null, null, null)); - } - } - - // - // OBJECT METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - /* The hash of DefaultIdentity is fine. */ - return super.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object object) { - /* The ID of the superclass is still enough. */ - return super.equals(object); - } - } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index 0a9fb93..2849da9 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -17,13 +17,14 @@ package net.pterodactylus.sone.freenet.wot; +import java.util.Collection; import java.util.Map; import java.util.Set; /** * Interface for web of trust identities, defining all functions that can be - * performed on an identity. The identity is the main entry point for identity - * management. + * performed on an identity. An identity is only a container for identity data + * and will not perform any updating in the WebOfTrust plugin itself. * * @author David ‘Bombe’ Roden */ @@ -68,6 +69,30 @@ public interface Identity { public boolean hasContext(String context); /** + * Adds the given context to this identity. + * + * @param context + * The context to add + */ + public void addContext(String context); + + /** + * Sets all contexts of this identity. + * + * @param contexts + * All contexts of the identity + */ + public void setContexts(Collection contexts); + + /** + * Removes the given context from this identity. + * + * @param context + * The context to remove + */ + public void removeContext(String context); + + /** * Returns all properties of this identity. * * @return All properties of this identity @@ -84,6 +109,32 @@ public interface Identity { public String getProperty(String name); /** + * Sets the property with the given name to the given value. + * + * @param name + * The name of the property + * @param value + * The value of the property + */ + public void setProperty(String name, String value); + + /** + * Sets all properties of this identity. + * + * @param properties + * The new properties of this identity + */ + public void setProperties(Map properties); + + /** + * Removes the property with the given name. + * + * @param name + * The name of the property to remove + */ + public void removeProperty(String name); + + /** * Retrieves the trust that this identity receives from the given own * identity. If this identity is not in the own identity’s trust tree, a * {@link Trust} is returned that has all its elements set to {@code null}. @@ -96,4 +147,23 @@ public interface Identity { */ public Trust getTrust(OwnIdentity ownIdentity); + /** + * Sets the trust given by an own identity to this identity. + * + * @param ownIdentity + * The own identity that gave trust to this identity + * @param trust + * The trust given by the given own identity + */ + public void setTrust(OwnIdentity ownIdentity, Trust trust); + + /** + * Removes trust assignment from the given own identity for this identity. + * + * @param ownIdentity + * The own identity that removed the trust assignment for this + * identity + */ + public void removeTrust(OwnIdentity ownIdentity); + } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java index 32c0d27..bca6ac1 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.freenet.wot; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -26,8 +27,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.util.collection.mapper.Mapper; -import net.pterodactylus.util.collection.mapper.Mappers; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.service.AbstractService; @@ -60,21 +59,28 @@ public class IdentityManager extends AbstractService { private final WebOfTrustConnector webOfTrustConnector; /** The context to filter for. */ - private volatile String context; + private final String context; /** The currently known own identities. */ /* synchronize access on syncObject. */ - private Map currentOwnIdentities = new HashMap(); + private final Map currentOwnIdentities = new HashMap(); + + /** The last time all identities were loaded. */ + private volatile long identitiesLastLoaded; /** * Creates a new identity manager. * * @param webOfTrustConnector * The Web of Trust connector + * @param context + * The context to focus on (may be {@code null} to ignore + * contexts) */ - public IdentityManager(WebOfTrustConnector webOfTrustConnector) { + public IdentityManager(WebOfTrustConnector webOfTrustConnector, String context) { super("Sone Identity Manager", false); this.webOfTrustConnector = webOfTrustConnector; + this.context = context; } // @@ -106,13 +112,13 @@ public class IdentityManager extends AbstractService { // /** - * Sets the context to filter own identities and trusted identities for. + * Returns the last time all identities were loaded. * - * @param context - * The context to filter for, or {@code null} to not filter + * @return The last time all identities were loaded (in milliseconds since + * Jan 1, 1970 UTC) */ - public void setContext(String context) { - this.context = context; + public long getIdentitiesLastLoaded() { + return identitiesLastLoaded; } /** @@ -143,7 +149,7 @@ public class IdentityManager extends AbstractService { Set allOwnIdentities = getAllOwnIdentities(); for (OwnIdentity ownIdentity : allOwnIdentities) { if (ownIdentity.getId().equals(id)) { - return new DefaultOwnIdentity(webOfTrustConnector, ownIdentity); + return new DefaultOwnIdentity(ownIdentity); } } return null; @@ -155,28 +161,7 @@ public class IdentityManager extends AbstractService { * @return All own identities */ public Set getAllOwnIdentities() { - try { - Set ownIdentities = webOfTrustConnector.loadAllOwnIdentities(); - Map newOwnIdentities = new HashMap(); - for (OwnIdentity ownIdentity : ownIdentities) { - newOwnIdentities.put(ownIdentity.getId(), ownIdentity); - } - checkOwnIdentities(newOwnIdentities); - return Mappers.mappedSet(ownIdentities, new Mapper() { - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("synthetic-access") - public OwnIdentity map(OwnIdentity input) { - return new DefaultOwnIdentity(webOfTrustConnector, input); - } - }); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not load all own identities!", wote1); - return Collections.emptySet(); - } + return new HashSet(currentOwnIdentities.values()); } // @@ -214,6 +199,7 @@ public class IdentityManager extends AbstractService { } } identitiesLoaded = true; + identitiesLastLoaded = System.currentTimeMillis(); } catch (WebOfTrustException wote1) { logger.log(Level.WARNING, "WoT has disappeared!", wote1); } @@ -310,7 +296,7 @@ public class IdentityManager extends AbstractService { for (OwnIdentity oldOwnIdentity : currentOwnIdentities.values()) { OwnIdentity newOwnIdentity = newOwnIdentities.get(oldOwnIdentity.getId()); if ((newOwnIdentity == null) || ((context != null) && oldOwnIdentity.hasContext(context) && !newOwnIdentity.hasContext(context))) { - identityListenerManager.fireOwnIdentityRemoved(new DefaultOwnIdentity(webOfTrustConnector, oldOwnIdentity)); + identityListenerManager.fireOwnIdentityRemoved(new DefaultOwnIdentity(oldOwnIdentity)); } } @@ -318,7 +304,7 @@ public class IdentityManager extends AbstractService { for (OwnIdentity currentOwnIdentity : newOwnIdentities.values()) { OwnIdentity oldOwnIdentity = currentOwnIdentities.get(currentOwnIdentity.getId()); if (((oldOwnIdentity == null) && ((context == null) || currentOwnIdentity.hasContext(context))) || ((oldOwnIdentity != null) && (context != null) && (!oldOwnIdentity.hasContext(context) && currentOwnIdentity.hasContext(context)))) { - identityListenerManager.fireOwnIdentityAdded(new DefaultOwnIdentity(webOfTrustConnector, currentOwnIdentity)); + identityListenerManager.fireOwnIdentityAdded(new DefaultOwnIdentity(currentOwnIdentity)); } } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java index 6c6224f..4272669 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java @@ -17,8 +17,6 @@ package net.pterodactylus.sone.freenet.wot; -import java.util.Map; -import java.util.Set; /** * Defines a local identity, an own identity. @@ -34,100 +32,4 @@ public interface OwnIdentity extends Identity { */ public String getInsertUri(); - /** - * Adds the given context to this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param context - * The context to add - * @throws WebOfTrustException - * if an error occurs - */ - public void addContext(String context) throws WebOfTrustException; - - /** - * Sets all contexts of this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param contexts - * All contexts of the identity - * @throws WebOfTrustException - * if an error occurs - */ - public void setContexts(Set contexts) throws WebOfTrustException; - - /** - * Removes the given context from this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param context - * The context to remove - * @throws WebOfTrustException - * if an error occurs - */ - public void removeContext(String context) throws WebOfTrustException; - - /** - * Sets the property with the given name to the given value. - * - * @param name - * The name of the property - * @param value - * The value of the property - * @throws WebOfTrustException - * if an error occurs - */ - public void setProperty(String name, String value) throws WebOfTrustException; - - /** - * Sets all properties of this identity. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param properties - * The new properties of this identity - * @throws WebOfTrustException - * if an error occurs - */ - public void setProperties(Map properties) throws WebOfTrustException; - - /** - * Removes the property with the given name. - *

- * This method is only called by the {@link IdentityManager}. - * - * @param name - * The name of the property to remove - * @throws WebOfTrustException - * if an error occurs - */ - public void removeProperty(String name) throws WebOfTrustException; - - /** - * Sets the trust for the given target identity. - * - * @param target - * The target to set the trust for - * @param trustValue - * The new trust value (from {@code -100} or {@code 100}) - * @param comment - * The comment for the trust assignment - * @throws WebOfTrustException - * if an error occurs - */ - public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException; - - /** - * Removes any trust assignment for the given target identity. - * - * @param target - * The targe to remove the trust assignment for - * @throws WebOfTrustException - * if an error occurs - */ - public void removeTrust(Identity target) throws WebOfTrustException; - } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java index a7c6524..53a45b1 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,6 +29,7 @@ import net.pterodactylus.sone.freenet.plugin.ConnectorListener; import net.pterodactylus.sone.freenet.plugin.PluginConnector; import net.pterodactylus.sone.freenet.plugin.PluginException; import net.pterodactylus.util.logging.Logging; +import net.pterodactylus.util.number.Numbers; import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -36,7 +38,7 @@ import freenet.support.api.Bucket; * * @author David ‘Bombe’ Roden */ -public class WebOfTrustConnector implements ConnectorListener { +public class WebOfTrustConnector { /** The logger. */ private static final Logger logger = Logging.getLogger(WebOfTrustConnector.class); @@ -44,11 +46,8 @@ public class WebOfTrustConnector implements ConnectorListener { /** The name of the WoT plugin. */ private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust"; - /** A random connection identifier. */ - private static final String PLUGIN_CONNECTION_IDENTIFIER = "Sone-WoT-Connector-" + Math.abs(Math.random()); - - /** The current reply. */ - private Reply reply; + /** Counter for connection identifiers. */ + private final AtomicLong counter = new AtomicLong(); /** The plugin connector. */ private final PluginConnector pluginConnector; @@ -62,7 +61,6 @@ public class WebOfTrustConnector implements ConnectorListener { */ public WebOfTrustConnector(PluginConnector pluginConnector) { this.pluginConnector = pluginConnector; - pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this); } // @@ -73,10 +71,7 @@ public class WebOfTrustConnector implements ConnectorListener { * Stops the web of trust connector. */ public void stop() { - pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this); - synchronized (reply) { - reply.notifyAll(); - } + /* does nothing. */ } /** @@ -99,9 +94,9 @@ public class WebOfTrustConnector implements ConnectorListener { String requestUri = fields.get("RequestURI" + ownIdentityCounter); String insertUri = fields.get("InsertURI" + ownIdentityCounter); String nickname = fields.get("Nickname" + ownIdentityCounter); - DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(this, id, nickname, requestUri, insertUri); - ownIdentity.setContextsPrivate(parseContexts("Contexts" + ownIdentityCounter + ".", fields)); - ownIdentity.setPropertiesPrivate(parseProperties("Properties" + ownIdentityCounter + ".", fields)); + DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri); + ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields)); + ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields)); ownIdentities.add(ownIdentity); } return ownIdentities; @@ -134,7 +129,7 @@ public class WebOfTrustConnector implements ConnectorListener { * if an error occured talking to the Web of Trust plugin */ public Set loadTrustedIdentities(OwnIdentity ownIdentity, String context) throws PluginException { - Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).get()); + Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).put("WantTrustValues", "true").get()); SimpleFieldSet fields = reply.getFields(); Set identities = new HashSet(); int identityCounter = -1; @@ -145,9 +140,13 @@ public class WebOfTrustConnector implements ConnectorListener { } String nickname = fields.get("Nickname" + identityCounter); String requestUri = fields.get("RequestURI" + identityCounter); - DefaultIdentity identity = new DefaultIdentity(this, id, nickname, requestUri); - identity.setContextsPrivate(parseContexts("Contexts" + identityCounter + ".", fields)); - identity.setPropertiesPrivate(parseProperties("Properties" + identityCounter + ".", fields)); + DefaultIdentity identity = new DefaultIdentity(id, nickname, requestUri); + identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields)); + identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields)); + Integer trust = Numbers.safeParseInteger(fields.get("Trust" + identityCounter), null); + int score = Numbers.safeParseInteger(fields.get("Score" + identityCounter)); + int rank = Numbers.safeParseInteger(fields.get("Rank" + identityCounter)); + identity.setTrust(ownIdentity, new Trust(trust, score, rank)); identities.add(identity); } return identities; @@ -318,7 +317,7 @@ public class WebOfTrustConnector implements ConnectorListener { * The fields to parse the contexts from * @return The parsed contexts */ - private Set parseContexts(String prefix, SimpleFieldSet fields) { + private static Set parseContexts(String prefix, SimpleFieldSet fields) { Set contexts = new HashSet(); int contextCounter = -1; while (true) { @@ -340,7 +339,7 @@ public class WebOfTrustConnector implements ConnectorListener { * The fields to parse the properties from * @return The parsed properties */ - private Map parseProperties(String prefix, SimpleFieldSet fields) { + private static Map parseProperties(String prefix, SimpleFieldSet fields) { Map properties = new HashMap(); int propertiesCounter = -1; while (true) { @@ -380,15 +379,37 @@ public class WebOfTrustConnector implements ConnectorListener { * @throws PluginException * if the request could not be sent */ - private synchronized Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException { - reply = new Reply(); + private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException { + final String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement(); + final Reply reply = new Reply(); logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message"))); + ConnectorListener connectorListener = new ConnectorListener() { + + @Override + @SuppressWarnings("synthetic-access") + public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) { + String messageName = fields.get("Message"); + logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", messageName)); + synchronized (reply) { + reply.setFields(fields); + reply.setData(data); + reply.notify(); + } + } + }; + pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener); synchronized (reply) { - pluginConnector.sendRequest(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, fields, data); try { - reply.wait(); - } catch (InterruptedException ie1) { - logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1); + pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data); + while (reply.getFields() == null) { + try { + reply.wait(); + } catch (InterruptedException ie1) { + logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1); + } + } + } finally { + pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener); } } logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null)); @@ -398,24 +419,6 @@ public class WebOfTrustConnector implements ConnectorListener { return reply; } - // - // INTERFACE ConnectorListener - // - - /** - * {@inheritDoc} - */ - @Override - public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) { - String messageName = fields.get("Message"); - logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", messageName)); - synchronized (reply) { - reply.setFields(fields); - reply.setData(data); - reply.notify(); - } - } - /** * Container for the data of the reply from a plugin. * diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index cca2c7b..0f8ba97 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -24,6 +24,7 @@ import java.util.logging.Logger; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.FreenetInterface; +import net.pterodactylus.sone.core.WebOfTrustUpdater; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend; import net.pterodactylus.sone.freenet.plugin.PluginConnector; @@ -83,7 +84,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr } /** The version. */ - public static final Version VERSION = new Version(0, 8, 2); + public static final Version VERSION = new Version(0, 8, 3); /** The logger. */ private static final Logger logger = Logging.getLogger(SonePlugin.class); @@ -185,11 +186,14 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* create web of trust connector. */ PluginConnector pluginConnector = new PluginConnector(pluginRespirator); webOfTrustConnector = new WebOfTrustConnector(pluginConnector); - identityManager = new IdentityManager(webOfTrustConnector); - identityManager.setContext("Sone"); + identityManager = new IdentityManager(webOfTrustConnector, "Sone"); + + /* create trust updater. */ + WebOfTrustUpdater trustUpdater = new WebOfTrustUpdater(webOfTrustConnector); + trustUpdater.init(); /* create core. */ - core = new Core(oldConfiguration, freenetInterface, identityManager); + core = new Core(oldConfiguration, freenetInterface, identityManager, trustUpdater); /* create the web interface. */ webInterface = new WebInterface(this); diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java index 51bb1c0..6efee27 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java @@ -233,7 +233,14 @@ public class ListNotificationFilters { return false; } } else { - return false; + /* + * a null trust means that the trust updater has not yet + * received a trust value for this relation. if we return false, + * the post feed will stay empty until the trust updater has + * received trust values. to prevent this we simply assume that + * posts are visible if there is no trust. + */ + return true; } if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.equals(post.getRecipient())) { return false; diff --git a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java index 7a5d6d3..e22e00b 100644 --- a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java @@ -68,7 +68,7 @@ public class AlbumAccessor extends ReflectionAccessor { * The name of the link * @return The created map containing the mappings */ - private Map createLink(String target, String name) { + private static Map createLink(String target, String name) { Map link = new HashMap(); link.put("target", target); link.put("name", name); diff --git a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java index 4602188..4fbe74c 100644 --- a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java @@ -93,7 +93,7 @@ public class IdentityAccessor extends ReflectionAccessor { * append to the nickname * @return The nickname with optional ID appendage */ - private String getAbbreviatedNickname(Identity identity, int length) { + private static String getAbbreviatedNickname(Identity identity, int length) { return identity.getNickname() + ((length > 0) ? "@" + identity.getId().substring(0, length) : ""); } diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index 8302369..75eff8b 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -60,10 +60,10 @@ public class ParserFilter implements Filter { private final TemplateContextFactory templateContextFactory; /** The template for {@link PlainTextPart}s. */ - private final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>")); + private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>")); /** The template for {@link FreenetLinkPart}s. */ - private final Template linkTemplate = TemplateParser.parse(new StringReader("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); + private static final Template linkTemplate = TemplateParser.parse(new StringReader("\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html>")); /** * Creates a new filter that runs its input through a {@link SoneTextParser} @@ -241,7 +241,28 @@ public class ParserFilter implements Filter { * The part to render */ private void render(Writer writer, PostPart postPart) { - renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), getExcerpt(postPart.getPost().getText(), 20), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone"); + SoneTextParser parser = new SoneTextParser(core, core); + SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone()); + try { + Iterable parts = parser.parse(parserContext, new StringReader(postPart.getPost().getText())); + StringBuilder excerpt = new StringBuilder(); + for (Part part : parts) { + excerpt.append(part.getText()); + if (excerpt.length() > 20) { + int lastSpace = excerpt.lastIndexOf(" ", 20); + if (lastSpace > -1) { + excerpt.setLength(lastSpace); + } else { + excerpt.setLength(20); + } + excerpt.append("…"); + break; + } + } + renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone"); + } catch (IOException ioe1) { + /* StringReader shouldn’t throw. */ + } } /** @@ -267,26 +288,4 @@ public class ParserFilter implements Filter { linkTemplate.render(templateContext, writer); } - // - // STATIC METHODS - // - - /** - * Returns up to {@code length} characters from the given text, appending - * “…” if the text is longer. - * - * @param text - * The text to get an excerpt from - * @param length - * The maximum length of the excerpt (without the ellipsis) - * @return The excerpt of the text - */ - private static String getExcerpt(String text, int length) { - String filteredText = text.replaceAll("(\r\n)+", "\r\n").replaceAll("\n+", "\n").replace("\r\n", " ").replace('\n', ' '); - if (filteredText.length() > length) { - return filteredText.substring(0, length) + "…"; - } - return filteredText; - } - } diff --git a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java index 1f29b65..f004967 100644 --- a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java @@ -21,6 +21,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.util.template.Accessor; import net.pterodactylus.util.template.ReflectionAccessor; @@ -83,7 +84,7 @@ public class ProfileAccessor extends ReflectionAccessor { if (showCustomAvatars == ShowCustomAvatars.FOLLOWED) { return currentSone.hasFriend(remoteSone.getId()) ? avatarId : null; } - Trust trust = core.getTrust(currentSone, remoteSone); + Trust trust = remoteSone.getIdentity().getTrust((OwnIdentity) currentSone.getIdentity()); if (trust == null) { return null; } diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index eac8a93..eca8654 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -24,6 +24,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.ajax.GetTimesAjaxPage; @@ -106,7 +107,7 @@ public class SoneAccessor extends ReflectionAccessor { if (currentSone == null) { return null; } - Trust trust = core.getTrust(currentSone, sone); + Trust trust = sone.getIdentity().getTrust((OwnIdentity) currentSone.getIdentity()); logger.log(Level.FINEST, String.format("Trust for %s by %s: %s", sone, currentSone, trust)); if (trust == null) { return new Trust(null, null, null); diff --git a/src/main/java/net/pterodactylus/sone/text/LinkPart.java b/src/main/java/net/pterodactylus/sone/text/LinkPart.java index 1b47080..202b9db 100644 --- a/src/main/java/net/pterodactylus/sone/text/LinkPart.java +++ b/src/main/java/net/pterodactylus/sone/text/LinkPart.java @@ -77,21 +77,26 @@ public class LinkPart implements Part { } /** - * Returns the text of this part. + * Returns the title of this part. * - * @return The text of this part + * @return The title of this part */ - public String getText() { - return text; + public String getTitle() { + return title; } + // + // PART METHODS + // + /** - * Returns the title of this part. + * Returns the text of this part. * - * @return The title of this part + * @return The text of this part */ - public String getTitle() { - return title; + @Override + public String getText() { + return text; } } diff --git a/src/main/java/net/pterodactylus/sone/text/Part.java b/src/main/java/net/pterodactylus/sone/text/Part.java index 76e80ef..79c59dc 100644 --- a/src/main/java/net/pterodactylus/sone/text/Part.java +++ b/src/main/java/net/pterodactylus/sone/text/Part.java @@ -26,6 +26,12 @@ package net.pterodactylus.sone.text; */ public interface Part { - /* no methods. */ + /** + * Returns the text contained in this part. This should return plain text + * without any format information. + * + * @return The plain text of this part + */ + public String getText(); } diff --git a/src/main/java/net/pterodactylus/sone/text/PartContainer.java b/src/main/java/net/pterodactylus/sone/text/PartContainer.java index e456cd9..a8a7e85 100644 --- a/src/main/java/net/pterodactylus/sone/text/PartContainer.java +++ b/src/main/java/net/pterodactylus/sone/text/PartContainer.java @@ -81,6 +81,22 @@ public class PartContainer implements Part, Iterable { } // + // PART METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + StringBuilder partText = new StringBuilder(); + for (Part part : parts) { + partText.append(part.getText()); + } + return partText.toString(); + } + + // // ITERABLE METHODS // diff --git a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java index 09c1fba..2c29ee2 100644 --- a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java @@ -38,7 +38,7 @@ public class PlainTextPart implements Part { } // - // ACCESSORS + // PART METHODS // /** @@ -46,6 +46,7 @@ public class PlainTextPart implements Part { * * @return The text of this part */ + @Override public String getText() { return text; } diff --git a/src/main/java/net/pterodactylus/sone/text/PostPart.java b/src/main/java/net/pterodactylus/sone/text/PostPart.java index c416c57..6241b7a 100644 --- a/src/main/java/net/pterodactylus/sone/text/PostPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PostPart.java @@ -52,4 +52,16 @@ public class PostPart implements Part { return post; } + // + // PART METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return post.getText(); + } + } diff --git a/src/main/java/net/pterodactylus/sone/text/SonePart.java b/src/main/java/net/pterodactylus/sone/text/SonePart.java index 475c091..37f098b 100644 --- a/src/main/java/net/pterodactylus/sone/text/SonePart.java +++ b/src/main/java/net/pterodactylus/sone/text/SonePart.java @@ -18,6 +18,7 @@ package net.pterodactylus.sone.text; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.template.SoneAccessor; /** * {@link Part} implementation that stores a reference to a {@link Sone}. @@ -52,4 +53,16 @@ public class SonePart implements Part { return sone; } + // + // PART METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return SoneAccessor.getNiceName(sone); + } + } diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index dd0da28..baa3dc9 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -30,6 +30,7 @@ import net.pterodactylus.sone.core.PostProvider; import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import freenet.keys.FreenetURI; @@ -109,187 +110,193 @@ public class SoneTextParser implements Parser { public Iterable parse(SoneTextParserContext context, Reader source) throws IOException { PartContainer parts = new PartContainer(); BufferedReader bufferedReader = (source instanceof BufferedReader) ? (BufferedReader) source : new BufferedReader(source); - String line; - boolean lastLineEmpty = true; - int emptyLines = 0; - while ((line = bufferedReader.readLine()) != null) { - if (line.trim().length() == 0) { - if (lastLineEmpty) { + try { + String line; + boolean lastLineEmpty = true; + int emptyLines = 0; + while ((line = bufferedReader.readLine()) != null) { + if (line.trim().length() == 0) { + if (lastLineEmpty) { + continue; + } + parts.add(new PlainTextPart("\n")); + ++emptyLines; + lastLineEmpty = emptyLines == 2; continue; } - parts.add(new PlainTextPart("\n")); - ++emptyLines; - lastLineEmpty = emptyLines == 2; - continue; - } - emptyLines = 0; - /* - * lineComplete tracks whether the block you are parsing is the - * first block of the line. this is important because sometimes you - * have to add an additional line break. - */ - boolean lineComplete = true; - while (line.length() > 0) { - int nextKsk = line.indexOf("KSK@"); - int nextChk = line.indexOf("CHK@"); - int nextSsk = line.indexOf("SSK@"); - int nextUsk = line.indexOf("USK@"); - int nextHttp = line.indexOf("http://"); - int nextHttps = line.indexOf("https://"); - int nextSone = line.indexOf("sone://"); - int nextPost = line.indexOf("post://"); - if ((nextKsk == -1) && (nextChk == -1) && (nextSsk == -1) && (nextUsk == -1) && (nextHttp == -1) && (nextHttps == -1) && (nextSone == -1) && (nextPost == -1)) { - if (lineComplete && !lastLineEmpty) { - parts.add(new PlainTextPart("\n" + line)); - } else { - parts.add(new PlainTextPart(line)); + emptyLines = 0; + /* + * lineComplete tracks whether the block you are parsing is the + * first block of the line. this is important because sometimes + * you have to add an additional line break. + */ + boolean lineComplete = true; + while (line.length() > 0) { + int nextKsk = line.indexOf("KSK@"); + int nextChk = line.indexOf("CHK@"); + int nextSsk = line.indexOf("SSK@"); + int nextUsk = line.indexOf("USK@"); + int nextHttp = line.indexOf("http://"); + int nextHttps = line.indexOf("https://"); + int nextSone = line.indexOf("sone://"); + int nextPost = line.indexOf("post://"); + if ((nextKsk == -1) && (nextChk == -1) && (nextSsk == -1) && (nextUsk == -1) && (nextHttp == -1) && (nextHttps == -1) && (nextSone == -1) && (nextPost == -1)) { + if (lineComplete && !lastLineEmpty) { + parts.add(new PlainTextPart("\n" + line)); + } else { + parts.add(new PlainTextPart(line)); + } + break; + } + int next = Integer.MAX_VALUE; + LinkType linkType = null; + if ((nextKsk > -1) && (nextKsk < next)) { + next = nextKsk; + linkType = LinkType.KSK; + } + if ((nextChk > -1) && (nextChk < next)) { + next = nextChk; + linkType = LinkType.CHK; + } + if ((nextSsk > -1) && (nextSsk < next)) { + next = nextSsk; + linkType = LinkType.SSK; + } + if ((nextUsk > -1) && (nextUsk < next)) { + next = nextUsk; + linkType = LinkType.USK; + } + if ((nextHttp > -1) && (nextHttp < next)) { + next = nextHttp; + linkType = LinkType.HTTP; + } + if ((nextHttps > -1) && (nextHttps < next)) { + next = nextHttps; + linkType = LinkType.HTTPS; + } + if ((nextSone > -1) && (nextSone < next)) { + next = nextSone; + linkType = LinkType.SONE; + } + if ((nextPost > -1) && (nextPost < next)) { + next = nextPost; + linkType = LinkType.POST; } - break; - } - int next = Integer.MAX_VALUE; - LinkType linkType = null; - if ((nextKsk > -1) && (nextKsk < next)) { - next = nextKsk; - linkType = LinkType.KSK; - } - if ((nextChk > -1) && (nextChk < next)) { - next = nextChk; - linkType = LinkType.CHK; - } - if ((nextSsk > -1) && (nextSsk < next)) { - next = nextSsk; - linkType = LinkType.SSK; - } - if ((nextUsk > -1) && (nextUsk < next)) { - next = nextUsk; - linkType = LinkType.USK; - } - if ((nextHttp > -1) && (nextHttp < next)) { - next = nextHttp; - linkType = LinkType.HTTP; - } - if ((nextHttps > -1) && (nextHttps < next)) { - next = nextHttps; - linkType = LinkType.HTTPS; - } - if ((nextSone > -1) && (nextSone < next)) { - next = nextSone; - linkType = LinkType.SONE; - } - if ((nextPost > -1) && (nextPost < next)) { - next = nextPost; - linkType = LinkType.POST; - } - /* cut off “freenet:” from before keys. */ - if (((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) { - next -= 8; - line = line.substring(0, next) + line.substring(next + 8); - } + /* cut off “freenet:” from before keys. */ + if (((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) { + next -= 8; + line = line.substring(0, next) + line.substring(next + 8); + } - /* if there is text before the next item, write it out. */ - if (lineComplete && !lastLineEmpty) { - parts.add(new PlainTextPart("\n")); - } - if (next > 0) { - parts.add(new PlainTextPart(line.substring(0, next))); - line = line.substring(next); - next = 0; - } - lineComplete = false; + /* if there is text before the next item, write it out. */ + if (lineComplete && !lastLineEmpty) { + parts.add(new PlainTextPart("\n")); + } + if (next > 0) { + parts.add(new PlainTextPart(line.substring(0, next))); + line = line.substring(next); + next = 0; + } + lineComplete = false; - if (linkType == LinkType.SONE) { - if (line.length() >= (7 + 43)) { - String soneId = line.substring(7, 50); - Sone sone = soneProvider.getSone(soneId, false); - if (sone == null) { - /* - * don’t use create=true above, we don’t want the - * empty shell. - */ - sone = new Sone(soneId); + if (linkType == LinkType.SONE) { + if (line.length() >= (7 + 43)) { + String soneId = line.substring(7, 50); + Sone sone = soneProvider.getSone(soneId, false); + if (sone == null) { + /* + * don’t use create=true above, we don’t want + * the empty shell. + */ + sone = new Sone(soneId); + } + parts.add(new SonePart(sone)); + line = line.substring(50); + } else { + parts.add(new PlainTextPart(line)); + line = ""; } - parts.add(new SonePart(sone)); - line = line.substring(50); - } else { - parts.add(new PlainTextPart(line)); - line = ""; + continue; } - continue; - } - if (linkType == LinkType.POST) { - if (line.length() >= (7 + 36)) { - String postId = line.substring(7, 43); - Post post = postProvider.getPost(postId, false); - if ((post != null) && (post.getSone() != null)) { - parts.add(new PostPart(post)); + if (linkType == LinkType.POST) { + if (line.length() >= (7 + 36)) { + String postId = line.substring(7, 43); + Post post = postProvider.getPost(postId, false); + if ((post != null) && (post.getSone() != null)) { + parts.add(new PostPart(post)); + } else { + parts.add(new PlainTextPart(line.substring(0, 43))); + } + line = line.substring(43); } else { - parts.add(new PlainTextPart(line.substring(0, 43))); + parts.add(new PlainTextPart(line)); + line = ""; } - line = line.substring(43); - } else { - parts.add(new PlainTextPart(line)); - line = ""; + continue; } - continue; - } - Matcher matcher = whitespacePattern.matcher(line); - int nextSpace = matcher.find(0) ? matcher.start() : line.length(); - String link = line.substring(0, nextSpace); - String name = link; - logger.log(Level.FINER, String.format("Found link: %s", link)); - logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); + Matcher matcher = whitespacePattern.matcher(line); + int nextSpace = matcher.find(0) ? matcher.start() : line.length(); + String link = line.substring(0, nextSpace); + String name = link; + logger.log(Level.FINER, String.format("Found link: %s", link)); + logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); - if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) { - FreenetURI uri; - if (name.indexOf('?') > -1) { - name = name.substring(0, name.indexOf('?')); - } - if (name.endsWith("/")) { - name = name.substring(0, name.length() - 1); - } - try { - uri = new FreenetURI(name); - name = uri.lastMetaString(); - if (name == null) { - name = uri.getDocName(); + if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) { + FreenetURI uri; + if (name.indexOf('?') > -1) { + name = name.substring(0, name.indexOf('?')); } - if (name == null) { - name = link.substring(0, Math.min(9, link.length())); + if (name.endsWith("/")) { + name = name.substring(0, name.length() - 1); } - boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId()); - parts.add(new FreenetLinkPart(link, name, fromPostingSone)); - } catch (MalformedURLException mue1) { - /* not a valid link, insert as plain text. */ - parts.add(new PlainTextPart(link)); - } catch (NullPointerException npe1) { - /* FreenetURI sometimes throws these, too. */ - parts.add(new PlainTextPart(link)); - } catch (ArrayIndexOutOfBoundsException aioobe1) { - /* oh, and these, too. */ - parts.add(new PlainTextPart(link)); - } - } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) { - name = link.substring(linkType == LinkType.HTTP ? 7 : 8); - int firstSlash = name.indexOf('/'); - int lastSlash = name.lastIndexOf('/'); - if ((lastSlash - firstSlash) > 3) { - name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash); - } - if (name.endsWith("/")) { - name = name.substring(0, name.length() - 1); - } - if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) { - name = name.substring(4); - } - if (name.indexOf('?') > -1) { - name = name.substring(0, name.indexOf('?')); + try { + uri = new FreenetURI(name); + name = uri.lastMetaString(); + if (name == null) { + name = uri.getDocName(); + } + if (name == null) { + name = link.substring(0, Math.min(9, link.length())); + } + boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId()); + parts.add(new FreenetLinkPart(link, name, fromPostingSone)); + } catch (MalformedURLException mue1) { + /* not a valid link, insert as plain text. */ + parts.add(new PlainTextPart(link)); + } catch (NullPointerException npe1) { + /* FreenetURI sometimes throws these, too. */ + parts.add(new PlainTextPart(link)); + } catch (ArrayIndexOutOfBoundsException aioobe1) { + /* oh, and these, too. */ + parts.add(new PlainTextPart(link)); + } + } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) { + name = link.substring(linkType == LinkType.HTTP ? 7 : 8); + int firstSlash = name.indexOf('/'); + int lastSlash = name.lastIndexOf('/'); + if ((lastSlash - firstSlash) > 3) { + name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash); + } + if (name.endsWith("/")) { + name = name.substring(0, name.length() - 1); + } + if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) { + name = name.substring(4); + } + if (name.indexOf('?') > -1) { + name = name.substring(0, name.indexOf('?')); + } + parts.add(new LinkPart(link, name)); } - parts.add(new LinkPart(link, name)); + line = line.substring(nextSpace); } - line = line.substring(nextSpace); + lastLineEmpty = false; + } + } finally { + if (bufferedReader != source) { + Closer.close(bufferedReader); } - lastLineEmpty = false; } for (int partIndex = parts.size() - 1; partIndex >= 0; --partIndex) { Part part = parts.getPart(partIndex); diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index 30c90bc..a1b759b 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -158,7 +158,7 @@ public class EditProfilePage extends SoneTemplatePage { * @return The parsed ID, or {@code null} if there was no part matching the * given string */ - private String getFieldId(FreenetRequest request, String partNameStart) { + private static String getFieldId(FreenetRequest request, String partNameStart) { for (String partName : request.getHttpRequest().getParts()) { if (partName.startsWith(partNameStart)) { return partName.substring(partNameStart.length()); diff --git a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java index a6d3daf..da48ec5 100644 --- a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java @@ -65,6 +65,7 @@ public class ImageBrowserPage extends SoneTemplatePage { Album album = webInterface.getCore().getAlbum(albumId, false); templateContext.set("albumRequested", true); templateContext.set("album", album); + templateContext.set("page", request.getHttpRequest().getParam("page")); return; } String imageId = request.getHttpRequest().getParam("image", null); diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index ab962d8..c770da3 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -132,10 +132,6 @@ public class OptionsPage extends SoneTemplatePage { Integer fcpFullAccessRequiredInteger = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("fcp-full-access-required", 1), preferences.getFcpFullAccessRequired().ordinal()); FullAccessRequired fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequiredInteger]; preferences.setFcpFullAccessRequired(fcpFullAccessRequired); - boolean clearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("clear-on-next-restart", 5)); - preferences.setClearOnNextRestart(clearOnNextRestart); - boolean reallyClearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("really-clear-on-next-restart", 5)); - preferences.setReallyClearOnNextRestart(reallyClearOnNextRestart); webInterface.getCore().touchConfiguration(); if (fieldErrors.isEmpty()) { throw new RedirectException(getPath()); @@ -161,8 +157,6 @@ public class OptionsPage extends SoneTemplatePage { templateContext.set("trust-comment", preferences.getTrustComment()); templateContext.set("fcp-interface-active", preferences.isFcpInterfaceActive()); templateContext.set("fcp-full-access-required", preferences.getFcpFullAccessRequired().ordinal()); - templateContext.set("clear-on-next-restart", preferences.isClearOnNextRestart()); - templateContext.set("really-clear-on-next-restart", preferences.isReallyClearOnNextRestart()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index a4da9f4..0f12abf 100644 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@ -111,6 +111,26 @@ public class SearchPage extends SoneTemplatePage { throw new RedirectException("index.html"); } + /* check for a couple of shortcuts. */ + if (phrases.size() == 1) { + String phrase = phrases.get(0).getPhrase(); + + /* is it a Sone ID? */ + redirectIfNotNull(getSoneId(phrase), "viewSone.html?sone="); + + /* is it a post ID? */ + redirectIfNotNull(getPostId(phrase), "viewPost.html?post="); + + /* is it a reply ID? show the post. */ + redirectIfNotNull(getReplyPostId(phrase), "viewPost.html?post="); + + /* is it an album ID? */ + redirectIfNotNull(getAlbumId(phrase), "imageBrowser.html?album="); + + /* is it an image ID? */ + redirectIfNotNull(getImageId(phrase), "imageBrowser.html?image="); + } + Set sones = webInterface.getCore().getSones(); Set> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR); @@ -168,7 +188,7 @@ public class SearchPage extends SoneTemplatePage { * The string generator for the objects * @return The hits for the given phrases */ - private Set> getHits(Collection objects, List phrases, StringGenerator stringGenerator) { + private static Set> getHits(Collection objects, List phrases, StringGenerator stringGenerator) { Set> hits = new HashSet>(); for (T object : objects) { String objectString = stringGenerator.generateString(object); @@ -189,7 +209,7 @@ public class SearchPage extends SoneTemplatePage { * The query to parse * @return The parsed phrases */ - private List parseSearchPhrases(String query) { + private static List parseSearchPhrases(String query) { List parsedPhrases = null; try { parsedPhrases = StringEscaper.parseLine(query); @@ -229,7 +249,7 @@ public class SearchPage extends SoneTemplatePage { * The expression to search * @return The score of the expression */ - private double calculateScore(List phrases, String expression) { + private static double calculateScore(List phrases, String expression) { logger.log(Level.FINEST, String.format("Calculating Score for “%s”…", expression)); double optionalHits = 0; double requiredHits = 0; @@ -271,6 +291,91 @@ public class SearchPage extends SoneTemplatePage { } /** + * Throws a + * {@link net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException} + * if the given object is not {@code null}, appending the object to the + * given target URL. + * + * @param object + * The object on which to redirect + * @param target + * The target of the redirect + * @throws RedirectException + * if {@code object} is not {@code null} + */ + private static void redirectIfNotNull(String object, String target) throws RedirectException { + if (object != null) { + throw new RedirectException(target + object); + } + } + + /** + * If the given phrase contains a Sone ID (optionally prefixed by + * “sone://”), returns said Sone ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is a Sone ID + * @return The Sone ID, or {@code null} + */ + private String getSoneId(String phrase) { + String soneId = phrase.startsWith("sone://") ? phrase.substring(7) : phrase; + return (webInterface.getCore().getSone(soneId, false) != null) ? soneId : null; + } + + /** + * If the given phrase contains a post ID (optionally prefixed by + * “post://”), returns said post ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is a post ID + * @return The post ID, or {@code null} + */ + private String getPostId(String phrase) { + String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase; + return (webInterface.getCore().getPost(postId, false) != null) ? postId : null; + } + + /** + * If the given phrase contains a reply ID (optionally prefixed by + * “reply://”), returns the ID of the post the reply belongs to, otherwise + * return {@code null}. + * + * @param phrase + * The phrase that maybe is a reply ID + * @return The reply’s post ID, or {@code null} + */ + private String getReplyPostId(String phrase) { + String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getReply(replyId, false) != null) ? webInterface.getCore().getReply(replyId, false).getPost().getId() : null; + } + + /** + * If the given phrase contains an album ID (optionally prefixed by + * “album://”), returns said album ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is an album ID + * @return The album ID, or {@code null} + */ + private String getAlbumId(String phrase) { + String albumId = phrase.startsWith("album://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getAlbum(albumId, false) != null) ? albumId : null; + } + + /** + * If the given phrase contains an image ID (optionally prefixed by + * “image://”), returns said image ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is an image ID + * @return The image ID, or {@code null} + */ + private String getImageId(String phrase) { + String imageId = phrase.startsWith("image://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getImage(imageId, false) != null) ? imageId : null; + } + + /** * Converts a given object into a {@link String}. * * @param diff --git a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java index 9045cdf..559a8ff 100644 --- a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -145,7 +145,7 @@ public class UploadImagePage extends SoneTemplatePage { * @return The MIME type of the image, or “application/octet-stream” if the * image type could not be detected */ - private String getMimeType(byte[] imageData) { + private static String getMimeType(byte[] imageData) { ByteArrayInputStream imageDataInputStream = new ByteArrayInputStream(imageData); try { ImageInputStream imageInputStream = ImageIO.createImageInputStream(imageDataInputStream); diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index c2bb1c8..00f8cf9 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -103,18 +103,13 @@ import net.pterodactylus.sone.web.ajax.UntrustAjaxPage; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.sone.web.page.PageToadlet; import net.pterodactylus.sone.web.page.PageToadletFactory; -import net.pterodactylus.util.cache.Cache; -import net.pterodactylus.util.cache.CacheException; -import net.pterodactylus.util.cache.CacheItem; -import net.pterodactylus.util.cache.DefaultCacheItem; -import net.pterodactylus.util.cache.MemoryCache; -import net.pterodactylus.util.cache.ValueRetriever; import net.pterodactylus.util.collection.SetBuilder; import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.notify.Notification; import net.pterodactylus.util.notify.NotificationManager; import net.pterodactylus.util.notify.TemplateNotification; +import net.pterodactylus.util.template.ClassPathTemplateProvider; import net.pterodactylus.util.template.CollectionSortFilter; import net.pterodactylus.util.template.ContainsFilter; import net.pterodactylus.util.template.DateFilter; @@ -123,15 +118,13 @@ import net.pterodactylus.util.template.HtmlFilter; import net.pterodactylus.util.template.MatchFilter; import net.pterodactylus.util.template.ModFilter; import net.pterodactylus.util.template.PaginationFilter; -import net.pterodactylus.util.template.Provider; import net.pterodactylus.util.template.ReflectionAccessor; import net.pterodactylus.util.template.ReplaceFilter; import net.pterodactylus.util.template.StoreFilter; import net.pterodactylus.util.template.Template; -import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.template.TemplateContextFactory; -import net.pterodactylus.util.template.TemplateException; import net.pterodactylus.util.template.TemplateParser; +import net.pterodactylus.util.template.TemplateProvider; import net.pterodactylus.util.template.XmlFilter; import net.pterodactylus.util.thread.Ticker; import net.pterodactylus.util.version.Version; @@ -222,7 +215,6 @@ public class WebInterface implements CoreListener { * @param sonePlugin * The Sone plugin */ - @SuppressWarnings("synthetic-access") public WebInterface(SonePlugin sonePlugin) { this.sonePlugin = sonePlugin; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); @@ -261,8 +253,8 @@ public class WebInterface implements CoreListener { templateContextFactory.addFilter("unique", new UniqueElementFilter()); templateContextFactory.addFilter("mod", new ModFilter()); templateContextFactory.addFilter("paginate", new PaginationFilter()); - templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER); - templateContextFactory.addProvider(new ClassPathTemplateProvider()); + templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER); + templateContextFactory.addProvider(new ClassPathTemplateProvider(WebInterface.class, "/templates/")); templateContextFactory.addTemplateObject("webInterface", this); templateContextFactory.addTemplateObject("formPassword", formPassword); @@ -1013,66 +1005,4 @@ public class WebInterface implements CoreListener { notificationManager.addNotification(imageInsertFailedNotification); } - /** - * Template provider implementation that uses - * {@link WebInterface#createReader(String)} to load templates for - * inclusion. - * - * @author David ‘Bombe’ Roden - */ - private class ClassPathTemplateProvider implements Provider { - - /** Cache for templates. */ - private final Cache templateCache = new MemoryCache(new ValueRetriever() { - - @Override - @SuppressWarnings("synthetic-access") - public CacheItem