Skip to content

Commit 82193b6

Browse files
authored
Nio Walk File Tree (Azure#27253)
* Wrote tests * Added recordings * CI fixes * Pr feedback * PR feedback * Updated readme samples * CI fix * Ci fixes * Pr feedback * woops * Updated recording file
1 parent 18a60d9 commit 82193b6

21 files changed

+3290
-424
lines changed

eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,16 @@
719719
<Bug pattern="NM_CONFUSING"/>
720720
</Match>
721721

722+
<!-- Returning null is correct in these cases as it represents the not-applicable case -->
723+
<Match>
724+
<Class name="com.azure.storage.blob.nio.AzureBlobFileAttributes"/>
725+
<Or>
726+
<Method name="isServerEncrypted"/>
727+
<Method name="isAccessTierInferred"/>
728+
</Or>
729+
<Bug pattern="NP_BOOLEAN_RETURN_NULL"/>
730+
</Match>
731+
722732
<!-- Incorrect flagging, there is already switchIfEmpty operator applied in the upstream to throw error on empty -->
723733
<Match>
724734
<Class name="com.azure.core.util.polling.DefaultSyncPoller"/>
@@ -2174,7 +2184,7 @@
21742184
UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
21752185
UWF_UNWRITTEN_FIELD"/>
21762186
</Match>
2177-
2187+
21782188
<!-- Exclude AAP source code types from spotbug -->
21792189
<Match>
21802190
<Class name="~com\.azure\.cosmos\.encryption\.implementation\.mdesrc(.+)"/>

sdk/storage/azure-storage-blob-nio/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
## 12.0.0-beta.17 (Unreleased)
44

55
### Features Added
6+
- Enabled support for Files.exists()
7+
- Enabled support for Files.walkFileTree()
68

79
### Breaking Changes
10+
- `AzureFileSystemProvider.readAttributes()` no longer throws an IOException for virtual directories and instead returns a set of attributes that are all empty except for an `isVirtual` property set to true.
811

912
### Bugs Fixed
1013

1114
### Other Changes
15+
- Enabling support for Files.exists() to support virtual directories required supporting virtual directories in reading file attributes. This required introducing a perf hit in the way of an extra getProps request
1216

1317
## 12.0.0-beta.16 (2022-02-11)
1418

sdk/storage/azure-storage-blob-nio/src/main/java/com/azure/storage/blob/nio/AzureBasicFileAttributes.java

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
package com.azure.storage.blob.nio;
55

66
import com.azure.core.util.logging.ClientLogger;
7-
import com.azure.storage.blob.models.BlobProperties;
8-
import com.azure.storage.blob.models.BlobStorageException;
97

108
import java.io.IOException;
119
import java.nio.file.Path;
@@ -24,6 +22,8 @@
2422
* {@link AzureBlobFileAttributes} is generally preferred.
2523
* <p>
2624
* Some attributes are not supported. Refer to the javadocs on each method for more information.
25+
* <p>
26+
* If the target file is a virtual directory, most attributes will be set to null.
2727
*/
2828
public final class AzureBasicFileAttributes implements BasicFileAttributes {
2929
private final ClientLogger logger = new ClientLogger(AzureBasicFileAttributes.class);
@@ -35,64 +35,60 @@ public final class AzureBasicFileAttributes implements BasicFileAttributes {
3535
set.add("lastModifiedTime");
3636
set.add("isRegularFile");
3737
set.add("isDirectory");
38+
set.add("isVirtualDirectory");
3839
set.add("isSymbolicLink");
3940
set.add("isOther");
4041
set.add("size");
4142
set.add("creationTime");
4243
ATTRIBUTE_STRINGS = Collections.unmodifiableSet(set);
4344
}
4445

45-
private final BlobProperties properties;
46-
private final AzureResource resource;
46+
private final AzureBlobFileAttributes internalAttributes;
4747

4848
/*
49-
There are some work-arounds we could do to try to accommodate virtual directories such as making a checkDirStatus
50-
call before or after getProperties to throw an appropriate error or adding an isVirtualDirectory method. However,
51-
the former wastes network time only to throw a slightly more specific error when we will throw on 404 anyway. The
52-
latter introduces virtual directories into the actual code path/api surface. While we are clear in our docs about
53-
the possible pitfalls of virtual directories, and customers should be aware of it, they shouldn't have to code
54-
against it. Therefore, we fall back to documenting that reading attributes on a virtual directory will throw.
49+
In order to support Files.exist() and other methods like Files.walkFileTree() which depend on it, we have had to add
50+
support for virtual directories. This is not ideal as customers will have to now perform null checks when inspecting
51+
attributes (or at least check if it is a virtual directory before inspecting properties). It also incurs extra
52+
network requests as we have to call a checkDirectoryExists() after receiving the initial 404. This is two
53+
additional network requests, though they only happen in the case when a file doesn't exist or is virtual, so it
54+
shouldn't happen in the majority of api calls.
5555
*/
5656
AzureBasicFileAttributes(Path path) throws IOException {
57-
try {
58-
this.resource = new AzureResource(path);
59-
this.properties = resource.getBlobClient().getProperties();
60-
} catch (BlobStorageException e) {
61-
throw LoggingUtility.logError(logger, new IOException(e));
62-
}
57+
this.internalAttributes = new AzureBlobFileAttributes(path);
6358
}
6459

6560
/**
66-
* Returns the time of last modification.
61+
* Returns the time of last modification or null if this is a virtual directory.
6762
*
68-
* @return the time of last modification.
63+
* @return the time of last modification or null if this is a virtual directory
6964
*/
7065
@Override
7166
public FileTime lastModifiedTime() {
72-
return FileTime.from(properties.getLastModified().toInstant());
67+
return this.internalAttributes.lastModifiedTime();
7368
}
7469

7570
/**
76-
* Returns the time of last modification.
71+
* Returns the time of last modification or null if this is a virtual directory
7772
* <p>
7873
* Last access time is not supported by the blob service. In this case, it is typical for implementations to return
7974
* the {@link #lastModifiedTime()}.
8075
*
81-
* @return the time of last modification.
76+
* @return the time of last modification or null if this is a virtual directory
8277
*/
8378
@Override
8479
public FileTime lastAccessTime() {
85-
return this.lastModifiedTime();
80+
return this.internalAttributes.lastAccessTime();
8681
}
8782

8883
/**
89-
* Returns the creation time. The creation time is the time that the file was created.
84+
* Returns the creation time. The creation time is the time that the file was created. Returns null if this is a
85+
* virtual directory.
9086
*
91-
* @return The creation time.
87+
* @return The creation time or null if this is a virtual directory
9288
*/
9389
@Override
9490
public FileTime creationTime() {
95-
return FileTime.from(properties.getCreationTime().toInstant());
91+
return this.internalAttributes.creationTime();
9692
}
9793

9894
/**
@@ -102,7 +98,7 @@ public FileTime creationTime() {
10298
*/
10399
@Override
104100
public boolean isRegularFile() {
105-
return !this.properties.getMetadata().getOrDefault(AzureResource.DIR_METADATA_MARKER, "false").equals("true");
101+
return this.internalAttributes.isRegularFile();
106102
}
107103

108104
/**
@@ -116,7 +112,19 @@ public boolean isRegularFile() {
116112
*/
117113
@Override
118114
public boolean isDirectory() {
119-
return !this.isRegularFile();
115+
return this.internalAttributes.isDirectory();
116+
}
117+
118+
/**
119+
* Tells whether the file is a virtual directory.
120+
* <p>
121+
* See {@link AzureFileSystemProvider#createDirectory(Path, FileAttribute[])} for more information on virtual and
122+
* concrete directories.
123+
*
124+
* @return whether the file is a virtual directory
125+
*/
126+
public boolean isVirtualDirectory() {
127+
return this.internalAttributes.isVirtualDirectory();
120128
}
121129

122130
/**
@@ -126,7 +134,7 @@ public boolean isDirectory() {
126134
*/
127135
@Override
128136
public boolean isSymbolicLink() {
129-
return false;
137+
return this.internalAttributes.isSymbolicLink();
130138
}
131139

132140
/**
@@ -136,7 +144,7 @@ public boolean isSymbolicLink() {
136144
*/
137145
@Override
138146
public boolean isOther() {
139-
return false;
147+
return this.internalAttributes.isOther();
140148
}
141149

142150
/**
@@ -146,7 +154,7 @@ public boolean isOther() {
146154
*/
147155
@Override
148156
public long size() {
149-
return properties.getBlobSize();
157+
return this.internalAttributes.size();
150158
}
151159

152160
/**
@@ -156,6 +164,6 @@ public long size() {
156164
*/
157165
@Override
158166
public Object fileKey() {
159-
return this.resource.getBlobClient().getBlobUrl();
167+
return this.internalAttributes.fileKey();
160168
}
161169
}

0 commit comments

Comments
 (0)