Add identity manager.
[Sone.git] / src / main / java / net / pterodactylus / sone / freenet / wot / IdentityManager.java
1 /*
2  * Sone - IdentityManager.java - Copyright © 2010 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.freenet.wot;
19
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 import net.pterodactylus.util.filter.Filter;
29 import net.pterodactylus.util.filter.Filters;
30 import net.pterodactylus.util.logging.Logging;
31 import net.pterodactylus.util.service.AbstractService;
32
33 /**
34  * The identity manager takes care of loading and storing identities, their
35  * contexts, and properties. It does so in a way that does not expose errors via
36  * exceptions but it only logs them and tries to return sensible defaults.
37  * <p>
38  * It is also responsible for polling identities from the Web of Trust plugin
39  * and notifying registered {@link IdentityListener}s when {@link Identity}s and
40  * {@link OwnIdentity}s are discovered or disappearing.
41  *
42  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
43  */
44 public class IdentityManager extends AbstractService {
45
46         /** The logger. */
47         private static final Logger logger = Logging.getLogger(IdentityManager.class);
48
49         /** The event manager. */
50         private final IdentityListenerManager identityListenerManager = new IdentityListenerManager();
51
52         /** The Web of Trust connector. */
53         private final WebOfTrustConnector webOfTrustConnector;
54
55         /** The context to filter for. */
56         private volatile String context;
57
58         /** Whether the Web of Trust plugin is connected. */
59         private volatile boolean wotPluginConnected = false;
60
61         /**
62          * Creates a new identity manager.
63          *
64          * @param webOfTrustConnector
65          *            The Web of Trust connector
66          */
67         public IdentityManager(WebOfTrustConnector webOfTrustConnector) {
68                 super("Sone Identity Manager", false);
69                 this.webOfTrustConnector = webOfTrustConnector;
70         }
71
72         //
73         // LISTENER MANAGEMENT
74         //
75
76         /**
77          * Adds a listener for identity events.
78          *
79          * @param identityListener
80          *            The listener to add
81          */
82         public void addIdentityListener(IdentityListener identityListener) {
83                 identityListenerManager.addListener(identityListener);
84         }
85
86         /**
87          * Removes a listener for identity events.
88          *
89          * @param identityListener
90          *            The listener to remove
91          */
92         public void removeIdentityListener(IdentityListener identityListener) {
93                 identityListenerManager.removeListener(identityListener);
94         }
95
96         //
97         // ACCESSORS
98         //
99
100         /**
101          * Sets the context to filter own identities and trusted identities for.
102          *
103          * @param context
104          *            The context to filter for, or {@code null} to not filter
105          */
106         public void setContext(String context) {
107                 this.context = context;
108         }
109
110         /**
111          * Returns whether the Web of Trust plugin could be reached during the last
112          * try.
113          *
114          * @return {@code true} if the Web of Trust plugin is connected,
115          *         {@code false} otherwise
116          */
117         public boolean isConnected() {
118                 return wotPluginConnected;
119         }
120
121         /**
122          * Returns the own identity with the given ID.
123          *
124          * @param id
125          *            The ID of the own identity
126          * @return The own identity, or {@code null} if there is no such identity
127          */
128         public OwnIdentity getOwnIdentity(String id) {
129                 Set<OwnIdentity> allOwnIdentities = getAllOwnIdentities();
130                 for (OwnIdentity ownIdentity : allOwnIdentities) {
131                         if (ownIdentity.getId().equals(id)) {
132                                 return ownIdentity;
133                         }
134                 }
135                 return null;
136         }
137
138         /**
139          * Returns all own identities.
140          *
141          * @return All own identities
142          */
143         public Set<OwnIdentity> getAllOwnIdentities() {
144                 try {
145                         Set<OwnIdentity> allOwnIdentities = webOfTrustConnector.loadAllOwnIdentities();
146                         wotPluginConnected = true;
147                         return allOwnIdentities;
148                 } catch (PluginException pe1) {
149                         logger.log(Level.WARNING, "Could not load all own identities!", pe1);
150                         wotPluginConnected = false;
151                         return Collections.emptySet();
152                 }
153         }
154
155         //
156         // ACTIONS
157         //
158
159         /**
160          * Adds a context to the given own identity.
161          *
162          * @param ownIdentity
163          *            The own identity
164          * @param context
165          *            The context to add
166          */
167         public void addContext(OwnIdentity ownIdentity, String context) {
168                 if (ownIdentity.hasContext(context)) {
169                         return;
170                 }
171                 try {
172                         webOfTrustConnector.addContext(ownIdentity, context);
173                         wotPluginConnected = true;
174                         ownIdentity.addContext(context);
175                 } catch (PluginException pe1) {
176                         logger.log(Level.WARNING, "Could not add context " + context + " to OwnIdentity " + ownIdentity + ".", pe1);
177                         wotPluginConnected = false;
178                 }
179         }
180
181         /**
182          * Removes a context from the given own identity.
183          *
184          * @param ownIdentity
185          *            The own identity
186          * @param context
187          *            The context to remove
188          */
189         public void removeContext(OwnIdentity ownIdentity, String context) {
190                 if (!ownIdentity.hasContext(context)) {
191                         return;
192                 }
193                 try {
194                         webOfTrustConnector.removeContext(ownIdentity, context);
195                         wotPluginConnected = true;
196                         ownIdentity.removeContext(context);
197                 } catch (PluginException pe1) {
198                         logger.log(Level.WARNING, "Could not remove context " + context + " from OwnIdentity " + ownIdentity + ".", pe1);
199                         wotPluginConnected = false;
200                 }
201         }
202
203         /**
204          * Sets the property with the given name to the given value.
205          *
206          * @param ownIdentity
207          *            The own identity
208          * @param name
209          *            The name of the property
210          * @param value
211          *            The value of the property
212          */
213         public void setProperty(OwnIdentity ownIdentity, String name, String value) {
214                 try {
215                         webOfTrustConnector.setProperty(ownIdentity, name, value);
216                         wotPluginConnected = true;
217                         ownIdentity.setProperty(name, value);
218                 } catch (PluginException pe1) {
219                         logger.log(Level.WARNING, "Could not set property “" + name + "” to “" + value + "” for OwnIdentity: " + ownIdentity, pe1);
220                         wotPluginConnected = false;
221                 }
222         }
223
224         /**
225          * Removes the property with the given name.
226          *
227          * @param ownIdentity
228          *            The own identity
229          * @param name
230          *            The name of the property to remove
231          */
232         public void removeProperty(OwnIdentity ownIdentity, String name) {
233                 try {
234                         webOfTrustConnector.removeProperty(ownIdentity, name);
235                         wotPluginConnected = true;
236                         ownIdentity.removeProperty(name);
237                 } catch (PluginException pe1) {
238                         logger.log(Level.WARNING, "Could not remove property “" + name + "” from OwnIdentity: " + ownIdentity, pe1);
239                         wotPluginConnected = false;
240                 }
241         }
242
243         //
244         // SERVICE METHODS
245         //
246
247         /**
248          * {@inheritDoc}
249          */
250         @Override
251         protected void serviceRun() {
252                 Map<String, Identity> oldIdentities = Collections.emptyMap();
253                 Map<String, OwnIdentity> oldOwnIdentities = Collections.emptyMap();
254                 while (!shouldStop()) {
255                         Map<String, Identity> currentIdentities = new HashMap<String, Identity>();
256                         Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
257
258                         /* get all identities with the wanted context from WoT. */
259                         Set<OwnIdentity> ownIdentities;
260                         try {
261                                 ownIdentities = Filters.filteredSet(webOfTrustConnector.loadAllOwnIdentities(), new Filter<OwnIdentity>() {
262
263                                         @Override
264                                         @SuppressWarnings("synthetic-access")
265                                         public boolean filterObject(OwnIdentity ownIdentity) {
266                                                 return (context == null) || ownIdentity.hasContext(context);
267                                         }
268
269                                 });
270                                 for (OwnIdentity ownIdentity : ownIdentities) {
271                                         currentOwnIdentities.put(ownIdentity.getId(), ownIdentity);
272                                         for (Identity identity : webOfTrustConnector.loadTrustedIdentities(ownIdentity, context)) {
273                                                 currentIdentities.put(identity.getId(), identity);
274                                         }
275                                 }
276
277                                 /* find removed own identities: */
278                                 for (OwnIdentity oldOwnIdentity : oldOwnIdentities.values()) {
279                                         if (!currentOwnIdentities.containsKey(oldOwnIdentity.getId())) {
280                                                 identityListenerManager.fireOwnIdentityRemoved(oldOwnIdentity);
281                                         }
282                                 }
283
284                                 /* find added own identities. */
285                                 for (OwnIdentity currentOwnIdentity : currentOwnIdentities.values()) {
286                                         if (!oldOwnIdentities.containsKey(currentOwnIdentity.getId())) {
287                                                 identityListenerManager.fireOwnIdentityAdded(currentOwnIdentity);
288                                         }
289                                 }
290
291                                 /* find removed identities. */
292                                 for (Identity oldIdentity : oldIdentities.values()) {
293                                         if (!currentIdentities.containsKey(oldIdentity.getId())) {
294                                                 identityListenerManager.fireIdentityRemoved(oldIdentity);
295                                         }
296                                 }
297
298                                 /* find new identities. */
299                                 for (Identity currentIdentity : currentIdentities.values()) {
300                                         if (!oldIdentities.containsKey(currentIdentity.getId())) {
301                                                 identityListenerManager.fireIdentityAdded(currentIdentity);
302                                         }
303                                 }
304
305                                 /* check for changes in the properties. */
306                                 for (Identity oldIdentity : oldIdentities.values()) {
307                                         if (!currentIdentities.containsKey(oldIdentity.getId())) {
308                                                 continue;
309                                         }
310                                         Identity newIdentity = currentIdentities.get(oldIdentity.getId());
311                                         Map<String, String> oldProperties = oldIdentity.getProperties();
312                                         Map<String, String> newProperties = newIdentity.getProperties();
313                                         if (oldProperties.size() != newProperties.size()) {
314                                                 identityListenerManager.fireIdentityUpdated(newIdentity);
315                                                 continue;
316                                         }
317                                         for (Entry<String, String> oldProperty : oldProperties.entrySet()) {
318                                                 if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) {
319                                                         identityListenerManager.fireIdentityUpdated(newIdentity);
320                                                         break;
321                                                 }
322                                         }
323                                 }
324
325                                 /* remember the current set of identities. */
326                                 oldIdentities = currentIdentities;
327                                 oldOwnIdentities = currentOwnIdentities;
328
329                         } catch (PluginException pe1) {
330                                 logger.log(Level.WARNING, "WoT has disappeared!", pe1);
331                         }
332
333                         /* wait a minute before checking again. */
334                         sleep(60 * 1000);
335                 }
336         }
337
338 }