Core foundational library for AWS SDK for Java 1.x providing authentication, HTTP transport, regions, protocols, and shared utilities for all AWS service clients
—
The AWS Java SDK Core provides a comprehensive exception hierarchy for handling different types of errors that can occur during AWS service interactions. Understanding this hierarchy is crucial for proper error handling and application resilience.
// Base SDK exception class (extends RuntimeException)
class SdkBaseException extends RuntimeException {
public SdkBaseException(String message);
public SdkBaseException(String message, Throwable cause);
public boolean isRetryable();
}
// Client-side exception (network, configuration, etc.)
class SdkClientException extends SdkBaseException {
public SdkClientException(String message);
public SdkClientException(String message, Throwable cause);
public boolean isRetryable();
}
// Legacy client exception (for backward compatibility)
class AmazonClientException extends SdkBaseException {
public AmazonClientException(String message);
public AmazonClientException(String message, Throwable cause);
public AmazonClientException(Throwable cause);
public boolean isRetryable();
}// Service-side exception (4xx/5xx HTTP responses)
class AmazonServiceException extends SdkClientException {
// Error details
public String getErrorCode();
public String getErrorMessage();
public String getErrorType();
public int getStatusCode();
public String getServiceName();
public String getRequestId();
// Request details
public String getRawResponseContent();
public HttpResponse getRawResponse();
public Map<String, String> getHttpHeaders();
public String getProxyHost();
// Error classification
public ErrorType getErrorType();
public boolean isRetryable();
// Setters for error details
public void setErrorCode(String errorCode);
public void setErrorMessage(String errorMessage);
public void setErrorType(String errorType);
public void setStatusCode(int statusCode);
public void setServiceName(String serviceName);
public void setRequestId(String requestId);
}
// Error type enumeration
enum ErrorType {
Client, // 4xx errors - client-side issues
Service, // 5xx errors - server-side issues
Unknown // Unclassified errors
}// Operation aborted exception
class AbortedException extends SdkClientException {
public AbortedException();
public AbortedException(String message);
public AbortedException(String message, Throwable cause);
}
// Stream reset exception
class ResetException extends SdkClientException {
public ResetException(String message);
public ResetException(String message, Throwable cause);
public String getExtraInfo();
public void setExtraInfo(String extraInfo);
}
// Connection timeout exception
class SdkInterruptedException extends SdkClientException {
public SdkInterruptedException(Throwable cause);
public SdkInterruptedException(String message, Throwable cause);
}import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
public void handleS3Operations(AmazonS3 s3Client, String bucket, String key) {
try {
// Perform S3 operation
s3Client.getObject(new GetObjectRequest(bucket, key));
} catch (AmazonServiceException ase) {
// Service-side error (4xx/5xx HTTP status codes)
System.err.println("Service Error:");
System.err.println(" Error Code: " + ase.getErrorCode());
System.err.println(" Error Message: " + ase.getErrorMessage());
System.err.println(" HTTP Status: " + ase.getStatusCode());
System.err.println(" Service: " + ase.getServiceName());
System.err.println(" Request ID: " + ase.getRequestId());
// Handle specific service errors
handleServiceError(ase);
} catch (SdkClientException ace) {
// Client-side error (network issues, configuration problems, etc.)
System.err.println("Client Error: " + ace.getMessage());
// Handle client errors
handleClientError(ace);
} catch (Exception e) {
// Unexpected errors
System.err.println("Unexpected error: " + e.getMessage());
throw new RuntimeException("Operation failed", e);
}
}private void handleServiceError(AmazonServiceException ase) {
// Handle errors by HTTP status code
switch (ase.getStatusCode()) {
case 400: // Bad Request
handleBadRequestError(ase);
break;
case 403: // Forbidden
handlePermissionError(ase);
break;
case 404: // Not Found
handleNotFoundError(ase);
break;
case 429: // Too Many Requests
handleThrottlingError(ase);
break;
case 500: // Internal Server Error
case 502: // Bad Gateway
case 503: // Service Unavailable
handleServerError(ase);
break;
default:
handleUnknownServiceError(ase);
}
// Handle errors by error code (service-specific)
String errorCode = ase.getErrorCode();
switch (errorCode) {
case "NoSuchBucket":
System.err.println("S3 bucket does not exist");
break;
case "NoSuchKey":
System.err.println("S3 object does not exist");
break;
case "AccessDenied":
System.err.println("Access denied - check permissions");
break;
case "InvalidBucketName":
System.err.println("Invalid S3 bucket name");
break;
case "ServiceUnavailable":
System.err.println("Service temporarily unavailable");
break;
default:
System.err.println("Service error: " + errorCode);
}
}private void handleClientError(SdkClientException ace) {
// Check for specific client error types
if (ace instanceof AbortedException) {
System.err.println("Operation was aborted");
// Handle abortion scenario
} else if (ace instanceof ResetException) {
ResetException re = (ResetException) ace;
System.err.println("Connection reset: " + re.getExtraInfo());
// Handle connection reset
} else if (ace instanceof SdkInterruptedException) {
System.err.println("Operation was interrupted");
// Handle interruption
} else {
// Generic client error handling
String message = ace.getMessage();
if (message.contains("UnknownHostException")) {
System.err.println("Network connectivity issue - check DNS/Internet connection");
} else if (message.contains("ConnectTimeoutException")) {
System.err.println("Connection timeout - service may be unavailable");
} else if (message.contains("SocketTimeoutException")) {
System.err.println("Socket timeout - operation took too long");
} else if (message.contains("SSLException")) {
System.err.println("SSL/TLS error - check certificates and protocol");
} else {
System.err.println("Client configuration or network error: " + message);
}
}
}import com.amazonaws.retry.RetryUtils;
import java.util.concurrent.TimeUnit;
public class ResilientAwsOperations {
private static final int MAX_RETRIES = 3;
private static final long INITIAL_BACKOFF_MS = 1000;
public <T> T executeWithRetry(Supplier<T> operation) {
int attempt = 0;
Exception lastException = null;
while (attempt < MAX_RETRIES) {
try {
return operation.get();
} catch (AmazonServiceException ase) {
lastException = ase;
// Check if error is retryable
if (shouldRetryServiceException(ase)) {
attempt++;
if (attempt < MAX_RETRIES) {
waitWithBackoff(attempt);
continue;
}
}
// Non-retryable service error
throw ase;
} catch (SdkClientException ace) {
lastException = ace;
// Check if client error is retryable
if (shouldRetryClientException(ace)) {
attempt++;
if (attempt < MAX_RETRIES) {
waitWithBackoff(attempt);
continue;
}
}
// Non-retryable client error
throw ace;
}
}
// Max retries exceeded
throw new RuntimeException("Max retries exceeded", lastException);
}
private boolean shouldRetryServiceException(AmazonServiceException ase) {
// Retry on server errors (5xx)
if (ase.getStatusCode() >= 500) {
return true;
}
// Retry on throttling (429)
if (ase.getStatusCode() == 429) {
return true;
}
// Retry on specific error codes
String errorCode = ase.getErrorCode();
return "ServiceUnavailable".equals(errorCode) ||
"Throttling".equals(errorCode) ||
"ThrottlingException".equals(errorCode) ||
"RequestTimeout".equals(errorCode);
}
private boolean shouldRetryClientException(SdkClientException ace) {
String message = ace.getMessage().toLowerCase();
// Retry on network-related errors
return message.contains("connecttimeoutexception") ||
message.contains("sockettimeoutexception") ||
message.contains("connection reset") ||
message.contains("connection refused") ||
ace instanceof ResetException;
}
private void waitWithBackoff(int attempt) {
long backoffMs = INITIAL_BACKOFF_MS * (1L << (attempt - 1)); // Exponential backoff
try {
TimeUnit.MILLISECONDS.sleep(backoffMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SdkInterruptedException("Interrupted during backoff", e);
}
}
}ResilientAwsOperations resilientOps = new ResilientAwsOperations();
// Retry S3 operations
S3Object result = resilientOps.executeWithRetry(() ->
s3Client.getObject("my-bucket", "my-key")
);
// Retry DynamoDB operations
GetItemResult dynamoResult = resilientOps.executeWithRetry(() ->
dynamoClient.getItem("my-table", key)
);import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class AwsErrorLogger {
private static final Logger logger = LoggerFactory.getLogger(AwsErrorLogger.class);
public static void logServiceException(AmazonServiceException ase, String operation) {
try {
// Add context to MDC
MDC.put("aws.service", ase.getServiceName());
MDC.put("aws.errorCode", ase.getErrorCode());
MDC.put("aws.requestId", ase.getRequestId());
MDC.put("aws.statusCode", String.valueOf(ase.getStatusCode()));
MDC.put("operation", operation);
// Log based on error severity
if (ase.getStatusCode() >= 500) {
logger.error("AWS service error: {} - {}", ase.getErrorCode(), ase.getErrorMessage(), ase);
} else {
logger.warn("AWS client error: {} - {}", ase.getErrorCode(), ase.getErrorMessage());
}
} finally {
MDC.clear();
}
}
public static void logClientException(SdkClientException ace, String operation) {
try {
MDC.put("operation", operation);
MDC.put("exceptionType", ace.getClass().getSimpleName());
logger.error("AWS client error during {}: {}", operation, ace.getMessage(), ace);
} finally {
MDC.clear();
}
}
}import com.amazonaws.metrics.RequestMetricCollector;
import java.util.concurrent.atomic.AtomicLong;
public class ErrorMetricsCollector extends RequestMetricCollector {
private final AtomicLong clientErrors = new AtomicLong();
private final AtomicLong serviceErrors = new AtomicLong();
private final AtomicLong throttlingErrors = new AtomicLong();
@Override
public void collectMetrics(Request<?> request, Response<?> response, Exception exception) {
if (exception instanceof AmazonServiceException) {
AmazonServiceException ase = (AmazonServiceException) exception;
serviceErrors.incrementAndGet();
if (ase.getStatusCode() == 429 || "Throttling".equals(ase.getErrorCode())) {
throttlingErrors.incrementAndGet();
}
// Log metrics
logger.info("Service error metrics - Total: {}, Throttling: {}",
serviceErrors.get(), throttlingErrors.get());
} else if (exception instanceof SdkClientException) {
clientErrors.incrementAndGet();
logger.info("Client error metrics - Total: {}", clientErrors.get());
}
}
// Expose metrics for monitoring systems
public long getClientErrorCount() { return clientErrors.get(); }
public long getServiceErrorCount() { return serviceErrors.get(); }
public long getThrottlingErrorCount() { return throttlingErrors.get(); }
}public class AwsCircuitBreaker {
private enum State { CLOSED, OPEN, HALF_OPEN }
private volatile State state = State.CLOSED;
private volatile long lastFailureTime;
private volatile int failureCount;
private final int failureThreshold;
private final long timeoutMs;
public AwsCircuitBreaker(int failureThreshold, long timeoutMs) {
this.failureThreshold = failureThreshold;
this.timeoutMs = timeoutMs;
}
public <T> T execute(Supplier<T> operation) throws Exception {
if (state == State.OPEN) {
if (System.currentTimeMillis() - lastFailureTime > timeoutMs) {
state = State.HALF_OPEN;
} else {
throw new RuntimeException("Circuit breaker is OPEN");
}
}
try {
T result = operation.get();
onSuccess();
return result;
} catch (Exception e) {
onFailure();
throw e;
}
}
private void onSuccess() {
failureCount = 0;
state = State.CLOSED;
}
private void onFailure() {
failureCount++;
lastFailureTime = System.currentTimeMillis();
if (failureCount >= failureThreshold) {
state = State.OPEN;
}
}
}public class FallbackAwsOperations {
private final AmazonS3 primaryS3Client;
private final AmazonS3 fallbackS3Client;
public FallbackAwsOperations(AmazonS3 primary, AmazonS3 fallback) {
this.primaryS3Client = primary;
this.fallbackS3Client = fallback;
}
public S3Object getObjectWithFallback(String bucket, String key) {
try {
return primaryS3Client.getObject(bucket, key);
} catch (AmazonServiceException ase) {
if (ase.getStatusCode() >= 500) {
logger.warn("Primary S3 service error, trying fallback: {}", ase.getErrorCode());
return fallbackS3Client.getObject(bucket, key);
}
throw ase;
} catch (SdkClientException ace) {
logger.warn("Primary S3 client error, trying fallback: {}", ace.getMessage());
return fallbackS3Client.getObject(bucket, key);
}
}
}AmazonServiceException before SdkClientExceptionThe AWS Java SDK's exception hierarchy provides the foundation for building resilient applications that can gracefully handle the various error conditions that occur in distributed cloud environments.
Install with Tessl CLI
npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-core