Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public CircuitBreaker getCircuitBreaker( @Nonnull final ResilienceConfiguration
return circuitBreaker;
}

@SuppressWarnings( "PMD.PreserveStackTrace" ) // The circuit breaker stack-trace doesn't contain any info
@Nonnull
@Override
public <T> Callable<T> decorateCallable(
Expand All @@ -83,9 +82,19 @@ public <T> Callable<T> decorateCallable(
return CircuitBreaker.decorateCallable(circuitBreaker, callable).call();
}
catch( CallNotPermittedException e ) {
log.debug("Circuit breaker '{}' is open, call not permitted.", circuitBreaker.getName());
val message =
"CircuitBreaker '" + circuitBreaker.getName() + "' is OPEN and does not permit further calls";
log.debug(message);
val lastException = lastExceptions.get(circuitBreaker.getName());
throw new ResilienceRuntimeException(lastException != null ? lastException : e);
if( lastException == null ) {
throw new ResilienceRuntimeException(message, e);
}
val resilienceRuntimeException =
new ResilienceRuntimeException(
message + ". Triggered by " + lastException.getMessage(),
lastException);
resilienceRuntimeException.addSuppressed(e);
throw resilienceRuntimeException;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand All @@ -21,6 +22,7 @@
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceRuntimeException;
import com.sap.cloud.sdk.cloudplatform.thread.exception.ThreadContextExecutionException;

import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand Down Expand Up @@ -76,12 +78,22 @@ void testCircuitBreakerOpens()
assertThat(callResults).hasSize(attemptedInvocations);
assertThat(callResults).startsWith(attempts.toArray(new Boolean[0]));

assertThatThrownBy(wrappedCallable::call)
Throwable thrown = catchThrowable(wrappedCallable::call);

assertThat(thrown)
.isExactlyInstanceOf(ResilienceRuntimeException.class)
.hasCauseExactlyInstanceOf(ThreadContextExecutionException.class)
.hasRootCauseExactlyInstanceOf(Exception.class)
.hasMessage(
"com.sap.cloud.sdk.cloudplatform.thread.exception.ThreadContextExecutionException: java.lang.Exception: Simulated failure, attempt nr: 3");
"CircuitBreaker 'circuitbreaker.test.2' is OPEN and does not permit further calls. Triggered by java.lang.Exception: Simulated failure, attempt nr: 3");

assertThat(thrown.getCause()).hasMessage("java.lang.Exception: Simulated failure, attempt nr: 3");

assertThat(thrown.getSuppressed().length).isEqualTo(1);
Throwable suppressed = thrown.getSuppressed()[0];
assertThat(suppressed)
.isExactlyInstanceOf(CallNotPermittedException.class)
.hasMessage("CircuitBreaker 'circuitbreaker.test.2' is OPEN and does not permit further calls");

verify(callable, times(circuitBreakerConfiguration.closedBufferSize())).call();
}
Expand Down
2 changes: 1 addition & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

### 📈 Improvements

-
- When the circuit breaker opens, the resulting `ResilienceRuntimeException` will have the original `CallNotPermittedException` from the circuit breaker stored as a suppressed exception.

### 🐛 Fixed Issues

Expand Down