Use dependency injection to create Sone instance.
[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 import com.google.inject.Inject;
37
38 /**
39  * Updates WebOfTrust identity data in a background thread because communicating
40  * with the WebOfTrust plugin can potentially last quite long.
41  *
42  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
43  */
44 public class WebOfTrustUpdater extends AbstractService {
45
46         /** The logger. */
47         private static final Logger logger = Logging.getLogger(WebOfTrustUpdater.class);
48
49         /** Stop job. */
50         @SuppressWarnings("synthetic-access")
51         private final WebOfTrustUpdateJob stopJob = new WebOfTrustUpdateJob();
52
53         /** The web of trust connector. */
54         private final WebOfTrustConnector webOfTrustConnector;
55
56         /** The queue for jobs. */
57         private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<WebOfTrustUpdateJob>();
58
59         /**
60          * Creates a new trust updater.
61          *
62          * @param webOfTrustConnector
63          *            The web of trust connector
64          */
65         @Inject
66         public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) {
67                 super("Trust Updater");
68                 this.webOfTrustConnector = webOfTrustConnector;
69         }
70
71         //
72         // ACTIONS
73         //
74
75         /**
76          * Updates the trust relation between the truster and the trustee. This
77          * method will return immediately and perform a trust update in the
78          * background.
79          *
80          * @param truster
81          *            The identity giving the trust
82          * @param trustee
83          *            The identity receiving the trust
84          * @param score
85          *            The new level of trust (from -100 to 100, may be {@code null}
86          *            to remove the trust completely)
87          * @param comment
88          *            The comment of the trust relation
89          */
90         public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) {
91                 SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment);
92                 if (updateJobs.contains(setTrustJob)) {
93                         updateJobs.remove(setTrustJob);
94                 }
95                 logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob);
96                 try {
97                         updateJobs.put(setTrustJob);
98                 } catch (InterruptedException e) {
99                         /* the queue is unbounded so it should never block. */
100                 }
101         }
102
103         /**
104          * Adds the given context to the given own identity.
105          *
106          * @param ownIdentity
107          *            The own identity to add the context to
108          * @param context
109          *            The context to add
110          */
111         public void addContext(OwnIdentity ownIdentity, String context) {
112                 addContextWait(ownIdentity, context, false);
113         }
114
115         /**
116          * Adds the given context to the given own identity, waiting for completion
117          * of the operation.
118          *
119          * @param ownIdentity
120          *            The own identity to add the context to
121          * @param context
122          *            The context to add
123          * @return {@code true} if the context was added successfully, {@code false}
124          *         otherwise
125          */
126         public boolean addContextWait(OwnIdentity ownIdentity, String context) {
127                 return addContextWait(ownIdentity, context, true);
128         }
129
130         /**
131          * Adds the given context to the given own identity, waiting for completion
132          * of the operation.
133          *
134          * @param ownIdentity
135          *            The own identity to add the context to
136          * @param context
137          *            The context to add
138          * @param wait
139          *            {@code true} to wait for the end of the operation,
140          *            {@code false} to return immediately
141          * @return {@code true} if the context was added successfully, {@code false}
142          *         if the context was not added successfully, or if the job should
143          *         not wait for completion
144          */
145         private boolean addContextWait(OwnIdentity ownIdentity, String context, boolean wait) {
146                 AddContextJob addContextJob = new AddContextJob(ownIdentity, context);
147                 if (!updateJobs.contains(addContextJob)) {
148                         logger.log(Level.FINER, "Adding Context Job: " + addContextJob);
149                         try {
150                                 updateJobs.put(addContextJob);
151                         } catch (InterruptedException ie1) {
152                                 /* the queue is unbounded so it should never block. */
153                         }
154                         if (wait) {
155                                 return addContextJob.waitForCompletion();
156                         }
157                 } else if (wait) {
158                         for (WebOfTrustUpdateJob updateJob : updateJobs) {
159                                 if (updateJob.equals(addContextJob)) {
160                                         return updateJob.waitForCompletion();
161                                 }
162                         }
163                 }
164                 return false;
165         }
166
167         /**
168          * Removes the given context from the given own identity.
169          *
170          * @param ownIdentity
171          *            The own identity to remove the context from
172          * @param context
173          *            The context to remove
174          */
175         public void removeContext(OwnIdentity ownIdentity, String context) {
176                 RemoveContextJob removeContextJob = new RemoveContextJob(ownIdentity, context);
177                 if (!updateJobs.contains(removeContextJob)) {
178                         logger.log(Level.FINER, "Adding Context Job: " + removeContextJob);
179                         try {
180                                 updateJobs.put(removeContextJob);
181                         } catch (InterruptedException ie1) {
182                                 /* the queue is unbounded so it should never block. */
183                         }
184                 }
185         }
186
187         /**
188          * Sets a property on the given own identity.
189          *
190          * @param ownIdentity
191          *            The own identity to set the property on
192          * @param propertyName
193          *            The name of the property to set
194          * @param propertyValue
195          *            The value of the property to set
196          */
197         public void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
198                 SetPropertyJob setPropertyJob = new SetPropertyJob(ownIdentity, propertyName, propertyValue);
199                 if (updateJobs.contains(setPropertyJob)) {
200                         updateJobs.remove(setPropertyJob);
201                 }
202                 logger.log(Level.FINER, "Adding Property Job: " + setPropertyJob);
203                 try {
204                         updateJobs.put(setPropertyJob);
205                 } catch (InterruptedException e) {
206                         /* the queue is unbounded so it should never block. */
207                 }
208         }
209
210         /**
211          * Removes a property from the given own identity.
212          *
213          * @param ownIdentity
214          *            The own identity to remove the property from
215          * @param propertyName
216          *            The name of the property to remove
217          */
218         public void removeProperty(OwnIdentity ownIdentity, String propertyName) {
219                 setProperty(ownIdentity, propertyName, null);
220         }
221
222         //
223         // SERVICE METHODS
224         //
225
226         /**
227          * {@inheritDoc}
228          */
229         @Override
230         protected void serviceRun() {
231                 while (!shouldStop()) {
232                         try {
233                                 WebOfTrustUpdateJob updateJob = updateJobs.take();
234                                 if (shouldStop() || (updateJob == stopJob)) {
235                                         break;
236                                 }
237                                 logger.log(Level.FINE, "Running Trust Update Job: " + updateJob);
238                                 long startTime = System.currentTimeMillis();
239                                 updateJob.run();
240                                 long endTime = System.currentTimeMillis();
241                                 logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms.");
242                         } catch (InterruptedException ie1) {
243                                 /* happens, ignore, loop. */
244                         }
245                 }
246         }
247
248         /**
249          * {@inheritDoc}
250          */
251         @Override
252         protected void serviceStop() {
253                 try {
254                         updateJobs.put(stopJob);
255                 } catch (InterruptedException ie1) {
256                         /* the queue is unbounded so it should never block. */
257                 }
258         }
259
260         /**
261          * Base class for WebOfTrust update jobs.
262          *
263          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
264          */
265         private class WebOfTrustUpdateJob {
266
267                 /** Object for synchronization. */
268                 @SuppressWarnings("hiding")
269                 private final Object syncObject = new Object();
270
271                 /** Whether the job has finished. */
272                 private boolean finished;
273
274                 /** Whether the job was successful. */
275                 private boolean success;
276
277                 //
278                 // ACTIONS
279                 //
280
281                 /**
282                  * Performs the actual update operation.
283                  * <p>
284                  * The implementation of this class does nothing.
285                  */
286                 public void run() {
287                         /* does nothing. */
288                 }
289
290                 /**
291                  * Waits for completion of this job or stopping of the WebOfTrust
292                  * updater.
293                  *
294                  * @return {@code true} if this job finished successfully, {@code false}
295                  *         otherwise
296                  *
297                  * @see WebOfTrustUpdater#stop()
298                  */
299                 @SuppressWarnings("synthetic-access")
300                 public boolean waitForCompletion() {
301                         synchronized (syncObject) {
302                                 while (!finished && !shouldStop()) {
303                                         try {
304                                                 syncObject.wait();
305                                         } catch (InterruptedException ie1) {
306                                                 /* we’re looping, ignore. */
307                                         }
308                                 }
309                                 return success;
310                         }
311                 }
312
313                 //
314                 // PROTECTED METHODS
315                 //
316
317                 /**
318                  * Signals that this job has finished.
319                  *
320                  * @param success
321                  *            {@code true} if this job finished successfully,
322                  *            {@code false} otherwise
323                  */
324                 protected void finish(boolean success) {
325                         synchronized (syncObject) {
326                                 finished = true;
327                                 this.success = success;
328                                 syncObject.notifyAll();
329                         }
330                 }
331
332         }
333
334         /**
335          * Base class for WebOfTrust trust update jobs.
336          *
337          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
338          */
339         private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob {
340
341                 /** The identity giving the trust. */
342                 protected final OwnIdentity truster;
343
344                 /** The identity receiving the trust. */
345                 protected final Identity trustee;
346
347                 /**
348                  * Creates a new trust update job.
349                  *
350                  * @param truster
351                  *            The identity giving the trust
352                  * @param trustee
353                  *            The identity receiving the trust
354                  */
355                 @SuppressWarnings("synthetic-access")
356                 public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) {
357                         this.truster = truster;
358                         this.trustee = trustee;
359                 }
360
361                 //
362                 // OBJECT METHODS
363                 //
364
365                 /**
366                  * {@inheritDoc}
367                  */
368                 @Override
369                 public boolean equals(Object object) {
370                         if ((object == null) || !object.getClass().equals(getClass())) {
371                                 return false;
372                         }
373                         WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object;
374                         return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
375                 }
376
377                 /**
378                  * {@inheritDoc}
379                  */
380                 @Override
381                 public int hashCode() {
382                         return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
383                 }
384
385                 /**
386                  * {@inheritDoc}
387                  */
388                 @Override
389                 public String toString() {
390                         return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
391                 }
392
393         }
394
395         /**
396          * Update job that sets the trust relation between two identities.
397          *
398          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
399          */
400         private class SetTrustJob extends WebOfTrustTrustUpdateJob {
401
402                 /** The score of the relation. */
403                 private final Integer score;
404
405                 /** The comment of the relation. */
406                 private final String comment;
407
408                 /**
409                  * Creates a new set trust job.
410                  *
411                  * @param truster
412                  *            The identity giving the trust
413                  * @param trustee
414                  *            The identity receiving the trust
415                  * @param score
416                  *            The score of the trust (from -100 to 100, may be
417                  *            {@code null} to remote the trust relation completely)
418                  * @param comment
419                  *            The comment of the trust relation
420                  */
421                 public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
422                         super(truster, trustee);
423                         this.score = score;
424                         this.comment = comment;
425                 }
426
427                 /**
428                  * {@inheritDoc}
429                  */
430                 @Override
431                 @SuppressWarnings("synthetic-access")
432                 public void run() {
433                         try {
434                                 if (score != null) {
435                                         if (trustee instanceof DefaultIdentity) {
436                                                 ((DefaultIdentity) trustee).setTrust(truster, new Trust(score, null, 0));
437                                         }
438                                         webOfTrustConnector.setTrust(truster, trustee, score, comment);
439                                 } else {
440                                         if (trustee instanceof DefaultIdentity) {
441                                                 ((DefaultIdentity) trustee).setTrust(truster, null);
442                                         }
443                                         webOfTrustConnector.removeTrust(truster, trustee);
444                                 }
445                                 finish(true);
446                         } catch (WebOfTrustException wote1) {
447                                 logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1);
448                                 finish(false);
449                         }
450                 }
451
452         }
453
454         /**
455          * Base class for context updates of an {@link OwnIdentity}.
456          *
457          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
458          */
459         private class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob {
460
461                 /** The own identity whose contexts to manage. */
462                 protected final OwnIdentity ownIdentity;
463
464                 /** The context to update. */
465                 protected final String context;
466
467                 /**
468                  * Creates a new context update job.
469                  *
470                  * @param ownIdentity
471                  *            The own identity to update
472                  * @param context
473                  *            The context to update
474                  */
475                 @SuppressWarnings("synthetic-access")
476                 public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) {
477                         Validation.begin().isNotNull("OwnIdentity", ownIdentity).isNotNull("Context", context).check();
478                         this.ownIdentity = ownIdentity;
479                         this.context = context;
480                 }
481
482                 //
483                 // OBJECT METHODS
484                 //
485
486                 /**
487                  * {@inheritDoc}
488                  */
489                 @Override
490                 public boolean equals(Object object) {
491                         if ((object == null) || !object.getClass().equals(getClass())) {
492                                 return false;
493                         }
494                         WebOfTrustContextUpdateJob updateJob = (WebOfTrustContextUpdateJob) object;
495                         return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context);
496                 }
497
498                 /**
499                  * {@inheritDoc}
500                  */
501                 @Override
502                 public int hashCode() {
503                         return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode();
504                 }
505
506                 /**
507                  * {@inheritDoc}
508                  */
509                 @Override
510                 public String toString() {
511                         return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context);
512                 }
513
514         }
515
516         /**
517          * Job that adds a context to an {@link OwnIdentity}.
518          *
519          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
520          */
521         private class AddContextJob extends WebOfTrustContextUpdateJob {
522
523                 /**
524                  * Creates a new add-context job.
525                  *
526                  * @param ownIdentity
527                  *            The own identity whose contexts to manage
528                  * @param context
529                  *            The context to add
530                  */
531                 public AddContextJob(OwnIdentity ownIdentity, String context) {
532                         super(ownIdentity, context);
533                 }
534
535                 /**
536                  * {@inheritDoc}
537                  */
538                 @Override
539                 @SuppressWarnings("synthetic-access")
540                 public void run() {
541                         try {
542                                 webOfTrustConnector.addContext(ownIdentity, context);
543                                 ownIdentity.addContext(context);
544                                 finish(true);
545                         } catch (PluginException pe1) {
546                                 logger.log(Level.WARNING, String.format("Could not add Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
547                                 finish(false);
548                         }
549                 }
550
551         }
552
553         /**
554          * Job that removes a context from an {@link OwnIdentity}.
555          *
556          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
557          */
558         private class RemoveContextJob extends WebOfTrustContextUpdateJob {
559
560                 /**
561                  * Creates a new remove-context job.
562                  *
563                  * @param ownIdentity
564                  *            The own identity whose contexts to manage
565                  * @param context
566                  *            The context to remove
567                  */
568                 public RemoveContextJob(OwnIdentity ownIdentity, String context) {
569                         super(ownIdentity, context);
570                 }
571
572                 /**
573                  * {@inheritDoc}
574                  */
575                 @Override
576                 @SuppressWarnings("synthetic-access")
577                 public void run() {
578                         try {
579                                 webOfTrustConnector.removeContext(ownIdentity, context);
580                                 ownIdentity.removeContext(context);
581                                 finish(true);
582                         } catch (PluginException pe1) {
583                                 logger.log(Level.WARNING, String.format("Could not remove Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
584                                 finish(false);
585                         }
586                 }
587
588         }
589
590         /**
591          * Base class for update jobs that deal with properties.
592          *
593          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
594          */
595         private class WebOfTrustPropertyUpdateJob extends WebOfTrustUpdateJob {
596
597                 /** The own identity to update properties on. */
598                 protected final OwnIdentity ownIdentity;
599
600                 /** The name of the property to update. */
601                 protected final String propertyName;
602
603                 /**
604                  * Creates a new property update job.
605                  *
606                  * @param ownIdentity
607                  *            The own identity to update the property on
608                  * @param propertyName
609                  *            The name of the property to update
610                  */
611                 @SuppressWarnings("synthetic-access")
612                 public WebOfTrustPropertyUpdateJob(OwnIdentity ownIdentity, String propertyName) {
613                         this.ownIdentity = ownIdentity;
614                         this.propertyName = propertyName;
615                 }
616
617                 //
618                 // OBJECT METHODS
619                 //
620
621                 /**
622                  * {@inheritDoc}
623                  */
624                 @Override
625                 public boolean equals(Object object) {
626                         if ((object == null) || !object.getClass().equals(getClass())) {
627                                 return false;
628                         }
629                         WebOfTrustPropertyUpdateJob updateJob = (WebOfTrustPropertyUpdateJob) object;
630                         return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName);
631                 }
632
633                 /**
634                  * {@inheritDoc}
635                  */
636                 @Override
637                 public int hashCode() {
638                         return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode();
639                 }
640
641                 /**
642                  * {@inheritDoc}
643                  */
644                 @Override
645                 public String toString() {
646                         return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName);
647                 }
648
649         }
650
651         /**
652          * WebOfTrust update job that sets a property on an {@link OwnIdentity}.
653          *
654          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
655          */
656         private class SetPropertyJob extends WebOfTrustPropertyUpdateJob {
657
658                 /** The value of the property to set. */
659                 private final String propertyValue;
660
661                 /**
662                  * Creates a new set-property job.
663                  *
664                  * @param ownIdentity
665                  *            The own identity to set the property on
666                  * @param propertyName
667                  *            The name of the property to set
668                  * @param propertyValue
669                  *            The value of the property to set
670                  */
671                 public SetPropertyJob(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
672                         super(ownIdentity, propertyName);
673                         this.propertyValue = propertyValue;
674                 }
675
676                 /**
677                  * {@inheritDoc}
678                  */
679                 @Override
680                 @SuppressWarnings("synthetic-access")
681                 public void run() {
682                         try {
683                                 if (propertyValue == null) {
684                                         webOfTrustConnector.removeProperty(ownIdentity, propertyName);
685                                         ownIdentity.removeProperty(propertyName);
686                                 } else {
687                                         webOfTrustConnector.setProperty(ownIdentity, propertyName, propertyValue);
688                                         ownIdentity.setProperty(propertyName, propertyValue);
689                                 }
690                                 finish(true);
691                         } catch (PluginException pe1) {
692                                 logger.log(Level.WARNING, String.format("Could not set Property “%2$s” to “%3$s” on Own Identity %1$s!", ownIdentity, propertyName, propertyValue), pe1);
693                                 finish(false);
694                         }
695                 }
696
697         }
698
699 }