Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completable result code throwable #6348

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,2 +1,7 @@
Comparing source compatibility of against
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)
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
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