59abc8e20c86435cc6a930b6ffe3e1dd76d6adc8
[Sone.git] / src / main / java / net / pterodactylus / sone / core / WebOfTrustUpdater.java
1 /*
2  * Sone - WebOfTrustUpdater.java - Copyright © 2012 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.core;
19
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.LinkedBlockingQueue;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24
25 import net.pterodactylus.sone.freenet.plugin.PluginException;
26 import net.pterodactylus.sone.freenet.wot.DefaultIdentity;
27 import net.pterodactylus.sone.freenet.wot.Identity;
28 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
29 import net.pterodactylus.sone.freenet.wot.Trust;
30 import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
31 import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
32 import net.pterodactylus.util.logging.Logging;
33 import net.pterodactylus.util.service.AbstractService;
34 import net.pterodactylus.util.validation.Validation;
35
36 /**
37  * Updates WebOfTrust identity data in a background thread because communicating
38  * with the WebOfTrust plugin can potentially last quite long.
39  *
40  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
41  */
42 public class WebOfTrustUpdater extends AbstractService {
43
44         /** The logger. */
45         private static final Logger logger = Logging.getLogger(WebOfTrustUpdater.class);
46
47         /** Stop job. */
48         @SuppressWarnings("synthetic-access")
49         private final WebOfTrustUpdateJob stopJob = new WebOfTrustUpdateJob();
50
51         /** The web of trust connector. */
52         private final WebOfTrustConnector webOfTrustConnector;
53
54         /** The queue for jobs. */
55         private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<WebOfTrustUpdateJob>();
56
57         /**
58          * Creates a new trust updater.
59          *
60          * @param webOfTrustConnector
61          *            The web of trust connector
62          */
63         public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) {
64                 super("Trust Updater");
65                 this.webOfTrustConnector = webOfTrustConnector;
66         }
67
68         //
69         // ACTIONS
70         //
71
72         /**
73          * Retrieves the trust relation between the truster and the trustee. This
74          * method will return immediately and perform a trust update in the
75          * background.
76          *
77          * @param truster
78          *            The identity giving the trust
79          * @param trustee
80          *            The identity receiving the trust
81          */
82         public void getTrust(OwnIdentity truster, Identity trustee) {
83                 GetTrustJob getTrustJob = new GetTrustJob(truster, trustee);
84                 if (!updateJobs.contains(getTrustJob)) {
85                         logger.log(Level.FINER, "Adding Trust Update Job: " + getTrustJob);
86                         try {
87                                 updateJobs.put(getTrustJob);
88                         } catch (InterruptedException ie1) {
89                                 /* the queue is unbounded so it should never block. */
90                         }
91                 }
92         }
93
94         /**
95          * Updates the trust relation between the truster and the trustee. This
96          * method will return immediately and perform a trust update in the
97          * background.
98          *
99          * @param truster
100          *            The identity giving the trust
101          * @param trustee
102          *            The identity receiving the trust
103          * @param score
104          *            The new level of trust (from -100 to 100, may be {@code null}
105          *            to remove the trust completely)
106          * @param comment
107          *            The comment of the trust relation
108          */
109         public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) {
110                 SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment);
111                 if (updateJobs.contains(setTrustJob)) {
112                         updateJobs.remove(setTrustJob);
113                 }
114                 logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob);
115                 try {
116                         updateJobs.put(setTrustJob);
117                 } catch (InterruptedException e) {
118                         /* the queue is unbounded so it should never block. */
119                 }
120         }
121
122         /**
123          * Adds the given context to the given own identity.
124          *
125          * @param ownIdentity
126          *            The own identity to add the context to
127          * @param context
128          *            The context to add
129          */
130         public void addContext(OwnIdentity ownIdentity, String context) {
131                 addContextWait(ownIdentity, context, false);
132         }
133
134         /**
135          * Adds the given context to the given own identity, waiting for completion
136          * of the operation.
137          *
138          * @param ownIdentity
139          *            The own identity to add the context to
140          * @param context
141          *            The context to add
142          * @return {@code true} if the context was added successfully, {@code false}
143          *         otherwise
144          */
145         public boolean addContextWait(OwnIdentity ownIdentity, String context) {
146                 return addContextWait(ownIdentity, context, true);
147         }
148
149         /**
150          * Adds the given context to the given own identity, waiting for completion
151          * of the operation.
152          *
153          * @param ownIdentity
154          *            The own identity to add the context to
155          * @param context
156          *            The context to add
157          * @param wait
158          *            {@code true} to wait for the end of the operation,
159          *            {@code false} to return immediately
160          * @return {@code true} if the context was added successfully, {@code false}
161          *         if the context was not added successfully, or if the job should
162          *         not wait for completion
163          */
164         private boolean addContextWait(OwnIdentity ownIdentity, String context, boolean wait) {
165                 AddContextJob addContextJob = new AddContextJob(ownIdentity, context);
166                 if (!updateJobs.contains(addContextJob)) {
167                         logger.log(Level.FINER, "Adding Context Job: " + addContextJob);
168                         try {
169                                 updateJobs.put(addContextJob);
170                         } catch (InterruptedException ie1) {
171                                 /* the queue is unbounded so it should never block. */
172                         }
173                         if (wait) {
174                                 return addContextJob.waitForCompletion();
175                         }
176                 } else if (wait) {
177                         for (WebOfTrustUpdateJob updateJob : updateJobs) {
178                                 if (updateJob.equals(addContextJob)) {
179                                         return updateJob.waitForCompletion();
180                                 }
181                         }
182                 }
183                 return false;
184         }
185
186         //
187         // SERVICE METHODS
188         //
189
190         /**
191          * {@inheritDoc}
192          */
193         @Override
194         protected void serviceRun() {
195                 while (!shouldStop()) {
196                         try {
197                                 WebOfTrustUpdateJob updateJob = updateJobs.take();
198                                 if (shouldStop() || (updateJob == stopJob)) {
199                                         break;
200                                 }
201                                 logger.log(Level.FINE, "Running Trust Update Job: " + updateJob);
202                                 long startTime = System.currentTimeMillis();
203                                 updateJob.run();
204                                 long endTime = System.currentTimeMillis();
205                                 logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms.");
206                         } catch (InterruptedException ie1) {
207                                 /* happens, ignore, loop. */
208                         }
209                 }
210         }
211
212         /**
213          * {@inheritDoc}
214          */
215         @Override
216         protected void serviceStop() {
217                 try {
218                         updateJobs.put(stopJob);
219                 } catch (InterruptedException ie1) {
220                         /* the queue is unbounded so it should never block. */
221                 }
222         }
223
224         /**
225          * Base class for WebOfTrust update jobs.
226          *
227          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
228          */
229         private class WebOfTrustUpdateJob {
230
231                 /** Object for synchronization. */
232                 @SuppressWarnings("hiding")
233                 private final Object syncObject = new Object();
234
235                 /** Whether the job has finished. */
236                 private boolean finished;
237
238                 /** Whether the job was successful. */
239                 private boolean success;
240
241                 //
242                 // ACTIONS
243                 //
244
245                 /**
246                  * Performs the actual update operation.
247                  * <p>
248                  * The implementation of this class does nothing.
249                  */
250                 public void run() {
251                         /* does nothing. */
252                 }
253
254                 /**
255                  * Waits for completion of this job or stopping of the WebOfTrust
256                  * updater.
257                  *
258                  * @return {@code true} if this job finished successfully, {@code false}
259                  *         otherwise
260                  *
261                  * @see WebOfTrustUpdater#stop()
262                  */
263                 @SuppressWarnings("synthetic-access")
264                 public boolean waitForCompletion() {
265                         synchronized (syncObject) {
266                                 while (!finished && !shouldStop()) {
267                                         try {
268                                                 syncObject.wait();
269                                         } catch (InterruptedException ie1) {
270                                                 /* we’re looping, ignore. */
271                                         }
272                                 }
273                                 return success;
274                         }
275                 }
276
277                 //
278                 // PROTECTED METHODS
279                 //
280
281                 /**
282                  * Signals that this job has finished.
283                  *
284                  * @param success
285                  *            {@code true} if this job finished successfully,
286                  *            {@code false} otherwise
287                  */
288                 protected void finish(boolean success) {
289                         synchronized (syncObject) {
290                                 finished = true;
291                                 this.success = success;
292                                 syncObject.notifyAll();
293                         }
294                 }
295
296         }
297
298         /**
299          * Base class for WebOfTrust trust update jobs.
300          *
301          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
302          */
303         private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob {
304
305                 /** The identity giving the trust. */
306                 protected final OwnIdentity truster;
307
308                 /** The identity receiving the trust. */
309                 protected final Identity trustee;
310
311                 /**
312                  * Creates a new trust update job.
313                  *
314                  * @param truster
315                  *            The identity giving the trust
316                  * @param trustee
317                  *            The identity receiving the trust
318                  */
319                 @SuppressWarnings("synthetic-access")
320                 public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) {
321                         this.truster = truster;
322                         this.trustee = trustee;
323                 }
324
325                 //
326                 // OBJECT METHODS
327                 //
328
329                 /**
330                  * {@inheritDoc}
331                  */
332                 @Override
333                 public boolean equals(Object object) {
334                         if ((object == null) || !object.getClass().equals(getClass())) {
335                                 return false;
336                         }
337                         WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object;
338                         return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
339                 }
340
341                 /**
342                  * {@inheritDoc}
343                  */
344                 @Override
345                 public int hashCode() {
346                         return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
347                 }
348
349                 /**
350                  * {@inheritDoc}
351                  */
352                 @Override
353                 public String toString() {
354                         return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
355                 }
356
357         }
358
359         /**
360          * Update job that sets the trust relation between two identities.
361          *
362          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
363          */
364         private class SetTrustJob extends WebOfTrustTrustUpdateJob {
365
366                 /** The score of the relation. */
367                 private final Integer score;
368
369                 /** The comment of the relation. */
370                 private final String comment;
371
372                 /**
373                  * Creates a new set trust job.
374                  *
375                  * @param truster
376                  *            The identity giving the trust
377                  * @param trustee
378                  *            The identity receiving the trust
379                  * @param score
380                  *            The score of the trust (from -100 to 100, may be
381                  *            {@code null} to remote the trust relation completely)
382                  * @param comment
383                  *            The comment of the trust relation
384                  */
385                 public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
386                         super(truster, trustee);
387                         this.score = score;
388                         this.comment = comment;
389                 }
390
391                 /**
392                  * {@inheritDoc}
393                  */
394                 @Override
395                 @SuppressWarnings("synthetic-access")
396                 public void run() {
397                         try {
398                                 if (score != null) {
399                                         if (trustee instanceof DefaultIdentity) {
400                                                 ((DefaultIdentity) trustee).setTrust(truster, new Trust(score, null, 0));
401                                         }
402                                         webOfTrustConnector.setTrust(truster, trustee, score, comment);
403                                 } else {
404                                         if (trustee instanceof DefaultIdentity) {
405                                                 ((DefaultIdentity) trustee).setTrust(truster, null);
406                                         }
407                                         webOfTrustConnector.removeTrust(truster, trustee);
408                                 }
409                                 finish(true);
410                         } catch (WebOfTrustException wote1) {
411                                 logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1);
412                                 finish(false);
413                         }
414                 }
415
416         }
417
418         /**
419          * Update job that retrieves the trust relation between two identities.
420          *
421          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
422          */
423         private class GetTrustJob extends WebOfTrustTrustUpdateJob {
424
425                 /**
426                  * Creates a new trust update job.
427                  *
428                  * @param truster
429                  *            The identity giving the trust
430                  * @param trustee
431                  *            The identity receiving the trust
432                  */
433                 public GetTrustJob(OwnIdentity truster, Identity trustee) {
434                         super(truster, trustee);
435                 }
436
437                 /**
438                  * {@inheritDoc}
439                  */
440                 @Override
441                 @SuppressWarnings("synthetic-access")
442                 public void run() {
443                         try {
444                                 Trust trust = webOfTrustConnector.getTrust(truster, trustee);
445                                 if (trustee instanceof DefaultIdentity) {
446                                         ((DefaultIdentity) trustee).setTrust(truster, trust);
447                                 }
448                                 finish(true);
449                         } catch (PluginException pe1) {
450                                 logger.log(Level.WARNING, "Could not get Trust value for " + truster + " -> " + trustee + "!", pe1);
451                                 finish(false);
452                         }
453                 }
454
455         }
456
457         /**
458          * Base class for context updates of an {@link OwnIdentity}.
459          *
460          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
461          */
462         private class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob {
463
464                 /** The own identity whose contexts to manage. */
465                 protected final OwnIdentity ownIdentity;
466
467                 /** The context to update. */
468                 protected final String context;
469
470                 /**
471                  * Creates a new context update job.
472                  *
473                  * @param ownIdentity
474                  *            The own identity to update
475                  * @param context
476                  *            The context to update
477                  */
478                 @SuppressWarnings("synthetic-access")
479                 public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) {
480                         Validation.begin().isNotNull("OwnIdentity", ownIdentity).isNotNull("Context", context).check();
481                         this.ownIdentity = ownIdentity;
482                         this.context = context;
483                 }
484
485                 //
486                 // OBJECT METHODS
487                 //
488
489                 /**
490                  * {@inheritDoc}
491                  */
492                 @Override
493                 public boolean equals(Object object) {
494                         if ((object == null) || !object.getClass().equals(getClass())) {
495                                 return false;
496                         }
497                         WebOfTrustContextUpdateJob updateJob = (WebOfTrustContextUpdateJob) object;
498                         return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context);
499                 }
500
501                 /**
502                  * {@inheritDoc}
503                  */
504                 @Override
505                 public int hashCode() {
506                         return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode();
507                 }
508
509                 /**
510                  * {@inheritDoc}
511                  */
512                 @Override
513                 public String toString() {
514                         return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context);
515                 }
516
517         }
518
519         /**
520          * Job that adds a context to an {@link OwnIdentity}.
521          *
522          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
523          */
524         private class AddContextJob extends WebOfTrustContextUpdateJob {
525
526                 /**
527                  * Creates a new add-context job.
528                  *
529                  * @param ownIdentity
530                  *            The own identity whose contexts to manage
531                  * @param context
532                  *            The context to add
533                  */
534                 public AddContextJob(OwnIdentity ownIdentity, String context) {
535                         super(ownIdentity, context);
536                 }
537
538                 /**
539                  * {@inheritDoc}
540                  */
541                 @Override
542                 @SuppressWarnings("synthetic-access")
543                 public void run() {
544                         try {
545                                 webOfTrustConnector.addContext(ownIdentity, context);
546                                 ownIdentity.addContext(context);
547                                 finish(true);
548                         } catch (PluginException pe1) {
549                                 logger.log(Level.WARNING, String.format("Could not add Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
550                                 finish(false);
551                         }
552                 }
553
554         }
555
556 }