/* * Sone - IdentityManager.java - Copyright © 2010 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.wotns.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.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.wotns.freenet.plugin.PluginException; /** * The identity manager takes care of loading and storing identities, their * contexts, and properties. It does so in a way that does not expose errors via * exceptions but it only logs them and tries to return sensible defaults. *

* It is also responsible for polling identities from the Web of Trust plugin * and notifying registered {@link IdentityListener}s when {@link Identity}s and * {@link OwnIdentity}s are discovered or disappearing. * * @author David ‘Bombe’ Roden */ public class IdentityManager extends AbstractService { /** Object used for synchronization. */ private final Object syncObject = new Object() { /* inner class for better lock names. */ }; /** The logger. */ private static final Logger logger = Logging.getLogger(IdentityManager.class); /** The event manager. */ private final IdentityListenerManager identityListenerManager = new IdentityListenerManager(); /** The Web of Trust connector. */ private final WebOfTrustConnector webOfTrustConnector; /** The context to filter for. */ private volatile String context; /** The currently known own identities. */ /* synchronize access on syncObject. */ private Map currentOwnIdentities = new HashMap(); /** The currently trusted identities. */ private Map> currentTrustedIdentities = new HashMap>(); /** * Creates a new identity manager. * * @param webOfTrustConnector * The Web of Trust connector */ public IdentityManager(WebOfTrustConnector webOfTrustConnector) { super("Sone Identity Manager", false); this.webOfTrustConnector = webOfTrustConnector; } // // LISTENER MANAGEMENT // /** * Adds a listener for identity events. * * @param identityListener * The listener to add */ public void addIdentityListener(IdentityListener identityListener) { identityListenerManager.addListener(identityListener); } /** * Removes a listener for identity events. * * @param identityListener * The listener to remove */ public void removeIdentityListener(IdentityListener identityListener) { identityListenerManager.removeListener(identityListener); } // // ACCESSORS // /** * Sets the context to filter own identities and trusted identities for. * * @param context * The context to filter for, or {@code null} to not filter */ public void setContext(String context) { this.context = context; } /** * Returns whether the Web of Trust plugin could be reached during the last * try. * * @return {@code true} if the Web of Trust plugin is connected, {@code * false} otherwise */ public boolean isConnected() { try { webOfTrustConnector.ping(); return true; } catch (PluginException pe1) { /* not connected, ignore. */ return false; } } /** * Returns the own identity with the given ID. * * @param id * The ID of the own identity * @return The own identity, or {@code null} if there is no such identity */ public OwnIdentity getOwnIdentity(String id) { Set allOwnIdentities = getAllOwnIdentities(); for (OwnIdentity ownIdentity : allOwnIdentities) { if (ownIdentity.getId().equals(id)) { return ownIdentity; } } return null; } /** * Returns all own identities. * * @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 ownIdentities; } catch (WebOfTrustException wote1) { logger.log(Level.WARNING, "Could not load all own identities!", wote1); return Collections.emptySet(); } } /** * Returns all identities that are trusted by the given own identity. In * addition to all non-own identities the given own identity is also * returned. * * @param ownIdentity * The own identity to get the trusted identities for * @return The identities trusted by the given own identity */ public Set getTrustedIdentities(OwnIdentity ownIdentity) { Set identities = new HashSet(); if ((context == null) || ownIdentity.getContexts().contains(context)) { identities.add(ownIdentity); } try { Set trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context); Map newTrustedIdentities = new HashMap(); for (Identity trustedIdentity : trustedIdentities) { newTrustedIdentities.put(trustedIdentity.getId(), trustedIdentity); } checkTrustedIdentities(ownIdentity, newTrustedIdentities); } catch (WebOfTrustException wote1) { logger.log(Level.WARNING, String.format("Could not load all trusted identities for %s.", ownIdentity), wote1); } return identities; } // // SERVICE METHODS // /** * {@inheritDoc} */ @Override protected void serviceRun() { while (!shouldStop()) { Map> currentIdentities = new HashMap>(); @SuppressWarnings("hiding") Map currentOwnIdentities = new HashMap(); Set ownIdentities = null; boolean identitiesLoaded = false; try { /* get all identities with the wanted context from WoT. */ ownIdentities = webOfTrustConnector.loadAllOwnIdentities(); /* load trusted identities. */ for (OwnIdentity ownIdentity : ownIdentities) { if ((context != null) && !ownIdentity.hasContext(context)) { continue; } currentOwnIdentities.put(ownIdentity.getId(), ownIdentity); Set trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context); Map identities = new HashMap(); currentIdentities.put(ownIdentity, identities); for (Identity identity : trustedIdentities) { identities.put(identity.getId(), identity); } /* * add own identities, too, as long as the WoT doesn’t do * that. */ for (OwnIdentity additionalOwnIdentity : ownIdentities) { if (additionalOwnIdentity == ownIdentity) { continue; } if ((context != null) && !additionalOwnIdentity.hasContext(context)) { continue; } identities.put(additionalOwnIdentity.getId(), additionalOwnIdentity); } } identitiesLoaded = true; } catch (WebOfTrustException wote1) { logger.log(Level.WARNING, "WoT has disappeared!", wote1); } if (identitiesLoaded) { /* check for changes. */ checkOwnIdentities(currentOwnIdentities); /* now check for changes in remote identities. */ for (OwnIdentity ownIdentity : currentOwnIdentities.values()) { checkTrustedIdentities(ownIdentity, currentIdentities.get(ownIdentity)); } } /* wait a minute before checking again. */ sleep(60 * 1000); } } // // PRIVATE METHODS // /** * Checks the given new list of own identities for added or removed own * identities, as compared to {@link #currentOwnIdentities}. * * @param newOwnIdentities * The new own identities */ private void checkOwnIdentities(Map newOwnIdentities) { synchronized (syncObject) { /* find removed own identities: */ for (OwnIdentity oldOwnIdentity : currentOwnIdentities.values()) { if (!newOwnIdentities.containsKey(oldOwnIdentity.getId())) { identityListenerManager.fireOwnIdentityRemoved(oldOwnIdentity); currentTrustedIdentities.remove(oldOwnIdentity); } } /* find added own identities. */ for (OwnIdentity currentOwnIdentity : newOwnIdentities.values()) { if (!currentOwnIdentities.containsKey(currentOwnIdentity.getId())) { identityListenerManager.fireOwnIdentityAdded(currentOwnIdentity); } } currentOwnIdentities.clear(); currentOwnIdentities.putAll(newOwnIdentities); } } /** * Checks the given identities for changes since the last check. * * @param ownIdentity * The own identity trusting the given identities * @param trustedIdentities * The trusted identities */ private void checkTrustedIdentities(OwnIdentity ownIdentity, Map trustedIdentities) { @SuppressWarnings("hiding") Map currentTrustedIdentities = new HashMap(); synchronized (syncObject) { if (this.currentTrustedIdentities.containsKey(ownIdentity)) { for (Identity identity : this.currentTrustedIdentities.get(ownIdentity)) { currentTrustedIdentities.put(identity.getId(), identity); } } } /* find new identities. */ for (Identity currentIdentity : trustedIdentities.values()) { if (!currentTrustedIdentities.containsKey(currentIdentity.getId())) { identityListenerManager.fireIdentityAdded(ownIdentity, currentIdentity); } } /* find removed identities. */ for (Identity oldIdentity : currentTrustedIdentities.values()) { if (!trustedIdentities.containsKey(oldIdentity.getId())) { identityListenerManager.fireIdentityRemoved(ownIdentity, oldIdentity); } } /* check for changes in the contexts. */ for (Identity oldIdentity : currentTrustedIdentities.values()) { if (!trustedIdentities.containsKey(oldIdentity.getId())) { continue; } Identity newIdentity = trustedIdentities.get(oldIdentity.getId()); Set oldContexts = oldIdentity.getContexts(); Set newContexts = newIdentity.getContexts(); if (oldContexts.size() != newContexts.size()) { identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); continue; } boolean changed = false; for (String oldContext : oldContexts) { if (!newContexts.contains(oldContext)) { identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); changed = true; break; } } if (changed) { continue; } Map oldProperties = oldIdentity.getProperties(); Map newProperties = newIdentity.getProperties(); if (oldProperties.size() != newProperties.size()) { identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); continue; } for (Entry oldProperty : oldProperties.entrySet()) { if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) { identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); break; } } } synchronized (syncObject) { this.currentTrustedIdentities.put(ownIdentity, trustedIdentities.values()); } } }