com.azure.storage.blob.BlobServiceAsyncClient Maven / Gradle / Ivy
Show all versions of azure-storage-blob Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.storage.blob;
import com.azure.core.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
import com.azure.core.annotation.ServiceMethod;
import com.azure.core.credential.TokenCredential;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.rest.PagedFlux;
import com.azure.core.http.rest.PagedResponse;
import com.azure.core.http.rest.PagedResponseBase;
import com.azure.core.http.rest.Response;
import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.util.Context;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.logging.ClientLogger;
import com.azure.storage.blob.implementation.AzureBlobStorageImpl;
import com.azure.storage.blob.implementation.AzureBlobStorageImplBuilder;
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.models.ServicesGetAccountInfoHeaders;
import com.azure.storage.blob.implementation.util.ModelHelper;
import com.azure.storage.blob.models.BlobContainerEncryptionScope;
import com.azure.storage.blob.models.BlobContainerItem;
import com.azure.storage.blob.models.BlobContainerListDetails;
import com.azure.storage.blob.models.BlobCorsRule;
import com.azure.storage.blob.models.BlobRetentionPolicy;
import com.azure.storage.blob.models.BlobServiceProperties;
import com.azure.storage.blob.models.BlobServiceStatistics;
import com.azure.storage.blob.models.BlobStorageException;
import com.azure.storage.blob.models.CpkInfo;
import com.azure.storage.blob.models.KeyInfo;
import com.azure.storage.blob.models.ListBlobContainersIncludeType;
import com.azure.storage.blob.models.ListBlobContainersOptions;
import com.azure.storage.blob.models.PublicAccessType;
import com.azure.storage.blob.models.StorageAccountInfo;
import com.azure.storage.blob.models.TaggedBlobItem;
import com.azure.storage.blob.models.UserDelegationKey;
import com.azure.storage.blob.options.BlobContainerCreateOptions;
import com.azure.storage.blob.options.FindBlobsOptions;
import com.azure.storage.blob.options.UndeleteBlobContainerOptions;
import com.azure.storage.common.StorageSharedKeyCredential;
import com.azure.storage.common.implementation.AccountSasImplUtil;
import com.azure.storage.common.implementation.Constants;
import com.azure.storage.common.implementation.SasImplUtils;
import com.azure.storage.common.implementation.StorageImplUtils;
import com.azure.storage.common.sas.AccountSasSignatureValues;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import static com.azure.core.util.FluxUtil.monoError;
import static com.azure.core.util.FluxUtil.pagedFluxError;
import static com.azure.core.util.FluxUtil.withContext;
/**
* Client to a storage account. It may only be instantiated through a {@link BlobServiceClientBuilder}. This class does
* not hold any state about a particular storage account but is instead a convenient way of sending off appropriate
* requests to the resource on the service. It may also be used to construct URLs to blobs and containers.
*
*
* This client contains operations on a blob. Operations on a container are available on {@link
* BlobContainerAsyncClient} through {@link #getBlobContainerAsyncClient(String)}, and operations on a blob are
* available on {@link BlobAsyncClient}.
*
*
* Please see the Azure Docs for
* more information on containers.
*
*
* Note this client is an async client that returns reactive responses from Spring Reactor Core project
* (https://projectreactor.io/). Calling the methods in this client will NOT start the actual network
* operation, until {@code .subscribe()} is called on the reactive response. You can simply convert one of these
* responses to a {@link java.util.concurrent.CompletableFuture} object through {@link Mono#toFuture()}.
*/
@ServiceClient(builder = BlobServiceClientBuilder.class, isAsync = true)
public final class BlobServiceAsyncClient {
private static final ClientLogger LOGGER = new ClientLogger(BlobServiceAsyncClient.class);
private final AzureBlobStorageImpl azureBlobStorage;
private final String accountName;
private final BlobServiceVersion serviceVersion;
private final CpkInfo customerProvidedKey; // only used to pass down to blob clients
private final EncryptionScope encryptionScope; // only used to pass down to blob clients
private final BlobContainerEncryptionScope blobContainerEncryptionScope; // only used to pass down to container
// clients
private final boolean anonymousAccess;
/**
* Package-private constructor for use by {@link BlobServiceClientBuilder}.
*
* @param pipeline The pipeline used to send and receive service requests.
* @param url The endpoint where to send service requests.
* @param serviceVersion The version of the service to receive requests.
* @param accountName The storage account name.
* @param customerProvidedKey Customer provided key used during encryption of the blob's data on the server, pass
* {@code null} to allow the service to use its own encryption.
* @param encryptionScope Encryption scope used during encryption of the blob's data on the server, pass
* {@code null} to allow the service to use its own encryption.
* @param anonymousAccess Whether the client was built with anonymousAccess
*/
BlobServiceAsyncClient(HttpPipeline pipeline, String url, BlobServiceVersion serviceVersion, String accountName,
CpkInfo customerProvidedKey, EncryptionScope encryptionScope,
BlobContainerEncryptionScope blobContainerEncryptionScope, boolean anonymousAccess) {
/* Check to make sure the uri is valid. We don't want the error to occur later in the generated layer
when the sas token has already been applied. */
try {
URI.create(url);
} catch (IllegalArgumentException ex) {
throw LOGGER.logExceptionAsError(ex);
}
this.azureBlobStorage = new AzureBlobStorageImplBuilder()
.pipeline(pipeline)
.url(url)
.version(serviceVersion.getVersion())
.buildClient();
this.serviceVersion = serviceVersion;
this.accountName = accountName;
this.customerProvidedKey = customerProvidedKey;
this.encryptionScope = encryptionScope;
this.blobContainerEncryptionScope = blobContainerEncryptionScope;
this.anonymousAccess = anonymousAccess;
}
/**
* Initializes a {@link BlobContainerAsyncClient} object pointing to the specified container. This method does not
* create a container. It simply constructs the URL to the container and offers access to methods relevant to
* containers.
*
*
Code Samples
*
*
*
* BlobContainerAsyncClient blobContainerAsyncClient = client.getBlobContainerAsyncClient("containerName");
*
*
*
* @param containerName The name of the container to point to. A value of null or empty string will be interpreted
* as pointing to the root container and will be replaced by "$root".
* @return A {@link BlobContainerAsyncClient} object pointing to the specified container
*/
public BlobContainerAsyncClient getBlobContainerAsyncClient(String containerName) {
if (CoreUtils.isNullOrEmpty(containerName)) {
containerName = BlobContainerAsyncClient.ROOT_CONTAINER_NAME;
}
return new BlobContainerAsyncClient(getHttpPipeline(), getAccountUrl(), getServiceVersion(),
getAccountName(), containerName, customerProvidedKey, encryptionScope, blobContainerEncryptionScope);
}
/**
* Gets the {@link HttpPipeline} powering this client.
*
* @return The pipeline.
*/
public HttpPipeline getHttpPipeline() {
return azureBlobStorage.getHttpPipeline();
}
/**
* Gets the service version the client is using.
*
* @return the service version the client is using.
*/
public BlobServiceVersion getServiceVersion() {
return serviceVersion;
}
/**
* Creates a new container within a storage account. If a container with the same name already exists, the operation
* fails. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* BlobContainerAsyncClient blobContainerAsyncClient =
* client.createBlobContainer("containerName").block();
*
*
*
* @param containerName Name of the container to create
* @return A {@link Mono} containing a {@link BlobContainerAsyncClient} used to interact with the container created.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono createBlobContainer(String containerName) {
return createBlobContainerWithResponse(containerName, null, null).flatMap(FluxUtil::toMono);
}
/**
* Creates a new container within a storage account. If a container with the same name already exists, the operation
* fails. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* Map<String, String> metadata = Collections.singletonMap("metadata", "value");
*
* BlobContainerAsyncClient containerClient = client
* .createBlobContainerWithResponse("containerName", metadata, PublicAccessType.CONTAINER).block().getValue();
*
*
*
* @param containerName Name of the container to create
* @param metadata Metadata to associate with the container. If there is leading or trailing whitespace in any
* metadata key or value, it must be removed or encoded.
* @param accessType Specifies how the data in this container is available to the public. See the
* x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access.
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains a {@link
* BlobContainerAsyncClient} used to interact with the container created.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> createBlobContainerWithResponse(String containerName,
Map metadata, PublicAccessType accessType) {
try {
return withContext(context -> createBlobContainerWithResponse(containerName, metadata, accessType,
context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> createBlobContainerWithResponse(String containerName,
Map metadata, PublicAccessType accessType, Context context) {
throwOnAnonymousAccess();
BlobContainerAsyncClient blobContainerAsyncClient = getBlobContainerAsyncClient(containerName);
return blobContainerAsyncClient.createWithResponse(metadata, accessType, context)
.map(response -> new SimpleResponse<>(response, blobContainerAsyncClient));
}
/**
* Creates a new container within a storage account if it does not exist. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* BlobContainerAsyncClient blobContainerAsyncClient =
* client.createBlobContainerIfNotExists("containerName").block();
*
*
*
* @param containerName Name of the container to create
* @return A {@link Mono} containing a {@link BlobContainerAsyncClient} used to interact with the container created.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono createBlobContainerIfNotExists(String containerName) {
return createBlobContainerIfNotExistsWithResponse(containerName, null, null).flatMap(FluxUtil::toMono);
}
/**
* Creates a new container within a storage account if it does not exist. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* Map<String, String> metadata = Collections.singletonMap("metadata", "value");
* BlobContainerCreateOptions options = new BlobContainerCreateOptions().setMetadata(metadata)
* .setPublicAccessType(PublicAccessType.CONTAINER);
*
* client.createBlobContainerIfNotExistsWithResponse("containerName", options).subscribe(response -> {
* if (response.getStatusCode() == 409) {
* System.out.println("Already exists.");
* } else {
* System.out.println("successfully created.");
* }
* });
*
*
*
* @param containerName Name of the container to create
* @param options {@link BlobContainerCreateOptions}
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains a {@link
* BlobContainerAsyncClient} used to interact with the container created. If {@link Response}'s status code is 201,
* a new container was successfully created. If status code is 409, a container with the same name already existed
* at this location.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> createBlobContainerIfNotExistsWithResponse(String containerName,
BlobContainerCreateOptions options) {
try {
return withContext(context -> createBlobContainerIfNotExistsWithResponse(containerName, options,
context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> createBlobContainerIfNotExistsWithResponse(String containerName,
BlobContainerCreateOptions options, Context context) {
try {
options = options == null ? new BlobContainerCreateOptions() : options;
return createBlobContainerWithResponse(containerName, options.getMetadata(), options.getPublicAccessType(),
context).onErrorResume(t -> t instanceof BlobStorageException && ((BlobStorageException) t)
.getStatusCode() == 409, t -> {
HttpResponse response = ((BlobStorageException) t).getResponse();
return Mono.just(new SimpleResponse<>(response.getRequest(), response.getStatusCode(),
response.getHeaders(), this.getBlobContainerAsyncClient(containerName)));
});
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
/**
* Deletes the specified container in the storage account. If the container doesn't exist the operation fails. For
* more information see the Azure Docs.
* Code Samples
*
*
*
* client.deleteBlobContainer("containerName").subscribe(
* response -> System.out.printf("Delete container completed%n"),
* error -> System.out.printf("Delete container failed: %s%n", error));
*
*
*
* @param containerName Name of the container to delete
* @return A {@link Mono} containing status code and HTTP headers
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono deleteBlobContainer(String containerName) {
return deleteBlobContainerWithResponse(containerName).flatMap(FluxUtil::toMono);
}
/**
* Deletes the specified container in the storage account. If the container doesn't exist the operation fails. For
* more information see the Azure
* Docs.
*
* Code Samples
*
*
*
* Context context = new Context("Key", "Value");
* client.deleteBlobContainerWithResponse("containerName").subscribe(response ->
* System.out.printf("Delete container completed with status %d%n", response.getStatusCode()));
*
*
*
* @param containerName Name of the container to delete
* @return A {@link Mono} containing status code and HTTP headers
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> deleteBlobContainerWithResponse(String containerName) {
try {
return withContext(context -> deleteBlobContainerWithResponse(containerName, context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> deleteBlobContainerWithResponse(String containerName, Context context) {
throwOnAnonymousAccess();
return getBlobContainerAsyncClient(containerName).deleteWithResponse(null, context);
}
/**
* Deletes the specified container in the storage account if it exists. For
* more information see the Azure
* Docs.
* Code Samples
*
*
*
* client.deleteBlobContainerIfExists("containerName").subscribe(deleted -> {
* if (deleted) {
* System.out.println("Successfully deleted.");
* } else {
* System.out.println("Does not exist.");
* }
* });
*
*
*
* @param containerName Name of the container to delete
* @return A reactive {@link Mono} signaling completion. {@code true} indicates that the container was deleted.
* {@code false} indicates the container does not exist at this location.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono deleteBlobContainerIfExists(String containerName) {
return deleteBlobContainerIfExistsWithResponse(containerName).flatMap(FluxUtil::toMono);
}
/**
* Deletes the specified container in the storage account if it exists.
* For more information see the Azure
* Docs.
*
* Code Samples
*
*
*
* Context context = new Context("Key", "Value");
* client.deleteBlobContainerIfExistsWithResponse("containerName").subscribe(response -> {
* if (response.getStatusCode() == 404) {
* System.out.println("Does not exist.");
* } else {
* System.out.println("successfully deleted.");
* }
* });
*
*
*
* @param containerName Name of the container to delete
* @return A reactive response signaling completion. If {@link Response}'s status code is 202, the blob container was
* successfully deleted. If status code is 404, the blob container does not exist.
*
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> deleteBlobContainerIfExistsWithResponse(String containerName) {
try {
return withContext(context -> deleteBlobContainerIfExistsWithResponse(containerName, context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> deleteBlobContainerIfExistsWithResponse(String containerName, Context context) {
try {
return deleteBlobContainerWithResponse(containerName, context)
.map(response -> (Response) new SimpleResponse<>(response, true))
.onErrorResume(t -> t instanceof BlobStorageException && ((BlobStorageException) t).getStatusCode() == 404,
t -> {
HttpResponse response = ((BlobStorageException) t).getResponse();
return Mono.just(new SimpleResponse<>(response.getRequest(), response.getStatusCode(),
response.getHeaders(), false));
});
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
/**
* Gets the URL of the storage account represented by this client.
*
* @return the URL.
*/
public String getAccountUrl() {
return azureBlobStorage.getUrl();
}
/**
* Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information,
* see the Azure Docs.
*
* Code Samples
*
*
*
* client.listBlobContainers().subscribe(container -> System.out.printf("Name: %s%n", container.getName()));
*
*
*
* @return A reactive response emitting the list of containers.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public PagedFlux listBlobContainers() {
return this.listBlobContainers(new ListBlobContainersOptions());
}
/**
* Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information,
* see the Azure Docs.
*
* Code Samples
*
*
*
* ListBlobContainersOptions options = new ListBlobContainersOptions()
* .setPrefix("containerNamePrefixToMatch")
* .setDetails(new BlobContainerListDetails().setRetrieveMetadata(true));
*
* client.listBlobContainers(options).subscribe(container -> System.out.printf("Name: %s%n", container.getName()));
*
*
*
* @param options A {@link ListBlobContainersOptions} which specifies what data should be returned by the service.
* @return A reactive response emitting the list of containers.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public PagedFlux listBlobContainers(ListBlobContainersOptions options) {
try {
return listBlobContainersWithOptionalTimeout(options, null);
} catch (RuntimeException ex) {
return pagedFluxError(LOGGER, ex);
}
}
PagedFlux listBlobContainersWithOptionalTimeout(ListBlobContainersOptions options,
Duration timeout) {
throwOnAnonymousAccess();
BiFunction>> func =
(marker, pageSize) -> {
ListBlobContainersOptions finalOptions;
if (pageSize != null) {
if (options == null) {
finalOptions = new ListBlobContainersOptions().setMaxResultsPerPage(pageSize);
} else {
finalOptions = new ListBlobContainersOptions()
.setMaxResultsPerPage(pageSize)
.setDetails(options.getDetails())
.setPrefix(options.getPrefix());
}
} else {
finalOptions = options;
}
return listBlobContainersSegment(marker, finalOptions, timeout);
};
return new PagedFlux<>(pageSize -> func.apply(null, pageSize), func);
}
private Mono> listBlobContainersSegment(String marker,
ListBlobContainersOptions options, Duration timeout) {
options = options == null ? new ListBlobContainersOptions() : options;
return StorageImplUtils.applyOptionalTimeout(
this.azureBlobStorage.getServices().listBlobContainersSegmentSinglePageAsync(
options.getPrefix(), marker, options.getMaxResultsPerPage(),
toIncludeTypes(options.getDetails()),
null, null, Context.NONE), timeout);
}
/**
* Returns a reactive Publisher emitting the blobs in this account whose tags match the query expression. For more
* information, including information on the query syntax, see the Azure Docs.
*
* Code Samples
*
*
*
* client.findBlobsByTags("where=tag=value").subscribe(blob -> System.out.printf("Name: %s%n", blob.getName()));
*
*
*
* @param query Filters the results to return only blobs whose tags match the specified expression.
* @return A reactive response emitting the list of blobs.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public PagedFlux findBlobsByTags(String query) {
return this.findBlobsByTags(new FindBlobsOptions(query));
}
/**
* Returns a reactive Publisher emitting the blobs in this account whose tags match the query expression. For more
* information, including information on the query syntax, see the Azure Docs.
*
* Code Samples
*
*
*
* client.findBlobsByTags(new FindBlobsOptions("where=tag=value").setMaxResultsPerPage(10))
* .subscribe(blob -> System.out.printf("Name: %s%n", blob.getName()));
*
*
*
* @param options {@link FindBlobsOptions}
* @return A reactive response emitting the list of blobs.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public PagedFlux findBlobsByTags(FindBlobsOptions options) {
try {
return findBlobsByTags(options, null, Context.NONE);
} catch (RuntimeException ex) {
return pagedFluxError(LOGGER, ex);
}
}
PagedFlux findBlobsByTags(FindBlobsOptions options, Duration timeout, Context context) {
throwOnAnonymousAccess();
StorageImplUtils.assertNotNull("options", options);
BiFunction>> func =
(marker, pageSize) -> {
FindBlobsOptions finalOptions;
if (pageSize != null) {
finalOptions = new FindBlobsOptions(options.getQuery())
.setMaxResultsPerPage(pageSize);
} else {
finalOptions = options;
}
return this.findBlobsByTags(finalOptions, marker, timeout, context);
};
return new PagedFlux<>(pageSize -> func.apply(null, pageSize), func);
}
private Mono> findBlobsByTags(
FindBlobsOptions options, String marker,
Duration timeout, Context context) {
throwOnAnonymousAccess();
StorageImplUtils.assertNotNull("options", options);
return StorageImplUtils.applyOptionalTimeout(
this.azureBlobStorage.getServices().filterBlobsWithResponseAsync(null, null,
options.getQuery(), marker, options.getMaxResultsPerPage(), null, context), timeout)
.map(response -> {
List value = response.getValue().getBlobs() == null
? Collections.emptyList()
: response.getValue().getBlobs().stream()
.map(ModelHelper::populateTaggedBlobItem)
.collect(Collectors.toList());
return new PagedResponseBase<>(
response.getRequest(),
response.getStatusCode(),
response.getHeaders(),
value,
response.getValue().getNextMarker(),
response.getDeserializedHeaders());
});
}
/**
* Converts {@link BlobContainerListDetails} into list of {@link ListBlobContainersIncludeType}
* that contains only options selected. If no option is selected then null is returned.
*
* @return a list of selected options converted into {@link ListBlobContainersIncludeType}, null if none
* of options has been selected.
*/
private List toIncludeTypes(BlobContainerListDetails blobContainerListDetails) {
boolean hasDetails = blobContainerListDetails != null
&& (blobContainerListDetails.getRetrieveMetadata()
|| blobContainerListDetails.getRetrieveDeleted()
|| blobContainerListDetails.getRetrieveSystemContainers());
if (hasDetails) {
List flags = new ArrayList<>(3);
if (blobContainerListDetails.getRetrieveDeleted()) {
flags.add(ListBlobContainersIncludeType.DELETED);
}
if (blobContainerListDetails.getRetrieveMetadata()) {
flags.add(ListBlobContainersIncludeType.METADATA);
}
if (blobContainerListDetails.getRetrieveSystemContainers()) {
flags.add(ListBlobContainersIncludeType.SYSTEM);
}
return flags;
} else {
return null;
}
}
/**
* Gets the properties of a storage account’s Blob service. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* client.getProperties().subscribe(response ->
* System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b%n",
* response.getHourMetrics().isEnabled(),
* response.getMinuteMetrics().isEnabled()));
*
*
*
* @return A reactive response containing the storage account properties.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getProperties() {
return getPropertiesWithResponse().flatMap(FluxUtil::toMono);
}
/**
* Gets the properties of a storage account’s Blob service. For more information, see the
* Azure Docs.
*
* Code Samples
*
*
*
* client.getPropertiesWithResponse().subscribe(response ->
* System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b%n",
* response.getValue().getHourMetrics().isEnabled(),
* response.getValue().getMinuteMetrics().isEnabled()));
*
*
*
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains the storage
* account properties.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> getPropertiesWithResponse() {
try {
return withContext(this::getPropertiesWithResponse);
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> getPropertiesWithResponse(Context context) {
context = context == null ? Context.NONE : context;
throwOnAnonymousAccess();
return this.azureBlobStorage.getServices().getPropertiesWithResponseAsync(null, null, context)
.map(rb -> new SimpleResponse<>(rb, rb.getValue()));
}
/**
* Sets properties for a storage account's Blob service endpoint. For more information, see the
* Azure Docs.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.
*
* Code Samples
*
*
*
* BlobRetentionPolicy loggingRetentionPolicy = new BlobRetentionPolicy().setEnabled(true).setDays(3);
* BlobRetentionPolicy metricsRetentionPolicy = new BlobRetentionPolicy().setEnabled(true).setDays(1);
*
* BlobServiceProperties properties = new BlobServiceProperties()
* .setLogging(new BlobAnalyticsLogging()
* .setWrite(true)
* .setDelete(true)
* .setRetentionPolicy(loggingRetentionPolicy))
* .setHourMetrics(new BlobMetrics()
* .setEnabled(true)
* .setRetentionPolicy(metricsRetentionPolicy))
* .setMinuteMetrics(new BlobMetrics()
* .setEnabled(true)
* .setRetentionPolicy(metricsRetentionPolicy));
*
* client.setProperties(properties).subscribe(
* response -> System.out.printf("Setting properties completed%n"),
* error -> System.out.printf("Setting properties failed: %s%n", error));
*
*
*
* @param properties Configures the service.
* @return A {@link Mono} containing the storage account properties.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono setProperties(BlobServiceProperties properties) {
return setPropertiesWithResponse(properties).flatMap(FluxUtil::toMono);
}
/**
* Sets properties for a storage account's Blob service endpoint. For more information, see the
* Azure Docs.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.
* Code Samples
*
*
*
* BlobRetentionPolicy loggingRetentionPolicy = new BlobRetentionPolicy().setEnabled(true).setDays(3);
* BlobRetentionPolicy metricsRetentionPolicy = new BlobRetentionPolicy().setEnabled(true).setDays(1);
*
* BlobServiceProperties properties = new BlobServiceProperties()
* .setLogging(new BlobAnalyticsLogging()
* .setWrite(true)
* .setDelete(true)
* .setRetentionPolicy(loggingRetentionPolicy))
* .setHourMetrics(new BlobMetrics()
* .setEnabled(true)
* .setRetentionPolicy(metricsRetentionPolicy))
* .setMinuteMetrics(new BlobMetrics()
* .setEnabled(true)
* .setRetentionPolicy(metricsRetentionPolicy));
*
* client.setPropertiesWithResponse(properties).subscribe(response ->
* System.out.printf("Setting properties completed with status %d%n", response.getStatusCode()));
*
*
*
* @param properties Configures the service.
* @return A {@link Mono} containing the storage account properties.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> setPropertiesWithResponse(BlobServiceProperties properties) {
try {
return withContext(context -> setPropertiesWithResponse(properties, context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> setPropertiesWithResponse(BlobServiceProperties properties, Context context) {
throwOnAnonymousAccess();
BlobServiceProperties finalProperties = null;
if (properties != null) {
finalProperties = new BlobServiceProperties();
// Logging
finalProperties.setLogging(properties.getLogging());
if (finalProperties.getLogging() != null) {
StorageImplUtils.assertNotNull("Logging Version", finalProperties.getLogging().getVersion());
validateRetentionPolicy(finalProperties.getLogging().getRetentionPolicy(), "Logging Retention Policy");
}
// Hour Metrics
finalProperties.setHourMetrics(properties.getHourMetrics());
if (finalProperties.getHourMetrics() != null) {
StorageImplUtils.assertNotNull("HourMetrics Version", finalProperties.getHourMetrics().getVersion());
validateRetentionPolicy(finalProperties.getHourMetrics().getRetentionPolicy(), "HourMetrics Retention "
+ "Policy");
if (finalProperties.getHourMetrics().isEnabled()) {
StorageImplUtils.assertNotNull("HourMetrics IncludeApis",
finalProperties.getHourMetrics().isIncludeApis());
}
}
// Minute Metrics
finalProperties.setMinuteMetrics(properties.getMinuteMetrics());
if (finalProperties.getMinuteMetrics() != null) {
StorageImplUtils.assertNotNull("MinuteMetrics Version",
finalProperties.getMinuteMetrics().getVersion());
validateRetentionPolicy(finalProperties.getMinuteMetrics().getRetentionPolicy(), "MinuteMetrics "
+ "Retention Policy");
if (finalProperties.getMinuteMetrics().isEnabled()) {
StorageImplUtils.assertNotNull("MinuteMetrics IncludeApis",
finalProperties.getHourMetrics().isIncludeApis());
}
}
// CORS
if (properties.getCors() != null) {
List corsRules = new ArrayList<>();
for (BlobCorsRule rule : properties.getCors()) {
corsRules.add(validatedCorsRule(rule));
}
finalProperties.setCors(corsRules);
}
// Default Service Version
finalProperties.setDefaultServiceVersion(properties.getDefaultServiceVersion());
// Delete Retention Policy
finalProperties.setDeleteRetentionPolicy(properties.getDeleteRetentionPolicy());
validateRetentionPolicy(finalProperties.getDeleteRetentionPolicy(), "DeleteRetentionPolicy Days");
// Static Website
finalProperties.setStaticWebsite(properties.getStaticWebsite());
}
context = context == null ? Context.NONE : context;
return this.azureBlobStorage.getServices().setPropertiesWithResponseAsync(finalProperties, null, null, context)
.map(response -> new SimpleResponse<>(response, null));
}
/**
* Sets any null fields to "" since the service requires all Cors rules to be set if some are set.
* @param originalRule {@link BlobCorsRule}
* @return The validated {@link BlobCorsRule}
*/
private BlobCorsRule validatedCorsRule(BlobCorsRule originalRule) {
if (originalRule == null) {
return null;
}
BlobCorsRule validRule = new BlobCorsRule();
validRule.setAllowedHeaders(StorageImplUtils.emptyIfNull(originalRule.getAllowedHeaders()));
validRule.setAllowedMethods(StorageImplUtils.emptyIfNull(originalRule.getAllowedMethods()));
validRule.setAllowedOrigins(StorageImplUtils.emptyIfNull(originalRule.getAllowedOrigins()));
validRule.setExposedHeaders(StorageImplUtils.emptyIfNull(originalRule.getExposedHeaders()));
validRule.setMaxAgeInSeconds(originalRule.getMaxAgeInSeconds());
return validRule;
}
/**
* Validates a {@link BlobRetentionPolicy} according to service specs for set properties.
* @param retentionPolicy {@link BlobRetentionPolicy}
* @param policyName The name of the variable for errors.
*/
private void validateRetentionPolicy(BlobRetentionPolicy retentionPolicy, String policyName) {
if (retentionPolicy == null) {
return;
}
if (retentionPolicy.isEnabled()) {
StorageImplUtils.assertInBounds(policyName, retentionPolicy.getDays(), 1, 365);
}
}
/**
* Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
*
* Code Samples
*
*
*
* client.getUserDelegationKey(delegationKeyStartTime, delegationKeyExpiryTime).subscribe(response ->
* System.out.printf("User delegation key: %s%n", response.getValue()));
*
*
*
* @param start Start time for the key's validity. Null indicates immediate start.
* @param expiry Expiration of the key's validity.
* @return A {@link Mono} containing the user delegation key.
* @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}.
* @throws NullPointerException If {@code expiry} is null.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) {
return getUserDelegationKeyWithResponse(start, expiry).flatMap(FluxUtil::toMono);
}
/**
* Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
*
* Code Samples
*
*
*
* client.getUserDelegationKeyWithResponse(delegationKeyStartTime, delegationKeyExpiryTime).subscribe(response ->
* System.out.printf("User delegation key: %s%n", response.getValue().getValue()));
*
*
*
* @param start Start time for the key's validity. Null indicates immediate start.
* @param expiry Expiration of the key's validity.
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} containing the user
* delegation key.
* @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}.
* @throws NullPointerException If {@code expiry} is null.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> getUserDelegationKeyWithResponse(OffsetDateTime start,
OffsetDateTime expiry) {
try {
return withContext(context -> getUserDelegationKeyWithResponse(start, expiry, context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> getUserDelegationKeyWithResponse(OffsetDateTime start, OffsetDateTime expiry,
Context context) {
StorageImplUtils.assertNotNull("expiry", expiry);
if (start != null && !start.isBefore(expiry)) {
throw LOGGER.logExceptionAsError(
new IllegalArgumentException("`start` must be null or a datetime before `expiry`."));
}
throwOnAnonymousAccess();
context = context == null ? Context.NONE : context;
return this.azureBlobStorage.getServices().getUserDelegationKeyWithResponseAsync(
new KeyInfo()
.setStart(start == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(start))
.setExpiry(Constants.ISO_8601_UTC_DATE_FORMATTER.format(expiry)),
null, null, context)
.map(rb -> new SimpleResponse<>(rb, rb.getValue()));
}
/**
* Retrieves statistics related to replication for the Blob service. It is only available on the secondary location
* endpoint when read-access geo-redundant replication is enabled for the storage account. For more information, see
* the
* Azure Docs.
*
* Code Samples
*
*
*
* client.getStatistics().subscribe(response ->
* System.out.printf("Geo-replication status: %s%n", response.getGeoReplication().getStatus()));
*
*
*
* @return A {@link Mono} containing the storage account statistics.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getStatistics() {
return getStatisticsWithResponse().flatMap(FluxUtil::toMono);
}
/**
* Retrieves statistics related to replication for the Blob service. It is only available on the secondary location
* endpoint when read-access geo-redundant replication is enabled for the storage account. For more information, see
* the
* Azure Docs.
*
* Code Samples
*
*
*
* client.getStatisticsWithResponse().subscribe(response ->
* System.out.printf("Geo-replication status: %s%n", response.getValue().getGeoReplication().getStatus()));
*
*
*
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} containing the
* storage account statistics.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> getStatisticsWithResponse() {
try {
return withContext(this::getStatisticsWithResponse);
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> getStatisticsWithResponse(Context context) {
throwOnAnonymousAccess();
context = context == null ? Context.NONE : context;
return this.azureBlobStorage.getServices().getStatisticsWithResponseAsync(null, null, context)
.map(rb -> new SimpleResponse<>(rb, rb.getValue()));
}
/**
* Returns the sku name and account kind for the account. For more information, please see the
* Azure Docs.
* Code Samples
*
*
*
* client.getAccountInfo().subscribe(response ->
* System.out.printf("Account kind: %s, SKU: %s%n", response.getAccountKind(), response.getSkuName()));
*
*
*
* @return A {@link Mono} containing the storage account info.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getAccountInfo() {
return getAccountInfoWithResponse().flatMap(FluxUtil::toMono);
}
/**
* Returns the sku name and account kind for the account. For more information, please see the
* Azure Docs.
*
* Code Samples
*
*
*
* client.getAccountInfoWithResponse().subscribe(response ->
* System.out.printf("Account kind: %s, SKU: %s%n", response.getValue().getAccountKind(),
* response.getValue().getSkuName()));
*
*
*
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} the storage account
* info.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> getAccountInfoWithResponse() {
try {
return withContext(this::getAccountInfoWithResponse);
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> getAccountInfoWithResponse(Context context) {
throwOnAnonymousAccess();
return this.azureBlobStorage.getServices().getAccountInfoWithResponseAsync(context)
.map(rb -> {
ServicesGetAccountInfoHeaders hd = rb.getDeserializedHeaders();
return new SimpleResponse<>(rb, new StorageAccountInfo(hd.getXMsSkuName(), hd.getXMsAccountKind(),
hd.isXMsIsHnsEnabled()));
});
}
/**
* Get associated account name.
*
* @return account name associated with this storage resource.
*/
public String getAccountName() {
return this.accountName;
}
/**
* Generates an account SAS for the Azure Storage account using the specified {@link AccountSasSignatureValues}.
* Note : The client must be authenticated via {@link StorageSharedKeyCredential}
*
See {@link AccountSasSignatureValues} for more information on how to construct an account SAS.
*
* The snippet below generates a SAS that lasts for two days and gives the user read and list access to blob
* containers and file shares.
*
*
* AccountSasPermission permissions = new AccountSasPermission()
* .setListPermission(true)
* .setReadPermission(true);
* AccountSasResourceType resourceTypes = new AccountSasResourceType().setContainer(true);
* AccountSasService services = new AccountSasService().setBlobAccess(true).setFileAccess(true);
* OffsetDateTime expiryTime = OffsetDateTime.now().plus(Duration.ofDays(2));
*
* AccountSasSignatureValues sasValues =
* new AccountSasSignatureValues(expiryTime, permissions, services, resourceTypes);
*
* // Client must be authenticated via StorageSharedKeyCredential
* String sas = client.generateAccountSas(sasValues);
*
*
*
* @param accountSasSignatureValues {@link AccountSasSignatureValues}
*
* @return A {@code String} representing the SAS query parameters.
*/
public String generateAccountSas(AccountSasSignatureValues accountSasSignatureValues) {
return generateAccountSas(accountSasSignatureValues, Context.NONE);
}
/**
* Generates an account SAS for the Azure Storage account using the specified {@link AccountSasSignatureValues}.
* Note : The client must be authenticated via {@link StorageSharedKeyCredential}
*
See {@link AccountSasSignatureValues} for more information on how to construct an account SAS.
*
* The snippet below generates a SAS that lasts for two days and gives the user read and list access to blob
* containers and file shares.
*
*
* AccountSasPermission permissions = new AccountSasPermission()
* .setListPermission(true)
* .setReadPermission(true);
* AccountSasResourceType resourceTypes = new AccountSasResourceType().setContainer(true);
* AccountSasService services = new AccountSasService().setBlobAccess(true).setFileAccess(true);
* OffsetDateTime expiryTime = OffsetDateTime.now().plus(Duration.ofDays(2));
*
* AccountSasSignatureValues sasValues =
* new AccountSasSignatureValues(expiryTime, permissions, services, resourceTypes);
*
* // Client must be authenticated via StorageSharedKeyCredential
* String sas = client.generateAccountSas(sasValues, new Context("key", "value"));
*
*
*
* @param accountSasSignatureValues {@link AccountSasSignatureValues}
* @param context Additional context that is passed through the code when generating a SAS.
*
* @return A {@code String} representing the SAS query parameters.
*/
public String generateAccountSas(AccountSasSignatureValues accountSasSignatureValues, Context context) {
throwOnAnonymousAccess();
return new AccountSasImplUtil(accountSasSignatureValues,
this.encryptionScope == null ? null : this.encryptionScope.getEncryptionScope())
.generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), context);
}
/**
* Checks if service client was built with credentials.
*/
private void throwOnAnonymousAccess() {
if (anonymousAccess) {
throw LOGGER.logExceptionAsError(new IllegalStateException("Service client cannot be accessed without "
+ "credentials"));
}
}
/**
* Restores a previously deleted container.
* If the container associated with provided deletedContainerName
* already exists, this call will result in a 409 (conflict).
* This API is only functional if Container Soft Delete is enabled
* for the storage account associated with the container.
*
* Code Samples
*
*
*
* ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions();
* listBlobContainersOptions.getDetails().setRetrieveDeleted(true);
* client.listBlobContainers(listBlobContainersOptions).flatMap(
* deletedContainer -> {
* Mono<BlobContainerAsyncClient> blobContainerClient = client.undeleteBlobContainer(
* deletedContainer.getName(), deletedContainer.getVersion());
* return blobContainerClient;
* }
* ).then().block();
*
*
*
* @param deletedContainerName The name of the previously deleted container.
* @param deletedContainerVersion The version of the previously deleted container.
* @return A {@link Mono} containing a {@link BlobContainerAsyncClient} used
* to interact with the restored container.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono undeleteBlobContainer(String deletedContainerName,
String deletedContainerVersion) {
try {
return this.undeleteBlobContainerWithResponse(new UndeleteBlobContainerOptions(deletedContainerName,
deletedContainerVersion))
.flatMap(FluxUtil::toMono);
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
/**
* Restores a previously deleted container. The restored container
* will be renamed to the destinationContainerName
if provided in options
.
* Otherwise deletedContainerName
is used as destination container name.
* If the container associated with provided destinationContainerName
* already exists, this call will result in a 409 (conflict).
* This API is only functional if Container Soft Delete is enabled
* for the storage account associated with the container.
*
* Code Samples
*
*
*
* ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions();
* listBlobContainersOptions.getDetails().setRetrieveDeleted(true);
* client.listBlobContainers(listBlobContainersOptions).flatMap(
* deletedContainer -> {
* Mono<BlobContainerAsyncClient> blobContainerClient = client.undeleteBlobContainerWithResponse(
* new UndeleteBlobContainerOptions(deletedContainer.getName(), deletedContainer.getVersion()))
* .map(Response::getValue);
* return blobContainerClient;
* }
* ).then().block();
*
*
*
* @param options {@link UndeleteBlobContainerOptions}.
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains a {@link
* BlobContainerAsyncClient} used to interact with the restored container.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono> undeleteBlobContainerWithResponse(
UndeleteBlobContainerOptions options) {
try {
return withContext(context -> undeleteBlobContainerWithResponse(options, context));
} catch (RuntimeException ex) {
return monoError(LOGGER, ex);
}
}
Mono> undeleteBlobContainerWithResponse(
UndeleteBlobContainerOptions options, Context context) {
StorageImplUtils.assertNotNull("options", options);
boolean hasOptionalDestinationContainerName = options.getDestinationContainerName() != null;
String finalDestinationContainerName =
hasOptionalDestinationContainerName ? options.getDestinationContainerName()
: options.getDeletedContainerName();
context = context == null ? Context.NONE : context;
return this.azureBlobStorage.getContainers().restoreWithResponseAsync(finalDestinationContainerName, null,
null, options.getDeletedContainerName(), options.getDeletedContainerVersion(), context)
.map(response -> new SimpleResponse<>(response,
getBlobContainerAsyncClient(finalDestinationContainerName)));
}
// /**
// * Renames an existing blob container.
// *
// * Code Samples
// *
// *
// *
// *
// * @param sourceContainerName The current name of the container.
// * @param destinationContainerName The new name of the container.
// * @return A {@link Mono} containing a {@link BlobContainerAsyncClient} used to interact with the renamed container.
// */
// @ServiceMethod(returns = ReturnType.SINGLE)
// Mono renameBlobContainer(String sourceContainerName,
// String destinationContainerName) {
// return renameBlobContainerWithResponse(sourceContainerName,
// new BlobContainerRenameOptions(destinationContainerName)).flatMap(FluxUtil::toMono);
// }
//
// /**
// * Renames an existing blob container.
// *
// * Code Samples
// *
// *
// *
// *
// * @param sourceContainerName The current name of the container.
// * @param options {@link BlobContainerRenameOptions}
// * @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains a
// * {@link BlobContainerAsyncClient} used to interact with the renamed container.
// */
// @ServiceMethod(returns = ReturnType.SINGLE)
// Mono> renameBlobContainerWithResponse(String sourceContainerName,
// BlobContainerRenameOptions options) {
// try {
// return withContext(context -> renameBlobContainerWithResponse(sourceContainerName, options, context));
// } catch (RuntimeException ex) {
// return monoError(logger, ex);
// }
// }
//
// Mono> renameBlobContainerWithResponse(String sourceContainerName,
// BlobContainerRenameOptions options, Context context) {
// BlobContainerAsyncClient destinationContainerClient = getBlobContainerAsyncClient(
// options.getDestinationContainerName());
// return destinationContainerClient.renameWithResponseHelper(sourceContainerName, options, context);
// }
}