Make it possible for update jobs to return whether they were successful.
[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
35 /**
36  * Updates WebOfTrust identity data in a background thread because communicating
37  * with the WebOfTrust plugin can potentially last quite long.
38  *
39  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
40  */
41 public class WebOfTrustUpdater extends AbstractService {
42
43         /** The logger. */
44         private static final Logger logger = Logging.getLogger(WebOfTrustUpdater.class);
45
46         /** Stop job. */
47         @SuppressWarnings("synthetic-access")
48         private final WebOfTrustUpdateJob stopJob = new WebOfTrustUpdateJob();
49
50         /** The web of trust connector. */
51         private final WebOfTrustConnector webOfTrustConnector;
52
53         /** The queue for jobs. */
54         private final BlockingQueue<WebOfTrustUpdateJob> updateJobs = new LinkedBlockingQueue<WebOfTrustUpdateJob>();
55
56         /**
57          * Creates a new trust updater.
58          *
59          * @param webOfTrustConnector
60          *            The web of trust connector
61          */
62         public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) {
63                 super("Trust Updater");
64                 this.webOfTrustConnector = webOfTrustConnector;
65         }
66
67         //
68         // ACTIONS
69         //
70
71         /**
72          * Retrieves the trust relation between the truster and the trustee. This
73          * method will return immediately and perform a trust update in the
74          * background.
75          *
76          * @param truster
77          *            The identity giving the trust
78          * @param trustee
79          *            The identity receiving the trust
80          */
81         public void getTrust(OwnIdentity truster, Identity trustee) {
82                 GetTrustJob getTrustJob = new GetTrustJob(truster, trustee);
83                 if (!updateJobs.contains(getTrustJob)) {
84                         logger.log(Level.FINER, "Adding Trust Update Job: " + getTrustJob);
85                         try {
86                                 updateJobs.put(getTrustJob);
87                         } catch (InterruptedException ie1) {
88                                 /* the queue is unbounded so it should never block. */
89                         }
90                 }
91         }
92
93         /**
94          * Updates the trust relation between the truster and the trustee. This
95          * method will return immediately and perform a trust update in the
96          * background.
97          *
98          * @param truster
99          *            The identity giving the trust
100          * @param trustee
101          *            The identity receiving the trust
102          * @param score
103          *            The new level of trust (from -100 to 100, may be {@code null}
104          *            to remove the trust completely)
105          * @param comment
106          *            The comment of the trust relation
107          */
108         public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) {
109                 SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment);
110                 if (updateJobs.contains(setTrustJob)) {
111                         updateJobs.remove(setTrustJob);
112                 }
113                 logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob);
114                 try {
115                         updateJobs.put(setTrustJob);
116                 } catch (InterruptedException e) {
117                         /* the queue is unbounded so it should never block. */
118                 }
119         }
120
121         //
122         // SERVICE METHODS
123         //
124
125         /**
126          * {@inheritDoc}
127          */
128         @Override
129         protected void serviceRun() {
130                 while (!shouldStop()) {
131                         try {
132                                 WebOfTrustUpdateJob updateJob = updateJobs.take();
133                                 if (shouldStop() || (updateJob == stopJob)) {
134                                         break;
135                                 }
136                                 logger.log(Level.FINE, "Running Trust Update Job: " + updateJob);
137                                 long startTime = System.currentTimeMillis();
138                                 updateJob.run();
139                                 long endTime = System.currentTimeMillis();
140                                 logger.log(Level.FINE, "Trust Update Job finished, took " + (endTime - startTime) + " ms.");
141                         } catch (InterruptedException ie1) {
142                                 /* happens, ignore, loop. */
143                         }
144                 }
145         }
146
147         /**
148          * {@inheritDoc}
149          */
150         @Override
151         protected void serviceStop() {
152                 try {
153                         updateJobs.put(stopJob);
154                 } catch (InterruptedException ie1) {
155                         /* the queue is unbounded so it should never block. */
156                 }
157         }
158
159         /**
160          * Base class for WebOfTrust update jobs.
161          *
162          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
163          */
164         private class WebOfTrustUpdateJob {
165
166                 /** Object for synchronization. */
167                 @SuppressWarnings("hiding")
168                 private final Object syncObject = new Object();
169
170                 /** Whether the job has finished. */
171                 private boolean finished;
172
173                 /** Whether the job was successful. */
174                 private boolean success;
175
176                 //
177                 // ACTIONS
178                 //
179
180                 /**
181                  * Performs the actual update operation.
182                  * <p>
183                  * The implementation of this class does nothing.
184                  */
185                 public void run() {
186                         /* does nothing. */
187                 }
188
189                 /**
190                  * Waits for completion of this job or stopping of the WebOfTrust
191                  * updater.
192                  *
193                  * @return {@code true} if this job finished successfully, {@code false}
194                  *         otherwise
195                  *
196                  * @see WebOfTrustUpdater#stop()
197                  */
198                 @SuppressWarnings("synthetic-access")
199                 public boolean waitForCompletion() {
200                         synchronized (syncObject) {
201                                 while (!finished && !shouldStop()) {
202                                         try {
203                                                 syncObject.wait();
204                                         } catch (InterruptedException ie1) {
205                                                 /* we’re looping, ignore. */
206                                         }
207                                 }
208                                 return success;
209                         }
210                 }
211
212                 //
213                 // PROTECTED METHODS
214                 //
215
216                 /**
217                  * Signals that this job has finished.
218                  *
219                  * @param success
220                  *            {@code true} if this job finished successfully,
221                  *            {@code false} otherwise
222                  */
223                 protected void finish(boolean success) {
224                         synchronized (syncObject) {
225                                 finished = true;
226                                 this.success = success;
227                                 syncObject.notifyAll();
228                         }
229                 }
230
231         }
232
233         /**
234          * Base class for WebOfTrust trust update jobs.
235          *
236          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
237          */
238         private class WebOfTrustTrustUpdateJob extends WebOfTrustUpdateJob {
239
240                 /** The identity giving the trust. */
241                 protected final OwnIdentity truster;
242
243                 /** The identity receiving the trust. */
244                 protected final Identity trustee;
245
246                 /**
247                  * Creates a new trust update job.
248                  *
249                  * @param truster
250                  *            The identity giving the trust
251                  * @param trustee
252                  *            The identity receiving the trust
253                  */
254                 @SuppressWarnings("synthetic-access")
255                 public WebOfTrustTrustUpdateJob(OwnIdentity truster, Identity trustee) {
256                         this.truster = truster;
257                         this.trustee = trustee;
258                 }
259
260                 //
261                 // OBJECT METHODS
262                 //
263
264                 /**
265                  * {@inheritDoc}
266                  */
267                 @Override
268                 public boolean equals(Object object) {
269                         if ((object == null) || !object.getClass().equals(getClass())) {
270                                 return false;
271                         }
272                         WebOfTrustTrustUpdateJob updateJob = (WebOfTrustTrustUpdateJob) object;
273                         return ((truster == null) ? (updateJob.truster == null) : updateJob.truster.equals(truster)) && ((trustee == null) ? (updateJob.trustee == null) : updateJob.trustee.equals(trustee));
274                 }
275
276                 /**
277                  * {@inheritDoc}
278                  */
279                 @Override
280                 public int hashCode() {
281                         return getClass().hashCode() ^ ((truster == null) ? 0 : truster.hashCode()) ^ ((trustee == null) ? 0 : trustee.hashCode());
282                 }
283
284                 /**
285                  * {@inheritDoc}
286                  */
287                 @Override
288                 public String toString() {
289                         return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), (truster == null) ? null : truster.getId(), (trustee == null) ? null : trustee.getId());
290                 }
291
292         }
293
294         /**
295          * Update job that sets the trust relation between two identities.
296          *
297          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
298          */
299         private class SetTrustJob extends WebOfTrustTrustUpdateJob {
300
301                 /** The score of the relation. */
302                 private final Integer score;
303
304                 /** The comment of the relation. */
305                 private final String comment;
306
307                 /**
308                  * Creates a new set trust job.
309                  *
310                  * @param truster
311                  *            The identity giving the trust
312                  * @param trustee
313                  *            The identity receiving the trust
314                  * @param score
315                  *            The score of the trust (from -100 to 100, may be
316                  *            {@code null} to remote the trust relation completely)
317                  * @param comment
318                  *            The comment of the trust relation
319                  */
320                 public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) {
321                         super(truster, trustee);
322                         this.score = score;
323                         this.comment = comment;
324                 }
325
326                 /**
327                  * {@inheritDoc}
328                  */
329                 @Override
330                 @SuppressWarnings("synthetic-access")
331                 public void run() {
332                         try {
333                                 if (score != null) {
334                                         if (trustee instanceof DefaultIdentity) {
335                                                 ((DefaultIdentity) trustee).setTrust(truster, new Trust(score, null, 0));
336                                         }
337                                         webOfTrustConnector.setTrust(truster, trustee, score, comment);
338                                 } else {
339                                         if (trustee instanceof DefaultIdentity) {
340                                                 ((DefaultIdentity) trustee).setTrust(truster, null);
341                                         }
342                                         webOfTrustConnector.removeTrust(truster, trustee);
343                                 }
344                                 finish(true);
345                         } catch (WebOfTrustException wote1) {
346                                 logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1);
347                                 finish(false);
348                         }
349                 }
350
351         }
352
353         /**
354          * Update job that retrieves the trust relation between two identities.
355          *
356          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
357          */
358         private class GetTrustJob extends WebOfTrustTrustUpdateJob {
359
360                 /**
361                  * Creates a new trust update job.
362                  *
363                  * @param truster
364                  *            The identity giving the trust
365                  * @param trustee
366                  *            The identity receiving the trust
367                  */
368                 public GetTrustJob(OwnIdentity truster, Identity trustee) {
369                         super(truster, trustee);
370                 }
371
372                 /**
373                  * {@inheritDoc}
374                  */
375                 @Override
376                 @SuppressWarnings("synthetic-access")
377                 public void run() {
378                         try {
379                                 Trust trust = webOfTrustConnector.getTrust(truster, trustee);
380                                 if (trustee instanceof DefaultIdentity) {
381                                         ((DefaultIdentity) trustee).setTrust(truster, trust);
382                                 }
383                                 finish(true);
384                         } catch (PluginException pe1) {
385                                 logger.log(Level.WARNING, "Could not get Trust value for " + truster + " -> " + trustee + "!", pe1);
386                                 finish(false);
387                         }
388                 }
389         }
390
391 }