Build fault-tolerant network services
—
Alternative execution paths when primary methods fail, including custom fallback handlers, fallback methods, and exception-based fallback logic.
Fallback to alternative methods in the same class with matching signatures.
@Fallback(
fallbackMethod = "alternativeMethod",
applyOn = {IOException.class, TimeoutException.class},
skipOn = {IllegalArgumentException.class}
)
public ReturnType methodWithFallback(ParameterType param);
// Fallback method must have matching signature
public ReturnType alternativeMethod(ParameterType param);fallbackMethod - Name of fallback method in same classapplyOn - Exception types that trigger fallback (default: Throwable.class)skipOn - Exception types that skip fallback (takes precedence)@ApplicationScoped
public class UserService {
@Inject
ExternalUserApi externalApi;
@Inject
UserCache cache;
// Primary method with cached fallback
@Fallback(
fallbackMethod = "getUserFromCache",
applyOn = {ConnectException.class, TimeoutException.class}
)
@Timeout(5000)
public User getUser(Long userId) throws UserNotFoundException {
return externalApi.fetchUser(userId);
}
public User getUserFromCache(Long userId) throws UserNotFoundException {
User cached = cache.get(userId);
if (cached != null) {
return cached;
}
throw new UserNotFoundException("User not found: " + userId);
}
// Order service with default fallback
@Fallback(fallbackMethod = "getDefaultOrderStatus")
public OrderStatus getOrderStatus(String orderId) throws ServiceException {
return orderServiceClient.getStatus(orderId);
}
public OrderStatus getDefaultOrderStatus(String orderId) {
return OrderStatus.PENDING; // Safe default
}
}Custom fallback handlers for complex fallback logic and cross-cutting concerns.
@Fallback(
value = CustomFallbackHandler.class,
applyOn = {IOException.class},
skipOn = {SecurityException.class}
)
public ReturnType methodWithHandler(ParameterType param);
// Fallback handler implementation
class CustomFallbackHandler implements FallbackHandler<ReturnType> {
public ReturnType handle(ExecutionContext context);
}@ApplicationScoped
public class PaymentService {
// Payment with sophisticated fallback handler
@Fallback(value = PaymentFallbackHandler.class)
@CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.4)
public PaymentResult processPayment(PaymentRequest request) throws PaymentException {
return primaryPaymentGateway.process(request);
}
}
// Sophisticated fallback handler
public class PaymentFallbackHandler implements FallbackHandler<PaymentResult> {
@Inject
SecondaryPaymentGateway secondaryGateway;
@Inject
PaymentQueue paymentQueue;
@Inject
NotificationService notifications;
@Override
public PaymentResult handle(ExecutionContext context) {
PaymentRequest request = (PaymentRequest) context.getParameters()[0];
Throwable failure = context.getFailure();
// Try secondary payment gateway first
try {
return secondaryGateway.process(request);
} catch (Exception e) {
// Queue for later processing
paymentQueue.enqueue(request);
// Notify customer
notifications.notifyPaymentDelayed(request.getCustomerId());
return PaymentResult.queued(request.getTransactionId());
}
}
}Fallback logic based on specific exception types and conditions.
@Fallback(
fallbackMethod = "specificFallback",
applyOn = {ServiceUnavailableException.class, TimeoutException.class},
skipOn = {ValidationException.class, SecurityException.class}
)
public ReturnType conditionalFallbackMethod();@ApplicationScoped
public class NotificationService {
@Inject
EmailService emailService;
@Inject
SmsService smsService;
@Inject
PushNotificationService pushService;
// Email with SMS fallback for delivery issues
@Fallback(
fallbackMethod = "sendViaSms",
applyOn = {MailServerException.class, DeliveryException.class},
skipOn = {InvalidEmailException.class}
)
public NotificationResult sendEmail(String recipient, String message) {
return emailService.send(recipient, message);
}
public NotificationResult sendViaSms(String recipient, String message) {
// Convert email recipient to phone number if possible
String phoneNumber = contactService.getPhoneNumber(recipient);
if (phoneNumber != null) {
return smsService.send(phoneNumber, message);
}
return NotificationResult.failed("No SMS fallback available");
}
// Push notification with multiple fallbacks
@Fallback(
fallbackMethod = "sendViaEmail",
applyOn = {PushServiceException.class}
)
public NotificationResult sendPushNotification(String userId, String message) {
return pushService.send(userId, message);
}
public NotificationResult sendViaEmail(String userId, String message) {
String email = userService.getEmail(userId);
try {
return sendEmail(email, message); // This has its own fallback
} catch (Exception e) {
return NotificationResult.failed("All notification methods failed");
}
}
}Combined fallback and circuit breaker patterns for comprehensive resilience.
@CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5)
@Fallback(fallbackMethod = "circuitBreakerFallback")
@Timeout(5000)
public ReturnType resilientMethod();@ApplicationScoped
public class CatalogService {
@Inject
ExternalCatalogApi externalApi;
@Inject
CatalogCache cache;
@Inject
DefaultCatalogProvider defaultProvider;
// Product catalog with circuit breaker and cached fallback
@CircuitBreaker(
requestVolumeThreshold = 15,
failureRatio = 0.3,
delay = 30000
)
@Fallback(fallbackMethod = "getCachedCatalog")
@Timeout(8000)
public ProductCatalog getProductCatalog(String category) throws CatalogException {
return externalApi.getCatalog(category);
}
public ProductCatalog getCachedCatalog(String category) throws CatalogException {
// Try cache first
ProductCatalog cached = cache.get(category);
if (cached != null && !cached.isExpired()) {
return cached.withStaleIndicator();
}
// Fall back to default catalog
return defaultProvider.getDefaultCatalog(category);
}
}// Execution context for fallback handlers
interface ExecutionContext {
Method getMethod();
Object[] getParameters();
Object getTarget();
Throwable getFailure();
Map<String, Object> getContextData();
}
// Fallback handler interface
interface FallbackHandler<T> {
/**
* Handle fallback execution
* @param context Execution context with method and failure information
* @return Fallback result
*/
T handle(ExecutionContext context);
}
// Method information
interface Method {
String getName();
Class<?>[] getParameterTypes();
Class<?> getReturnType();
Class<?> getDeclaringClass();
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-smallrye-fault-tolerance