Skip to content
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
538cf80
generate new api based on swagger docs
browndav-msft Mar 23, 2026
ab93774
generate code to maintain current xml without arrow
browndav-msft Mar 23, 2026
f30233a
add apache arrow dep 19.0.0
browndav-msft Mar 24, 2026
18b7093
fix pom dependencies
browndav-msft Mar 24, 2026
3e664de
change pom to bom 18.1.0
browndav-msft Mar 24, 2026
12c6019
test direct call to arrow endpoint
browndav-msft Mar 26, 2026
b41a60c
hard code "preprod." into blob endpoint
browndav-msft Mar 26, 2026
6d5749f
fix preprod endpoint
browndav-msft Mar 26, 2026
3da9e23
add java.nio=ALL-UNNAMED to blob pom
browndav-msft Mar 26, 2026
d11b56f
addressing illegal dependencies
ibrandes Mar 26, 2026
cdbb843
add ArrowBlobListDeserializer and diagnostic tests for Arrow ListBlobs
browndav-msft Mar 26, 2026
d47fad1
wire Arrow path in BlobContainerClient.listBlobs with XML fallback
browndav-msft Mar 26, 2026
fa0cc50
add Arrow listBlobs integration tests with recordings
browndav-msft Mar 26, 2026
e3a26b8
update based on new swagger file
browndav-msft Mar 27, 2026
12e5ec2
change method signatures based on updates from swagger
browndav-msft Mar 27, 2026
d5dec10
add Arrow hierarchy schema discovery test
browndav-msft Mar 27, 2026
d98fc36
wire Arrow path in listBlobsByHierarchy with integration tests
browndav-msft Mar 28, 2026
24eb7f1
complete Arrow ListBlobs gap remediation: missing fields, NumberOfRec…
browndav-msft Mar 28, 2026
343492a
add javadocs to new methods in listblobsoptions
browndav-msft Mar 28, 2026
10fad8b
changed liveonly tests to recorded tests, minus cpk
browndav-msft Mar 30, 2026
d283f7f
remove local blob file and redirect to Nick's repo
browndav-msft Mar 30, 2026
da9d407
rename useArrow to isApacheArrowEnabled
browndav-msft Mar 30, 2026
2701e63
update dependency files
browndav-msft Apr 24, 2026
e4c6e58
add service minimums for tests
browndav-msft Jun 4, 2026
c30efa7
remove tests for schema discovery
browndav-msft Jun 4, 2026
754aa51
remove todo for nonphoton account
browndav-msft Jun 4, 2026
b3afa47
add rehydrate tests with recordings
browndav-msft Jun 4, 2026
e3c89d9
update listblowsarrow pagination test to include calling specific pag…
browndav-msft Jun 4, 2026
f800f8b
add async Arrow listBlobs tests for Photon
browndav-msft Jun 10, 2026
509ebc7
fix async tests
browndav-msft Jun 15, 2026
e78fad3
fix listBLobsArrowDeserializer async test
browndav-msft Jun 15, 2026
9673f85
add recordinds for async tests
browndav-msft Jun 15, 2026
6b6dfe7
replace all references for boolean with StorageResponseSerializationF…
browndav-msft Jun 15, 2026
46d2082
fix linting issues
browndav-msft Jun 16, 2026
b139c0d
remove bom, add versions to pom
browndav-msft Jun 16, 2026
c33aeae
change hardcoded serviceVersion in listBlobsArrowDeserializer
browndav-msft Jun 16, 2026
55f10ce
swagger - add Accept header for Arrow,Xml
browndav-msft Jun 17, 2026
52b3816
Replace Apache Arrow vector reader with custom ListBlobs Arrow stream…
browndav-msft Jun 17, 2026
aff29c1
Revert "Storage - STG104 Add Blob Access Tier to Get Blob Response (#…
browndav-msft Jun 18, 2026
4fbcbb6
Merge branch 'feature/storage/stg104base' into stg104/photon
browndav-msft Jun 18, 2026
5658599
wip - clean up var names
browndav-msft Jun 19, 2026
52525ef
remove getContentCrc64, getOrsPolicySourceBlob, getAffinityId
browndav-msft Jun 22, 2026
6461282
add external dependencies to external_dependencies.txt, add google bu…
browndav-msft Jun 22, 2026
a4c6e68
remove arrow-format for java 8 failures, vendor, create parity tests
browndav-msft Jun 22, 2026
3e983b5
add dependency exclusion for tests that require arrow deps that cause…
browndav-msft Jun 22, 2026
d5077f8
update dependency exclusion so that it works
browndav-msft Jun 22, 2026
d06b731
bump arrow deps to 19.0 for tests
browndav-msft Jun 23, 2026
b506f90
remove arrow deps from banndedDeps includes block
browndav-msft Jun 23, 2026
2a8853d
update comments in blob pom.xml to explicitly state workaround for ba…
browndav-msft Jun 23, 2026
a551d20
Merge remote-tracking branch 'azure/feature/storage/stg104base' into …
browndav-msft Jun 23, 2026
753397d
Merge branch 'feature/storage/stg104base' into stg104/photon
browndav-msft Jun 29, 2026
50e3eeb
remove unnecessary arrow parity tests, add remaining parity tests to …
browndav-msft Jun 25, 2026
2ec403b
lint files in arrow package
browndav-msft Jun 25, 2026
ac2eb80
add arrow golden decode tests and rejection tests, add test for all k…
browndav-msft Jun 26, 2026
de6a97f
change arrow classes to be more human readable
browndav-msft Jun 26, 2026
8d4bd47
change DecodedArrowStream to record
browndav-msft Jun 26, 2026
9799a9b
Merge branch 'feature/storage/stg104base' into stg104/photon
browndav-msft Jul 1, 2026
bc247e9
Revert "change DecodedArrowStream to record"
browndav-msft Jul 1, 2026
d1d9062
add changes based on feedback
browndav-msft Jul 1, 2026
d2623fe
update recording
browndav-msft Jul 2, 2026
f2bf13e
rename arrow to apachearrow
browndav-msft Jul 2, 2026
022e05e
add apache licensing
browndav-msft Jul 2, 2026
fc15bc8
add apache share profile to pom
browndav-msft Jul 6, 2026
3554b48
Merge branch 'feature/storage/stg104base' into stg104/photon
browndav-msft Jul 6, 2026
0268178
add versions to pom shade profiling, add "Accept" header to match onl…
browndav-msft Jul 6, 2026
169a280
add arrow 19 dep to external_dependencies.txt file
browndav-msft Jul 6, 2026
c5e13c8
fix linting issues
browndav-msft Jul 6, 2026
f75a768
fix more linting errors with spotless
browndav-msft Jul 6, 2026
913fff3
remove unused imports
browndav-msft Jul 6, 2026
2063b04
revert add "Accept" to blobbasetest, add recordings
browndav-msft Jul 8, 2026
5d5df18
Merge branch 'feature/storage/stg104base' into stg104/photon
browndav-msft Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ com.github.spotbugs:spotbugs;4.8.3
com.github.spotbugs:spotbugs-annotations;4.8.3
com.github.spotbugs:spotbugs-maven-plugin;4.8.3.1
com.google.code.gson:gson;2.14.0
com.google.flatbuffers:flatbuffers-java;24.3.25
com.google.guava:guava;33.6.0-jre
com.h2database:h2;2.2.220
com.h3xstream.findsecbugs:findsecbugs-plugin;1.9.0
Expand Down
4 changes: 4 additions & 0 deletions sdk/parents/azure-client-sdk-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@

