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: - * - *

- * - *

- * 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: + *

*/ 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: - * - *

- * - *

- * 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: - * - *

- * - *

- * 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.