1 package eu.fbk.knowledgestore;
2
3 import java.io.Serializable;
4 import java.util.Map;
5
6 import javax.annotation.Nullable;
7
8 import com.google.common.base.Function;
9 import com.google.common.base.Preconditions;
10 import com.google.common.collect.ImmutableMap;
11 import com.google.common.collect.ImmutableSet;
12
13 import org.openrdf.model.Statement;
14 import org.openrdf.model.URI;
15
16 import eu.fbk.knowledgestore.data.Data;
17 import eu.fbk.knowledgestore.data.Record;
18 import eu.fbk.knowledgestore.data.Stream;
19 import eu.fbk.knowledgestore.vocabulary.KSR;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public final class Outcome implements Comparable<Outcome>, Serializable {
44
45 private static final long serialVersionUID = 1L;
46
47 private final Status status;
48
49 private final URI invocationID;
50
51 @Nullable
52 private final URI objectID;
53
54 @Nullable
55 private final String message;
56
57 private Outcome(final Status status, final URI invocationID, @Nullable final URI objectID,
58 @Nullable final String message) {
59 this.status = Preconditions.checkNotNull(status);
60 this.invocationID = Preconditions.checkNotNull(invocationID);
61 this.objectID = objectID;
62 this.message = message;
63 }
64
65
66
67
68
69
70
71
72
73
74 public static Outcome create(final Status status, final URI invocationID) {
75 return create(status, invocationID, null, null);
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public static Outcome create(final Status status, final URI invocationID,
91 @Nullable final URI objectID) {
92 return create(status, invocationID, objectID, null);
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public static Outcome create(final Status status, final URI invocationID,
110 @Nullable final URI objectID, @Nullable final String message) {
111 return new Outcome(status, invocationID, objectID, message);
112 }
113
114
115
116
117
118
119
120
121
122
123
124 public static Outcome create(final Record record) {
125 final URI invocationID = record.getID();
126 final Status status = Status.valueOf(record.getUnique(KSR.STATUS, URI.class));
127 final URI objectID = record.getUnique(KSR.OBJECT, URI.class);
128 final String message = record.getUnique(KSR.MESSAGE, String.class);
129 return create(status, invocationID, objectID, message);
130 }
131
132
133
134
135
136
137
138
139 public Status getStatus() {
140 return this.status;
141 }
142
143
144
145
146
147
148 public URI getInvocationID() {
149 return this.invocationID;
150 }
151
152
153
154
155
156
157 @Nullable
158 public URI getObjectID() {
159 return this.objectID;
160 }
161
162
163
164
165
166
167 @Nullable
168 public String getMessage() {
169 return this.message;
170 }
171
172
173
174
175 @Override
176 public int compareTo(final Outcome other) {
177 return Data.getTotalComparator().compare(this.invocationID, other.invocationID);
178 }
179
180
181
182
183 @Override
184 public boolean equals(@Nullable final Object object) {
185 if (object == this) {
186 return true;
187 }
188 if (!(object instanceof Outcome)) {
189 return false;
190 }
191 final Outcome other = (Outcome) object;
192 return this.invocationID.equals(other.invocationID);
193 }
194
195
196
197
198 @Override
199 public int hashCode() {
200 return this.invocationID.hashCode();
201 }
202
203
204
205
206
207
208
209
210
211 public Record toRecord() {
212 final Record record = Record.create(this.invocationID);
213 record.add(KSR.STATUS, this.status.getURI());
214 if (this.objectID != null) {
215 record.add(KSR.OBJECT, this.objectID);
216 }
217 if (this.message != null) {
218 record.add(KSR.MESSAGE, Data.getValueFactory().createLiteral(this.message));
219 }
220 return record;
221 }
222
223
224
225
226
227 @Override
228 public String toString() {
229 final StringBuilder builder = new StringBuilder();
230 builder.append(this.status);
231 builder.append(' ');
232 builder.append(Data.toString(this.invocationID, Data.getNamespaceMap()));
233 if (this.objectID != null) {
234 builder.append(' ');
235 builder.append('(');
236 builder.append(Data.toString(this.objectID, Data.getNamespaceMap()));
237 builder.append(')');
238 }
239 if (this.message != null) {
240 builder.append(' ');
241 builder.append(this.message);
242 }
243 return builder.toString();
244 }
245
246
247
248
249
250
251
252
253
254 public static Stream<Statement> encode(final Stream<? extends Outcome> stream) {
255 Preconditions.checkNotNull(stream);
256 return Record.encode(stream.transform(new Function<Outcome, Record>() {
257
258 @Override
259 public Record apply(final Outcome outcome) {
260 return outcome.toRecord();
261 }
262
263 }, 0), ImmutableSet.of(KSR.INVOCATION));
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283 public static Stream<Outcome> decode(final Stream<Statement> stream,
284 @Nullable final Boolean chunked) {
285 Preconditions.checkNotNull(stream);
286 return Record.decode(stream, ImmutableSet.of(KSR.INVOCATION), chunked).transform(
287 new Function<Record, Outcome>() {
288
289 @Override
290 public Outcome apply(final Record record) {
291 return Outcome.create(record);
292 }
293
294 }, 0);
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 public enum Status {
309
310
311
312
313
314
315
316 OK_BULK(KSR.OK_BULK, "Bulk operation succeeded for all affected objects", 200, true),
317
318
319
320
321
322
323 OK_CREATED(KSR.OK_CREATED, "Object created", 201, true),
324
325
326
327
328
329
330 OK_MODIFIED(KSR.OK_MODIFIED, "Object modified", 200, true),
331
332
333
334
335
336
337 OK_UNMODIFIED(KSR.OK_UNMODIFIED, "Object not modified", 200, true),
338
339
340
341
342
343
344 OK_DELETED(KSR.OK_DELETED, "Object deleted", 200, true),
345
346
347
348
349
350
351
352 ERROR_BULK(KSR.ERROR_BULK, "Bulk operation failed for at least one affected object", 200,
353 false),
354
355
356
357
358
359
360
361 ERROR_NOT_ACCEPTABLE(KSR.ERROR_NOT_ACCEPTABLE,
362 "Operation failed as result would not be acceptable to client", 406, false),
363
364
365
366
367
368
369 ERROR_OBJECT_NOT_FOUND(KSR.ERROR_OBJECT_NOT_FOUND,
370 "Operation failed as target object does not exist", 404, false),
371
372
373
374
375
376
377 ERROR_OBJECT_ALREADY_EXISTS(KSR.ERROR_OBJECT_ALREADY_EXISTS,
378 "Operation failed as target object already exists", 409, false),
379
380
381
382
383
384
385
386 ERROR_DEPENDENCY_NOT_FOUND(KSR.ERROR_DEPENDENCY_NOT_FOUND,
387 "Operation failed as object it depends on does not exist", 400, false),
388
389
390
391
392
393
394 ERROR_INVALID_INPUT(KSR.ERROR_INVALID_INPUT,
395 "Operation failed as required input arguments are missing or wrong", 400, false),
396
397
398
399
400
401
402
403 ERROR_FORBIDDEN(KSR.ERROR_FORBIDDEN, "Operation forbidden", 403, false),
404
405
406
407
408
409
410
411 ERROR_INTERRUPTED(KSR.ERROR_INTERRUPTED, "Operation interrupted", 503, false),
412
413
414
415
416
417
418 ERROR_UNEXPECTED(KSR.ERROR_UNEXPECTED, "Unexpected error", 500, false),
419
420
421
422
423
424
425 UNKNOWN(KSR.UNKNOWN, "Unknown outcome", 503, false);
426
427 @Nullable
428 private static Map<URI, Status> uriToStatusMap = null;
429
430 private URI uri;
431
432 private String comment;
433
434 private int httpStatus;
435
436 private boolean isOK;
437
438 private boolean isError;
439
440 private Status(final URI uri, final String comment, final int httpStatus,
441 @Nullable final boolean okOrError) {
442 this.uri = uri;
443 this.comment = comment;
444 this.httpStatus = httpStatus;
445 this.isOK = okOrError == Boolean.TRUE;
446 this.isError = okOrError == Boolean.FALSE;
447 }
448
449
450
451
452
453
454
455 public URI getURI() {
456 return this.uri;
457 }
458
459
460
461
462
463
464 public String getComment() {
465 return this.comment;
466 }
467
468
469
470
471
472
473
474 public int getHTTPStatus() {
475 return this.httpStatus;
476 }
477
478
479
480
481
482
483 public boolean isOK() {
484 return this.isOK;
485 }
486
487
488
489
490
491
492 public boolean isError() {
493 return this.isError;
494 }
495
496
497
498
499
500
501
502
503
504
505 public static Status valueOf(final URI uri) throws IllegalArgumentException {
506 if (uriToStatusMap == null) {
507 final ImmutableMap.Builder<URI, Status> builder = ImmutableMap.builder();
508 for (final Status status : Status.values()) {
509 builder.put(status.uri, status);
510 }
511 uriToStatusMap = builder.build();
512 }
513 final Status status = uriToStatusMap.get(uri);
514 if (status == null) {
515 throw new IllegalArgumentException("Invalid status URI: " + uri);
516 }
517 return status;
518 }
519
520
521
522
523
524
525
526
527
528
529 public static Status valueOf(final int httpStatus) {
530 if (httpStatus == 400) {
531 return Status.ERROR_INVALID_INPUT;
532 } else if (httpStatus == 401 || httpStatus == 403) {
533 return Status.ERROR_FORBIDDEN;
534 } else if (httpStatus == 404) {
535 return Status.ERROR_OBJECT_NOT_FOUND;
536 } else if (httpStatus == 406) {
537 return Status.ERROR_NOT_ACCEPTABLE;
538 }
539 return Outcome.Status.ERROR_UNEXPECTED;
540 }
541
542 }
543
544 }