<!-- Used by many libraries to bring in annotations used by Reactor -->
<include>com.google.code.findbugs:jsr305:[3.0.2]</include> <!-- {x-include-update;com.google.code.findbugs:jsr305;external_dependency} -->

<!-- Apache Arrow format parsing. flatbuffers-java is the sole (compile-scoped) dependency used by
azure-storage-blob's vendored Arrow FlatBuffer accessors -->
<include>com.google.flatbuffers:flatbuffers-java:[24.3.25]</include> <!-- {x-include-update;com.google.flatbuffers:flatbuffers-java;external_dependency} -->
</includes>
</bannedDependencies>

Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/storage/azure-storage-blob",
"Tag": "java/storage/azure-storage-blob_494b72fca7"
"Tag": "java/storage/azure-storage-blob_c4cdc68ef6"
}
14 changes: 14 additions & 0 deletions sdk/storage/azure-storage-blob/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@
<version>1.17.7</version> <!-- {x-version-update;testdep_net.bytebuddy:byte-buddy-agent;external_dependency} -->
<scope>test</scope>
</dependency>

<!-- Arrow dependency -->
<!-- The internal ListBlobs Arrow reader decodes the IPC stream using vendored FlatBuffer metadata accessors
(com.azure.storage.blob.implementation.util.arrow) backed by flatbuffers-java, so flatbuffers-java is the sole
dependency needed to parse the Arrow format. The arrow-format artifact is deliberately NOT a dependency: it
ships Java 11 bytecode and would break the Java 8 baseline build. The reader's behavior is validated against
committed golden base64 fixtures (src/test/resources/arrow) rather than the official arrow-vector writer, so no
Apache Arrow test dependency is required. -->
<dependency>
<groupId>com.google.flatbuffers</groupId>
<artifactId>flatbuffers-java</artifactId>
<version>24.3.25</version> <!-- {x-version-update;com.google.flatbuffers:flatbuffers-java;external_dependency} -->
</dependency>

