diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java
index 60376a3d990a..7275f0fb326f 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java
@@ -24,8 +24,8 @@
/**
- * Client to an append blob. It may only be instantiated through a {@link AppendBlobClientBuilder#buildAsyncClient()}, via
- * the method {@link BlobAsyncClient#asAppendBlobAsyncClient()}, or via the method
+ * Client to an append blob. It may only be instantiated through a {@link BlobClientBuilder#buildAppendBlobAsyncClient()},
+ * via the method {@link BlobAsyncClient#asAppendBlobAsyncClient()}, or via the method
* {@link ContainerAsyncClient#getAppendBlobAsyncClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
* requests to the resource on the service.
@@ -58,7 +58,7 @@ public final class AppendBlobAsyncClient extends BlobAsyncClient {
public static final int MAX_BLOCKS = 50000;
/**
- * Package-private constructor for use by {@link AppendBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param azureBlobStorageBuilder the API client builder for blob storage API
*/
AppendBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java
index 6be2bf7f4e5a..246fe645af17 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java
@@ -23,7 +23,7 @@
/**
- * Client to an append blob. It may only be instantiated through a {@link AppendBlobClientBuilder}, via
+ * Client to an append blob. It may only be instantiated through a {@link BlobClientBuilder}, via
* the method {@link BlobClient#asAppendBlobClient()}, or via the method
* {@link ContainerClient#getAppendBlobClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
@@ -51,7 +51,7 @@ public final class AppendBlobClient extends BlobClient {
public static final int MAX_BLOCKS = AppendBlobAsyncClient.MAX_BLOCKS;
/**
- * Package-private constructor for use by {@link AppendBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param appendBlobAsyncClient the async append blob client
*/
AppendBlobClient(AppendBlobAsyncClient appendBlobAsyncClient) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java
deleted file mode 100644
index c46883e7fecf..000000000000
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.storage.blob;
-
-import com.azure.core.credentials.TokenCredential;
-import com.azure.core.http.HttpClient;
-import com.azure.core.http.HttpPipeline;
-import com.azure.core.http.policy.AddDatePolicy;
-import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
-import com.azure.core.http.policy.HttpLogDetailLevel;
-import com.azure.core.http.policy.HttpLoggingPolicy;
-import com.azure.core.http.policy.HttpPipelinePolicy;
-import com.azure.core.http.policy.RequestIdPolicy;
-import com.azure.core.http.policy.UserAgentPolicy;
-import com.azure.core.implementation.util.ImplUtils;
-import com.azure.core.util.configuration.Configuration;
-import com.azure.core.util.configuration.ConfigurationManager;
-import com.azure.storage.blob.implementation.AzureBlobStorageBuilder;
-import com.azure.storage.common.credentials.SASTokenCredential;
-import com.azure.storage.common.credentials.SharedKeyCredential;
-import com.azure.storage.common.policy.RequestRetryOptions;
-import com.azure.storage.common.policy.RequestRetryPolicy;
-import com.azure.storage.common.policy.SASTokenCredentialPolicy;
-import com.azure.storage.common.policy.SharedKeyCredentialPolicy;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Fluent AppendBlobClientBuilder for instantiating a {@link AppendBlobClient} or {@link AppendBlobAsyncClient}
- * using {@link AppendBlobClientBuilder#buildAsyncClient()} or {@link AppendBlobClientBuilder#buildAsyncClient()} respectively.
- *
- *
- * The following information must be provided on this builder:
- *
- *
- * - the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}.
- *
- the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible.
- *
- *
- *
- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a
- * {@link AppendBlobClient} or {@code .buildAsyncClient()} to create a {@link AppendBlobAsyncClient}.
- */
-public final class AppendBlobClientBuilder {
- private static final String ACCOUNT_NAME = "accountname";
- private static final String ACCOUNT_KEY = "accountkey";
- private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol";
- private static final String ENDPOINT_SUFFIX = "endpointsuffix";
-
- private final List policies;
-
- private String endpoint;
- private String containerName;
- private String blobName;
- private String snapshot;
- private SharedKeyCredential sharedKeyCredential;
- private TokenCredential tokenCredential;
- private SASTokenCredential sasTokenCredential;
- private HttpClient httpClient;
- private HttpLogDetailLevel logLevel;
- private RequestRetryOptions retryOptions;
- private Configuration configuration;
-
- /**
- * Creates a builder instance that is able to configure and construct {@link AppendBlobClient AppendBlobClients}
- * and {@link AppendBlobAsyncClient AppendBlobAsyncClients}.
- */
- public AppendBlobClientBuilder() {
- retryOptions = new RequestRetryOptions();
- logLevel = HttpLogDetailLevel.NONE;
- policies = new ArrayList<>();
- }
-
- private AzureBlobStorageBuilder buildImpl() {
- Objects.requireNonNull(endpoint);
- Objects.requireNonNull(containerName);
- Objects.requireNonNull(blobName);
-
- // Closest to API goes first, closest to wire goes last.
- final List policies = new ArrayList<>();
-
- if (configuration == null) {
- configuration = ConfigurationManager.getConfiguration();
- }
- policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration));
- policies.add(new RequestIdPolicy());
- policies.add(new AddDatePolicy());
-
- if (sharedKeyCredential != null) {
- policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
- } else if (tokenCredential != null) {
- policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint)));
- } else if (sasTokenCredential != null) {
- policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
- } else {
- policies.add(new AnonymousCredentialPolicy());
- }
-
- policies.add(new RequestRetryPolicy(retryOptions));
-
- policies.addAll(this.policies);
- policies.add(new HttpLoggingPolicy(logLevel));
-
- HttpPipeline pipeline = HttpPipeline.builder()
- .policies(policies.toArray(new HttpPipelinePolicy[0]))
- .httpClient(httpClient)
- .build();
-
- return new AzureBlobStorageBuilder()
- .url(String.format("%s/%s/%s", endpoint, containerName, blobName))
- .pipeline(pipeline);
- }
-
- /**
- * @return a {@link AppendBlobClient} created from the configurations in this builder.
- */
- public AppendBlobClient buildClient() {
- return new AppendBlobClient(buildAsyncClient());
- }
-
- /**
- * @return a {@link AppendBlobAsyncClient} created from the configurations in this builder.
- */
- public AppendBlobAsyncClient buildAsyncClient() {
- return new AppendBlobAsyncClient(buildImpl(), snapshot);
- }
-
- /**
- * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name)
- * @param endpoint URL of the service
- * @return the updated AppendBlobClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL or is using an unknown host.
- */
- public AppendBlobClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
- try {
- url = new URL(endpoint);
- BlobURLParts parts = URLParser.parse(url);
- this.endpoint = parts.scheme() + "://" + parts.host();
-
- if (parts.containerName() != null) {
- this.containerName = parts.containerName();
- }
-
- if (parts.blobName() != null) {
- this.blobName = parts.blobName();
- }
-
- if (parts.snapshot() != null) {
- this.snapshot = parts.snapshot();
- }
- } catch (MalformedURLException ex) {
- throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed.");
- }
-
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
- return this;
- }
-
- /**
- * Sets the name of the container this client is connecting to.
- * @param containerName the name of the container
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder containerName(String containerName) {
- this.containerName = containerName;
- return this;
- }
-
- /**
- * Sets the name of the blob this client is connecting to.
- * @param blobName the name of the blob
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder blobName(String blobName) {
- this.blobName = blobName;
- return this;
- }
-
- /**
- * Sets the snapshot of the blob this client is connecting to.
- * @param snapshot the snapshot identifier for the blob
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder snapshot(String snapshot) {
- this.snapshot = snapshot;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
- this.sharedKeyCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- return this;
- }
-
- /**
- * Clears the credential used to authorize requests sent to the service
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder anonymousCredential() {
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the connection string for the service, parses it for authentication information (account name, account key)
- * @param connectionString connection string from access keys section
- * @return the updated AppendBlobClientBuilder object
- * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey.
- */
- public AppendBlobClientBuilder connectionString(String connectionString) {
- Objects.requireNonNull(connectionString);
-
- Map connectionKVPs = new HashMap<>();
- for (String s : connectionString.split(";")) {
- String[] kvp = s.split("=", 2);
- connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
- }
-
- String accountName = connectionKVPs.get(ACCOUNT_NAME);
- String accountKey = connectionKVPs.get(ACCOUNT_KEY);
- String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL);
- String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX);
-
- if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) {
- throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'.");
- }
-
- if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) {
- String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", ""));
- endpoint(endpoint);
- }
-
- // Use accountName and accountKey to get the SAS token using the credential class.
- return credential(new SharedKeyCredential(accountName, accountKey));
- }
-
- /**
- * Sets the http client used to send service requests
- * @param httpClient http client to send requests
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
- return this;
- }
-
- /**
- * Adds a pipeline policy to apply on each request sent
- * @param pipelinePolicy a pipeline policy
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
- return this;
- }
-
- /**
- * Sets the logging level for service requests
- * @param logLevel logging level
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
- this.logLevel = logLevel;
- return this;
- }
-
- /**
- * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with
- * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE
- * @param configuration configuration store
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder configuration(Configuration configuration) {
- this.configuration = configuration;
- return this;
- }
-
- /**
- * Sets the request retry options for all the requests made through the client.
- * @param retryOptions the options to configure retry behaviors
- * @return the updated AppendBlobClientBuilder object
- */
- public AppendBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
- return this;
- }
-}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java
index c8ada7d8722f..c8ceb3f311d3 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java
@@ -71,11 +71,12 @@
* 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()}.
*/
+@SuppressWarnings({"unused", "WeakerAccess"})
public class BlobAsyncClient {
private static final int BLOB_DEFAULT_DOWNLOAD_BLOCK_SIZE = 4 * Constants.MB;
private static final int BLOB_MAX_DOWNLOAD_BLOCK_SIZE = 100 * Constants.MB;
- protected final AzureBlobStorageImpl azureBlobStorage;
+ final AzureBlobStorageImpl azureBlobStorage;
protected final String snapshot;
/**
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java
index a937b8ca4761..e2c6eab47a76 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java
@@ -45,6 +45,7 @@
* Please refer to the Azure
* Docs for more information.
*/
+@SuppressWarnings({"unused", "WeakerAccess"})
public class BlobClient {
private final BlobAsyncClient blobAsyncClient;
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java
index 2238af18e48e..b0e3c98d8904 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java
@@ -17,13 +17,17 @@
import com.azure.core.util.configuration.Configuration;
import com.azure.core.util.configuration.ConfigurationManager;
import com.azure.storage.blob.implementation.AzureBlobStorageBuilder;
+import com.azure.storage.blob.models.PageRange;
import com.azure.storage.common.credentials.SASTokenCredential;
import com.azure.storage.common.credentials.SharedKeyCredential;
import com.azure.storage.common.policy.RequestRetryOptions;
import com.azure.storage.common.policy.RequestRetryPolicy;
import com.azure.storage.common.policy.SASTokenCredentialPolicy;
import com.azure.storage.common.policy.SharedKeyCredentialPolicy;
+import reactor.core.publisher.Flux;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -34,8 +38,7 @@
import java.util.Objects;
/**
- * Fluent BlobClientBuilder for instantiating a {@link BlobClient} or {@link BlobAsyncClient}
- * using {@link BlobClientBuilder#buildClient()} or {@link BlobClientBuilder#buildAsyncClient()} respectively.
+ * This class provides a fluent builder API to help aid the configuration and instantiation Storage Blob clients.
*
*
* The following information must be provided on this builder:
@@ -46,8 +49,17 @@
*
*
*
- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a
- * {@link BlobClient} or {@code .buildAsyncClient()} to create a {@link BlobAsyncClient}.
+ * Once all the configurations are set on this builder use the following mapping to construct the given client:
+ *
+ * - {@link BlobClientBuilder#buildBlobClient()} - {@link BlobClient}
+ * - {@link BlobClientBuilder#buildBlobAsyncClient()} - {@link BlobAsyncClient}
+ * - {@link BlobClientBuilder#buildAppendBlobClient()} - {@link AppendBlobClient}
+ * - {@link BlobClientBuilder#buildAppendBlobAsyncClient()} - {@link AppendBlobAsyncClient}
+ * - {@link BlobClientBuilder#buildBlockBlobClient()} - {@link BlockBlobClient}
+ * - {@link BlobClientBuilder#buildBlockBlobAsyncClient()} - {@link BlockBlobAsyncClient}
+ * - {@link BlobClientBuilder#buildPageBlobClient()} - {@link PageBlobClient}
+ * - {@link BlobClientBuilder#buildPageBlobAsyncClient()} - {@link PageBlobAsyncClient}
+ *
*/
public final class BlobClientBuilder {
private static final String ACCOUNT_NAME = "accountname";
@@ -70,8 +82,7 @@ public final class BlobClientBuilder {
private Configuration configuration;
/**
- * Creates a builder instance that is able to configure and construct {@link BlobClient BlobClients}
- * and {@link BlobAsyncClient BlobAsyncClients}.
+ * Creates a builder instance that is able to configure and construct Storage Blob clients.
*/
public BlobClientBuilder() {
retryOptions = new RequestRetryOptions();
@@ -120,53 +131,130 @@ private AzureBlobStorageBuilder buildImpl() {
}
/**
+ * Creates a {@link BlobClient} based on options set in the Builder. BlobClients are used to perform generic blob
+ * methods such as {@link BlobClient#download(OutputStream) download} and
+ * {@link BlobClient#getProperties() get properties}, use this when the blob type is unknown.
+ *
* @return a {@link BlobClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
*/
- public BlobClient buildClient() {
- return new BlobClient(buildAsyncClient());
+ public BlobClient buildBlobClient() {
+ return new BlobClient(buildBlobAsyncClient());
}
/**
+ * Creates a {@link BlobAsyncClient} based on options set in the Builder. BlobAsyncClients are used to perform
+ * generic blob methods such as {@link BlobAsyncClient#download() download} and
+ * {@link BlobAsyncClient#getProperties()}, use this when the blob type is unknown.
+ *
* @return a {@link BlobAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
*/
- public BlobAsyncClient buildAsyncClient() {
+ public BlobAsyncClient buildBlobAsyncClient() {
return new BlobAsyncClient(buildImpl(), snapshot);
}
+ /**
+ * Creates a {@link AppendBlobClient} based on options set in the Builder. AppendBlobClients are used to perform
+ * append blob specific operations such as {@link AppendBlobClient#appendBlock(InputStream, long) append block},
+ * only use this when the blob is known to be an append blob.
+ *
+ * @return a {@link AppendBlobClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public AppendBlobClient buildAppendBlobClient() {
+ return new AppendBlobClient(buildAppendBlobAsyncClient());
+ }
+
+ /**
+ * Creates a {@link AppendBlobAsyncClient} based on options set in the Builder. AppendBlobAsyncClients are used to
+ * perform append blob specific operations such as {@link AppendBlobAsyncClient#appendBlock(Flux, long) append blob},
+ * only use this when the blob is known to be an append blob.
+ *
+ * @return a {@link AppendBlobAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public AppendBlobAsyncClient buildAppendBlobAsyncClient() {
+ return new AppendBlobAsyncClient(buildImpl(), snapshot);
+ }
+
+ /**
+ * Creates a {@link BlockBlobClient} based on options set in the Builder. BlockBlobClients are used to perform
+ * generic upload operations such as {@link BlockBlobClient#uploadFromFile(String) upload from file} and block
+ * blob specific operations such as {@link BlockBlobClient#stageBlock(String, InputStream, long) stage block} and
+ * {@link BlockBlobClient#commitBlockList(List)}, only use this when the blob is known to be a block blob.
+ *
+ * @return a {@link BlockBlobClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public BlockBlobClient buildBlockBlobClient() {
+ return new BlockBlobClient(buildBlockBlobAsyncClient());
+ }
+
+ /**
+ * Creates a {@link BlockBlobAsyncClient} based on options set in the Builder. BlockBlobAsyncClients are used to
+ * perform generic upload operations such as {@link BlockBlobAsyncClient#uploadFromFile(String) upload from file}
+ * and block blob specific operations such as {@link BlockBlobAsyncClient#stageBlock(String, Flux, long) stage block}
+ * and {@link BlockBlobAsyncClient#commitBlockList(List) commit block list}, only use this when the blob is known to
+ * be a block blob.
+ *
+ * @return a {@link BlockBlobAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public BlockBlobAsyncClient buildBlockBlobAsyncClient() {
+ return new BlockBlobAsyncClient(buildImpl(), snapshot);
+ }
+
+ /**
+ * Creates a {@link PageBlobClient} based on options set in the Builder. PageBlobClients are used to perform page
+ * blob specific operations such as {@link PageBlobClient#uploadPages(PageRange, InputStream) upload pages} and
+ * {@link PageBlobClient#clearPages(PageRange) clear pages}, only use this when the blob is known to be a page blob.
+ *
+ * @return a {@link PageBlobClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public PageBlobClient buildPageBlobClient() {
+ return new PageBlobClient(buildPageBlobAsyncClient());
+ }
+
+ /**
+ * Creates a {@link PageBlobAsyncClient} based on options set in the Builder. PageBlobAsyncClients are used to
+ * perform page blob specific operations such as {@link PageBlobAsyncClient#uploadPages(PageRange, Flux) upload pages}
+ * and {@link PageBlobAsyncClient#clearPages(PageRange) clear pages}, only use this when the blob is known to be a
+ * page blob.
+ *
+ * @return a {@link PageBlobAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}.
+ */
+ public PageBlobAsyncClient buildPageBlobAsyncClient() {
+ return new PageBlobAsyncClient(buildImpl(), snapshot);
+ }
+
/**
* Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name)
* @param endpoint URL of the service
* @return the updated BlobClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL.
+ * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL.
*/
public BlobClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
try {
- url = new URL(endpoint);
+ URL url = new URL(endpoint);
BlobURLParts parts = URLParser.parse(url);
- this.endpoint = parts.scheme() + "://" + parts.host();
-
- if (parts.containerName() != null) {
- this.containerName = parts.containerName();
- }
-
- if (parts.blobName() != null) {
- this.blobName = parts.blobName();
- }
- if (parts.snapshot() != null) {
- this.snapshot = parts.snapshot();
+ this.endpoint = parts.scheme() + "://" + parts.host();
+ this.containerName = parts.containerName();
+ this.blobName = parts.blobName();
+ this.snapshot = parts.snapshot();
+
+ this.sasTokenCredential = SASTokenCredential.fromQueryParameters(parts.sasQueryParameters());
+ if (this.sasTokenCredential != null) {
+ this.tokenCredential = null;
+ this.sharedKeyCredential = null;
}
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed.");
}
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
return this;
}
@@ -174,9 +262,10 @@ public BlobClientBuilder endpoint(String endpoint) {
* Sets the name of the container this client is connecting to.
* @param containerName the name of the container
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code containerName} is {@code null}
*/
public BlobClientBuilder containerName(String containerName) {
- this.containerName = containerName;
+ this.containerName = Objects.requireNonNull(containerName);
return this;
}
@@ -184,9 +273,10 @@ public BlobClientBuilder containerName(String containerName) {
* Sets the name of the blob this client is connecting to.
* @param blobName the name of the blob
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code blobName} is {@code null}
*/
public BlobClientBuilder blobName(String blobName) {
- this.blobName = blobName;
+ this.blobName = Objects.requireNonNull(blobName);
return this;
}
@@ -204,9 +294,10 @@ public BlobClientBuilder snapshot(String snapshot) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}
*/
public BlobClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
+ this.sharedKeyCredential = Objects.requireNonNull(credential);
this.tokenCredential = null;
this.sasTokenCredential = null;
return this;
@@ -216,9 +307,10 @@ public BlobClientBuilder credential(SharedKeyCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}
*/
public BlobClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
+ this.tokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.sasTokenCredential = null;
return this;
@@ -228,9 +320,10 @@ public BlobClientBuilder credential(TokenCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}
*/
public BlobClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
+ this.sasTokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.tokenCredential = null;
return this;
@@ -251,6 +344,7 @@ public BlobClientBuilder anonymousCredential() {
* Sets the connection string for the service, parses it for authentication information (account name, account key)
* @param connectionString connection string from access keys section
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code connectionString} is {@code null}
* @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey.
*/
public BlobClientBuilder connectionString(String connectionString) {
@@ -284,9 +378,10 @@ public BlobClientBuilder connectionString(String connectionString) {
* Sets the http client used to send service requests
* @param httpClient http client to send requests
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code httpClient} is {@code null}
*/
public BlobClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
+ this.httpClient = Objects.requireNonNull(httpClient);
return this;
}
@@ -294,9 +389,10 @@ public BlobClientBuilder httpClient(HttpClient httpClient) {
* Adds a pipeline policy to apply on each request sent
* @param pipelinePolicy a pipeline policy
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}
*/
public BlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
+ this.policies.add(Objects.requireNonNull(pipelinePolicy));
return this;
}
@@ -325,9 +421,10 @@ public BlobClientBuilder configuration(Configuration configuration) {
* Sets the request retry options for all the requests made through the client.
* @param retryOptions the options to configure retry behaviors
* @return the updated BlobClientBuilder object
+ * @throws NullPointerException If {@code retryOptions} is {@code null}
*/
public BlobClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
+ this.retryOptions = Objects.requireNonNull(retryOptions);
return this;
}
}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java
index e7d8aa9b0c03..cdb0a06fa68e 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java
@@ -42,7 +42,7 @@
import static com.azure.storage.blob.Utility.postProcessResponse;
/**
- * Client to a block blob. It may only be instantiated through a {@link BlockBlobClientBuilder}, via
+ * Client to a block blob. It may only be instantiated through a {@link BlobClientBuilder}, via
* the method {@link BlobAsyncClient#asBlockBlobAsyncClient()}, or via the method
* {@link ContainerAsyncClient#getBlockBlobAsyncClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
@@ -84,7 +84,7 @@ public final class BlockBlobAsyncClient extends BlobAsyncClient {
public static final int MAX_BLOCKS = 50000;
/**
- * Package-private constructor for use by {@link BlockBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param azureBlobStorageBuilder the API client builder for blob storage API
*/
BlockBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java
index ec76f684c1bb..6af92555c2b0 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java
@@ -28,7 +28,7 @@
import java.util.List;
/**
- * Client to a block blob. It may only be instantiated through a {@link BlockBlobClientBuilder}, via
+ * Client to a block blob. It may only be instantiated through a {@link BlobClientBuilder}, via
* the method {@link BlobClient#asBlockBlobClient()}, or via the method
* {@link ContainerClient#getBlockBlobClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
@@ -61,7 +61,7 @@ public final class BlockBlobClient extends BlobClient {
public static final int MAX_BLOCKS = BlockBlobAsyncClient.MAX_BLOCKS;
/**
- * Package-private constructor for use by {@link BlockBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param blockBlobAsyncClient the async block blob client
*/
BlockBlobClient(BlockBlobAsyncClient blockBlobAsyncClient) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java
deleted file mode 100644
index 4774228c2764..000000000000
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.storage.blob;
-
-import com.azure.core.credentials.TokenCredential;
-import com.azure.core.http.HttpClient;
-import com.azure.core.http.HttpPipeline;
-import com.azure.core.http.policy.AddDatePolicy;
-import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
-import com.azure.core.http.policy.HttpLogDetailLevel;
-import com.azure.core.http.policy.HttpLoggingPolicy;
-import com.azure.core.http.policy.HttpPipelinePolicy;
-import com.azure.core.http.policy.RequestIdPolicy;
-import com.azure.core.http.policy.UserAgentPolicy;
-import com.azure.core.implementation.util.ImplUtils;
-import com.azure.core.util.configuration.Configuration;
-import com.azure.core.util.configuration.ConfigurationManager;
-import com.azure.storage.blob.implementation.AzureBlobStorageBuilder;
-import com.azure.storage.common.credentials.SASTokenCredential;
-import com.azure.storage.common.credentials.SharedKeyCredential;
-import com.azure.storage.common.policy.RequestRetryOptions;
-import com.azure.storage.common.policy.RequestRetryPolicy;
-import com.azure.storage.common.policy.SASTokenCredentialPolicy;
-import com.azure.storage.common.policy.SharedKeyCredentialPolicy;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Fluent BlockBlobClientBuilder for instantiating a {@link BlockBlobClient} or {@link BlockBlobAsyncClient}
- * using {@link BlockBlobClientBuilder#buildClient()} or {@link BlockBlobClientBuilder#buildAsyncClient()} respectively.
- *
- *
- * The following information must be provided on this builder:
- *
- *
- * - the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}.
- *
- the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible.
- *
- *
- *
- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a
- * {@link BlockBlobClient} or {@code .buildAsyncClient()} to create a {@link BlockBlobAsyncClient}.
- */
-public final class BlockBlobClientBuilder {
- private static final String ACCOUNT_NAME = "accountname";
- private static final String ACCOUNT_KEY = "accountkey";
- private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol";
- private static final String ENDPOINT_SUFFIX = "endpointsuffix";
-
- private final List policies;
-
- private String endpoint;
- private String containerName;
- private String blobName;
- private String snapshot;
- private SharedKeyCredential sharedKeyCredential;
- private TokenCredential tokenCredential;
- private SASTokenCredential sasTokenCredential;
- private HttpClient httpClient;
- private HttpLogDetailLevel logLevel;
- private RequestRetryOptions retryOptions;
- private Configuration configuration;
-
- /**
- * Creates a builder instance that is able to configure and construct {@link BlockBlobClient BlockBlobClients}
- * and {@link BlockBlobAsyncClient BlockBlobAsyncClients}.
- */
- public BlockBlobClientBuilder() {
- retryOptions = new RequestRetryOptions();
- logLevel = HttpLogDetailLevel.NONE;
- policies = new ArrayList<>();
- }
-
- /**
- * Constructs an instance of BlockBlobAsyncClient based on the configurations stored in the appendBlobClientBuilder.
- * @return a new client instance
- */
- private AzureBlobStorageBuilder buildImpl() {
- Objects.requireNonNull(endpoint);
- Objects.requireNonNull(containerName);
- Objects.requireNonNull(blobName);
-
- // Closest to API goes first, closest to wire goes last.
- final List policies = new ArrayList<>();
-
- if (configuration == null) {
- configuration = ConfigurationManager.getConfiguration();
- }
- policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration));
- policies.add(new RequestIdPolicy());
- policies.add(new AddDatePolicy());
-
- if (sharedKeyCredential != null) {
- policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
- } else if (tokenCredential != null) {
- policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint)));
- } else if (sasTokenCredential != null) {
- policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
- } else {
- policies.add(new AnonymousCredentialPolicy());
- }
-
- policies.add(new RequestRetryPolicy(retryOptions));
-
- policies.addAll(this.policies);
- policies.add(new HttpLoggingPolicy(logLevel));
-
- HttpPipeline pipeline = HttpPipeline.builder()
- .policies(policies.toArray(new HttpPipelinePolicy[0]))
- .httpClient(httpClient)
- .build();
-
- return new AzureBlobStorageBuilder()
- .url(String.format("%s/%s/%s", endpoint, containerName, blobName))
- .pipeline(pipeline);
- }
-
- /**
- * @return a {@link BlockBlobClient} created from the configurations in this builder.
- */
- public BlockBlobClient buildClient() {
- return new BlockBlobClient(buildAsyncClient());
- }
-
- /**
- * @return a {@link BlockBlobAsyncClient} created from the configurations in this builder.
- */
- public BlockBlobAsyncClient buildAsyncClient() {
- return new BlockBlobAsyncClient(buildImpl(), snapshot);
- }
-
- /**
- * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name)
- * @param endpoint URL of the service
- * @return the updated BlockBlobClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL.
- */
- public BlockBlobClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
- try {
- url = new URL(endpoint);
- BlobURLParts parts = URLParser.parse(url);
- this.endpoint = parts.scheme() + "://" + parts.host();
-
- if (parts.containerName() != null) {
- this.containerName = parts.containerName();
- }
-
- if (parts.blobName() != null) {
- this.blobName = parts.blobName();
- }
-
- if (parts.snapshot() != null) {
- this.snapshot = parts.snapshot();
- }
- } catch (MalformedURLException ex) {
- throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed.");
- }
-
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
- return this;
- }
-
- /**
- * Sets the name of the container this client is connecting to.
- * @param containerName the name of the container
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder containerName(String containerName) {
- this.containerName = containerName;
- return this;
- }
-
- /**
- * Sets the name of the blob this client is connecting to.
- * @param blobName the name of the blob
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder blobName(String blobName) {
- this.blobName = blobName;
- return this;
- }
-
- /**
- * Sets the snapshot of the blob this client is connecting to.
- * @param snapshot the snapshot identifier for the blob
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder snapshot(String snapshot) {
- this.snapshot = snapshot;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
- this.sharedKeyCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- return this;
- }
-
- /**
- * Clears the credential used to authorize requests sent to the service
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder anonymousCredential() {
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the connection string for the service, parses it for authentication information (account name, account key)
- * @param connectionString connection string from access keys section
- * @return the updated BlockBlobClientBuilder object
- * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey
- */
- public BlockBlobClientBuilder connectionString(String connectionString) {
- Objects.requireNonNull(connectionString);
-
- Map connectionKVPs = new HashMap<>();
- for (String s : connectionString.split(";")) {
- String[] kvp = s.split("=", 2);
- connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
- }
-
- String accountName = connectionKVPs.get(ACCOUNT_NAME);
- String accountKey = connectionKVPs.get(ACCOUNT_KEY);
- String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL);
- String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX);
-
- if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) {
- throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'.");
- }
-
- if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) {
- String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", ""));
- endpoint(endpoint);
- }
-
- // Use accountName and accountKey to get the SAS token using the credential class.
- return credential(new SharedKeyCredential(accountName, accountKey));
- }
-
- /**
- * Sets the http client used to send service requests
- * @param httpClient http client to send requests
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
- return this;
- }
-
- /**
- * Adds a pipeline policy to apply on each request sent
- * @param pipelinePolicy a pipeline policy
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
- return this;
- }
-
- /**
- * Sets the logging level for service requests
- * @param logLevel logging level
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
- this.logLevel = logLevel;
- return this;
- }
-
- /**
- * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with
- * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE
- * @param configuration configuration store
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder configuration(Configuration configuration) {
- this.configuration = configuration;
- return this;
- }
-
- /**
- * Sets the request retry options for all the requests made through the client.
- * @param retryOptions the options to configure retry behaviors
- * @return the updated BlockBlobClientBuilder object
- */
- public BlockBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
- return this;
- }
-}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java
index c1e4e6bbdaeb..7c9e9543ffd9 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java
@@ -121,14 +121,20 @@ private AzureBlobStorageBuilder buildImpl() {
}
/**
+ * Creates a {@link ContainerClient} based on options set in the Builder.
+ *
* @return a {@link ContainerClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null} or {@code containerName} is {@code null}.
*/
public ContainerClient buildClient() {
return new ContainerClient(buildAsyncClient());
}
/**
+ * Creates a {@link ContainerAsyncClient} based on options set in the Builder.
+ *
* @return a {@link ContainerAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null} or {@code containerName} is {@code null}.
*/
public ContainerAsyncClient buildAsyncClient() {
return new ContainerAsyncClient(buildImpl());
@@ -138,32 +144,25 @@ public ContainerAsyncClient buildAsyncClient() {
* Sets the service endpoint, additionally parses it for information (SAS token, container name)
* @param endpoint URL of the service
* @return the updated ContainerClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL.
+ * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL.
*/
public ContainerClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
try {
- url = new URL(endpoint);
- this.endpoint = url.getProtocol() + "://" + url.getAuthority();
- String path = url.getPath();
- if (path != null && !path.isEmpty() && !path.equals("/")) {
- path = path.replaceAll("^/", "").replaceAll("/$", "");
- if (path.contains("/")) {
- throw new IllegalArgumentException("Endpoint should contain exactly 0 or 1 path segments");
- } else {
- this.containerName = path;
- }
+ URL url = new URL(endpoint);
+ BlobURLParts parts = URLParser.parse(url);
+
+ this.endpoint = parts.scheme() + "://" + parts.host();
+ this.containerName = parts.containerName();
+
+ this.sasTokenCredential = SASTokenCredential.fromQueryParameters(parts.sasQueryParameters());
+ if (this.sasTokenCredential != null) {
+ this.tokenCredential = null;
+ this.sharedKeyCredential = null;
}
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed.");
}
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
return this;
}
@@ -185,9 +184,10 @@ String endpoint() {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public ContainerClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
+ this.sharedKeyCredential = Objects.requireNonNull(credential);
this.tokenCredential = null;
this.sasTokenCredential = null;
return this;
@@ -197,9 +197,10 @@ public ContainerClientBuilder credential(SharedKeyCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public ContainerClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
+ this.tokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.sasTokenCredential = null;
return this;
@@ -209,9 +210,10 @@ public ContainerClientBuilder credential(TokenCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public ContainerClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
+ this.sasTokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.tokenCredential = null;
return this;
@@ -265,9 +267,10 @@ public ContainerClientBuilder connectionString(String connectionString) {
* Sets the http client used to send service requests
* @param httpClient http client to send requests
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code httpClient} is {@code null}.
*/
public ContainerClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
+ this.httpClient = Objects.requireNonNull(httpClient);
return this;
}
@@ -275,9 +278,10 @@ public ContainerClientBuilder httpClient(HttpClient httpClient) {
* Adds a pipeline policy to apply on each request sent
* @param pipelinePolicy a pipeline policy
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}.
*/
public ContainerClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
+ this.policies.add(Objects.requireNonNull(pipelinePolicy));
return this;
}
@@ -306,9 +310,10 @@ public ContainerClientBuilder configuration(Configuration configuration) {
* Sets the request retry options for all the requests made through the client.
* @param retryOptions the options to configure retry behaviors
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code retryOptions} is {@code null}.
*/
public ContainerClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
+ this.retryOptions = Objects.requireNonNull(retryOptions);
return this;
}
}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java
index 72f052fd2757..925f167b2ad9 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java
@@ -29,7 +29,7 @@
import static com.azure.storage.blob.Utility.postProcessResponse;
/**
- * Client to a page blob. It may only be instantiated through a {@link PageBlobClientBuilder}, via
+ * Client to a page blob. It may only be instantiated through a {@link BlobClientBuilder}, via
* the method {@link BlobAsyncClient#asPageBlobAsyncClient()}, or via the method
* {@link ContainerAsyncClient#getPageBlobAsyncClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
@@ -63,7 +63,7 @@ public final class PageBlobAsyncClient extends BlobAsyncClient {
public static final int MAX_PUT_PAGES_BYTES = 4 * Constants.MB;
/**
- * Package-private constructor for use by {@link PageBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param azureBlobStorageBuilder the API client builder for blob storage API
*/
PageBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java
index 62d2146ea8d6..14c0ea4644de 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java
@@ -26,7 +26,7 @@
import java.time.Duration;
/**
- * Client to a page blob. It may only be instantiated through a {@link PageBlobClientBuilder}, via
+ * Client to a page blob. It may only be instantiated through a {@link BlobClientBuilder}, via
* the method {@link BlobClient#asPageBlobClient()}, or via the method
* {@link ContainerClient#getPageBlobClient(String)}. This class does not hold
* any state about a particular blob, but is instead a convenient way of sending appropriate
@@ -54,7 +54,7 @@ public final class PageBlobClient extends BlobClient {
public static final int MAX_PUT_PAGES_BYTES = PageBlobAsyncClient.MAX_PUT_PAGES_BYTES;
/**
- * Package-private constructor for use by {@link PageBlobClientBuilder}.
+ * Package-private constructor for use by {@link BlobClientBuilder}.
* @param pageBlobAsyncClient the async page blob client
*/
PageBlobClient(PageBlobAsyncClient pageBlobAsyncClient) {
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java
deleted file mode 100644
index 77010e0607f8..000000000000
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.storage.blob;
-
-import com.azure.core.credentials.TokenCredential;
-import com.azure.core.http.HttpClient;
-import com.azure.core.http.HttpPipeline;
-import com.azure.core.http.policy.AddDatePolicy;
-import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
-import com.azure.core.http.policy.HttpLogDetailLevel;
-import com.azure.core.http.policy.HttpLoggingPolicy;
-import com.azure.core.http.policy.HttpPipelinePolicy;
-import com.azure.core.http.policy.RequestIdPolicy;
-import com.azure.core.http.policy.UserAgentPolicy;
-import com.azure.core.implementation.util.ImplUtils;
-import com.azure.core.util.configuration.Configuration;
-import com.azure.core.util.configuration.ConfigurationManager;
-import com.azure.storage.blob.implementation.AzureBlobStorageBuilder;
-import com.azure.storage.common.credentials.SASTokenCredential;
-import com.azure.storage.common.credentials.SharedKeyCredential;
-import com.azure.storage.common.policy.RequestRetryOptions;
-import com.azure.storage.common.policy.RequestRetryPolicy;
-import com.azure.storage.common.policy.SASTokenCredentialPolicy;
-import com.azure.storage.common.policy.SharedKeyCredentialPolicy;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Fluent PageBlobClientBuilder for instantiating a {@link PageBlobClient} or {@link PageBlobAsyncClient}
- * using {@link PageBlobClientBuilder#buildClient()} or {@link PageBlobClientBuilder#buildAsyncClient()} respectively.
- *
- *
- * The following information must be provided on this builder:
- *
- *
- * - the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}.
- *
- the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible.
- *
- *
- *
- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a
- * {@link PageBlobClient} or {@code .buildAsyncClient()} to create a {@link PageBlobAsyncClient}.
- */
-public final class PageBlobClientBuilder {
- private static final String ACCOUNT_NAME = "accountname";
- private static final String ACCOUNT_KEY = "accountkey";
- private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol";
- private static final String ENDPOINT_SUFFIX = "endpointsuffix";
-
- private final List policies;
-
- private String endpoint;
- private String containerName;
- private String blobName;
- private String snapshot;
- private SharedKeyCredential sharedKeyCredential;
- private TokenCredential tokenCredential;
- private SASTokenCredential sasTokenCredential;
- private HttpClient httpClient;
- private HttpLogDetailLevel logLevel;
- private RequestRetryOptions retryOptions;
- private Configuration configuration;
-
- /**
- * Creates a builder instance that is able to configure and construct {@link PageBlobClient PageBlobClients}
- * and {@link PageBlobAsyncClient PageBlobAsyncClients}.
- */
- public PageBlobClientBuilder() {
- retryOptions = new RequestRetryOptions();
- logLevel = HttpLogDetailLevel.NONE;
- policies = new ArrayList<>();
- }
-
- private AzureBlobStorageBuilder buildImpl() {
- Objects.requireNonNull(endpoint);
- Objects.requireNonNull(containerName);
- Objects.requireNonNull(blobName);
-
- // Closest to API goes first, closest to wire goes last.
- final List policies = new ArrayList<>();
-
- if (configuration == null) {
- configuration = ConfigurationManager.getConfiguration();
- }
- policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration));
- policies.add(new RequestIdPolicy());
- policies.add(new AddDatePolicy());
-
- if (sharedKeyCredential != null) {
- policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
- } else if (tokenCredential != null) {
- policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint)));
- } else if (sasTokenCredential != null) {
- policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
- } else {
- policies.add(new AnonymousCredentialPolicy());
- }
-
- policies.add(new RequestRetryPolicy(retryOptions));
-
- policies.addAll(this.policies);
- policies.add(new HttpLoggingPolicy(logLevel));
-
- HttpPipeline pipeline = HttpPipeline.builder()
- .policies(policies.toArray(new HttpPipelinePolicy[0]))
- .httpClient(httpClient)
- .build();
-
- return new AzureBlobStorageBuilder()
- .url(String.format("%s/%s/%s", endpoint, containerName, blobName))
- .pipeline(pipeline);
- }
-
- /**
- * @return a {@link PageBlobClient} created from the configurations in this builder.
- */
- public PageBlobClient buildClient() {
- return new PageBlobClient(buildAsyncClient());
- }
-
- /**
- * @return a {@link PageBlobAsyncClient} created from the configurations in this builder.
- */
- public PageBlobAsyncClient buildAsyncClient() {
- return new PageBlobAsyncClient(buildImpl(), snapshot);
- }
-
- /**
- * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name)
- * @param endpoint URL of the service
- * @return the updated PageBlobClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL.
- */
- public PageBlobClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
- try {
- url = new URL(endpoint);
- BlobURLParts parts = URLParser.parse(url);
- this.endpoint = parts.scheme() + "://" + parts.host();
-
- if (parts.containerName() != null) {
- this.containerName = parts.containerName();
- }
-
- if (parts.blobName() != null) {
- this.blobName = parts.blobName();
- }
-
- if (parts.snapshot() != null) {
- this.snapshot = parts.snapshot();
- }
- } catch (MalformedURLException ex) {
- throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed.");
- }
-
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
- return this;
- }
-
- /**
- * Sets the name of the container this client is connecting to.
- * @param containerName the name of the container
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder containerName(String containerName) {
- this.containerName = containerName;
- return this;
- }
-
- /**
- * Sets the name of the blob this client is connecting to.
- * @param blobName the name of the blob
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder blobName(String blobName) {
- this.blobName = blobName;
- return this;
- }
-
- /**
- * Sets the snapshot of the blob this client is connecting to.
- * @param snapshot the snapshot identifier for the blob
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder snapshot(String snapshot) {
- this.snapshot = snapshot;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
- this.sharedKeyCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the credential used to authorize requests sent to the service
- * @param credential authorization credential
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- return this;
- }
-
- /**
- * Clears the credential used to authorize requests sent to the service
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder anonymousCredential() {
- this.sharedKeyCredential = null;
- this.tokenCredential = null;
- this.sasTokenCredential = null;
- return this;
- }
-
- /**
- * Sets the connection string for the service, parses it for authentication information (account name, account key)
- * @param connectionString connection string from access keys section
- * @return the updated PageBlobClientBuilder object
- * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey
- */
- public PageBlobClientBuilder connectionString(String connectionString) {
- Objects.requireNonNull(connectionString);
-
- Map connectionKVPs = new HashMap<>();
- for (String s : connectionString.split(";")) {
- String[] kvp = s.split("=", 2);
- connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
- }
-
- String accountName = connectionKVPs.get(ACCOUNT_NAME);
- String accountKey = connectionKVPs.get(ACCOUNT_KEY);
- String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL);
- String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX);
-
- if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) {
- throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'.");
- }
-
- if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) {
- String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", ""));
- endpoint(endpoint);
- }
-
- // Use accountName and accountKey to get the SAS token using the credential class.
- return credential(new SharedKeyCredential(accountName, accountKey));
- }
-
- /**
- * Sets the http client used to send service requests
- * @param httpClient http client to send requests
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
- return this;
- }
-
- /**
- * Adds a pipeline policy to apply on each request sent
- * @param pipelinePolicy a pipeline policy
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
- return this;
- }
-
- /**
- * Sets the logging level for service requests
- * @param logLevel logging level
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
- this.logLevel = logLevel;
- return this;
- }
-
- /**
- * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with
- * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE
- * @param configuration configuration store
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder configuration(Configuration configuration) {
- this.configuration = configuration;
- return this;
- }
-
- /**
- * Sets the request retry options for all the requests made through the client.
- * @param retryOptions the options to configure retry behaviors
- * @return the updated PageBlobClientBuilder object
- */
- public PageBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
- return this;
- }
-}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java b/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java
index cd918e636bbd..399e7ea1b6e6 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java
@@ -19,7 +19,7 @@
* are existing query parameters, which might affect the appropriate means of appending these query parameters).
* NOTE: Instances of this class are immutable to ensure thread safety.
*/
-final class SASQueryParameters {
+public final class SASQueryParameters {
private final String version;
@@ -297,26 +297,44 @@ public String contentType() {
return contentType;
}
+ /**
+ * @return the object ID of the key.
+ */
public String keyOid() {
return keyOid;
}
+ /**
+ * @return the tenant ID of the key.
+ */
public String keyTid() {
return keyTid;
}
+ /**
+ * @return the datetime when the key becomes active.
+ */
public OffsetDateTime keyStart() {
return keyStart;
}
+ /**
+ * @return the datetime when the key expires.
+ */
public OffsetDateTime keyExpiry() {
return keyExpiry;
}
+ /**
+ * @return the services that are permitted by the key.
+ */
public String keyService() {
return keyService;
}
+ /**
+ * @return the service version that created the key.
+ */
public String keyVersion() {
return keyVersion;
}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java
index 0aff9d26aa02..fbbdc0f438ac 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java
@@ -115,14 +115,20 @@ private AzureBlobStorageBuilder buildImpl() {
}
/**
+ * Creates a {@link StorageClient} based on options set in the Builder.
+ *
* @return a {@link StorageClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null}.
*/
public StorageClient buildClient() {
return new StorageClient(buildAsyncClient());
}
/**
+ * Creates a {@link StorageAsyncClient} based on options set in the Builder.
+ *
* @return a {@link StorageAsyncClient} created from the configurations in this builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null}.
*/
public StorageAsyncClient buildAsyncClient() {
return new StorageAsyncClient(buildImpl());
@@ -132,23 +138,22 @@ public StorageAsyncClient buildAsyncClient() {
* Sets the blob service endpoint, additionally parses it for information (SAS token, queue name)
* @param endpoint URL of the service
* @return the updated StorageClientBuilder object
- * @throws IllegalArgumentException If {@code endpoint} is a malformed URL.
+ * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL.
*/
public StorageClientBuilder endpoint(String endpoint) {
- Objects.requireNonNull(endpoint);
- URL url;
try {
- url = new URL(endpoint);
+ URL url = new URL(endpoint);
this.endpoint = url.getProtocol() + "://" + url.getAuthority();
+
+ this.sasTokenCredential = SASTokenCredential.fromQueryParameters(URLParser.parse(url).sasQueryParameters());
+ if (this.sasTokenCredential != null) {
+ this.tokenCredential = null;
+ this.sharedKeyCredential = null;
+ }
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("The Azure Storage endpoint url is malformed.");
}
- SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery());
- if (credential != null) {
- this.credential(credential);
- }
-
return this;
}
@@ -160,9 +165,10 @@ String endpoint() {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated ContainerClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public StorageClientBuilder credential(SharedKeyCredential credential) {
- this.sharedKeyCredential = credential;
+ this.sharedKeyCredential = Objects.requireNonNull(credential);
this.tokenCredential = null;
this.sasTokenCredential = null;
return this;
@@ -172,9 +178,10 @@ public StorageClientBuilder credential(SharedKeyCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated StorageClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public StorageClientBuilder credential(TokenCredential credential) {
- this.tokenCredential = credential;
+ this.tokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.sasTokenCredential = null;
return this;
@@ -184,9 +191,10 @@ public StorageClientBuilder credential(TokenCredential credential) {
* Sets the credential used to authorize requests sent to the service
* @param credential authorization credential
* @return the updated StorageClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
*/
public StorageClientBuilder credential(SASTokenCredential credential) {
- this.sasTokenCredential = credential;
+ this.sasTokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.tokenCredential = null;
return this;
@@ -240,9 +248,10 @@ public StorageClientBuilder connectionString(String connectionString) {
* Sets the http client used to send service requests
* @param httpClient http client to send requests
* @return the updated StorageClientBuilder object
+ * @throws NullPointerException If {@code httpClient} is {@code null}.
*/
public StorageClientBuilder httpClient(HttpClient httpClient) {
- this.httpClient = httpClient;
+ this.httpClient = Objects.requireNonNull(httpClient);
return this;
}
@@ -250,9 +259,10 @@ public StorageClientBuilder httpClient(HttpClient httpClient) {
* Adds a pipeline policy to apply on each request sent
* @param pipelinePolicy a pipeline policy
* @return the updated StorageClientBuilder object
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}.
*/
public StorageClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
- this.policies.add(pipelinePolicy);
+ this.policies.add(Objects.requireNonNull(pipelinePolicy));
return this;
}
@@ -281,9 +291,10 @@ public StorageClientBuilder configuration(Configuration configuration) {
* Sets the request retry options for all the requests made through the client.
* @param retryOptions the options to configure retry behaviors
* @return the updated StorageClientBuilder object
+ * @throws NullPointerException If {@code retryOptions} is {@code null}.
*/
public StorageClientBuilder retryOptions(RequestRetryOptions retryOptions) {
- this.retryOptions = retryOptions;
+ this.retryOptions = Objects.requireNonNull(retryOptions);
return this;
}
}
diff --git a/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java b/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java
index cff7bc91f776..8a18f449c8c9 100644
--- a/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java
+++ b/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java
@@ -4,41 +4,12 @@
package com.azure.storage.common.credentials;
import com.azure.core.implementation.util.ImplUtils;
-
-import java.util.HashMap;
+import com.azure.storage.blob.SASQueryParameters;
/**
* Holds a SAS token used for authenticating requests.
*/
public final class SASTokenCredential {
- // Required SAS token pieces
- private static final String SIGNED_VERSION = "sv";
- private static final String SIGNED_SERVICES = "ss";
- private static final String SIGNED_RESOURCE_TYPES = "srt";
- private static final String SIGNED_PERMISSIONS = "sp";
- private static final String SIGNED_EXPIRY = "se";
- private static final String SIGNATURE = "sig";
- private static final String SIGNED_RESOURCE = "sr";
-
- // Optional SAS token pieces
- private static final String SIGNED_START = "st";
- private static final String SIGNED_PROTOCOL = "spr";
- private static final String SIGNED_IP = "sip";
-
- private static final String CACHE_CONTROL = "rscc";
- private static final String CONTENT_DISPOSITION = "rscd";
- private static final String CONTENT_ENCODING = "rsce";
- private static final String CONTENT_LANGUAGE = "rscl";
- private static final String CONTENT_TYPE = "rsct";
-
- // Possible User Delegation Key pieces
- private static final String SIGNED_KEY_O_ID = "skoid";
- private static final String SIGNED_KEY_T_ID = "sktid";
- private static final String SIGNED_KEY_START = "skt";
- private static final String SIGNED_KEY_EXPIRY = "ske";
- private static final String SIGNED_KEY_SERVICE = "sks";
- private static final String SIGNED_KEY_VERSION = "skv";
-
private final String sasToken;
/**
@@ -46,7 +17,7 @@ public final class SASTokenCredential {
*
* @param sasToken SAS token used to authenticate requests with the service.
*/
- public SASTokenCredential(String sasToken) {
+ private SASTokenCredential(String sasToken) {
this.sasToken = sasToken;
}
@@ -58,115 +29,31 @@ public String sasToken() {
}
/**
- * Creates a SAS token credential from the passed URL query string
+ * Creates a SAS token credential from the passed SAS token.
*
- * @param query URL query used to build the SAS token
- * @return a SAS token credential if the query param contains all the necessary pieces
+ * @param sasToken SAS token
+ * @return a SAS token credential if {@code sasToken} is not {@code null} or empty, otherwise null.
*/
- public static SASTokenCredential fromQuery(String query) {
- if (ImplUtils.isNullOrEmpty(query)) {
+ public static SASTokenCredential fromSASTokenString(String sasToken) {
+ if (ImplUtils.isNullOrEmpty(sasToken)) {
return null;
}
- HashMap queryParams = new HashMap<>();
- for (String queryParam : query.split("&")) {
- String key = queryParam.split("=", 2)[0];
- queryParams.put(key, queryParam);
- }
+ return new SASTokenCredential(sasToken);
+ }
- /* Because ServiceSAS only requires expiry and permissions, both of which could be on the container
- acl, the only guaranteed indication of a SAS is the signature. We'll let the service validate
- the other query parameters. */
- if (!queryParams.containsKey(SIGNATURE)) {
+ /**
+ * Creates a SAS token credential from the passed {@link SASQueryParameters}.
+ *
+ * @param queryParameters SAS token query parameters object
+ * @return a SAS token credential if {@code queryParameters} is not {@code null} and has
+ * {@link SASQueryParameters#signature() signature} set, otherwise returns {@code null}.
+ */
+ public static SASTokenCredential fromQueryParameters(SASQueryParameters queryParameters) {
+ if (queryParameters == null || ImplUtils.isNullOrEmpty(queryParameters.signature())) {
return null;
}
- StringBuilder sasTokenBuilder = new StringBuilder();
-
- if (queryParams.containsKey(SIGNED_VERSION)) {
- sasTokenBuilder.append(queryParams.get(SIGNED_VERSION));
- }
-
- if (queryParams.containsKey(SIGNED_SERVICES)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_SERVICES));
- }
-
- if (queryParams.containsKey(SIGNED_RESOURCE_TYPES)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_RESOURCE_TYPES));
- }
-
- if (queryParams.containsKey(SIGNED_PERMISSIONS)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_PERMISSIONS));
- }
-
- if (queryParams.containsKey(SIGNED_RESOURCE)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_RESOURCE));
- }
-
- // SIGNED_START is optional
- if (queryParams.containsKey(SIGNED_START)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_START));
- }
-
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_EXPIRY));
-
- // SIGNED_IP is optional
- if (queryParams.containsKey(SIGNED_IP)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_IP));
- }
-
- // SIGNED_PROTOCOL is optional
- if (queryParams.containsKey(SIGNED_PROTOCOL)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_PROTOCOL));
- }
-
- if (queryParams.containsKey(CACHE_CONTROL)) {
- sasTokenBuilder.append("&").append(queryParams.get(CACHE_CONTROL));
- }
-
- if (queryParams.containsKey(CONTENT_DISPOSITION)) {
- sasTokenBuilder.append("&").append(queryParams.get(CONTENT_DISPOSITION));
- }
-
- if (queryParams.containsKey(CONTENT_ENCODING)) {
- sasTokenBuilder.append("&").append(queryParams.get(CONTENT_ENCODING));
- }
-
- if (queryParams.containsKey(CONTENT_LANGUAGE)) {
- sasTokenBuilder.append("&").append(queryParams.get(CONTENT_LANGUAGE));
- }
-
- if (queryParams.containsKey(CONTENT_TYPE)) {
- sasTokenBuilder.append("&").append(queryParams.get(CONTENT_TYPE));
- }
-
- // User Delegation Key Parameters
- if (queryParams.containsKey(SIGNED_KEY_O_ID)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_O_ID));
- }
-
- if (queryParams.containsKey(SIGNED_KEY_T_ID)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_T_ID));
- }
-
- if (queryParams.containsKey(SIGNED_KEY_START)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_START));
- }
-
- if (queryParams.containsKey(SIGNED_KEY_EXPIRY)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_EXPIRY));
- }
-
- if (queryParams.containsKey(SIGNED_KEY_SERVICE)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_SERVICE));
- }
-
- if (queryParams.containsKey(SIGNED_KEY_VERSION)) {
- sasTokenBuilder.append("&").append(queryParams.get(SIGNED_KEY_VERSION));
- }
-
- sasTokenBuilder.append("&").append(queryParams.get(SIGNATURE));
-
- return new SASTokenCredential(sasTokenBuilder.toString());
+ return new SASTokenCredential(queryParameters.encode());
}
}
diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy
index c59095e5f66c..fb306c17ff35 100644
--- a/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy
+++ b/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy
@@ -7,10 +7,8 @@ import com.azure.core.http.*
import com.azure.core.http.policy.HttpLogDetailLevel
import com.azure.core.http.policy.HttpPipelinePolicy
import com.azure.core.http.rest.Response
-import com.azure.core.util.Context
import com.azure.core.util.configuration.ConfigurationManager
import com.azure.identity.credential.EnvironmentCredential
-import com.azure.storage.blob.BlobProperties
import com.azure.storage.blob.models.*
import com.azure.storage.common.credentials.SharedKeyCredential
import org.junit.Assume
@@ -42,8 +40,6 @@ class APISpec extends Specification {
static final ByteBuffer defaultData = ByteBuffer.wrap(defaultText.getBytes(StandardCharsets.UTF_8))
- static final Flux defaultFlux = Flux.just(defaultData)
-
static final Supplier defaultInputStream = new Supplier() {
@Override
InputStream get() {
@@ -112,10 +108,6 @@ class APISpec extends Specification {
*/
static final String defaultContextKey = "Key"
- static final String defaultContextValue = "Value"
-
- static final Context defaultContext = new Context(defaultContextKey, defaultContextValue)
-
static String getTestName(ISpecificationContext ctx) {
return ctx.getCurrentFeature().name.replace(' ', '').toLowerCase()
}
@@ -271,11 +263,13 @@ class APISpec extends Specification {
}
catch (Exception e) {
}
+
try {
blobStorageServiceURL = getGenericServiceURL(getGenericCreds("BLOB_STORAGE_"))
}
catch (Exception e) {
}
+
try {
premiumServiceURL = getGenericServiceURL(getGenericCreds("PREMIUM_STORAGE_"))
}
diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy
index 0cf250d85d17..d0be4d532642 100644
--- a/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy
+++ b/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy
@@ -7,7 +7,6 @@ import com.azure.core.http.HttpHeaders
import com.azure.core.http.rest.Response
import com.azure.core.http.rest.VoidResponse
import com.azure.core.implementation.util.ImplUtils
-import com.azure.storage.blob.BlobProperties
import com.azure.storage.blob.models.*
import spock.lang.Unroll
@@ -241,7 +240,7 @@ class BlobAPITest extends APISpec {
.endpoint(bu.getBlobUrl().toString())
.snapshot(snapshot)
.credential(primaryCreds)
- .buildClient()
+ .buildBlobClient()
ByteArrayOutputStream snapshotStream = new ByteArrayOutputStream()
bu3.download(snapshotStream)
snapshotStream.toByteArray() == originalStream.toByteArray()
@@ -987,7 +986,7 @@ class BlobAPITest extends APISpec {
.endpoint(bu.getBlobUrl().toString())
.credential(primaryCreds)
.snapshot(snapshotResponse.value())
- .buildClient()
+ .buildBlobClient()
then:
bu2.getProperties().statusCode() == 200
@@ -1015,7 +1014,7 @@ class BlobAPITest extends APISpec {
.endpoint(bu.getBlobUrl().toString())
.credential(primaryCreds)
.snapshot(response.value())
- .buildClient()
+ .buildBlobClient()
expect:
response.statusCode() == 201
diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy
index 9b28d87cd7b3..86da9838d53b 100644
--- a/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy
+++ b/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy
@@ -1539,11 +1539,11 @@ class ContainerAPITest extends APISpec {
cu.create()
}
- AppendBlobClient bu = new AppendBlobClientBuilder()
+ AppendBlobClient bu = new BlobClientBuilder()
.credential(primaryCreds)
.endpoint("http://" + primaryCreds.accountName() + ".blob.core.windows.net/\$root/rootblob")
.httpClient(getHttpClient())
- .buildClient()
+ .buildAppendBlobClient()
when:
Response createResponse = bu.create()
@@ -1565,11 +1565,11 @@ class ContainerAPITest extends APISpec {
cu.create()
}
- AppendBlobClient bu = new AppendBlobClientBuilder()
+ AppendBlobClient bu = new BlobClientBuilder()
.credential(primaryCreds)
.endpoint("http://" + primaryCreds.accountName() + ".blob.core.windows.net/rootblob")
.httpClient(getHttpClient())
- .buildClient()
+ .buildAppendBlobClient()
when:
Response createResponse = bu.create()
diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy
index 1ebf9860f2ac..2824ac1d3067 100644
--- a/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy
+++ b/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy
@@ -135,12 +135,12 @@ class SASTest extends APISpec {
when:
def sas = bu.generateSAS(null, permissions, expiryTime, startTime, null, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType)
- def builder = new BlockBlobClientBuilder()
- builder.endpoint(cu.getContainerUrl().toString())
+ def client = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client = builder.buildClient()
+ .buildBlockBlobClient()
def os = new ByteArrayOutputStream()
client.download(os)
@@ -187,13 +187,13 @@ class SASTest extends APISpec {
when:
def sas = snapshotBlob.generateSAS(null, permissions, expiryTime, startTime, null, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType)
- def builder = new BlockBlobClientBuilder()
- builder.endpoint(cu.getContainerUrl().toString())
+ def client = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
.snapshot(snapshotId)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client = builder.buildClient()
+ .buildBlockBlobClient()
def os = new ByteArrayOutputStream()
client.download(os)
@@ -227,7 +227,7 @@ class SASTest extends APISpec {
def builder1 = new ContainerClientBuilder()
builder1.endpoint(cu.getContainerUrl().toString())
- .credential(new SASTokenCredential(sasWithId))
+ .credential(SASTokenCredential.fromSASTokenString(sasWithId))
.httpClient(getHttpClient())
def client1 = builder1.buildClient()
@@ -237,7 +237,7 @@ class SASTest extends APISpec {
def builder2 = new ContainerClientBuilder()
builder2.endpoint(cu.getContainerUrl().toString())
- .credential(new SASTokenCredential(sasWithPermissions))
+ .credential(SASTokenCredential.fromSASTokenString(sasWithPermissions))
.httpClient(getHttpClient())
def client2 = builder2.buildClient()
@@ -279,12 +279,12 @@ class SASTest extends APISpec {
def sas = bu.generateUserDelegationSAS(key, cu.getContainerUrl().getHost().split("\\.")[0], permissions, expiryTime, startTime, null, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType)
- def builder = new BlockBlobClientBuilder()
- builder.endpoint(cu.getContainerUrl().toString())
+ def client = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client = builder.buildClient()
+ .buildBlockBlobClient()
def os = new ByteArrayOutputStream()
client.download(os)
@@ -333,12 +333,12 @@ class SASTest extends APISpec {
def sas = snapshotBlob.generateUserDelegationSAS(key, cu.getContainerUrl().getHost().split("\\.")[0], permissions, expiryTime, startTime, null, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType)
// base blob with snapshot SAS
- def builder1 = new BlockBlobClientBuilder()
- builder1.endpoint(cu.getContainerUrl().toString())
+ def client1 = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client1 = builder1.buildClient()
+ .buildBlockBlobClient()
client1.download(new ByteArrayOutputStream())
then:
@@ -348,13 +348,13 @@ class SASTest extends APISpec {
when:
// blob snapshot with snapshot SAS
- def builder2 = new BlockBlobClientBuilder()
- builder2.endpoint(cu.getContainerUrl().toString())
+ def client2 = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
.snapshot(snapshotId)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client2 = builder2.buildClient()
+ .buildBlockBlobClient()
def os = new ByteArrayOutputStream()
client2.download(os)
@@ -390,7 +390,7 @@ class SASTest extends APISpec {
def builder = new ContainerClientBuilder()
builder.endpoint(cu.getContainerUrl().toString())
- .credential(new SASTokenCredential(sasWithPermissions))
+ .credential(SASTokenCredential.fromSASTokenString(sasWithPermissions))
.httpClient(getHttpClient())
def client = builder.buildClient()
@@ -420,12 +420,12 @@ class SASTest extends APISpec {
when:
def sas = primaryServiceURL.generateAccountSAS(service, resourceType, permissions, expiryTime, null, null, null, null)
- def builder = new BlockBlobClientBuilder()
- builder.endpoint(cu.getContainerUrl().toString())
+ def client = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client = builder.buildClient()
+ .buildBlockBlobClient()
def os = new ByteArrayOutputStream()
client.download(os)
@@ -454,12 +454,12 @@ class SASTest extends APISpec {
when:
def sas = primaryServiceURL.generateAccountSAS(service, resourceType, permissions, expiryTime, null, null, null, null)
- def builder = new BlockBlobClientBuilder()
- builder.endpoint(cu.getContainerUrl().toString())
+ def client = new BlobClientBuilder()
+ .endpoint(cu.getContainerUrl().toString())
.blobName(blobName)
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
.httpClient(getHttpClient())
- def client = builder.buildClient()
+ .buildBlockBlobClient()
client.delete()
then:
@@ -485,7 +485,7 @@ class SASTest extends APISpec {
def scBuilder = new StorageClientBuilder()
scBuilder.endpoint(primaryServiceURL.getAccountUrl().toString())
.httpClient(getHttpClient())
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
def sc = scBuilder.buildClient()
sc.createContainer("container")
@@ -512,7 +512,7 @@ class SASTest extends APISpec {
def scBuilder = new StorageClientBuilder()
scBuilder.endpoint(primaryServiceURL.getAccountUrl().toString())
.httpClient(getHttpClient())
- .credential(new SASTokenCredential(sas))
+ .credential(SASTokenCredential.fromSASTokenString(sas))
def sc = scBuilder.buildClient()
sc.createContainer("container")
diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java
index 2d3bd7636aea..8df2931f44d2 100644
--- a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java
+++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java
@@ -24,13 +24,14 @@
import com.azure.storage.queue.models.SignedIdentifier;
import com.azure.storage.queue.models.StorageErrorException;
import com.azure.storage.queue.models.UpdatedMessage;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import java.util.Map;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
/**
* This class provides a client that contains all the operations for interacting with a queue in Azure Storage Queue.