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