From 273cc2d4e972dc7cc371761a4ab2d1bc20dd5ca0 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Fri, 5 Oct 2018 11:29:03 +0100 Subject: [PATCH] HADOOP-15809. ABFS: better exception handling when making getAccessToken call. Contributed by Da Zhou --- .../AbfsRestOperationException.java | 9 +++++ .../azurebfs/oauth2/AzureADAuthenticator.java | 35 +++++++++++-------- .../azurebfs/services/AbfsRestOperation.java | 9 +++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/AbfsRestOperationException.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/AbfsRestOperationException.java index f0b69ef91d..149f916c0a 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/AbfsRestOperationException.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/AbfsRestOperationException.java @@ -22,6 +22,7 @@ import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode; +import org.apache.hadoop.fs.azurebfs.oauth2.AzureADAuthenticator.HttpException; import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation; /** @@ -59,6 +60,14 @@ public AbfsRestOperationException( this.errorMessage = errorMessage; } + public AbfsRestOperationException(final HttpException innerException) { + super(innerException.getMessage()); + + this.statusCode = innerException.getHttpErrorCode(); + this.errorCode = AzureServiceErrorCode.UNKNOWN; + this.errorMessage = innerException.getMessage(); + } + public int getStatusCode() { return this.statusCode; } diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/oauth2/AzureADAuthenticator.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/oauth2/AzureADAuthenticator.java index e82dc954f5..97415ce712 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/oauth2/AzureADAuthenticator.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/oauth2/AzureADAuthenticator.java @@ -161,14 +161,28 @@ public static AzureADToken getTokenUsingRefreshToken(String clientId, return getTokenCall(authEndpoint, qp.serialize(), null, null); } - private static class HttpException extends IOException { + + /** + * This exception class contains the http error code, + * requestId and error message, it is thrown when AzureADAuthenticator + * failed to get the Azure Active Directory token. + */ + public static class HttpException extends IOException { private int httpErrorCode; private String requestId; + /** + * Gets Http error status code. + * @return http error code. + */ public int getHttpErrorCode() { return this.httpErrorCode; } + /** + * Gets http request id . + * @return http request id. + */ public String getRequestId() { return this.requestId; } @@ -188,21 +202,17 @@ private static AzureADToken getTokenCall(String authEndpoint, String body, = new ExponentialRetryPolicy(3, 0, 1000, 2); int httperror = 0; - String requestId; - String httpExceptionMessage = null; IOException ex = null; boolean succeeded = false; int retryCount = 0; do { httperror = 0; - requestId = ""; ex = null; try { token = getTokenSingleCall(authEndpoint, body, headers, httpMethod); } catch (HttpException e) { httperror = e.httpErrorCode; - requestId = e.requestId; - httpExceptionMessage = e.getMessage(); + ex = e; } catch (IOException e) { ex = e; } @@ -210,12 +220,7 @@ private static AzureADToken getTokenCall(String authEndpoint, String body, retryCount++; } while (!succeeded && retryPolicy.shouldRetry(retryCount, httperror)); if (!succeeded) { - if (ex != null) { - throw ex; - } - if (httperror != 0) { - throw new IOException(httpExceptionMessage); - } + throw ex; } return token; } @@ -263,7 +268,7 @@ private static AzureADToken getTokenSingleCall( InputStream httpResponseStream = conn.getInputStream(); token = parseTokenFromStream(httpResponseStream); } else { - String responseBody = consumeInputStream(conn.getInputStream(), 1024); + String responseBody = consumeInputStream(conn.getErrorStream(), 1024); String proxies = "none"; String httpProxy = System.getProperty("http.proxy"); String httpsProxy = System.getProperty("https.proxy"); @@ -273,11 +278,11 @@ private static AzureADToken getTokenSingleCall( String logMessage = "AADToken: HTTP connection failed for getting token from AzureAD. Http response: " + httpResponseCode + " " + conn.getResponseMessage() - + " Content-Type: " + responseContentType + + "\nContent-Type: " + responseContentType + " Content-Length: " + responseContentLength + " Request ID: " + requestId.toString() + " Proxies: " + proxies - + " First 1K of Body: " + responseBody; + + "\nFirst 1K of Body: " + responseBody; LOG.debug(logMessage); throw new HttpException(httpResponseCode, requestId, logMessage); } diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsRestOperation.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsRestOperation.java index 3f5717ee7e..9c60f7c384 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsRestOperation.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsRestOperation.java @@ -31,6 +31,7 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException; import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidAbfsRestOperationException; import org.apache.hadoop.fs.azurebfs.constants.HttpHeaderConfigurations; +import org.apache.hadoop.fs.azurebfs.oauth2.AzureADAuthenticator.HttpException; /** * The AbfsRestOperation for Rest AbfsClient. @@ -175,6 +176,14 @@ private boolean executeHttpOperation(final int retryCount) throws AzureBlobFileS if (!client.getRetryPolicy().shouldRetry(retryCount, -1)) { throw new InvalidAbfsRestOperationException(ex); } + + // once HttpException is thrown by AzureADAuthenticator, + // it indicates the policy in AzureADAuthenticator determined + // retry is not needed + if (ex instanceof HttpException) { + throw new AbfsRestOperationException((HttpException) ex); + } + return false; } finally { AbfsClientThrottlingIntercept.updateMetrics(operationType, httpOperation);