Remove unnecessary 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                 AddContextJob addContextJob = new AddContextJob(ownIdentity, context);
117                 if (!updateJobs.contains(addContextJob)) {
118                         logger.log(Level.FINER, "Adding Context Job: " + addContextJob);
119                         try {
120                                 updateJobs.put(addContextJob);
121                         } catch (InterruptedException ie1) {
122                                 /* the queue is unbounded so it should never block. */
123                         }
124                         return addContextJob.waitForCompletion();
125                 } else {
126                         for (WebOfTrustUpdateJob updateJob : updateJobs) {
127                                 if (updateJob.equals(addContextJob)) {
128                                         return updateJob.waitForCompletion();
129                                 }
130                         }
131                 }
132                 return false;
133         }
134
135         /**
136          * Removes the given context from the given own identity.
137          *
138          * @param ownIdentity
139          *              The own identity to remove the context from
140          * @param context
141          *              The context to remove
142          */
143         public void removeContext(OwnIdentity ownIdentity, String context) {
144                 RemoveContextJob removeContextJob = new RemoveContextJob(ownIdentity, context);
145                 if (!updateJobs.contains(removeContextJob)) {
146                         logger.log(Level.FINER, "Adding Context Job: " + removeContextJob);
147                         try {
148                                 updateJobs.put(removeContextJob);
149                         } catch (InterruptedException ie1) {
150                                 /* the queue is unbounded so it should never block. */
151                         }
152                 }
153         }
154
155         /**
156          * Sets a property on the given own identity.
157          *
158          * @param ownIdentity
159          *              The own identity to set the property on
160          * @param propertyName
161          *              The name of the property to set
162          * @param propertyValue
163          *              The value of the property to set
164          */
165         public void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
166                 SetPropertyJob setPropertyJob = new SetPropertyJob(ownIdentity, propertyName, propertyValue);
167                 if (updateJobs.contains(setPropertyJob)) {
168                         updateJobs.remove(setPropertyJob);
169                 }
170                 logger.log(Level.FINER, "Adding Property Job: " + setPropertyJob);
171                 try {
172                         updateJobs.put(setPropertyJob);
173                 } catch (InterruptedException e) {
174                         /* the queue is unbounded so it should never block. */
175                 }
176         }
177
178         /**
179          * Removes a property from the given own identity.
180          *
181          * @param ownIdentity
182          *              The own identity to remove the property from
183          * @param propertyName
184          *              The name of the property to remove
185          */
186         public void removeProperty(OwnIdentity ownIdentity, String propertyName) {
187                 setProperty(ownIdentity, propertyName, null);
188         }
189
190         //
191         // SERVICE METHODS
192         //
193
194         /** {@inheritDoc} */
195         @Override
196         protected void serviceRun() {
197                 while (!shouldStop()) {
198                         try {
199                                 WebOfTrustUpdateJob updateJob = updateJobs.take();
200                                 if (shouldStop()) {
201                                         break;
202                                 }
203                                 logger.log(Level.FINE, "Running Trust Update Job: " + updateJob);
204                                 long startTime = System.currentTimeMillis();
205                                 updateJob.run();
206                                 long endTime = System.currentTimeMillis();
207                                 logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms.");
208                         } catch (InterruptedException ie1) {
209                                 /* happens, ignore, loop. */
210                         }
211                 }
212         }
213
214         /** {@inheritDoc} */
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         @VisibleForTesting
230         class WebOfTrustUpdateJob implements Runnable {
231
232                 /** Object for synchronization. */
233                 @SuppressWarnings("hiding")
234                 private final Object syncObject = new Object();
235
236                 /** Whether the job has finished. */
237                 private boolean finished;
238
239                 /** Whether the job was successful. */
240                 private boolean success;
241
242                 //
243                 // ACTIONS
244                 //
245
246                 /**
247                  * Performs the actual update operation.
248                  * <p/>
249                  * The implementation of this class does nothing.
250                  */
251                 @Override
252                 public void run() {
253                         /* does nothing. */
254                 }
255
256                 /**
257                  * Waits for completion of this job or stopping of the WebOfTrust updater.
258                  *
259                  * @return {@code true} if this job finished successfully, {@code false}
260                  *         otherwise
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, {@code false} otherwise
286                  */
287                 protected void finish(boolean success) {
288                         synchronized (syncObject) {
289                                 finished = true;
290                                 this.success = success;
291                                 syncObject.notifyAll();
292                         }
293                 }
294
295         }
296
297         /**
298          * Update job that sets the trust relation between two identities.
299          *
300          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
301          */
302         @VisibleForTesting
303         class SetTrustJob extends WebOfTrustUpdateJob {
304
305                 /** The identity giving the trust. */
306                 private final OwnIdentity truster;
307
308                 /** The identity receiving the trust. */
309                 private final Identity trustee;
310
311                 /** The score of the relation. */
312                 private final Integer score;
313
314                 /** The comment of the relation. */
315                 private final String comment;
316
317                 /**
318                  * Creates a new set trust job.
319                  *
320                  * @param truster
321                  *              The identity giving the trust
322                  * @param trustee
323                  *              The identity receiving the trust
324                  * @param score
325                  *              The score of the trust (from -100 to 100, may be {@code null} to remote
326                  *              the trust relation completely)
327                  * @param comment
328                  *              The comment of the trust relation
329                  */
330                 public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
331                         this.truster = checkNotNull(truster, "truster must not be null");
332                         this.trustee = checkNotNull(trustee, "trustee must not be null");
333                         this.score = score;
334                         this.comment = comment;
335                 }
336
337                 /** {@inheritDoc} */
338                 @Override
339                 @SuppressWarnings("synthetic-access")
340                 public void run() {
341                         try {
342                                 if (score != null) {
343                                         webOfTrustConnector.setTrust(truster, trustee, score, comment);
344                                         trustee.setTrust(truster, new Trust(score, null, 0));
345                                 } else {
346                                         webOfTrustConnector.removeTrust(truster, trustee);
347                                         trustee.removeTrust(truster);
348                                 }
349                                 finish(true);
350                         } catch (WebOfTrustException wote1) {
351                                 logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1);
352                                 finish(false);
353                         }
354                 }
355
356                 //
357                 // OBJECT METHODS
358                 //
359
360                 /** {@inheritDoc} */
361                 @Override
362                 public boolean equals(Object object) {
363                         if ((object == null) || !object.getClass().equals(getClass())) {
364                                 return false;
365                         }
366                         SetTrustJob updateJob = (SetTrustJob) object;
367                         return updateJob.truster.equals(truster) && updateJob.trustee.equals(trustee);
368                 }
369
370                 /** {@inheritDoc} */
371                 @Override
372                 public int hashCode() {
373                         return getClass().hashCode() ^ truster.hashCode() ^ trustee.hashCode();
374                 }
375
376                 /** {@inheritDoc} */
377                 @Override
378                 public String toString() {
379                         return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), truster.getId(), trustee.getId());
380                 }
381
382         }
383
384         /**
385          * Base class for context updates of an {@link OwnIdentity}.
386          *
387          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
388          */
389         @VisibleForTesting
390         class WebOfTrustContextUpdateJob extends WebOfTrustUpdateJob {
391
392                 /** The own identity whose contexts to manage. */
393                 protected final OwnIdentity ownIdentity;
394
395                 /** The context to update. */
396                 protected final String context;
397
398                 /**
399                  * Creates a new context update job.
400                  *
401                  * @param ownIdentity
402                  *              The own identity to update
403                  * @param context
404                  *              The context to update
405                  */
406                 @SuppressWarnings("synthetic-access")
407                 public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) {
408                         this.ownIdentity = checkNotNull(ownIdentity, "ownIdentity must not be null");
409                         this.context = checkNotNull(context, "context must not be null");
410                 }
411
412                 //
413                 // OBJECT METHODS
414                 //
415
416                 /** {@inheritDoc} */
417                 @Override
418                 public boolean equals(Object object) {
419                         if ((object == null) || !object.getClass().equals(getClass())) {
420                                 return false;
421                         }
422                         WebOfTrustContextUpdateJob updateJob = (WebOfTrustContextUpdateJob) object;
423                         return updateJob.ownIdentity.equals(ownIdentity) && updateJob.context.equals(context);
424                 }
425
426                 /** {@inheritDoc} */
427                 @Override
428                 public int hashCode() {
429                         return getClass().hashCode() ^ ownIdentity.hashCode() ^ context.hashCode();
430                 }
431
432                 /** {@inheritDoc} */
433                 @Override
434                 public String toString() {
435                         return String.format("%s[ownIdentity=%s,context=%s]", getClass().getSimpleName(), ownIdentity, context);
436                 }
437
438         }
439
440         /**
441          * Job that adds a context to an {@link OwnIdentity}.
442          *
443          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
444          */
445         @VisibleForTesting
446         class AddContextJob extends WebOfTrustContextUpdateJob {
447
448                 /**
449                  * Creates a new add-context job.
450                  *
451                  * @param ownIdentity
452                  *              The own identity whose contexts to manage
453                  * @param context
454                  *              The context to add
455                  */
456                 public AddContextJob(OwnIdentity ownIdentity, String context) {
457                         super(ownIdentity, context);
458                 }
459
460                 /** {@inheritDoc} */
461                 @Override
462                 @SuppressWarnings("synthetic-access")
463                 public void run() {
464                         try {
465                                 webOfTrustConnector.addContext(ownIdentity, context);
466                                 ownIdentity.addContext(context);
467                                 finish(true);
468                         } catch (PluginException pe1) {
469                                 logger.log(Level.WARNING, String.format("Could not add Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
470                                 finish(false);
471                         }
472                 }
473
474         }
475
476         /**
477          * Job that removes a context from an {@link OwnIdentity}.
478          *
479          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
480          */
481         @VisibleForTesting
482         class RemoveContextJob extends WebOfTrustContextUpdateJob {
483
484                 /**
485                  * Creates a new remove-context job.
486                  *
487                  * @param ownIdentity
488                  *              The own identity whose contexts to manage
489                  * @param context
490                  *              The context to remove
491                  */
492                 public RemoveContextJob(OwnIdentity ownIdentity, String context) {
493                         super(ownIdentity, context);
494                 }
495
496                 /** {@inheritDoc} */
497                 @Override
498                 @SuppressWarnings("synthetic-access")
499                 public void run() {
500                         try {
501                                 webOfTrustConnector.removeContext(ownIdentity, context);
502                                 ownIdentity.removeContext(context);
503                                 finish(true);
504                         } catch (PluginException pe1) {
505                                 logger.log(Level.WARNING, String.format("Could not remove Context “%2$s” to Own Identity %1$s!", ownIdentity, context), pe1);
506                                 finish(false);
507                         }
508                 }
509
510         }
511
512         /**
513          * WebOfTrust update job that sets a property on an {@link OwnIdentity}.
514          *
515          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
516          */
517         @VisibleForTesting
518         class SetPropertyJob extends WebOfTrustUpdateJob {
519
520                 /** The own identity to update properties on. */
521                 private final OwnIdentity ownIdentity;
522
523                 /** The name of the property to update. */
524                 private final String propertyName;
525
526                 /** The value of the property to set. */
527                 private final String propertyValue;
528
529                 /**
530                  * Creates a new set-property job.
531                  *
532                  * @param ownIdentity
533                  *              The own identity to set the property on
534                  * @param propertyName
535                  *              The name of the property to set
536                  * @param propertyValue
537                  *              The value of the property to set
538                  */
539                 public SetPropertyJob(OwnIdentity ownIdentity, String propertyName, String propertyValue) {
540                         this.ownIdentity = ownIdentity;
541                         this.propertyName = propertyName;
542                         this.propertyValue = propertyValue;
543                 }
544
545                 /** {@inheritDoc} */
546                 @Override
547                 @SuppressWarnings("synthetic-access")
548                 public void run() {
549                         try {
550                                 if (propertyValue == null) {
551                                         webOfTrustConnector.removeProperty(ownIdentity, propertyName);
552                                         ownIdentity.removeProperty(propertyName);
553                                 } else {
554                                         webOfTrustConnector.setProperty(ownIdentity, propertyName, propertyValue);
555                                         ownIdentity.setProperty(propertyName, propertyValue);
556                                 }
557                                 finish(true);
558                         } catch (PluginException pe1) {
559                                 logger.log(Level.WARNING, String.format("Could not set Property “%2$s” to “%3$s” on Own Identity %1$s!", ownIdentity, propertyName, propertyValue), pe1);
560                                 finish(false);
561                         }
562                 }
563
564                 //
565                 // OBJECT METHODS
566                 //
567
568                 /** {@inheritDoc} */
569                 @Override
570                 public boolean equals(Object object) {
571                         if ((object == null) || !object.getClass().equals(getClass())) {
572                                 return false;
573                         }
574                         SetPropertyJob updateJob = (SetPropertyJob) object;
575                         return updateJob.ownIdentity.equals(ownIdentity) && updateJob.propertyName.equals(propertyName);
576                 }
577
578                 /** {@inheritDoc} */
579                 @Override
580                 public int hashCode() {
581                         return getClass().hashCode() ^ ownIdentity.hashCode() ^ propertyName.hashCode();
582                 }
583
584                 /** {@inheritDoc} */
585                 @Override
586                 public String toString() {
587                         return String.format("%s[ownIdentity=%s,propertyName=%s]", getClass().getSimpleName(), ownIdentity, propertyName);
588                 }
589
590         }
591
592 }