29334836da39d940765bbe8b70c158c0b1ffa9d7
[Sone.git] / src / main / java / net / pterodactylus / sone / freenet / wot / IdentityManager.java
1 /*
2  * Sone - IdentityManager.java - Copyright © 2010–2013 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 static com.google.common.collect.HashMultimap.create;
21
22 import java.util.Collection;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 import net.pterodactylus.sone.freenet.plugin.PluginException;
29 import net.pterodactylus.sone.freenet.wot.IdentityChangeDetector.IdentityProcessor;
30 import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent;
31 import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent;
32 import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent;
33 import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent;
34 import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent;
35 import net.pterodactylus.util.logging.Logging;
36 import net.pterodactylus.util.service.AbstractService;
37
38 import com.google.common.collect.Multimap;
39 import com.google.common.collect.Sets;
40 import com.google.common.eventbus.EventBus;
41 import com.google.inject.Inject;
42 import com.google.inject.name.Named;
43
44 /**
45  * The identity manager takes care of loading and storing identities, their
46  * contexts, and properties. It does so in a way that does not expose errors via
47  * exceptions but it only logs them and tries to return sensible defaults.
48  * <p/>
49  * It is also responsible for polling identities from the Web of Trust plugin
50  * and sending events to the {@link EventBus} when {@link Identity}s and {@link
51  * OwnIdentity}s are discovered or disappearing.
52  *
53  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
54  */
55 public class IdentityManager extends AbstractService {
56
57         /** Object used for synchronization. */
58         @SuppressWarnings("hiding")
59         private final Object syncObject = new Object() {
60                 /* inner class for better lock names. */
61         };
62
63         /** The logger. */
64         private static final Logger logger = Logging.getLogger(IdentityManager.class);
65
66         /** The event bus. */
67         private final EventBus eventBus;
68
69         /** The Web of Trust connector. */
70         private final WebOfTrustConnector webOfTrustConnector;
71
72         /** The context to filter for. */
73         private final String context;
74
75         /** The currently known own identities. */
76         /* synchronize access on syncObject. */
77         private final Set<OwnIdentity> currentOwnIdentities = Sets.newHashSet();
78
79         /**
80          * Creates a new identity manager.
81          *
82          * @param eventBus
83          *              The event bus
84          * @param webOfTrustConnector
85          *              The Web of Trust connector
86          * @param context
87          *              The context to focus on (may be {@code null} to ignore contexts)
88          */
89         @Inject
90         public IdentityManager(EventBus eventBus, WebOfTrustConnector webOfTrustConnector, @Named("WebOfTrustContext") String context) {
91                 super("Sone Identity Manager", false);
92                 this.eventBus = eventBus;
93                 this.webOfTrustConnector = webOfTrustConnector;
94                 this.context = context;
95         }
96
97         //
98         // ACCESSORS
99         //
100
101         /**
102          * Returns whether the Web of Trust plugin could be reached during the last
103          * try.
104          *
105          * @return {@code true} if the Web of Trust plugin is connected, {@code false}
106          *         otherwise
107          */
108         public boolean isConnected() {
109                 try {
110                         webOfTrustConnector.ping();
111                         return true;
112                 } catch (PluginException pe1) {
113                         /* not connected, ignore. */
114                         return false;
115                 }
116         }
117
118         /**
119          * Returns all own identities.
120          *
121          * @return All own identities
122          */
123         public Set<OwnIdentity> getAllOwnIdentities() {
124                 synchronized (syncObject) {
125                         return new HashSet<OwnIdentity>(currentOwnIdentities);
126                 }
127         }
128
129         //
130         // SERVICE METHODS
131         //
132
133         @Override
134         protected void serviceRun() {
135                 Multimap<OwnIdentity, Identity> oldIdentities = create();
136
137                 while (!shouldStop()) {
138                         try {
139                                 Collection<OwnIdentity> currentOwnIdentities = webOfTrustConnector.loadAllOwnIdentities();
140                                 Multimap<OwnIdentity, Identity> currentIdentities = loadTrustedIdentitiesForOwnIdentities(currentOwnIdentities);
141
142                                 detectChangesInIdentities(currentOwnIdentities, currentIdentities, oldIdentities);
143                                 oldIdentities = currentIdentities;
144
145                                 synchronized (syncObject) {
146                                         this.currentOwnIdentities.clear();
147                                         this.currentOwnIdentities.addAll(currentOwnIdentities);
148                                 }
149                         } catch (WebOfTrustException wote1) {
150                                 logger.log(Level.WARNING, "WoT has disappeared!", wote1);
151                         }
152
153                         /* wait a minute before checking again. */
154                         sleep(60 * 1000);
155                 }
156         }
157
158         private void detectChangesInIdentities(Collection<OwnIdentity> currentOwnIdentities, Multimap<OwnIdentity, Identity> newIdentities, Multimap<OwnIdentity, Identity> oldIdentities) {
159                 IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(getAllOwnIdentities());
160                 identityChangeDetector.onNewIdentity(addNewOwnIdentityAndItsTrustedIdentities(newIdentities));
161                 identityChangeDetector.onRemovedIdentity(removeOwnIdentityAndItsTrustedIdentities(oldIdentities));
162                 identityChangeDetector.onUnchangedIdentity(detectChangesInTrustedIdentities(newIdentities, oldIdentities));
163                 identityChangeDetector.detectChanges(currentOwnIdentities);
164         }
165
166         private IdentityProcessor detectChangesInTrustedIdentities(Multimap<OwnIdentity, Identity> newIdentities, Multimap<OwnIdentity, Identity> oldIdentities) {
167                 return new DefaultIdentityProcessor(oldIdentities, newIdentities);
168         }
169
170         private IdentityProcessor removeOwnIdentityAndItsTrustedIdentities(final Multimap<OwnIdentity, Identity> oldIdentities) {
171                 return new IdentityProcessor() {
172                         @Override
173                         public void processIdentity(Identity identity) {
174                                 eventBus.post(new OwnIdentityRemovedEvent((OwnIdentity) identity));
175                                 for (Identity removedIdentity : oldIdentities.get((OwnIdentity) identity)) {
176                                         eventBus.post(new IdentityRemovedEvent((OwnIdentity) identity, removedIdentity));
177                                 }
178                         }
179                 };
180         }
181
182         private IdentityProcessor addNewOwnIdentityAndItsTrustedIdentities(final Multimap<OwnIdentity, Identity> newIdentities) {
183                 return new IdentityProcessor() {
184                         @Override
185                         public void processIdentity(Identity identity) {
186                                 eventBus.post(new OwnIdentityAddedEvent((OwnIdentity) identity));
187                                 for (Identity newIdentity : newIdentities.get((OwnIdentity) identity)) {
188                                         eventBus.post(new IdentityAddedEvent((OwnIdentity) identity, newIdentity));
189                                 }
190                         }
191                 };
192         }
193
194         private Multimap<OwnIdentity, Identity> loadTrustedIdentitiesForOwnIdentities(Collection<OwnIdentity> ownIdentities) throws PluginException {
195                 Multimap<OwnIdentity, Identity> currentIdentities = create();
196
197                 for (OwnIdentity ownIdentity : ownIdentities) {
198                         if ((context != null) && !ownIdentity.hasContext(context)) {
199                                 continue;
200                         }
201
202                         logger.finer(String.format("Getting trusted identities for %s...", ownIdentity.getId()));
203                         Set<Identity> trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context);
204                         logger.finest(String.format("Got %d trusted identities.", trustedIdentities.size()));
205                         currentIdentities.putAll(ownIdentity, trustedIdentities);
206                 }
207
208                 return currentIdentities;
209         }
210
211         private class DefaultIdentityProcessor implements IdentityProcessor {
212
213                 private final Multimap<OwnIdentity, Identity> oldIdentities;
214                 private final Multimap<OwnIdentity, Identity> newIdentities;
215
216                 public DefaultIdentityProcessor(Multimap<OwnIdentity, Identity> oldIdentities, Multimap<OwnIdentity, Identity> newIdentities) {
217                         this.oldIdentities = oldIdentities;
218                         this.newIdentities = newIdentities;
219                 }
220
221                 @Override
222                 public void processIdentity(Identity ownIdentity) {
223                         IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(oldIdentities.get((OwnIdentity) ownIdentity));
224                         identityChangeDetector.onNewIdentity(notifyForAddedIdentities((OwnIdentity) ownIdentity));
225                         identityChangeDetector.onRemovedIdentity(notifyForRemovedIdentities((OwnIdentity) ownIdentity));
226                         identityChangeDetector.onChangedIdentity(notifyForChangedIdentities((OwnIdentity) ownIdentity));
227                         identityChangeDetector.detectChanges(newIdentities.get((OwnIdentity) ownIdentity));
228                 }
229
230                 private IdentityProcessor notifyForChangedIdentities(final OwnIdentity ownIdentity) {
231                         return new IdentityProcessor() {
232                                 @Override
233                                 public void processIdentity(Identity identity) {
234                                         eventBus.post(new IdentityUpdatedEvent(ownIdentity, identity));
235                                 }
236                         };
237                 }
238
239                 private IdentityProcessor notifyForRemovedIdentities(final OwnIdentity ownIdentity) {
240                         return new IdentityProcessor() {
241                                 @Override
242                                 public void processIdentity(Identity identity) {
243                                         eventBus.post(new IdentityRemovedEvent(ownIdentity, identity));
244                                 }
245                         };
246                 }
247
248                 private IdentityProcessor notifyForAddedIdentities(final OwnIdentity ownIdentity) {
249                         return new IdentityProcessor() {
250                                 @Override
251                                 public void processIdentity(Identity identity) {
252                                         eventBus.post(new IdentityAddedEvent(ownIdentity, identity));
253                                 }
254                         };
255                 }
256
257         }
258
259 }