Class FallbackResolver

java.lang.Object
com.tnsai.resilience.FallbackResolver

public final class FallbackResolver extends Object
Resolves the @Fallback annotation on a Role's methods and invokes the matching handler when an action's primary execution fails. Issue #173, Phase 1.

How @Fallback works in this codebase

Unlike @Traced / @Metered (which wrap the action's own dispatch), @Fallback marks a separate method on the same Role class as a recovery handler:

 @ActionSpec(type = ActionType.WEB_SERVICE, ...)
 public PaymentResult charge(String requestId, Money amount) {
     return paymentApi.charge(requestId, amount);
 }

 @Fallback(forAction = "charge", maxRetries = 0)
 public PaymentResult chargeFallback(String requestId, Money amount, Exception error) {
     return PaymentResult.queued(requestId);
 }
 

Resolution

Per-Role-class scan, cached in a ConcurrentHashMap keyed by Role class so the reflection cost is paid once per JVM. For a failing action A, the resolver looks for:
  1. A method with @Fallback(forAction = "A"), OR
  2. A method with @Fallback (no forAction) — the global fallback for any unhandled action failure on this role.

Method-named fallbacks win over the global one. Multiple @Fallback(forAction = "A") on the same class is a config error logged at first lookup; the resolver picks the first one encountered for stability.

Argument binding

The fallback method is invoked with the original action's working parameters in declaration order (matched by name via
invalid reference
ActionMetadata#getParameters()
), followed by the captured Throwable as the last argument. Phase 1 supports the two canonical signatures:
  • fallback(originalArgs..., Exception error)
  • fallback(originalArgs..., Throwable error)

Other parameter shapes are tolerated by best-effort positional binding (extras get null); fail-fast validation lands in Phase 2.

maxRetries

Phase 1 honours Fallback.maxRetries() with simple immediate retries (no exponential backoff — that's Phase 2). Each retry re-executes the failed primary action via the supplied retryCallback. The fallback method itself is invoked only once, after the final retry fails.
Since:
0.5.4 (issue #173, Phase 1)