Skip to content

Commit

Permalink
Completable result code throwable (#6348)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Aug 8, 2024
1 parent d16ba00 commit fc283ba
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 7 deletions.
7 changes: 6 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
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)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;

/**
Expand All @@ -33,9 +34,19 @@ public static CompletableResultCode ofFailure() {
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
* 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) {
if (codes.isEmpty()) {
Expand All @@ -44,15 +55,20 @@ public static CompletableResultCode ofAll(Collection<CompletableResultCode> code
CompletableResultCode result = new CompletableResultCode();
AtomicInteger pending = new AtomicInteger(codes.size());
AtomicBoolean failed = new AtomicBoolean();
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
for (CompletableResultCode code : codes) {
code.whenComplete(
() -> {
if (!code.isSuccess()) {
failed.set(true);
Throwable codeThrowable = code.getFailureThrowable();
if (codeThrowable != null) {
throwableRef.compareAndSet(null, codeThrowable);
}
}
if (pending.decrementAndGet() == 0) {
if (failed.get()) {
result.fail();
result.failInternal(throwableRef.get());
} else {
result.succeed();
}
Expand All @@ -71,6 +87,10 @@ public CompletableResultCode() {}
@GuardedBy("lock")
private Boolean succeeded = null;

@Nullable
@GuardedBy("lock")
private Throwable throwable = null;

@GuardedBy("lock")
private final List<Runnable> completionActions = new ArrayList<>();

Expand All @@ -89,11 +109,27 @@ public CompletableResultCode succeed() {
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() {
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) {
if (succeeded == null) {
succeeded = false;
this.throwable = throwable;
for (Runnable action : completionActions) {
action.run();
}
Expand All @@ -104,7 +140,7 @@ public CompletableResultCode fail() {

/**
* 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
*/
Expand All @@ -114,6 +150,21 @@ public boolean isSuccess() {
}
}

/**
* 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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,32 @@ class CompletableResultCodeTest {

@Test
void ofSuccess() {
assertThat(CompletableResultCode.ofSuccess().isSuccess()).isTrue();
assertThat(CompletableResultCode.ofSuccess())
.satisfies(
code -> {
assertThat(code.isSuccess()).isTrue();
assertThat(code.getFailureThrowable()).isNull();
});
}

@Test
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
Expand Down Expand Up @@ -149,6 +169,24 @@ void ofAllWithFailure() {
.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
void join() {
CompletableResultCode result = new CompletableResultCode();
Expand Down

0 comments on commit fc283ba

Please sign in to comment.