</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.models.ListBlobsFlatSegmentResponse;
import com.azure.storage.blob.implementation.models.ListBlobsHierarchySegmentResponse;
import com.azure.storage.blob.implementation.util.ArrowBlobListDeserializer;
import com.azure.storage.blob.implementation.util.ArrowBlobListDeserializer.ArrowListBlobsResult;
import com.azure.storage.blob.implementation.util.BlobConstants;
import com.azure.storage.blob.implementation.util.BlobSasImplUtil;
import com.azure.storage.blob.implementation.util.ModelHelper;
Expand All @@ -42,6 +44,7 @@
import com.azure.storage.blob.models.CustomerProvidedKey;
import com.azure.storage.blob.models.ListBlobsIncludeItem;
import com.azure.storage.blob.models.ListBlobsOptions;
import com.azure.storage.blob.models.StorageResponseSerializationFormat;
import com.azure.storage.blob.models.PublicAccessType;
import com.azure.storage.blob.models.StorageAccountInfo;
import com.azure.storage.blob.models.TaggedBlobItem;
Expand Down Expand Up @@ -1118,18 +1121,35 @@ PagedFlux<BlobItem> listBlobsFlatWithOptionalTimeout(ListBlobsOptions options, S
Duration timeout) {
BiFunction<String, Integer, Mono<PagedResponse<BlobItem>>> func = (marker, pageSize) -> {
ListBlobsOptions finalOptions;
/*
If pageSize was not set in a .byPage(int) method, the page size from options will be preserved.
Otherwise, prefer the new value.
*/
if (pageSize != null) {
if (options == null) {
finalOptions = new ListBlobsOptions().setMaxResultsPerPage(pageSize);
} else {
// Note that this prefers the value passed to .byPage(int) over the value on the options
finalOptions = new ListBlobsOptions().setMaxResultsPerPage(pageSize)
.setPrefix(options.getPrefix())
.setDetails(options.getDetails());
.setDetails(options.getDetails())
.setStartFrom(options.getStartFrom());
if (ModelHelper.resolveSerializationFormat(options.getStorageResponseSerializationFormat())
== StorageResponseSerializationFormat.ARROW) {
finalOptions.setStorageResponseSerializationFormat(StorageResponseSerializationFormat.ARROW)
.setEndBefore(options.getEndBefore());
}
}
} else {
finalOptions = options;
}

if (finalOptions != null
&& ModelHelper.resolveSerializationFormat(finalOptions.getStorageResponseSerializationFormat())
== StorageResponseSerializationFormat.ARROW) {
return listBlobsFlatSegmentArrow(marker, finalOptions, timeout);
}

return listBlobsFlatSegment(marker, finalOptions, timeout).map(response -> {
List<BlobItem> value = response.getValue().getSegment() == null
? Collections.emptyList()
Expand Down Expand Up @@ -1177,6 +1197,61 @@ PagedFlux<BlobItem> listBlobsFlatWithOptionalTimeout(ListBlobsOptions options, S
timeout);
}

private Mono<PagedResponse<BlobItem>> listBlobsFlatSegmentArrow(String marker, ListBlobsOptions options,
Duration timeout) {
options = options == null ? new ListBlobsOptions() : options;

ArrayList<ListBlobsIncludeItem> include
= options.getDetails().toList().isEmpty() ? null : options.getDetails().toList();

ListBlobsOptions finalOptions = options;
return StorageImplUtils.applyOptionalTimeout(this.azureBlobStorage.getContainers()
.listBlobFlatSegmentApacheArrowWithResponseAsync(containerName, finalOptions.getPrefix(), marker,
finalOptions.getMaxResultsPerPage(), include, null, finalOptions.getStartFrom(),
finalOptions.getEndBefore(), null, Context.NONE),
timeout).flatMap(response -> {
String contentType = response.getHeaders().getValue(com.azure.core.http.HttpHeaderName.CONTENT_TYPE);

return FluxUtil.collectBytesInByteBufferStream(response.getValue()).map(bytes -> {
java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(bytes);

if (contentType != null && contentType.contentEquals("application/vnd.apache.arrow.stream")) {
ArrowListBlobsResult arrowResult = ArrowBlobListDeserializer.deserialize(inputStream);
Comment thread
browndav-msft marked this conversation as resolved.

List<BlobItem> value = arrowResult.getBlobItems()
.stream()
.map(ModelHelper::populateBlobItem)
.collect(Collectors.toList());

return (PagedResponse<BlobItem>) new PagedResponseBase<>(response.getRequest(),
response.getStatusCode(), response.getHeaders(), value, arrowResult.getNextMarker(),
response.getDeserializedHeaders());
} else {
// XML fallback
try {
ListBlobsFlatSegmentResponse xmlResponse
= ListBlobsFlatSegmentResponse.fromXml(com.azure.xml.XmlReader.fromStream(inputStream));

List<BlobItem> value = xmlResponse.getSegment() == null
? Collections.emptyList()
: xmlResponse.getSegment()
.getBlobItems()
.stream()
.map(ModelHelper::populateBlobItem)
.collect(Collectors.toList());

return (PagedResponse<BlobItem>) new PagedResponseBase<>(response.getRequest(),
response.getStatusCode(), response.getHeaders(), value, xmlResponse.getNextMarker(),
null);
} catch (javax.xml.stream.XMLStreamException e) {
throw LOGGER
.logExceptionAsError(new RuntimeException("Failed to parse XML fallback response", e));
}
}
});
});
}

/**
* Returns a reactive Publisher emitting all the blobs and directories (prefixes) under the given directory
* (prefix). Directories will have {@link BlobItem#isPrefix()} set to true.
Expand Down Expand Up @@ -1302,10 +1377,22 @@ PagedFlux<BlobItem> listBlobsHierarchyWithOptionalTimeout(String delimiter, List
.setPrefix(options.getPrefix())
.setDetails(options.getDetails())
.setStartFrom(options.getStartFrom());
if (ModelHelper.resolveSerializationFormat(options.getStorageResponseSerializationFormat())
== StorageResponseSerializationFormat.ARROW) {
finalOptions.setStorageResponseSerializationFormat(StorageResponseSerializationFormat.ARROW)
.setEndBefore(options.getEndBefore());
}
}
} else {
finalOptions = options;
}

if (finalOptions != null
&& ModelHelper.resolveSerializationFormat(finalOptions.getStorageResponseSerializationFormat())
== StorageResponseSerializationFormat.ARROW) {
return listBlobsHierarchySegmentArrow(marker, delimiter, finalOptions, timeout);
}

return listBlobsHierarchySegment(marker, delimiter, finalOptions, timeout).map(response -> {
BlobHierarchyListSegment segment = response.getValue().getSegment();
List<BlobItem> value;
Expand Down Expand Up @@ -1344,6 +1431,70 @@ PagedFlux<BlobItem> listBlobsHierarchyWithOptionalTimeout(String delimiter, List
timeout);
}

private Mono<PagedResponse<BlobItem>> listBlobsHierarchySegmentArrow(String marker, String delimiter,
ListBlobsOptions options, Duration timeout) {
options = options == null ? new ListBlobsOptions() : options;
if (options.getDetails().getRetrieveSnapshots()) {
throw LOGGER.logExceptionAsError(
new UnsupportedOperationException("Including snapshots in a hierarchical listing is not supported."));
}

ArrayList<ListBlobsIncludeItem> include
= options.getDetails().toList().isEmpty() ? null : options.getDetails().toList();

ListBlobsOptions finalOptions = options;
return StorageImplUtils
.applyOptionalTimeout(this.azureBlobStorage.getContainers()
.listBlobHierarchySegmentApacheArrowWithResponseAsync(containerName, delimiter,
finalOptions.getPrefix(), marker, finalOptions.getMaxResultsPerPage(), include, null,
finalOptions.getStartFrom(), finalOptions.getEndBefore(), null, Context.NONE),
timeout)
.flatMap(response -> {
String contentType = response.getHeaders().getValue(com.azure.core.http.HttpHeaderName.CONTENT_TYPE);

return FluxUtil.collectBytesInByteBufferStream(response.getValue()).map(bytes -> {
java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(bytes);

if (contentType != null && contentType.contentEquals("application/vnd.apache.arrow.stream")) {
ArrowListBlobsResult arrowResult = ArrowBlobListDeserializer.deserialize(inputStream);
Comment thread
browndav-msft marked this conversation as resolved.

List<BlobItem> value = arrowResult.getBlobItems()
.stream()
.map(ModelHelper::populateBlobItem)
.collect(Collectors.toList());

return (PagedResponse<BlobItem>) new PagedResponseBase<>(response.getRequest(),
response.getStatusCode(), response.getHeaders(), value, arrowResult.getNextMarker(),
response.getDeserializedHeaders());
} else {
// XML fallback
try {
ListBlobsHierarchySegmentResponse xmlResponse = ListBlobsHierarchySegmentResponse
.fromXml(com.azure.xml.XmlReader.fromStream(inputStream));

BlobHierarchyListSegment segment = xmlResponse.getSegment();
List<BlobItem> value = new ArrayList<>();
if (segment != null) {
segment.getBlobItems()
.forEach(item -> value.add(BlobItemConstructorProxy.create(item)));
segment.getBlobPrefixes()
.forEach(prefix -> value
.add(new BlobItem().setName(ModelHelper.toBlobNameString(prefix.getName()))
.setIsPrefix(true)));
}

return (PagedResponse<BlobItem>) new PagedResponseBase<>(response.getRequest(),
response.getStatusCode(), response.getHeaders(), value, xmlResponse.getNextMarker(),
null);
} catch (javax.xml.stream.XMLStreamException e) {
throw LOGGER
.logExceptionAsError(new RuntimeException("Failed to parse XML fallback response", e));
}
}
});
});
}

/**
* Returns a reactive Publisher emitting the blobs in this container whose tags match the query expression. For more
* information, including information on the query syntax, see the <a href="https://docs.microsoft.com/rest/api/storageservices/find-blobs-by-tags">Azure Docs</a>.
Expand Down
Loading
Loading