13
13
import java .util .concurrent .TimeUnit ;
14
14
import java .util .concurrent .atomic .AtomicBoolean ;
15
15
import java .util .concurrent .atomic .AtomicInteger ;
16
+ import java .util .concurrent .atomic .AtomicReference ;
16
17
import javax .annotation .Nullable ;
17
18
18
19
/**
@@ -33,9 +34,19 @@ public static CompletableResultCode ofFailure() {
33
34
return FAILURE ;
34
35
}
35
36
37
+ /**
38
+ * Returns a {@link CompletableResultCode} that has been {@link #failExceptionally(Throwable)
39
+ * failed exceptionally}.
40
+ */
41
+ public static CompletableResultCode ofExceptionalFailure (Throwable throwable ) {
42
+ return new CompletableResultCode ().failExceptionally (throwable );
43
+ }
44
+
36
45
/**
37
46
* Returns a {@link CompletableResultCode} that completes after all the provided {@link
38
- * CompletableResultCode}s complete. If any of the results fail, the result will be failed.
47
+ * CompletableResultCode}s complete. If any of the results fail, the result will be failed. If any
48
+ * {@link #failExceptionally(Throwable) failed exceptionally}, the result will be failed
49
+ * exceptionally with the first {@link Throwable} from {@code codes}.
39
50
*/
40
51
public static CompletableResultCode ofAll (Collection <CompletableResultCode > codes ) {
41
52
if (codes .isEmpty ()) {
@@ -44,15 +55,20 @@ public static CompletableResultCode ofAll(Collection<CompletableResultCode> code
44
55
CompletableResultCode result = new CompletableResultCode ();
45
56
AtomicInteger pending = new AtomicInteger (codes .size ());
46
57
AtomicBoolean failed = new AtomicBoolean ();
58
+ AtomicReference <Throwable > throwableRef = new AtomicReference <>();
47
59
for (CompletableResultCode code : codes ) {
48
60
code .whenComplete (
49
61
() -> {
50
62
if (!code .isSuccess ()) {
51
63
failed .set (true );
64
+ Throwable codeThrowable = code .getFailureThrowable ();
65
+ if (codeThrowable != null ) {
66
+ throwableRef .compareAndSet (null , codeThrowable );
67
+ }
52
68
}
53
69
if (pending .decrementAndGet () == 0 ) {
54
70
if (failed .get ()) {
55
- result .fail ( );
71
+ result .failInternal ( throwableRef . get () );
56
72
} else {
57
73
result .succeed ();
58
74
}
@@ -71,6 +87,10 @@ public CompletableResultCode() {}
71
87
@ GuardedBy ("lock" )
72
88
private Boolean succeeded = null ;
73
89
90
+ @ Nullable
91
+ @ GuardedBy ("lock" )
92
+ private Throwable throwable = null ;
93
+
74
94
@ GuardedBy ("lock" )
75
95
private final List <Runnable > completionActions = new ArrayList <>();
76
96
@@ -89,11 +109,27 @@ public CompletableResultCode succeed() {
89
109
return this ;
90
110
}
91
111
92
- /** Complete this {@link CompletableResultCode} unsuccessfully if it is not already completed. */
112
+ /**
113
+ * Complete this {@link CompletableResultCode} unsuccessfully if it is not already completed,
114
+ * setting the {@link #getFailureThrowable() failure throwable} to {@code null}.
115
+ */
93
116
public CompletableResultCode fail () {
117
+ return failInternal (null );
118
+ }
119
+
120
+ /**
121
+ * Completes this {@link CompletableResultCode} unsuccessfully if it is not already completed,
122
+ * setting the {@link #getFailureThrowable() failure throwable} to {@code throwable}.
123
+ */
124
+ public CompletableResultCode failExceptionally (Throwable throwable ) {
125
+ return failInternal (throwable );
126
+ }
127
+
128
+ private CompletableResultCode failInternal (@ Nullable Throwable throwable ) {
94
129
synchronized (lock ) {
95
130
if (succeeded == null ) {
96
131
succeeded = false ;
132
+ this .throwable = throwable ;
97
133
for (Runnable action : completionActions ) {
98
134
action .run ();
99
135
}
@@ -104,7 +140,7 @@ public CompletableResultCode fail() {
104
140
105
141
/**
106
142
* Obtain the current state of completion. Generally call once completion is achieved via the
107
- * thenRun method.
143
+ * {@link #whenComplete(Runnable)} method.
108
144
*
109
145
* @return the current state of completion
110
146
*/
@@ -114,6 +150,21 @@ public boolean isSuccess() {
114
150
}
115
151
}
116
152
153
+ /**
154
+ * Returns {@link Throwable} if this {@link CompletableResultCode} was {@link
155
+ * #failExceptionally(Throwable) failed exceptionally}. Generally call once completion is achieved
156
+ * via the {@link #whenComplete(Runnable)} method.
157
+ *
158
+ * @return the throwable if failed exceptionally, or null if: {@link #fail() failed without
159
+ * exception}, {@link #succeed() succeeded}, or not complete.
160
+ */
161
+ @ Nullable
162
+ public Throwable getFailureThrowable () {
163
+ synchronized (lock ) {
164
+ return throwable ;
165
+ }
166
+ }
167
+
117
168
/**
118
169
* Perform an action on completion. Actions are guaranteed to be called only once.
119
170
*
0 commit comments