150150import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .CHAR_STAR ;
151151import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .CHAR_UNDERSCORE ;
152152import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .DIRECTORY ;
153+ import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .EMPTY_STRING ;
153154import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .FILE ;
154155import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .ROOT_PATH ;
155156import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .SINGLE_WHITE_SPACE ;
@@ -343,11 +344,13 @@ public void close() throws IOException {
343344 }
344345
345346 byte [] encodeAttribute (String value ) throws UnsupportedEncodingException {
346- return value .getBytes (XMS_PROPERTIES_ENCODING );
347+ // DFS Client works with ISO_8859_1 encoding, Blob Works with UTF-8.
348+ return getClient ().encodeAttribute (value );
347349 }
348350
349351 String decodeAttribute (byte [] value ) throws UnsupportedEncodingException {
350- return new String (value , XMS_PROPERTIES_ENCODING );
352+ // DFS Client works with ISO_8859_1 encoding, Blob Works with UTF-8.
353+ return getClient ().decodeAttribute (value );
351354 }
352355
353356 private String [] authorityParts (URI uri ) throws InvalidUriAuthorityException , InvalidUriException {
@@ -485,9 +488,8 @@ public Hashtable<String, String> getFilesystemProperties(
485488 .getFilesystemProperties (tracingContext );
486489 perfInfo .registerResult (op .getResult ());
487490
488- final String xMsProperties = op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_PROPERTIES );
489-
490- parsedXmsProperties = parseCommaSeparatedXmsProperties (xMsProperties );
491+ // Handling difference in request headers formats between DFS and Blob Clients.
492+ parsedXmsProperties = getClient ().getXMSProperties (op .getResult ());
491493 perfInfo .registerSuccess (true );
492494
493495 return parsedXmsProperties ;
@@ -533,10 +535,8 @@ public Hashtable<String, String> getPathStatus(final Path path,
533535 perfInfo .registerResult (op .getResult ());
534536 contextEncryptionAdapter .destroy ();
535537
536- final String xMsProperties = op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_PROPERTIES );
537-
538- parsedXmsProperties = parseCommaSeparatedXmsProperties (xMsProperties );
539-
538+ // Handling difference in request headers formats between DFS and Blob Clients.
539+ parsedXmsProperties = getClient ().getXMSProperties (op .getResult ());
540540 perfInfo .registerSuccess (true );
541541
542542 return parsedXmsProperties ;
@@ -899,10 +899,8 @@ public AbfsInputStream openFileForRead(Path path,
899899 } else {
900900 AbfsHttpOperation op = getClient ().getPathStatus (relativePath , false ,
901901 tracingContext , null ).getResult ();
902- resourceType = op .getResponseHeader (
903- HttpHeaderConfigurations .X_MS_RESOURCE_TYPE );
904- contentLength = Long .parseLong (
905- op .getResponseHeader (HttpHeaderConfigurations .CONTENT_LENGTH ));
902+ resourceType = getClient ().checkIsDir (op ) ? DIRECTORY : FILE ;
903+ contentLength = extractContentLength (op );
906904 eTag = op .getResponseHeader (HttpHeaderConfigurations .ETAG );
907905 /*
908906 * For file created with ENCRYPTION_CONTEXT, client shall receive
@@ -983,17 +981,15 @@ public OutputStream openFileForWrite(final Path path,
983981 .getPathStatus (relativePath , false , tracingContext , null );
984982 perfInfo .registerResult (op .getResult ());
985983
986- final String resourceType = op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_RESOURCE_TYPE );
987- final Long contentLength = Long .valueOf (op .getResult ().getResponseHeader (HttpHeaderConfigurations .CONTENT_LENGTH ));
988-
989- if (parseIsDirectory (resourceType )) {
984+ if (getClient ().checkIsDir (op .getResult ())) {
990985 throw new AbfsRestOperationException (
991986 AzureServiceErrorCode .PATH_NOT_FOUND .getStatusCode (),
992987 AzureServiceErrorCode .PATH_NOT_FOUND .getErrorCode (),
993- "openFileForRead must be used with files and not directories" ,
988+ "openFileForWrite must be used with files and not directories" ,
994989 null );
995990 }
996991
992+ final long contentLength = extractContentLength (op .getResult ());
997993 final long offset = overwrite ? 0 : contentLength ;
998994
999995 perfInfo .registerSuccess (true );
@@ -1180,8 +1176,8 @@ public FileStatus getFileStatus(final Path path,
11801176 contentLength = 0 ;
11811177 resourceIsDir = true ;
11821178 } else {
1183- contentLength = parseContentLength (result . getResponseHeader ( HttpHeaderConfigurations . CONTENT_LENGTH ) );
1184- resourceIsDir = parseIsDirectory ( result . getResponseHeader ( HttpHeaderConfigurations . X_MS_RESOURCE_TYPE ) );
1179+ contentLength = extractContentLength (result );
1180+ resourceIsDir = getClient (). checkIsDir ( result );
11851181 }
11861182
11871183 final String transformedOwner = identityTransformer .transformIdentityForGetRequest (
@@ -1256,10 +1252,16 @@ public String listStatus(final Path path, final String startFrom,
12561252 startFrom );
12571253
12581254 final String relativePath = getRelativePath (path );
1255+ AbfsClient listingClient = getClient ();
12591256
12601257 if (continuation == null || continuation .isEmpty ()) {
12611258 // generate continuation token if a valid startFrom is provided.
12621259 if (startFrom != null && !startFrom .isEmpty ()) {
1260+ /*
1261+ * Blob Endpoint Does not support startFrom yet. Fallback to DFS Client.
1262+ * startFrom remains null for all HDFS APIs. This is only for internal use.
1263+ */
1264+ listingClient = getClient (AbfsServiceType .DFS );
12631265 continuation = getIsNamespaceEnabled (tracingContext )
12641266 ? generateContinuationTokenForXns (startFrom )
12651267 : generateContinuationTokenForNonXns (relativePath , startFrom );
@@ -1268,11 +1270,11 @@ public String listStatus(final Path path, final String startFrom,
12681270
12691271 do {
12701272 try (AbfsPerfInfo perfInfo = startTracking ("listStatus" , "listPath" )) {
1271- AbfsRestOperation op = getClient () .listPath (relativePath , false ,
1273+ AbfsRestOperation op = listingClient .listPath (relativePath , false ,
12721274 abfsConfiguration .getListMaxResults (), continuation ,
12731275 tracingContext );
12741276 perfInfo .registerResult (op .getResult ());
1275- continuation = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . X_MS_CONTINUATION );
1277+ continuation = listingClient . getContinuationFromResponse ( op .getResult ());
12761278 ListResultSchema retrievedSchema = op .getResult ().getListResultSchema ();
12771279 if (retrievedSchema == null ) {
12781280 throw new AbfsRestOperationException (
@@ -1465,7 +1467,7 @@ public void modifyAclEntries(final Path path, final List<AclEntry> aclSpec,
14651467 final AbfsRestOperation op = getClient ()
14661468 .getAclStatus (relativePath , useUpn , tracingContext );
14671469 perfInfoGet .registerResult (op .getResult ());
1468- final String eTag = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . ETAG );
1470+ final String eTag = extractEtagHeader ( op .getResult ());
14691471
14701472 final Map <String , String > aclEntries = AbfsAclHelper .deserializeAclSpec (op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_ACL ));
14711473
@@ -1508,7 +1510,7 @@ public void removeAclEntries(final Path path, final List<AclEntry> aclSpec,
15081510 final AbfsRestOperation op = getClient ()
15091511 .getAclStatus (relativePath , isUpnFormat , tracingContext );
15101512 perfInfoGet .registerResult (op .getResult ());
1511- final String eTag = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . ETAG );
1513+ final String eTag = extractEtagHeader ( op .getResult ());
15121514
15131515 final Map <String , String > aclEntries = AbfsAclHelper .deserializeAclSpec (op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_ACL ));
15141516
@@ -1546,7 +1548,7 @@ public void removeDefaultAcl(final Path path, TracingContext tracingContext)
15461548 final AbfsRestOperation op = getClient ()
15471549 .getAclStatus (relativePath , tracingContext );
15481550 perfInfoGet .registerResult (op .getResult ());
1549- final String eTag = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . ETAG );
1551+ final String eTag = extractEtagHeader ( op .getResult ());
15501552 final Map <String , String > aclEntries = AbfsAclHelper .deserializeAclSpec (op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_ACL ));
15511553 final Map <String , String > defaultAclEntries = new HashMap <>();
15521554
@@ -1590,7 +1592,7 @@ public void removeAcl(final Path path, TracingContext tracingContext)
15901592 final AbfsRestOperation op = getClient ()
15911593 .getAclStatus (relativePath , tracingContext );
15921594 perfInfoGet .registerResult (op .getResult ());
1593- final String eTag = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . ETAG );
1595+ final String eTag = extractEtagHeader ( op .getResult ());
15941596
15951597 final Map <String , String > aclEntries = AbfsAclHelper .deserializeAclSpec (op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_ACL ));
15961598 final Map <String , String > newAclEntries = new HashMap <>();
@@ -1636,7 +1638,7 @@ public void setAcl(final Path path, final List<AclEntry> aclSpec,
16361638 final AbfsRestOperation op = getClient ()
16371639 .getAclStatus (relativePath , isUpnFormat , tracingContext );
16381640 perfInfoGet .registerResult (op .getResult ());
1639- final String eTag = op .getResult (). getResponseHeader ( HttpHeaderConfigurations . ETAG );
1641+ final String eTag = extractEtagHeader ( op .getResult ());
16401642
16411643 final Map <String , String > getAclEntries = AbfsAclHelper .deserializeAclSpec (op .getResult ().getResponseHeader (HttpHeaderConfigurations .X_MS_ACL ));
16421644
@@ -1859,12 +1861,24 @@ public String getRelativePath(final Path path) {
18591861 return relPath ;
18601862 }
18611863
1862- private long parseContentLength (final String contentLength ) {
1863- if (contentLength == null ) {
1864- return -1 ;
1864+ /**
1865+ * Extracts the content length from the HTTP operation's response headers.
1866+ *
1867+ * @param op The AbfsHttpOperation instance from which to extract the content length.
1868+ * This operation contains the HTTP response headers.
1869+ * @return The content length as a long value. If the Content-Length header is
1870+ * not present or is empty, returns 0.
1871+ */
1872+ private long extractContentLength (AbfsHttpOperation op ) {
1873+ long contentLength ;
1874+ String contentLengthHeader = op .getResponseHeader (
1875+ HttpHeaderConfigurations .CONTENT_LENGTH );
1876+ if (!contentLengthHeader .equals (EMPTY_STRING )) {
1877+ contentLength = Long .parseLong (contentLengthHeader );
1878+ } else {
1879+ contentLength = 0 ;
18651880 }
1866-
1867- return Long .parseLong (contentLength );
1881+ return contentLength ;
18681882 }
18691883
18701884 private boolean parseIsDirectory (final String resourceType ) {
0 commit comments