Completable result code throwable (#6348)
This commit is contained in:
parent
d16ba00e15
commit
fc283ba763
|
|
@ -1,2 +1,7 @@
|
||||||
Comparing source compatibility of opentelemetry-sdk-common-1.41.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.40.0.jar
|
Comparing source compatibility of opentelemetry-sdk-common-1.41.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.40.0.jar
|
||||||
No changes.
|
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.common.CompletableResultCode (not serializable)
|
||||||
|
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
|
||||||
|
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.CompletableResultCode failExceptionally(java.lang.Throwable)
|
||||||
|
+++ NEW METHOD: PUBLIC(+) java.lang.Throwable getFailureThrowable()
|
||||||
|
+++ NEW ANNOTATION: javax.annotation.Nullable
|
||||||
|
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.CompletableResultCode ofExceptionalFailure(java.lang.Throwable)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,9 +34,19 @@ public final class CompletableResultCode {
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link CompletableResultCode} that has been {@link #failExceptionally(Throwable)
|
||||||
|
* failed exceptionally}.
|
||||||
|
*/
|
||||||
|
public static CompletableResultCode ofExceptionalFailure(Throwable throwable) {
|
||||||
|
return new CompletableResultCode().failExceptionally(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link CompletableResultCode} that completes after all the provided {@link
|
* Returns a {@link CompletableResultCode} that completes after all the provided {@link
|
||||||
* CompletableResultCode}s complete. If any of the results fail, the result will be failed.
|
* CompletableResultCode}s complete. If any of the results fail, the result will be failed. If any
|
||||||
|
* {@link #failExceptionally(Throwable) failed exceptionally}, the result will be failed
|
||||||
|
* exceptionally with the first {@link Throwable} from {@code codes}.
|
||||||
*/
|
*/
|
||||||
public static CompletableResultCode ofAll(Collection<CompletableResultCode> codes) {
|
public static CompletableResultCode ofAll(Collection<CompletableResultCode> codes) {
|
||||||
if (codes.isEmpty()) {
|
if (codes.isEmpty()) {
|
||||||
|
|
@ -44,15 +55,20 @@ public final class CompletableResultCode {
|
||||||
CompletableResultCode result = new CompletableResultCode();
|
CompletableResultCode result = new CompletableResultCode();
|
||||||
AtomicInteger pending = new AtomicInteger(codes.size());
|
AtomicInteger pending = new AtomicInteger(codes.size());
|
||||||
AtomicBoolean failed = new AtomicBoolean();
|
AtomicBoolean failed = new AtomicBoolean();
|
||||||
|
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||||
for (CompletableResultCode code : codes) {
|
for (CompletableResultCode code : codes) {
|
||||||
code.whenComplete(
|
code.whenComplete(
|
||||||
() -> {
|
() -> {
|
||||||
if (!code.isSuccess()) {
|
if (!code.isSuccess()) {
|
||||||
failed.set(true);
|
failed.set(true);
|
||||||
|
Throwable codeThrowable = code.getFailureThrowable();
|
||||||
|
if (codeThrowable != null) {
|
||||||
|
throwableRef.compareAndSet(null, codeThrowable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pending.decrementAndGet() == 0) {
|
if (pending.decrementAndGet() == 0) {
|
||||||
if (failed.get()) {
|
if (failed.get()) {
|
||||||
result.fail();
|
result.failInternal(throwableRef.get());
|
||||||
} else {
|
} else {
|
||||||
result.succeed();
|
result.succeed();
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +87,10 @@ public final class CompletableResultCode {
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private Boolean succeeded = null;
|
private Boolean succeeded = null;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private Throwable throwable = null;
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final List<Runnable> completionActions = new ArrayList<>();
|
private final List<Runnable> completionActions = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -89,11 +109,27 @@ public final class CompletableResultCode {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Complete this {@link CompletableResultCode} unsuccessfully if it is not already completed. */
|
/**
|
||||||
|
* Complete this {@link CompletableResultCode} unsuccessfully if it is not already completed,
|
||||||
|
* setting the {@link #getFailureThrowable() failure throwable} to {@code null}.
|
||||||
|
*/
|
||||||
public CompletableResultCode fail() {
|
public CompletableResultCode fail() {
|
||||||
|
return failInternal(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes this {@link CompletableResultCode} unsuccessfully if it is not already completed,
|
||||||
|
* setting the {@link #getFailureThrowable() failure throwable} to {@code throwable}.
|
||||||
|
*/
|
||||||
|
public CompletableResultCode failExceptionally(Throwable throwable) {
|
||||||
|
return failInternal(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableResultCode failInternal(@Nullable Throwable throwable) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (succeeded == null) {
|
if (succeeded == null) {
|
||||||
succeeded = false;
|
succeeded = false;
|
||||||
|
this.throwable = throwable;
|
||||||
for (Runnable action : completionActions) {
|
for (Runnable action : completionActions) {
|
||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +140,7 @@ public final class CompletableResultCode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the current state of completion. Generally call once completion is achieved via the
|
* Obtain the current state of completion. Generally call once completion is achieved via the
|
||||||
* thenRun method.
|
* {@link #whenComplete(Runnable)} method.
|
||||||
*
|
*
|
||||||
* @return the current state of completion
|
* @return the current state of completion
|
||||||
*/
|
*/
|
||||||
|
|
@ -114,6 +150,21 @@ public final class CompletableResultCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link Throwable} if this {@link CompletableResultCode} was {@link
|
||||||
|
* #failExceptionally(Throwable) failed exceptionally}. Generally call once completion is achieved
|
||||||
|
* via the {@link #whenComplete(Runnable)} method.
|
||||||
|
*
|
||||||
|
* @return the throwable if failed exceptionally, or null if: {@link #fail() failed without
|
||||||
|
* exception}, {@link #succeed() succeeded}, or not complete.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Throwable getFailureThrowable() {
|
||||||
|
synchronized (lock) {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an action on completion. Actions are guaranteed to be called only once.
|
* Perform an action on completion. Actions are guaranteed to be called only once.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,32 @@ class CompletableResultCodeTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void ofSuccess() {
|
void ofSuccess() {
|
||||||
assertThat(CompletableResultCode.ofSuccess().isSuccess()).isTrue();
|
assertThat(CompletableResultCode.ofSuccess())
|
||||||
|
.satisfies(
|
||||||
|
code -> {
|
||||||
|
assertThat(code.isSuccess()).isTrue();
|
||||||
|
assertThat(code.getFailureThrowable()).isNull();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void ofFailure() {
|
void ofFailure() {
|
||||||
assertThat(CompletableResultCode.ofFailure().isSuccess()).isFalse();
|
assertThat(CompletableResultCode.ofFailure())
|
||||||
|
.satisfies(
|
||||||
|
code -> {
|
||||||
|
assertThat(code.isSuccess()).isFalse();
|
||||||
|
assertThat(code.getFailureThrowable()).isNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofExceptionalFailure() {
|
||||||
|
assertThat(CompletableResultCode.ofExceptionalFailure(new Exception("error")))
|
||||||
|
.satisfies(
|
||||||
|
code -> {
|
||||||
|
assertThat(code.isSuccess()).isFalse();
|
||||||
|
assertThat(code.getFailureThrowable()).hasMessage("error");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -149,6 +169,24 @@ class CompletableResultCodeTest {
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofAllWithExceptionalFailure() {
|
||||||
|
assertThat(
|
||||||
|
CompletableResultCode.ofAll(
|
||||||
|
Arrays.asList(
|
||||||
|
CompletableResultCode.ofSuccess(),
|
||||||
|
CompletableResultCode.ofFailure(),
|
||||||
|
CompletableResultCode.ofExceptionalFailure(new Exception("error1")),
|
||||||
|
CompletableResultCode.ofExceptionalFailure(new Exception("error2")),
|
||||||
|
CompletableResultCode.ofSuccess())))
|
||||||
|
.satisfies(
|
||||||
|
code -> {
|
||||||
|
assertThat(code.isSuccess()).isFalse();
|
||||||
|
// failure throwable is set to first throwable seen in the collection
|
||||||
|
assertThat(code.getFailureThrowable()).hasMessage("error1");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void join() {
|
void join() {
|
||||||
CompletableResultCode result = new CompletableResultCode();
|
CompletableResultCode result = new CompletableResultCode();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue