View {@link DirectoryClientBuilder this} for additional ways to construct the client.
+ *
+ * @see DirectoryClientBuilder
+ * @see DirectoryClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class DirectoryAsyncClient {
+ private final AzureFileStorageImpl azureFileStorageClient;
+ private final String shareName;
+ private final String directoryName;
+ private final String shareSnapshot;
+
+ /**
+ * Creates a DirectoryAsyncClient that sends requests to the storage directory at {@link AzureFileStorageImpl#url() endpoint}.
+ * Each service call goes through the {@link HttpPipeline pipeline} in the {@code client}.
+ * @param azureFileStorageClient Client that interacts with the service interfaces
+ * @param shareName Name of the share
+ * @param directoryName Name of the directory
+ * @param shareSnapshot The snapshot of the share
+ */
+ DirectoryAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryName, String shareSnapshot) {
+ this.shareName = shareName;
+ this.directoryName = directoryName;
+ this.shareSnapshot = shareSnapshot;
+ this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(azureFileStorageClient.httpPipeline())
+ .url(azureFileStorageClient.url())
+ .version(azureFileStorageClient.version())
+ .build();
+ }
- DirectoryAsyncClient() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a DirectoryAsyncClient that sends requests to the storage account at {@code endpoint}.
+ * Each service call goes through the {@code httpPipeline}.
+ * @param endpoint URL for the Storage File service
+ * @param httpPipeline HttpPipeline that HTTP requests and response flow through
+ * @param shareName Name of the share
+ * @param directoryName Name of the directory
+ * @param shareSnapshot Optional. The snapshot of the share
+ */
+ DirectoryAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String directoryName, String shareSnapshot) {
+ this.shareName = shareName;
+ this.directoryName = directoryName;
+ this.shareSnapshot = shareSnapshot;
+ this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline)
+ .url(endpoint.toString())
+ .build();
}
- public static DirectoryClientBuilder asyncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * Get the getDirectoryUrl of the storage directory client.
+ * @return the URL of the storage directory client
+ */
+ public String getDirectoryUrl() {
+ return azureFileStorageClient.url();
}
- public FileAsyncClient getFileClient(String name) {
- throw new UnsupportedOperationException();
+ /**
+ * Constructs a FileAsyncClient that interacts with the specified file.
+ *
+ *
If the file doesn't exist in the storage account {@link FileAsyncClient#create(long)} create} in the client will
+ * need to be called before interaction with the file can happen.
+ *
+ * @param fileName Name of the file
+ * @return a FileAsyncClient that interacts with the specified share
+ */
+ public FileAsyncClient getFileClient(String fileName) {
+ String filePath = directoryName + "/" + fileName;
+ return new FileAsyncClient(azureFileStorageClient, shareName, filePath, null);
}
- public DirectoryAsyncClient getDirectoryClient(String directoryName) {
- throw new UnsupportedOperationException();
+ /**
+ * Constructs a DirectoryAsyncClient that interacts with the specified directory.
+ *
+ *
If the file doesn't exist in the storage account {@link DirectoryAsyncClient#create()} create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @param subDirectoryName Name of the directory
+ * @return a DirectoryAsyncClient that interacts with the specified directory
+ */
+ public DirectoryAsyncClient getSubDirectoryClient(String subDirectoryName) {
+ String directoryPath = directoryName + "/" + subDirectoryName;
+ return new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryPath, shareSnapshot);
}
+ /**
+ * Creates a directory in the storage account and returns a response of {@link DirectoryInfo} to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the directory
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.create}
+ *
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name.
+ */
+ public Mono> create() {
+ return create(null);
+ }
+
+ /**
+ * Creates a directory in the storage account and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the directory
+ *
+ *
+ * client.create(Collections.singletonMap("directory", "metadata"))
+ * .subscribe(response -> System.out.printf("Creating the directory completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Optional. Metadata to associate with the directory
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name.
+ */
public Mono> create(Map metadata) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.directorys().createWithRestResponseAsync(shareName, directoryName, null, metadata, Context.NONE)
+ .map(this::createWithRestResponse);
}
+ /**
+ * Deletes the directory in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Delete the directory
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.delete}
+ *
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist
+ */
public Mono delete() {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.directorys().deleteWithRestResponseAsync(shareName, directoryName, Context.NONE).map(VoidResponse::new)
+ .map(VoidResponse::new);
+ }
+
+ /**
+ * Retrieves the properties of the storage account's directory.
+ * The properties includes directory metadata, last modified date, is server encrypted, and eTag.
+ *
+ *
+ *
+ * @return Storage directory properties
+ */
+ public Mono> getProperties() {
+ return azureFileStorageClient.directorys().getPropertiesWithRestResponseAsync(shareName, directoryName, shareSnapshot, null, Context.NONE)
+ .map(this::getPropertiesResponse);
}
- public Mono> getProperties(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the user-defined metadata to associate to the directory.
+ *
+ *
If {@code null} is passed for the metadata it will clear the metadata associated to the directory.
+ *
+ *
Code Samples
+ *
+ *
Set the metadata to "directory:updatedMetadata"
+ *
+ *
+ * client.setMetadata(Collections.singletonMap("directory", "updatedMetadata"))
+ * .subscribe(response -> System.out.printf("Setting the directory metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Clear the metadata of the directory
+ *
+ *
+ * client.setMetadata(null)
+ * .subscribe(response -> System.out.printf("Clearing the directory metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Optional. Metadata to set on the directory, if null is passed the metadata for the directory is cleared
+ * @return information about the directory
+ * @throws StorageErrorException If the directory doesn't exist or the metadata contains invalid keys
+ */
+ public Mono> setMetadata(Map metadata) {
+ return azureFileStorageClient.directorys().setMetadataWithRestResponseAsync(shareName, directoryName, null, metadata, Context.NONE)
+ .map(this::setMetadataResponse);
}
- public Mono> setMetadata(Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all directories and files in the storage account without their prefix or maxResult.
+ *
+ *
Code Samples
+ *
+ *
List all directories and files in the account
+ *
+ *
+ * client.listFilesAndDirectories()
+ * .subscribe(result -> System.out.printf("The file or directory %s exists in the account", result.name()));
+ *
+ *
+ * @return {@link FileRef File info} in the storage directory
+ */
+ public Flux listFilesAndDirectories() {
+ return listFilesAndDirectories(null, null);
}
- public Flux listFilesAndDirectories(String prefix, int maxResults, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all shares in the storage account with their prefix or snapshots.
+ *
+ *
Code Samples
+ *
+ *
List all directories with "subdir" prefix and return 10 results in the account
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories}
+ *
+ * @param prefix Optional. Filters the results to return only files and directories whose name begins with the specified prefix.
+ * @param maxResults Optional. Specifies the maximum number of files and/or directories to return per page.
+ * If the request does not specify maxresults or specifies a value greater than 5,000, the server will return up to 5,000 items.
+ * @return {@link FileRef File info} in the storage account with prefix and max number of return results.
+ */
+ public Flux listFilesAndDirectories(String prefix, Integer maxResults) {
+ return azureFileStorageClient.directorys().listFilesAndDirectoriesSegmentWithRestResponseAsync(shareName, directoryName, prefix, shareSnapshot, null, maxResults, null, Context.NONE)
+ .flatMapMany(response -> nextPageForFileAndDirecotries(response, prefix, maxResults));
}
- public Flux getHandles(int maxResult, boolean recursive) {
- throw new UnsupportedOperationException();
+ /**
+ * List of open handles on a directory or a file.
+ *
+ *
Code Samples
+ *
+ *
Get 10 handles with recursive call.
+ *
+ *
+ * client.getHandles(10, true)
+ * .subscribe(handleItem -> System.out.printf("Get handles completed with handle id %s", handleItem.handleId()));
+ *
+ * @param maxResult Optional. The number of results will return per page
+ * @param recursive Specifies operation should apply to the directory specified in the URI, its files, its subdirectories and their files.
+ * @return {@link HandleItem handles} in the directory that satisfy the requirements
+ */
+ public Flux getHandles(Integer maxResult, boolean recursive) {
+ return azureFileStorageClient.directorys().listHandlesWithRestResponseAsync(shareName, directoryName, null, maxResult, null, shareSnapshot, recursive, Context.NONE)
+ .flatMapMany(response -> nextPageForHandles(response, maxResult, recursive));
}
+ /**
+ * Closes a handle or handles opened on a directory or a file at the service. It is intended to be used alongside {@link DirectoryAsyncClient#getHandles(Integer, boolean)} .
+ *
+ *
Code Samples
+ *
+ *
Force close handles with handles returned by get handles in recursive.
+ * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles.
+ * @param recursive A boolean value that specifies if the operation should also apply to the files and subdirectories of the directory specified in the URI.
+ * @return The counts of number of handles closed
+ */
public Flux forceCloseHandles(String handleId, boolean recursive) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.directorys().forceCloseHandlesWithRestResponseAsync(shareName, directoryName, handleId, null, null, shareSnapshot, recursive, Context.NONE)
+ .flatMapMany(response -> nextPageForForceCloseHandles(response, handleId, recursive));
}
- public Mono> createSubDirectory(String directoryName, Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a subdirectory under current directory with specific name and returns a response of DirectoryAsyncClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the sub directory "subdir"
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.createSubDirectory#string}
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @return A response containing the subdirectory client and the status of creating the directory.
+ * @throws StorageErrorException If the subdirectory has already existed, the parent directory does not exist or directory is an invalid resource name.
+ */
+ public Mono> createSubDirectory(String subDirectoryName) {
+ String directoryPath = directoryName + "/" + subDirectoryName;
+ DirectoryAsyncClient createSubClient = new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryPath, shareSnapshot);
+ return createSubClient.create()
+ .map(response -> new SimpleResponse<>(response, createSubClient));
}
- public Mono deleteSubDirectory(String directoryName) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a subdirectory under current directory with specific name , metadata and returns a response of DirectoryAsyncClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the subdirectory named "subdir", with metadata
+ *
+ *
+ * client.createSubDirectory("subdir", Collections.singletonMap("directory", "metadata"))
+ * .subscribe(response -> System.out.printf("Creating the subdirectory completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @param metadata Optional. Metadata to associate with the subdirectory
+ * @return A response containing the subdirectory client and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or subdirectory is an invalid resource name.
+ */
+ public Mono> createSubDirectory(String subDirectoryName, Map metadata) {
+ String directoryPath = directoryName + "/" + subDirectoryName;
+ DirectoryAsyncClient createSubClient = new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryPath, shareSnapshot);
+ return createSubClient.create(metadata)
+ .map(response -> new SimpleResponse<>(response, createSubClient));
}
- public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map meatadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the subdirectory with specific name in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Delete the subdirectory named "subdir"
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.deleteSubDirectory#string}
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the subdirectory doesn't exist, the parent directory does not exist or subdirectory name is an invalid resource name.
+ */
+ public Mono deleteSubDirectory(String subDirectoryName) {
+ String directoryPath = directoryName + "/" + subDirectoryName;
+ DirectoryAsyncClient deleteSubClient = new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryPath, shareSnapshot);
+ return deleteSubClient.delete().map(VoidResponse::new);
}
+ /**
+ * Creates a file in the storage account with specific name, max number of results and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create 1k file with named "myFile"
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.createFile#string-long}
+ *
+ * @param fileName Name of the file
+ * @param maxSize Size of the file
+ * @return A response containing the FileAsyncClient and the status of creating the directory.
+ * @throws StorageErrorException If the file has already existed, the parent directory does not exist or file name is an invalid resource name.
+ */
+ public Mono> createFile(String fileName, long maxSize) {
+ String filePath = directoryName + "/" + fileName;
+ FileAsyncClient fileAsyncClient = new FileAsyncClient(azureFileStorageClient, shareName, filePath, shareSnapshot);
+ return fileAsyncClient.create(maxSize).map(response -> new SimpleResponse<>(response, fileAsyncClient));
+ }
+
+ /**
+ * Creates a file in the storage account with specific name and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file named "myFile"
+ *
+ *
+ * client.createFile("myFile", Collections.singletonMap("directory", "metadata"))
+ * .subscribe(response -> System.out.printf("Creating the file completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param fileName Name of the file
+ * @param maxSize Max size of the file
+ * @param httpHeaders the Http headers set to the file
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or file name is an invalid resource name.
+ */
+ public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map metadata) {
+ String filePath = directoryName + "/" + fileName;
+ FileAsyncClient fileAsyncClient = new FileAsyncClient(azureFileStorageClient, shareName, filePath, shareSnapshot);
+ return fileAsyncClient.create(maxSize, httpHeaders, metadata).map(response -> new SimpleResponse<>(response, fileAsyncClient));
+ }
+
+ /**
+ * Deletes the file with specific name in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Delete the file "filetest"
+ *
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.deleteFile#string}
+ *
+ * @param fileName Name of the file
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist or file name is an invalid resource name.
+ */
public Mono deleteFile(String fileName) {
- throw new UnsupportedOperationException();
+ String filePath = directoryName + "/" + fileName;
+ FileAsyncClient fileAsyncClient = new FileAsyncClient(azureFileStorageClient, shareName, filePath, shareSnapshot);
+ return fileAsyncClient.delete().map(VoidResponse::new);
+ }
+
+
+ private Response createWithRestResponse(final DirectorysCreateResponse response) {
+ String eTag = response.deserializedHeaders().eTag();
+ OffsetDateTime lastModified = response.deserializedHeaders().lastModified();
+ DirectoryInfo directoryInfo = new DirectoryInfo(eTag, lastModified);
+ return new SimpleResponse<>(response, directoryInfo);
+
+ }
+
+ private Response getPropertiesResponse(DirectorysGetPropertiesResponse response) {
+ Map metadata = response.deserializedHeaders().metadata();
+ String eTag = response.deserializedHeaders().eTag();
+ OffsetDateTime offsetDateTime = response.deserializedHeaders().lastModified();
+ boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted();
+
+ DirectoryProperties directoryProperties = new DirectoryProperties(metadata, eTag, offsetDateTime, isServerEncrypted);
+ return new SimpleResponse<>(response, directoryProperties);
+ }
+
+ private Response setMetadataResponse(final DirectorysSetMetadataResponse response) {
+ String eTag = response.deserializedHeaders().eTag();
+ boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted();
+
+ DirectorySetMetadataInfo directorySetMetadataInfo = new DirectorySetMetadataInfo(eTag, isServerEncrypted);
+ return new SimpleResponse<>(response, directorySetMetadataInfo);
+ }
+
+ private Flux nextPageForFileAndDirecotries(final DirectorysListFilesAndDirectoriesSegmentResponse response, final String prefix, final Integer maxResult) {
+ List fileRefs = convertResponseAndGetNumOfResults(response);
+
+ if (response.value().nextMarker() == null) {
+ return Flux.fromIterable(fileRefs);
+ }
+ Mono listResponse = azureFileStorageClient.directorys().listFilesAndDirectoriesSegmentWithRestResponseAsync(shareName, directoryName, prefix, shareSnapshot, response.value().nextMarker(), maxResult, null, Context.NONE);
+ Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForFileAndDirecotries(newResponse, prefix, maxResult));
+ return Flux.fromIterable(fileRefs).concatWith(fileRefPublisher);
+ }
+
+ private Flux nextPageForHandles(DirectorysListHandlesResponse response, Integer maxResult, boolean recursive) {
+ List handleItems = response.value().handleList();
+
+ if (response.value().nextMarker() == null) {
+ return Flux.fromIterable(handleItems);
+ }
+ Mono listResponse = azureFileStorageClient.directorys().listHandlesWithRestResponseAsync(shareName, directoryName, response.value().nextMarker(), maxResult, null, shareSnapshot, recursive, Context.NONE);
+ Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForHandles(newResponse, maxResult, recursive));
+ return Flux.fromIterable(handleItems).concatWith(fileRefPublisher);
+ }
+
+ private Flux nextPageForForceCloseHandles(DirectorysForceCloseHandlesResponse response, String handleId, boolean recursive) {
+ List handleCount = Arrays.asList(response.deserializedHeaders().numberOfHandlesClosed());
+
+ if (response.deserializedHeaders().marker() == null) {
+ return Flux.fromIterable(handleCount);
+ }
+ Mono listResponse = azureFileStorageClient.directorys().forceCloseHandlesWithRestResponseAsync(shareName, directoryName, handleId, null, response.deserializedHeaders().marker(), shareSnapshot, recursive, Context.NONE);
+ Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForForceCloseHandles(newResponse, handleId, recursive));
+ return Flux.fromIterable(handleCount).concatWith(fileRefPublisher);
+ }
+
+ private List convertResponseAndGetNumOfResults(DirectorysListFilesAndDirectoriesSegmentResponse response) {
+ List fileRefs = new ArrayList<>();
+ response.value().segment().directoryItems().forEach(directoryItem -> fileRefs.add(new FileRef(directoryItem.name(), true, null)));
+ response.value().segment().fileItems().forEach(fileItem -> fileRefs.add(new FileRef(fileItem.name(), false, fileItem.properties())));
+ return fileRefs;
}
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java
index 22d4063b0f410..814acc8f22a60 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java
@@ -4,78 +4,376 @@
package com.azure.storage.file;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
import com.azure.storage.file.models.DirectoryInfo;
import com.azure.storage.file.models.DirectoryProperties;
+import com.azure.storage.file.models.DirectorySetMetadataInfo;
import com.azure.storage.file.models.FileHTTPHeaders;
import com.azure.storage.file.models.FileRef;
import com.azure.storage.file.models.HandleItem;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
+import com.azure.storage.file.models.StorageErrorException;
import java.util.Map;
+/**
+ * This class provides a client that contains all the operations for interacting with directory in Azure Storage File Service.
+ * Operations allowed by the client are creating, deleting and listing subdirectory and file, retrieving properties, , setting metadata
+ * and list or force close handles of the directory or file.
+ *
+ *
View {@link DirectoryClientBuilder this} for additional ways to construct the client.
+ *
+ * @see DirectoryClientBuilder
+ * @see DirectoryClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class DirectoryClient {
- private final DirectoryAsyncClient client;
+ private final DirectoryAsyncClient directoryAsyncClient;
+
+ /**
+ * Creates a DirectoryClient that wraps a DirectoryAsyncClient and blocks requests.
+ *
+ * @param directoryAsyncClient DirectoryAsyncClient that is used to send requests
+ */
+ DirectoryClient(DirectoryAsyncClient directoryAsyncClient) {
+ this.directoryAsyncClient = directoryAsyncClient;
+ }
+
+ /**
+ * Get the getDirectoryUrl of the storage directory client.
+ * @return the URL of the storage directory client
+ */
+ public String getDirectoryUrl() {
+ return directoryAsyncClient.getDirectoryUrl();
+ }
+
+ /**
+ * Constructs a FileClient that interacts with the specified file.
+ *
+ *
If the file doesn't exist in the storage account {@link FileClient#create(long)} create} in the client will
+ * need to be called before interaction with the file can happen.
+ *
+ * @param fileName Name of the file
+ * @return a FileClient that interacts with the specified share
+ */
+ public FileClient getFileClient(String fileName) {
+ return new FileClient(directoryAsyncClient.getFileClient(fileName));
+ }
+
+ /**
+ * Constructs a DirectoryClient that interacts with the specified directory.
+ *
+ *
If the file doesn't exist in the storage account {@link DirectoryClient#create()} create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @param subDirectoryName Name of the directory
+ * @return a DirectoryClient that interacts with the specified directory
+ */
+ public DirectoryClient getSubDirectoryClient(String subDirectoryName) {
+ return new DirectoryClient(directoryAsyncClient.getSubDirectoryClient(subDirectoryName));
+ }
- DirectoryClient() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a directory in the storage account and returns a response of {@link DirectoryInfo} to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the directory
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.createDirectory}
+ *
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name.
+ */
+ public Response create() {
+ return create(null);
}
- public static DirectoryClientBuilder syncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a directory in the storage account and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the directory
+ *
+ *
+ * Response<DirectoryInfo> response = client.create(Collections.singletonMap("directory", "metadata"));
+ * System.out.printf("Creating the directory completed with status code %d", response.statusCode());
+ *
+ *
+ * @param metadata Optional. Metadata to associate with the directory
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name.
+ */
+ public Response create(Map metadata) {
+ return directoryAsyncClient.create(metadata).block();
}
- public FileClient getFileClient(String name) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the directory in the storage account. The directory must be empty before it can be deleted.
+ *
+ *
Code Samples
+ *
+ *
Delete the directory
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.delete}
+ *
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist
+ */
+ public VoidResponse delete() {
+ return directoryAsyncClient.delete().block();
}
- public DirectoryClient getDirectoryClient(String directoryName) {
- throw new UnsupportedOperationException();
+ /**
+ * Retrieves the properties of the storage account's directory.
+ * The properties includes directory metadata, last modified date, is server encrypted, and eTag.
+ *
+ *
Code Samples
+ *
+ *
Retrieve directory properties
+ *
+ *
+ * Response<DirectoryProperties> response = client.getProperties();
+ * System.out.printf("Directory latest modified date is %s.", properties.value().lastModified());
+ *
+ *
+ * @return Storage directory properties
+ */
+ public Response getProperties() {
+ return directoryAsyncClient.getProperties().block();
}
- public Mono> create(Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the user-defined metadata to associate to the directory.
+ *
+ *
If {@code null} is passed for the metadata it will clear the metadata associated to the directory.
+ *
+ *
Code Samples
+ *
+ *
Set the metadata to "directory:updatedMetadata"
+ *
+ *
+ * Response<DirectorySetMetadataInfo> response = client.setMetadata(Collections.singletonMap("directory", "updatedMetadata"));
+ * System.out.printf("Setting the directory metadata completed with status code %d", response.statusCode());
+ *
+ *
+ *
Clear the metadata of the directory
+ *
+ *
+ * client.setMetadata(null)
+ * .subscribe(response -> System.out.printf("Clearing the directory metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Optional. Metadata to set on the directory, if null is passed the metadata for the directory is cleared
+ * @return information about the directory
+ * @throws StorageErrorException If the directory doesn't exist or the metadata contains invalid keys
+ */
+ public Response setMetadata(Map metadata) {
+ return directoryAsyncClient.setMetadata(metadata).block();
}
- public Mono delete() {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all directories and files in the storage account without their prefix or maxResult.
+ *
+ *
Code Samples
+ *
+ *
List all directories and files in the account
+ *
+ *
+ * Iterable<FileRef> result = client.listFilesAndDirectories()
+ * System.out.printf("The file or directory %s exists in the account", result.iterator().next().name());
+ *
+ *
+ * @return {@link FileRef File info} in the storage directory
+ */
+ public Iterable listFilesAndDirectories() {
+ return listFilesAndDirectories(null, null);
}
- public Mono> getProperties(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all shares in the storage account with their prefix or snapshots.
+ *
+ *
Code Samples
+ *
+ *
List all directories with "subdir" prefix and return 10 results in the account
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.listFilesAndDirectories}
+ *
+ * @param prefix Optional. Filters the results to return only files and directories whose name begins with the specified prefix.
+ * @param maxResults Optional. Specifies the maximum number of files and/or directories to return per page.
+ * If the request does not specify maxresults or specifies a value greater than 5,000, the server will return up to 5,000 items.
+ * @return {@link FileRef File info} in the storage account with prefix and max number of return results.
+ */
+ public Iterable listFilesAndDirectories(String prefix, Integer maxResults) {
+ return directoryAsyncClient.listFilesAndDirectories(prefix, maxResults).toIterable();
}
- public Mono> setMetadata(Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * List of open handles on a directory or a file.
+ *
+ *
Code Samples
+ *
+ *
Get 10 handles with recursive call.
+ *
+ *
+ * Iterable<HandleItem> result = client.getHandles(10, true)
+ * System.out.printf("Get handles completed with handle id %s", result.iterator().next().handleId());
+ *
+ * @param maxResult Optional. The number of results will return per page
+ * @param recursive Specifies operation should apply to the directory specified in the URI, its files, its subdirectories and their files.
+ * @return {@link HandleItem handles} in the directory that satisfy the requirements
+ */
+ public Iterable getHandles(Integer maxResult, boolean recursive) {
+ return directoryAsyncClient.getHandles(maxResult, recursive).collectList().block();
}
- public Flux listFilesAndDirectories(String prefix, int maxResults, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Closes a handle or handles opened on a directory or a file at the service. It is intended to be used alongside {@link DirectoryClient#getHandles(Integer, boolean)} .
+ *
+ *
Code Samples
+ *
+ *
Force close handles with handles returned by get handles in recursive.
+ * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles.
+ * @param recursive A boolean value that specifies if the operation should also apply to the files and subdirectories of the directory specified in the URI.
+ * @return The counts of number of handles closed.
+ */
+ public Iterable forceCloseHandles(String handleId, boolean recursive) {
+ return directoryAsyncClient.forceCloseHandles(handleId, recursive).collectList().block();
}
- public Flux getHandles(int maxResult, boolean recursive) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a subdirectory under current directory with specific name and returns a response of DirectoryClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the sub directory "subdir"
+ *
+ *
+ * Response<DirectoryClient> response = client.createSubDirectory("subdir")
+ * System.out.printf("Creating the sub directory completed with status code %d", response.statusCode());
+ *
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @return A response containing the subdirectory client and the status of creating the directory.
+ * @throws StorageErrorException If the subdirectory has already existed, the parent directory does not exist or directory is an invalid resource name.
+ */
+ public Response createSubDirectory(String subDirectoryName) {
+ return createSubDirectory(subDirectoryName, null);
}
- public Flux forceCloseHandles(String handleId, boolean recursive) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a subdirectory under current directory with specific name , metadata and returns a response of DirectoryClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the subdirectory named "subdir", with metadata
+ *
+ * com.azure.storage.file.directoryClient.createSubDirectory#string
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @param metadata Optional. Metadata to associate with the subdirectory
+ * @return A response containing the subdirectory client and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or subdirectory is an invalid resource name.
+ */
+ public Response createSubDirectory(String subDirectoryName, Map metadata) {
+ DirectoryClient directoryClient = getSubDirectoryClient(subDirectoryName);
+ return new SimpleResponse<>(directoryClient.create(metadata), directoryClient);
}
- public Mono> createSubDirectory(String directoryName, Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the subdirectory with specific name in the storage account. The directory must be empty before it can be deleted.
+ *
+ *
Code Samples
+ *
+ *
Delete the subdirectory named "subdir"
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.deleteSubDirectory#string}
+ *
+ * @param subDirectoryName Name of the subdirectory
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the subdirectory doesn't exist, the parent directory does not exist or subdirectory name is an invalid resource name.
+ */
+ public VoidResponse deleteSubDirectory(String subDirectoryName) {
+ return directoryAsyncClient.deleteSubDirectory(subDirectoryName).block();
}
- public Mono deleteSubDirectory(String directoryName) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a file in the storage account with specific name, max number of results and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create 1k file with named "myFile"
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.createFile#string-long}
+ *
+ * @param fileName Name of the file
+ * @param maxSize Size of the file
+ * @return A response containing the FileClient and the status of creating the directory.
+ * @throws StorageErrorException If the file has already existed, the parent directory does not exist or file name is an invalid resource name.
+ */
+ public Response createFile(String fileName, long maxSize) {
+ return createFile(fileName, maxSize, null, null);
}
- public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map meatadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a file in the storage account with specific name and returns a response of DirectoryInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file named "myFile"
+ *
+ *
+ * Response<FileClient> response = client.createFile("myFile", Collections.singletonMap("directory", "metadata"))
+ * System.out.printf("Creating the file completed with status code %d", response.statusCode());
+ *
+ *
+ * @param fileName Name of the file
+ * @param maxSize Max size of the file
+ * @param httpHeaders the Http headers set to the file
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or file name is an invalid resource name.
+ */
+ public Response createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map metadata) {
+ return directoryAsyncClient.createFile(fileName, maxSize, httpHeaders, metadata)
+ .map(response -> new SimpleResponse<>(response, new FileClient(response.value()))).block();
}
- public Mono deleteFile(String fileName) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the file with specific name in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Delete the file "filetest"
+ *
+ * {@codesnippet com.azure.storage.file.directoryClient.deleteFile#string}
+ *
+ * @param fileName Name of the file
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist or file name is an invalid resource name.
+ */
+ public VoidResponse deleteFile(String fileName) {
+ return directoryAsyncClient.deleteFile(fileName).block();
}
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java
index 7bbd3cb138d2e..37c8c01b4d2ad 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java
@@ -3,7 +3,352 @@
package com.azure.storage.file;
+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.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.RetryPolicy;
+import com.azure.core.http.policy.UserAgentPolicy;
+import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders;
+import com.azure.core.util.configuration.Configuration;
+import com.azure.core.util.configuration.ConfigurationManager;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+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;
+
+/**
+ * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link DirectoryClient FileClients}
+ * and {@link DirectoryAsyncClient FileAsyncClients}, calling {@link DirectoryClientBuilder#buildClient() buildClient}
+ * constructs an instance of FileClient and calling {@link DirectoryClientBuilder#buildAsyncClient() buildAsyncClient}
+ * constructs an instance of FileAsyncClient.
+ *
+ *
The client needs the endpoint of the Azure Storage File service, name of the share, and authorization credential.
+ * {@link DirectoryClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the
+ * {@link DirectoryClientBuilder#shareName(String)}, {@link DirectoryClientBuilder#directoryName(String)} and a {@link SASTokenCredential} that authorizes the client.
+ *
+ *
Instantiating a synchronous Directory Client with SAS token
If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may
+ * be set using {@link DirectoryClientBuilder#credential(SASTokenCredential) credential}.
+ *
+ *
+ *
Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential
+ * a connection string from the Storage File service must be used. Set the SharedKeyCredential with
+ * {@link DirectoryClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and
+ * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.
+ *
+ *
Instantiating a synchronous Directory Client with connection string.
Instantiating an Asynchronous Directory Client with connection string.
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring}
+ *
+ * @see DirectoryClient
+ * @see DirectoryAsyncClient
+ * @see SASTokenCredential
+ * @see SharedKeyCredential
+ */
public class DirectoryClientBuilder {
+ private static final String ACCOUNT_NAME = "accountname";
+ private final List policies;
+ private final RetryPolicy retryPolicy;
+
+ private HttpLogDetailLevel logLevel;
+ private Configuration configuration;
+ private URL endpoint;
+ private String shareName;
+ private String directoryName;
+ private SASTokenCredential sasTokenCredential;
+ private SharedKeyCredential sharedKeyCredential;
+ private HttpClient httpClient;
+ private HttpPipeline pipeline;
+ private String shareSnapshot;
+
+ /**
+ * Creates a builder instance that is able to configure and construct {@link DirectoryClient DirectoryClients}
+ * and {@link DirectoryAsyncClient DirectoryAsyncClients}.
+ */
+ public DirectoryClientBuilder() {
+ retryPolicy = new RetryPolicy();
+ logLevel = HttpLogDetailLevel.NONE;
+ policies = new ArrayList<>();
+
+ configuration = ConfigurationManager.getConfiguration();
+ }
+
+ /**
+ * Creates a {@link DirectoryAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is
+ * called a new instance of {@link DirectoryAsyncClient} is created.
+ *
+ *
+ * If {@link DirectoryClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link DirectoryClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link DirectoryAsyncClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A ShareAsyncClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint} or {@code shareName} is {@code null}.
+ * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public DirectoryAsyncClient buildAsyncClient() {
+ Objects.requireNonNull(endpoint);
+
+ if (pipeline != null) {
+ return new DirectoryAsyncClient(endpoint, pipeline, shareName, directoryName, shareSnapshot);
+ }
+
+ if (sasTokenCredential == null && sharedKeyCredential == null) {
+ throw new IllegalArgumentException("Credentials are required for authorization");
+ }
+
+ // Closest to API goes first, closest to wire goes last.
+ final List policies = new ArrayList<>();
+
+ policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration));
+ policies.add(new RequestIdPolicy());
+ policies.add(new AddDatePolicy());
+
+ if (sharedKeyCredential != null) {
+ policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
+ } else {
+ policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
+ }
+
+ HttpPolicyProviders.addBeforeRetryPolicies(policies);
+
+ policies.add(retryPolicy);
+
+ policies.addAll(this.policies);
+ HttpPolicyProviders.addAfterRetryPolicies(policies);
+ policies.add(new HttpLoggingPolicy(logLevel));
+
+ HttpPipeline pipeline = HttpPipeline.builder()
+ .policies(policies.toArray(new HttpPipelinePolicy[0]))
+ .httpClient(httpClient)
+ .build();
+
+ return new DirectoryAsyncClient(endpoint, pipeline, shareName, directoryName, shareSnapshot);
+ }
+
+ /**
+ * Creates a {@link DirectoryClient} based on options set in the builder. Every time {@code buildClient()} is
+ * called a new instance of {@link DirectoryClient} is created.
+ *
+ *
+ * If {@link DirectoryClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link DirectoryClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link DirectoryClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A DirectoryClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint}, {@code shareName} or {@code directoryName} is {@code null}.
+ * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public DirectoryClient buildClient() {
+ return new DirectoryClient(this.buildAsyncClient());
+ }
+
+ /**
+ * Sets the endpoint for the Azure Storage File instance that the client will interact with.
+ *
+ *
The first path segment, if the endpoint contains path segments, will be assumed to be the name of the share
+ * that the client will interact with. Rest of the path segments should be the path of the directory.
+ *
+ *
Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String)} in an
+ * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.
+ *
+ * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from.
+ * @return the updated DirectoryClientBuilder object
+ * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is an invalid URL
+ */
+ public DirectoryClientBuilder endpoint(String endpoint) {
+ Objects.requireNonNull(endpoint);
+ try {
+ URL fullURL = new URL(endpoint);
+ this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost());
+ String[] pathSegments = fullURL.getPath().split("/");
+ int length = pathSegments.length;
+ this.shareName = length >= 2 ? pathSegments[1] : this.shareName;
+ this.directoryName = length >= 3 ? pathSegments[2] : this.directoryName;
+
+ // Attempt to get the SAS token from the URL passed
+ SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery());
+ if (credential != null) {
+ this.sasTokenCredential = credential;
+ }
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("The Azure Storage Directory endpoint url is malformed.");
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the {@link SASTokenCredential} used to authenticate requests sent to the File service.
+ *
+ * @param credential SAS token credential generated from the Storage account that authorizes requests
+ * @return the updated DirectoryClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
+ */
+ public DirectoryClientBuilder credential(SASTokenCredential credential) {
+ this.sasTokenCredential = credential;
+ return this;
+ }
+
+ /**
+ * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the
+ * File service.
+ *
+ * @param connectionString Connection string from the Access Keys section in the Storage account
+ * @return the updated DirectoryClientBuilder object
+ * @throws NullPointerException If {@code connectionString} is {@code null}.
+ */
+ public DirectoryClientBuilder connectionString(String connectionString) {
+ Objects.requireNonNull(connectionString);
+ this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString);
+ getEndPointFromConnectionString(connectionString);
+ return this;
+ }
+
+ private void getEndPointFromConnectionString(String connectionString) {
+ Map connectionStringPieces = new HashMap<>();
+ for (String connectionStringPiece : connectionString.split(";")) {
+ String[] kvp = connectionStringPiece.split("=", 2);
+ connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
+ }
+ String accountName = connectionStringPieces.get(ACCOUNT_NAME);
+ try {
+ this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName));
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. "
+ + "Connection String: %s", connectionString));
+ }
+ }
+
+ /**
+ * Sets the share that the constructed clients will interact with
+ *
+ * @param shareName Name of the share
+ * @return the updated DirectoryClientBuilder object
+ * @throws NullPointerException If {@code shareName} is {@code null}.
+ */
+ public DirectoryClientBuilder shareName(String shareName) {
+ this.shareName = shareName;
+ return this;
+ }
+
+ /**
+ * Sets the directory that the constructed clients will interact with
+ *
+ * @param directoryName Path to the directory
+ * @return the updated DirectoryClientBuilder object
+ * @throws NullPointerException If {@code directoryName} is {@code null}.
+ */
+ public DirectoryClientBuilder directoryName(String directoryName) {
+ this.directoryName = directoryName;
+ return this;
+ }
+
+ /**
+ * Sets the HTTP client to use for sending and receiving requests to and from the service.
+ *
+ * @param httpClient The HTTP client to use for requests.
+ * @return The updated DirectoryClientBuilder object.
+ * @throws NullPointerException If {@code httpClient} is {@code null}.
+ */
+ public DirectoryClientBuilder httpClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ /**
+ * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}.
+ *
+ * @param pipelinePolicy The retry policy for service requests.
+ * @return The updated DirectoryClientBuilder object.
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}.
+ */
+ public DirectoryClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
+ this.policies.add(pipelinePolicy);
+ return this;
+ }
+
+ /**
+ * Sets the logging level for HTTP requests and responses.
+ *
+ * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses.
+ * @return The updated DirectoryClientBuilder object.
+ */
+ public DirectoryClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
+ this.logLevel = logLevel;
+ return this;
+ }
+
+ /**
+ * Sets the HTTP pipeline to use for the service client.
+ *
+ *
If {@code pipeline} is set, all other settings are ignored, aside from {@link DirectoryClientBuilder#endpoint(String) endpoint},
+ * {@link DirectoryClientBuilder#shareName(String) shareName} @{link DirectoryClientBuilder#directoryName(String) filePath}, and {@link DirectoryClientBuilder#shareSnapshot(String) snaphotShot}
+ * when building clients.
+ *
+ * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses.
+ * @return The updated DirectoryClientBuilder object.
+ * @throws NullPointerException If {@code pipeline} is {@code null}.
+ */
+ public DirectoryClientBuilder pipeline(HttpPipeline pipeline) {
+ this.pipeline = Objects.requireNonNull(pipeline);
+ 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 builder, defaults to Configuration.NONE
+ * @param configuration configuration store
+ * @return the updated DirectoryClientBuilder object
+ */
+ public DirectoryClientBuilder configuration(Configuration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
-// connectionString, shareName, directoryName, FileClientOptions, SharedKeyCredential,
+ /**
+ * Sets the snapshot that the constructed clients will interact with. This snapshot must be linked to the share
+ * that has been specified in the builder.
+ *
+ * @param shareSnapshot Identifier of the snapshot
+ * @return the updated DirectoryClientBuilder object
+ * @throws NullPointerException If {@code shareSnapshot} is {@code null}.
+ */
+ public DirectoryClientBuilder shareSnapshot(String shareSnapshot) {
+ this.shareSnapshot = shareSnapshot;
+ return this;
+ }
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java
index 0297818f4650b..02b2f809675b7 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java
@@ -3,77 +3,775 @@
package com.azure.storage.file;
+import com.azure.core.http.HttpPipeline;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.core.implementation.util.FluxUtil;
+import com.azure.core.util.Context;
+import com.azure.core.util.logging.ClientLogger;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+import com.azure.storage.file.implementation.AzureFileStorageBuilder;
+import com.azure.storage.file.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.models.CopyStatusType;
import com.azure.storage.file.models.FileCopyInfo;
import com.azure.storage.file.models.FileDownloadInfo;
+import com.azure.storage.file.models.FileGetPropertiesHeaders;
import com.azure.storage.file.models.FileHTTPHeaders;
import com.azure.storage.file.models.FileInfo;
+import com.azure.storage.file.models.FileMetadataInfo;
import com.azure.storage.file.models.FileProperties;
-import com.azure.storage.file.models.FileRangeInfo;
+import com.azure.storage.file.models.FileRange;
import com.azure.storage.file.models.FileRangeWriteType;
import com.azure.storage.file.models.FileUploadInfo;
+import com.azure.storage.file.models.FileUploadRangeHeaders;
+import com.azure.storage.file.models.FilesCreateResponse;
+import com.azure.storage.file.models.FilesDownloadResponse;
+import com.azure.storage.file.models.FilesForceCloseHandlesResponse;
+import com.azure.storage.file.models.FilesGetPropertiesResponse;
+import com.azure.storage.file.models.FilesGetRangeListResponse;
+import com.azure.storage.file.models.FilesListHandlesResponse;
+import com.azure.storage.file.models.FilesSetHTTPHeadersResponse;
+import com.azure.storage.file.models.FilesSetMetadataResponse;
+import com.azure.storage.file.models.FilesStartCopyResponse;
+import com.azure.storage.file.models.FilesUploadRangeResponse;
import com.azure.storage.file.models.HandleItem;
+import com.azure.storage.file.models.StorageErrorException;
import io.netty.buffer.ByteBuf;
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
-import java.util.Map;
-
+/**
+ * This class provides a client that contains all the operations for interacting with file in Azure Storage File Service.
+ * Operations allowed by the client are creating, copying, uploading, downloading, deleting and listing on a file, retrieving properties, setting metadata
+ * and list or force close handles of the file.
+ *
+ *
View {@link FileClientBuilder this} for additional ways to construct the client.
+ *
+ * @see FileClientBuilder
+ * @see FileClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class FileAsyncClient {
- FileAsyncClient() {
- throw new UnsupportedOperationException();
+ private static final ClientLogger LOGGER = new ClientLogger(FileAsyncClient.class);
+ private static final long FILE_DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024L;
+
+ private final AzureFileStorageImpl azureFileStorageClient;
+ private final String shareName;
+ private final String filePath;
+ private final String shareSnapshot;
+
+ /**
+ * Creates a FileAsyncClient that sends requests to the storage file at {@link AzureFileStorageImpl#url() endpoint}.
+ * Each service call goes through the {@link HttpPipeline pipeline} in the {@code client}.
+ * @param azureFileStorageClient Client that interacts with the service interfaces
+ * @param shareName Name of the share
+ * @param filePath Path to the file
+ * @param shareSnapshot The snapshot of the share
+ */
+ FileAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String filePath, String shareSnapshot) {
+ this.shareName = shareName;
+ this.filePath = filePath;
+ this.shareSnapshot = shareSnapshot;
+ this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(azureFileStorageClient.httpPipeline())
+ .url(azureFileStorageClient.url())
+ .version(azureFileStorageClient.version())
+ .build();
+ }
+
+ /**
+ * Creates a FileAsyncClient that sends requests to the storage account at {@code endpoint}.
+ * Each service call goes through the {@code httpPipeline}.
+ * @param endpoint URL for the Storage File service
+ * @param httpPipeline HttpPipeline that HTTP requests and response flow through
+ * @param shareName Name of the share
+ * @param filePath Path to the file
+ * @param shareSnapshot Optional. The snapshot of the share
+ */
+ FileAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String filePath, String shareSnapshot) {
+ this.shareName = shareName;
+ this.filePath = filePath;
+ this.shareSnapshot = shareSnapshot;
+ this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline)
+ .url(endpoint.toString())
+ .build();
}
- public static FileClientBuilder asyncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * Get the getFileUrl of the storage file client.
+ * @return the URL of the storage file client
+ * @throws MalformedURLException if no protocol is specified, or an
+ * unknown protocol is found, or {@code spec} is {@code null}.
+ */
+ public URL getFileUrl() throws MalformedURLException {
+ return new URL(azureFileStorageClient.url());
}
+ /**
+ * Creates a file in the storage account and returns a response of {@link FileInfo} to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file with size 1KB.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.create}
+ *
+ * @param maxSize The maximum size in bytes for the file, up to 1 TiB.
+ * @return A response containing the file info and the status of creating the file.
+ * @throws StorageErrorException If the file has already existed, the parent directory does not exist or fileName is an invalid resource name.
+ */
+ public Mono> create(long maxSize) {
+ return create(maxSize, null, null);
+ }
+
+ /**
+ * Creates a file in the storage account and returns a response of FileInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file with length of 1024 bytes, some headers and metadata.
+ *
+ *
+ * FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain");
+ * client.create(1024, httpHeaders, Collections.singletonMap("file", "updatedMetadata"))
+ * .subscribe(response -> System.out.printf("Creating the file completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param maxSize The maximum size in bytes for the file, up to 1 TiB.
+ * @param httpHeaders Additional parameters for the operation.
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * @see C# identifiers
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory is an invalid resource name.
+ */
public Mono> create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.files().createWithRestResponseAsync(shareName, filePath, maxSize, null, metadata, httpHeaders, Context.NONE)
+ .map(this::createResponse);
}
+ /**
+ * Copies a blob or file to a destination file within the storage account.
+ *
+ *
Code Samples
+ *
+ *
Copy file from source url to the {@code filePath}
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.startCopy#string-map}
+ *
+ * @param sourceUrl Specifies the URL of the source file or blob, up to 2 KB in length.
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * * @see C# identifiers
+ * @return A response containing the file copy info and the status of copying the file.
+ */
public Mono> startCopy(String sourceUrl, Map metadata) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.files().startCopyWithRestResponseAsync(shareName, filePath, sourceUrl, null, metadata, Context.NONE)
+ .map(this::startCopyResponse);
}
+ /**
+ * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata.
+ *
+ *
Code Samples
+ *
+ *
Abort copy file from copy id("someCopyId")
+ *
+ *
+ * client.abortCopy("someCopyId")
+ * .subscribe(response -> System.out.printf("Abort copying the file completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param copyId Specifies the copy id which has copying pending status associate with it.
+ * @return A response containing the status of aborting copy the file.
+ */
public Mono abortCopy(String copyId) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.files().abortCopyWithRestResponseAsync(shareName, filePath, copyId, Context.NONE)
+ .map(VoidResponse::new);
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
+ *
+ * @param downloadFilePath The path where store the downloaded file
+ * @return An empty response.
+ */
+ public Mono downloadToFile(String downloadFilePath) {
+ return downloadToFile(downloadFilePath, null);
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file from 1024 to 2048 bytes to current folder.
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadToFile}
+ *
+ * @param downloadFilePath The path where store the downloaded file
+ * @param range Optional. Return file data only from the specified byte range.
+ * @return An empty response.
+ * @throws UncheckedIOException If an I/O error occurs.
+ */
+ public Mono downloadToFile(String downloadFilePath, FileRange range) {
+ AsynchronousFileChannel channel = channelSetup(downloadFilePath);
+ return sliceFileRange(range)
+ .flatMap(chunk -> downloadWithProperties(chunk, false)
+ .map(dar -> dar.value().body())
+ .subscribeOn(Schedulers.elastic())
+ .flatMap(fbb -> FluxUtil.bytebufStreamToFile(fbb, channel, chunk.start() - (range == null ? 0 : range.start()))
+ .subscribeOn(Schedulers.elastic())
+ .timeout(Duration.ofSeconds(300))
+ .retry(3, throwable -> throwable instanceof IOException || throwable instanceof TimeoutException))
+ .doOnTerminate(() ->
+ LOGGER.asInfo().log("Saved " + chunk.toString() + " on thread " + Thread.currentThread().getName())))
+ .then()
+ .doOnTerminate(() -> channelCleanUp(channel));
+ }
+
+ private AsynchronousFileChannel channelSetup(String filePath) {
+ try {
+ return AsynchronousFileChannel.open(Paths.get(filePath), StandardOpenOption.READ, StandardOpenOption.WRITE);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void channelCleanUp(AsynchronousFileChannel channel) {
+ try {
+ channel.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private Flux sliceFileRange(FileRange fileRange) {
+ long offset = fileRange == null ? 0L : fileRange.start();
+ Mono end;
+ if (fileRange != null) {
+ end = Mono.just(fileRange.end());
+ } else {
+ end = Mono.empty();
+ }
+ end = end.switchIfEmpty(getProperties().map(rb -> rb.value().contentLength()));
+ return end
+ .map(e -> {
+ List chunks = new ArrayList<>();
+ for (long pos = offset; pos < e; pos += FILE_DEFAULT_BLOCK_SIZE) {
+ long count = FILE_DEFAULT_BLOCK_SIZE;
+ if (pos + count > e) {
+ count = e - pos;
+ }
+ chunks.add(new FileRange(pos, pos + count - 1));
+ }
+ return chunks;
+ })
+ .flatMapMany(Flux::fromIterable);
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file with its metadata and properties.
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadWithProperties}
+ *
+ * @return A response that only contains headers and response status code
+ */
+ public Mono> downloadWithProperties() {
+ return downloadWithProperties(null, null);
}
- public Mono> downloadWithProperties(long offset, long length, boolean rangeGetContentMD5) {
- throw new UnsupportedOperationException();
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.
+ *
+ *
+ * client.downloadWithProperties(new Range(1024, 2048), false)
+ * .subscribe(response -> System.out.printf("Downloading the file range completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param range Optional. Return file data only from the specified byte range.
+ * @param rangeGetContentMD5 Optional. When this header is set to true and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size.
+ * @return A response that only contains headers and response status code
+ */
+ public Mono> downloadWithProperties(FileRange range, Boolean rangeGetContentMD5) {
+ String rangeString = range == null ? null : range.toString();
+ return azureFileStorageClient.files().downloadWithRestResponseAsync(shareName, filePath, null, rangeString, rangeGetContentMD5, Context.NONE)
+ .map(this::downloadWithPropertiesResponse);
}
+ /**
+ * Deletes the file associate with the client.
+ *
+ *
Code Samples
+ *
+ *
Delete the file
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.delete}
+ *
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist.
+ */
public Mono delete() {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.files().deleteWithRestResponseAsync(shareName, filePath, Context.NONE)
+ .map(VoidResponse::new);
}
- public Mono> getProperties(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Retrieves the properties of the storage account's file.
+ * The properties includes file metadata, last modified date, is server encrypted, and eTag.
+ *
+ *
+ *
+ * @return Storage file properties
+ */
+ public Mono> getProperties() {
+ return azureFileStorageClient.files().getPropertiesWithRestResponseAsync(shareName, filePath, shareSnapshot, null, Context.NONE)
+ .map(this::getPropertiesResponse);
}
+ /**
+ * Sets the user-defined httpHeaders to associate to the file.
+ *
+ *
If {@code null} is passed for the httpHeaders it will clear the httpHeaders associated to the file.
+ *
+ *
Code Samples
+ *
+ *
Set the httpHeaders of contentType of "text/plain"
+ *
+ *
+ * FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain");
+ * client.setHttpHeaders(1024, httpHeaders)
+ * .subscribe(response -> System.out.printf("Setting the file httpHeaders completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Clear the metadata of the file
+ *
+ *
+ * client.setHttpHeaders(1024, null)
+ * .subscribe(response -> System.out.printf("Clearing the file httpHeaders completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param newFileSize New file size of the file
+ * @param httpHeaders Resizes a file to the specified size. If the specified byte value is less than the current size of the file, then all ranges above the specified byte value are cleared.
+ * @return Response of the information about the file
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ */
public Mono> setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) {
- throw new UnsupportedOperationException();
+ return azureFileStorageClient.files().setHTTPHeadersWithRestResponseAsync(shareName, filePath, null, newFileSize, httpHeaders, Context.NONE)
+ .map(this::setHttpHeadersResponse);
}
- public Mono> setMeatadata(Map meatadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the user-defined metadata to associate to the file.
+ *
+ *
If {@code null} is passed for the metadata it will clear the metadata associated to the file.
+ *
+ *
Code Samples
+ *
+ *
Set the metadata to "file:updatedMetadata"
+ *
+ *
+ * client.setMetadata(Collections.singletonMap("file", "updatedMetadata"))
+ * .subscribe(response -> System.out.printf("Setting the file metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Clear the metadata of the file
+ *
+ *
+ * client.setMetadata(null)
+ * .subscribe(response -> System.out.printf("Clearing the file metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Options.Metadata to set on the file, if null is passed the metadata for the file is cleared
+ * @return information about the file
+ * @throws StorageErrorException If the file doesn't exist or the metadata contains invalid keys
+ */
+ public Mono> setMetadata(Map metadata) {
+ return azureFileStorageClient.files().setMetadataWithRestResponseAsync(shareName, filePath, null, metadata, Context.NONE)
+ .map(this::setMeatadataResponse);
}
- public Mono> upload(FileRangeWriteType type, long offset, long length, Flux data) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads a range of bytes to the beginning of a file in storage file service. Upload operations performs an in-place write on the specified file.
+ *
+ *
Code Samples
+ *
+ *
Upload "default" to the file.
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.upload}
+ *
+ * @param data The data which will upload to the storage file.
+ * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero..
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large)
+ */
+ public Mono> upload(Flux data, long length) {
+ FileRange range = new FileRange(0, length - 1);
+ return azureFileStorageClient.files().uploadRangeWithRestResponseAsync(shareName, filePath, range.toString(), FileRangeWriteType.UPDATE, length, data, null, null, Context.NONE)
+ .map(this::uploadResponse);
}
- public Flux listRanges(long offset, long length, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads a range of bytes to specific of a file in storage file service. Upload operations performs an in-place write on the specified file.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.
+ *
+ *
+ * ByteBuf defaultData = Unpooled.wrappedBuffer(defaultText.getBytes(StandardCharsets.UTF_8));
+ * client.upload(defaultData, defaultData.readableBytes())
+ * .subscribe(response -> System.out.printf("Upload the bytes to file range completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param data The data which will upload to the storage file.
+ * @param offset Optional. The starting point of the upload range. It will start from the beginning if it is {@code null}
+ * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero.
+ * @param type You may specify one of the following options:
+ * - Update: Writes the bytes specified by the request body into the specified range.
+ * - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large)
+ */
+ public Mono> upload(Flux data, long length, long offset, FileRangeWriteType type) {
+ FileRange range = new FileRange(offset, offset + length - 1);
+ return azureFileStorageClient.files().uploadRangeWithRestResponseAsync(shareName, filePath, range.toString(), type, length, data, null, null, Context.NONE)
+ .map(this::uploadResponse);
}
- public Flux listHandles(int maxResults) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads file to storage file service.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from the source file path.
+ *
+ * {@codesnippet com.azure.storage.file.fileAsyncClient.uploadFromFile}
+ *
+ * @param uploadFilePath The path where store the source file to upload
+ * @return An empty response.
+ */
+ public Mono uploadFromFile(String uploadFilePath) {
+ return uploadFromFile(uploadFilePath, FileRangeWriteType.UPDATE);
}
+ /**
+ * Uploads file to storage file service.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from the source file path.
+ *
+ *
+ * client.uploadFromFile("someFilePath", FileRangeWriteType.UPDATE)
+ * .doOnTerminate(() -> if (client.getProperties() != null) {
+ * System.out.printf("Upload the file with length of %d completed", client.getProperties().block().value().contentLength());
+ * });
+ *
+ *
+ * @param uploadFilePath The path where store the source file to upload
+ * @param type You may specify one of the following options:
+ * - Update: Writes the bytes specified by the request body into the specified range.
+ * - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
+ * @return An empty response.
+ * @throws UncheckedIOException If an I/O error occurs.
+ */
+ public Mono uploadFromFile(String uploadFilePath, FileRangeWriteType type) {
+ AsynchronousFileChannel channel = channelSetup(uploadFilePath);
+ return Flux.fromIterable(sliceFile(uploadFilePath))
+ .flatMap(chunk -> {
+ return upload(FluxUtil.byteBufStreamFromFile(channel, chunk.start(), chunk.end() - chunk.start() + 1), chunk.end() - chunk.start() + 1, chunk.start(), type)
+ .timeout(Duration.ofSeconds(300))
+ .retry(3, throwable -> throwable instanceof IOException || throwable instanceof TimeoutException);
+ })
+ .then()
+ .doOnTerminate(() -> channelCleanUp(channel));
+ }
+
+ private List sliceFile(String path) {
+ File file = new File(path);
+ assert file.exists();
+ List ranges = new ArrayList<>();
+ for (long pos = 0; pos < file.length(); pos += FILE_DEFAULT_BLOCK_SIZE) {
+ long count = FILE_DEFAULT_BLOCK_SIZE;
+ if (pos + count > file.length()) {
+ count = file.length() - pos;
+ }
+ ranges.add(new FileRange(pos, pos + count - 1));
+ }
+ return ranges;
+ }
+
+ /**
+ * List of valid ranges for a file.
+ *
+ *
+ *
+ * @param range Optional. Return file data only from the specified byte range.
+ * @return {@link FileRange ranges} in the files that satisfy the requirements
+ */
+ public Flux listRanges(FileRange range) {
+ String rangeString = range == null ? null : range.toString();
+ return azureFileStorageClient.files().getRangeListWithRestResponseAsync(shareName, filePath, shareSnapshot, null, rangeString, Context.NONE)
+ .flatMapMany(this::convertListRangesResponseToFileRangeInfo);
+ }
+
+ /**
+ * List of open handles on a file.
+ *
+ *
Code Samples
+ *
+ *
List all handles for the file client.
+ *
+ *
+ * client.listHandles()
+ * .subscribe(result -> System.out.printf("List handles completed with handle id %s", result.handleId()));
+ *
+ *
+ * @return {@link HandleItem handles} in the files that satisfy the requirements
+ */
+ public Flux listHandles() {
+ return listHandles(null);
+ }
+
+ /**
+ * List of open handles on a file.
+ *
+ *
Code Samples
+ *
+ *
List 10 handles for the file client.
+ *
+ *
+ * client.listHandles(10)
+ * .subscribe(result -> System.out.printf("List handles completed with handle id %s", result.handleId())); *
+ * @param maxResults Optional. The number of results will return per page
+ * @return {@link HandleItem handles} in the file that satisfy the requirements
+ */
+ public Flux listHandles(Integer maxResults) {
+ return azureFileStorageClient.files().listHandlesWithRestResponseAsync(shareName, filePath, null, maxResults, null, shareSnapshot, Context.NONE)
+ .flatMapMany(response -> nextPageForHandles(response, maxResults));
+ }
+
+ /**
+ * Closes a handle or handles opened on a file at the service. It is intended to be used alongside {@link FileAsyncClient#listHandles()} (Integer)} .
+ *
+ *
Code Samples
+ *
+ *
Force close handles with handles returned by list handles in recursive.
View {@link FileClientBuilder this} for additional ways to construct the client.
+ *
+ * @see FileClientBuilder
+ * @see FileAsyncClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class FileClient {
- private final FileAsyncClient client;
+ private final FileAsyncClient fileAsyncClient;
+
+ /**
+ * Creates a FileClient that wraps a FileAsyncClient and blocks requests.
+ *
+ * @param fileAsyncClient FileAsyncClient that is used to send requests
+ */
+ FileClient(FileAsyncClient fileAsyncClient) {
+ this.fileAsyncClient = fileAsyncClient;
+ }
+
+ /**
+ * Get the getFileUrl of the storage file client.
+ * @return the URL of the storage file client
+ * @throws MalformedURLException if no protocol is specified, or an
+ * unknown protocol is found, or {@code spec} is {@code null}.
+ */
+ public URL getFileUrl() throws MalformedURLException {
+ return fileAsyncClient.getFileUrl();
+ }
+
+ /**
+ * Creates a file in the storage account and returns a response of {@link FileInfo} to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file with length of 1024 bytes, some headers and metadata.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.create}
+ *
+ * @param maxSize The maximum size in bytes for the file, up to 1 TiB.
+ * @return A response containing the file info and the status of creating the file.
+ * @throws StorageErrorException If the file has already existed, the parent directory does not exist or fileName is an invalid resource name.
+ */
+ public Response create(long maxSize) {
+ return fileAsyncClient.create(maxSize).block();
+ }
+
+ /**
+ * Creates a file in the storage account and returns a response of FileInfo to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the file with length of 1024 bytes, some headers and metadata.
+ *
+ *
+ * FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain");
+ * Response<FileInfo> response = client.create(1024, httpHeaders, Collections.singletonMap("file", "updatedMetadata"));
+ * System.out.printf("Creating the file completed with status code %d", response.statusCode());
+ *
+ *
+ * @param maxSize The maximum size in bytes for the file, up to 1 TiB.
+ * @param httpHeaders Additional parameters for the operation.
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * @see C# identifiers
+ * @return A response containing the directory info and the status of creating the directory.
+ * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory is an invalid resource name.
+ */
+ public Response create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) {
+ return fileAsyncClient.create(maxSize, httpHeaders, metadata).block();
+ }
+
+ /**
+ * Copies a blob or file to a destination file within the storage account.
+ *
+ *
Code Samples
+ *
+ *
Copy file from source getDirectoryUrl to the {@code filePath}
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.startCopy#string-map}
+ *
+ * @param sourceUrl Specifies the URL of the source file or blob, up to 2 KB in length.
+ * @param metadata Optional. Name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules.
+ * * @see C# identifiers
+ * @return A response containing the file copy info and the status of copying the file.
+ */
+ public Response startCopy(String sourceUrl, Map metadata) {
+ return fileAsyncClient.startCopy(sourceUrl, metadata).block();
+ }
+
+ /**
+ * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata.
+ *
+ *
Code Samples
+ *
+ *
Abort copy file from copy id("someCopyId")
+ *
+ *
+ * VoidResponse response = client.abortCopy("someCopyId")
+ * System.out.printf("Abort copying the file completed with status code %d", response.statusCode());
+ *
+ *
+ * @param copyId Specifies the copy id which has copying pending status associate with it.
+ * @return A response containing the status of aborting copy the file.
+ */
+ public VoidResponse abortCopy(String copyId) {
+ return fileAsyncClient.abortCopy(copyId).block();
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file to current folder.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.downloadToFile}
+ *
+ * @param downloadFilePath The path where store the downloaded file
+ */
+ public void downloadToFile(String downloadFilePath) {
+ downloadToFile(downloadFilePath, null);
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file from 1024 to 2048 bytes to current folder.
+ *
+ *
+ * client.downloadToFile("someFilePath", new FileRange(1024, 2048));
+ * if (Files.exist(Paths.get(downloadFilePath))) {
+ * System.out.println("Download the file completed");
+ * }
+ *
+ *
+ * @param downloadFilePath The path where store the downloaded file
+ * @param range Optional. Return file data only from the specified byte range.
+ */
+ public void downloadToFile(String downloadFilePath, FileRange range) {
+ fileAsyncClient.downloadToFile(downloadFilePath, range).block();
+ }
+
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file with its metadata and properties.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.downloadWithProperties}
+ *
+ * @return A response that only contains headers and response status code
+ */
+ public Response downloadWithProperties() {
+ return fileAsyncClient.downloadWithProperties(null, null).block();
+ }
- FileClient() {
- throw new UnsupportedOperationException();
+ /**
+ * Downloads a file from the system, including its metadata and properties
+ *
+ *
Code Samples
+ *
+ *
Download the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.
+ *
+ *
+ * Response<FileDownloadInfo> response = client.downloadWithProperties()
+ * System.out.printf("Downloading the file completed with status code %d", response.statusCode());
+ *
+ *
+ * @param range Optional. Return file data only from the specified byte range.
+ * @param rangeGetContentMD5 Optional. When this header is set to true and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size.
+ * @return A response that only contains headers and response status code
+ */
+ public Response downloadWithProperties(FileRange range, Boolean rangeGetContentMD5) {
+ return fileAsyncClient.downloadWithProperties(range, rangeGetContentMD5).block();
}
- public static FileClientBuilder syncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the file associate with the client.
+ *
+ *
Code Samples
+ *
+ *
Delete the file
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.delete}
+ *
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist.
+ */
+ public VoidResponse delete() {
+ return fileAsyncClient.delete().block();
}
- public Mono> create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Retrieves the properties of the storage account's file.
+ * The properties includes file metadata, last modified date, is server encrypted, and eTag.
+ *
+ *
+ *
+ * @return Storage file properties
+ */
+ public Response getProperties() {
+ return fileAsyncClient.getProperties().block();
}
- public Mono> startCopy(String sourceUrl, Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the user-defined httpHeaders to associate to the file.
+ *
+ *
If {@code null} is passed for the httpHeaders it will clear the httpHeaders associated to the file.
+ *
+ *
Code Samples
+ *
+ *
Set the httpHeaders of contentType of "text/plain"
+ *
+ *
+ * FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain");
+ * Response<FileInfo> response = client.setHttpHeaders(1024, httpHeaders);
+ * System.out.printf("Setting the file httpHeaders completed with status code %d", response.statusCode());
+ *
+ *
+ *
Clear the metadata of the file
+ *
+ *
+ * Response<FileInfo> response = client.setHttpHeaders(1024, null)
+ * System.out.printf("Clearing the file httpHeaders completed with status code %d", response.statusCode());
+ *
+ *
+ * @param newFileSize New file size of the file
+ * @param httpHeaders Resizes a file to the specified size. If the specified byte value is less than the current size of the file, then all ranges above the specified byte value are cleared.
+ * @return Response of the information about the file
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ */
+ public Response setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) {
+ return fileAsyncClient.setHttpHeaders(newFileSize, httpHeaders).block();
}
- public Mono abortCopy(String copyId) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the user-defined metadata to associate to the file.
+ *
+ *
If {@code null} is passed for the metadata it will clear the metadata associated to the file.
+ *
+ *
Code Samples
+ *
+ *
Set the metadata to "file:updatedMetadata"
+ *
+ *
+ * Response<FileMetadataInfo> response = client.setMetadata(Collections.singletonMap("file", "updatedMetadata"));
+ * System.out.printf("Setting the file metadata completed with status code %d", response.statusCode());
+ *
+ *
+ *
Clear the metadata of the file
+ *
+ *
+ * client.setMetadata(null)
+ * .subscribe(response -> System.out.printf("Clearing the file metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Options.Metadata to set on the file, if null is passed the metadata for the file is cleared
+ * @return information about the file
+ * @throws StorageErrorException If the file doesn't exist or the metadata contains invalid keys
+ */
+ public Response setMeatadata(Map metadata) {
+ return fileAsyncClient.setMetadata(metadata).block();
}
- public Mono> downloadWithProperties(long offset, long length, boolean rangeGetContentMD5) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads a range of bytes to the beginning of a file in storage file service. Upload operations performs an in-place write on the specified file.
+ *
+ *
Code Samples
+ *
+ *
Upload "default" to the file.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.upload}
+ *
+ * @param data The data which will upload to the storage file.
+ * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero..
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large)
+ */
+ public Response upload(ByteBuf data, long length) {
+ return fileAsyncClient.upload(Flux.just(data), length).block();
}
- public Mono delete() {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads a range of bytes to specific of a file in storage file service. Upload operations performs an in-place write on the specified file.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.
+ *
+ *
+ * ByteBuf defaultData = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8));
+ * Response<FileUploadInfo> response = client.upload(defaultData, defaultData.readableBytes());
+ * System.out.printf("Upload the bytes to file range completed with status code %d", response.statusCode());
+ *
+ *
+ * @param data The data which will upload to the storage file.
+ * @param offset Optional. The starting point of the upload range. It will start from the beginning if it is {@code null}
+ * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero.
+ * @param type You may specify one of the following options:
+ * - Update: Writes the bytes specified by the request body into the specified range.
+ * - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large)
+ */
+ public Response upload(ByteBuf data, long length, int offset, FileRangeWriteType type) {
+ return fileAsyncClient.upload(Flux.just(data), length, offset, type).block();
}
- public Mono> getProperties(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads file to storage file service.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from the source file path.
+ *
+ * {@codesnippet com.azure.storage.file.fileClient.uploadFromFile}
+ *
+ * @param uploadFilePath The path where store the source file to upload
+ */
+ public void uploadFromFile(String uploadFilePath) {
+ uploadFromFile(uploadFilePath, FileRangeWriteType.UPDATE);
}
- public Mono> setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) {
- throw new UnsupportedOperationException();
+ /**
+ * Uploads file to storage file service.
+ *
+ *
Code Samples
+ *
+ *
Upload the file from the source file path.
+ *
+ *
+ * client.uploadFromFile("someFilePath", FileRangeWriteType.UPDATE);
+ * if (client.getProperties() != null) {
+ * System.out.printf("Upload the file with length of %d completed", client.getProperties().block().value().contentLength());
+ * };
+ *
+ *
+ * @param uploadFilePath The path where store the source file to upload
+ * @param type You may specify one of the following options:
+ * - Update: Writes the bytes specified by the request body into the specified range.
+ * - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
+ */
+ public void uploadFromFile(String uploadFilePath, FileRangeWriteType type) {
+ fileAsyncClient.uploadFromFile(uploadFilePath, type).block();
}
- public Mono> setMeatadata(Map meatadata) {
- throw new UnsupportedOperationException();
+ /**
+ * List of valid ranges for a file.
+ *
+ *
+ *
+ * @return {@link FileRange ranges} in the files.
+ */
+ public Iterable listRanges() {
+ return fileAsyncClient.listRanges(null).toIterable();
}
- public Mono> upload(FileRangeWriteType type, long offset, long length, Flux data) {
- throw new UnsupportedOperationException();
+ /**
+ * List of valid ranges for a file.
+ *
+ *
Code Samples
+ *
+ *
List all ranges within the file range from 1KB to 2KB.
+ *
+ * @param range Optional. Return file data only from the specified byte range.
+ * @return {@link FileRange ranges} in the files that satisfy the requirements
+ */
+ public Iterable listRanges(FileRange range) {
+ return fileAsyncClient.listRanges(range).toIterable();
}
- public Flux listRanges(long offset, long length, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * List of open handles on a file.
+ *
+ *
+ *
+ * @return {@link HandleItem handles} in the files that satisfy the requirements
+ */
+ public Iterable listHandles() {
+ return listHandles(null);
}
- public Flux listHandles(int maxResults) {
- throw new UnsupportedOperationException();
+ /**
+ * List of open handles on a file.
+ *
+ *
+ * @param maxResults Optional. The number of results will return per page
+ * @return {@link HandleItem handles} in the file that satisfy the requirements
+ */
+ public Iterable listHandles(Integer maxResults) {
+ return fileAsyncClient.listHandles(maxResults).toIterable();
}
- public Flux forceCloseHandles(String handleId) {
- throw new UnsupportedOperationException();
+ /**
+ * Closes a handle or handles opened on a file at the service. It is intended to be used alongside {@link FileClient#listHandles()} (Integer)} .
+ *
+ *
Code Samples
+ *
+ *
Force close handles with handles returned by list handles in recursive.
+ * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles.
+ * @return The counts of number of handles closed
+ */
+ public Iterable forceCloseHandles(String handleId) {
+ return fileAsyncClient.forceCloseHandles(handleId).toIterable();
}
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java
index 610466b2aed52..cc80c4dc43a95 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java
@@ -3,7 +3,362 @@
package com.azure.storage.file;
+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.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.RetryPolicy;
+import com.azure.core.http.policy.UserAgentPolicy;
+import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders;
+import com.azure.core.util.configuration.Configuration;
+import com.azure.core.util.configuration.ConfigurationManager;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+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.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link FileClient FileClients}
+ * and {@link FileAsyncClient FileAsyncClients}, calling {@link FileClientBuilder#buildClient() buildClient}
+ * constructs an instance of FileClient and calling {@link FileClientBuilder#buildAsyncClient() buildAsyncClient}
+ * constructs an instance of FileAsyncClient.
+ *
+ *
The client needs the endpoint of the Azure Storage File service, name of the share, and authorization credential.
+ * {@link FileClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the
+ * {@link FileClientBuilder#shareName(String)}, {@link FileClientBuilder#filePath(String)} and a {@link SASTokenCredential} that authorizes the client.
+ *
+ *
Instantiating a synchronous File Client with SAS token
If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may
+ * be set using {@link FileClientBuilder#credential(SASTokenCredential) credential}.
Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential
+ * a connection string from the Storage File service must be used. Set the SharedKeyCredential with
+ * {@link FileClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and
+ * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.
+ *
+ *
Instantiating a synchronous File Client with connection string.
Instantiating an Asynchronous File Client with connection string.
+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring}
+ *
+ * @see FileClient
+ * @see FileAsyncClient
+ * @see SASTokenCredential
+ * @see SharedKeyCredential
+ */
public class FileClientBuilder {
+ private static final String ACCOUNT_NAME = "accountname";
+ private final List policies;
+ private final RetryPolicy retryPolicy;
+
+ private HttpLogDetailLevel logLevel;
+ private Configuration configuration;
+ private URL endpoint;
+ private String shareName;
+ private String filePath;
+ private SASTokenCredential sasTokenCredential;
+ private SharedKeyCredential sharedKeyCredential;
+ private HttpClient httpClient;
+ private HttpPipeline pipeline;
+ private String shareSnapshot;
+
+ /**
+ * Creates a builder instance that is able to configure and construct {@link FileClient FileClients}
+ * and {@link FileAsyncClient FileAsyncClients}.
+ */
+ public FileClientBuilder() {
+ retryPolicy = new RetryPolicy();
+ logLevel = HttpLogDetailLevel.NONE;
+ policies = new ArrayList<>();
+
+ configuration = ConfigurationManager.getConfiguration();
+ }
+
+ /**
+ * Creates a {@link FileAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is
+ * called a new instance of {@link FileAsyncClient} is created.
+ *
+ *
+ * If {@link FileClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link FileClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link FileAsyncClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A ShareAsyncClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint} or {@code shareName} is {@code null}.
+ * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public FileAsyncClient buildAsyncClient() {
+ Objects.requireNonNull(endpoint);
+
+ if (pipeline != null) {
+ return new FileAsyncClient(endpoint, pipeline, shareName, filePath, shareSnapshot);
+ }
+
+ if (sasTokenCredential == null && sharedKeyCredential == null) {
+ throw new IllegalArgumentException("Credentials are required for authorization");
+ }
+
+ // Closest to API goes first, closest to wire goes last.
+ final List policies = new ArrayList<>();
+
+ policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration));
+ policies.add(new RequestIdPolicy());
+ policies.add(new AddDatePolicy());
+
+ if (sharedKeyCredential != null) {
+ policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
+ } else {
+ policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
+ }
+
+ HttpPolicyProviders.addBeforeRetryPolicies(policies);
+
+ policies.add(retryPolicy);
+
+ policies.addAll(this.policies);
+ HttpPolicyProviders.addAfterRetryPolicies(policies);
+ policies.add(new HttpLoggingPolicy(logLevel));
+
+ HttpPipeline pipeline = HttpPipeline.builder()
+ .policies(policies.toArray(new HttpPipelinePolicy[0]))
+ .httpClient(httpClient)
+ .build();
+
+ return new FileAsyncClient(endpoint, pipeline, shareName, filePath, shareSnapshot);
+ }
+
+ /**
+ * Creates a {@link FileClient} based on options set in the builder. Every time {@code buildClient()} is
+ * called a new instance of {@link FileClient} is created.
+ *
+ *
+ * If {@link FileClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link FileClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link FileClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A FileClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint}, {@code shareName} or {@code filePath} is {@code null}.
+ * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public FileClient buildClient() {
+ return new FileClient(this.buildAsyncClient());
+ }
+
+ /**
+ * Sets the endpoint for the Azure Storage File instance that the client will interact with.
+ *
+ *
The first path segment, if the endpoint contains path segments, will be assumed to be the name of the share
+ * that the client will interact with. Rest of the path segments should be the path of the file.
+ * It mush end up with the file name if more segments exist.
+ *
+ *
Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String)} in an
+ * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.
+ *
+ * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from.
+ * @return the updated FileClientBuilder object
+ * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is an invalid URL
+ */
+ public FileClientBuilder endpoint(String endpoint) {
+ Objects.requireNonNull(endpoint);
+ try {
+ URL fullURL = new URL(endpoint);
+ this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost());
+
+ // Attempt to get the share name and file path from the URL passed
+ String[] pathSegments = fullURL.getPath().split("/");
+ int length = pathSegments.length;
+ this.shareName = length >= 2 ? pathSegments[1] : this.shareName;
+ String[] filePathParams = length >= 3 ? Arrays.copyOfRange(pathSegments, 2, length) : null;
+ this.filePath = filePathParams != null ? String.join("/", filePathParams) : this.filePath;
+
+ // Attempt to get the SAS token from the URL passed
+ SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery());
+ if (credential != null) {
+ this.sasTokenCredential = credential;
+ }
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("The Azure Storage File endpoint url is malformed.");
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the {@link SASTokenCredential} used to authenticate requests sent to the File service.
+ *
+ * @param credential SAS token credential generated from the Storage account that authorizes requests
+ * @return the updated FileClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
+ */
+ public FileClientBuilder credential(SASTokenCredential credential) {
+ this.sasTokenCredential = credential;
+ return this;
+ }
+
+ /**
+ * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the
+ * File service.
+ *
+ * @param connectionString Connection string from the Access Keys section in the Storage account
+ * @return the updated FileClientBuilder object
+ * @throws NullPointerException If {@code connectionString} is {@code null}.
+ */
+ public FileClientBuilder connectionString(String connectionString) {
+ Objects.requireNonNull(connectionString);
+ this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString);
+ getEndPointFromConnectionString(connectionString);
+ return this;
+ }
+
+ private void getEndPointFromConnectionString(String connectionString) {
+ Map connectionStringPieces = new HashMap<>();
+ for (String connectionStringPiece : connectionString.split(";")) {
+ String[] kvp = connectionStringPiece.split("=", 2);
+ connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
+ }
+ String accountName = connectionStringPieces.get(ACCOUNT_NAME);
+ try {
+ this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName));
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. "
+ + "Connection String: %s", connectionString));
+ }
+ }
+
+ /**
+ * Sets the share that the constructed clients will interact with
+ *
+ * @param shareName Name of the share
+ * @return the updated FileClientBuilder object
+ * @throws NullPointerException If {@code shareName} is {@code null}.
+ */
+ public FileClientBuilder shareName(String shareName) {
+ this.shareName = shareName;
+ return this;
+ }
+
+ /**
+ * Sets the file that the constructed clients will interact with
+ *
+ * @param filePath Path of the file, mush end up with the file name.
+ * @return the updated FileClientBuilder object
+ * @throws NullPointerException If {@code filePath} is {@code null}.
+ */
+ public FileClientBuilder filePath(String filePath) {
+ this.filePath = filePath;
+ return this;
+ }
+
+ /**
+ * Sets the HTTP client to use for sending and receiving requests to and from the service.
+ *
+ * @param httpClient The HTTP client to use for requests.
+ * @return The updated FileClientBuilder object.
+ * @throws NullPointerException If {@code httpClient} is {@code null}.
+ */
+ public FileClientBuilder httpClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ /**
+ * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}.
+ *
+ * @param pipelinePolicy The retry policy for service requests.
+ * @return The updated FileClientBuilder object.
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}.
+ */
+ public FileClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
+ this.policies.add(pipelinePolicy);
+ return this;
+ }
+
+ /**
+ * Sets the logging level for HTTP requests and responses.
+ *
+ * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses.
+ * @return The updated FileClientBuilder object.
+ */
+ public FileClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
+ this.logLevel = logLevel;
+ return this;
+ }
+
+ /**
+ * Sets the HTTP pipeline to use for the service client.
+ *
+ *
If {@code pipeline} is set, all other settings are ignored, aside from {@link FileClientBuilder#endpoint(String) endpoint},
+ * {@link FileClientBuilder#shareName(String) shareName} @{link FileClientBuilder#filePath(String) filePath}, and {@link FileClientBuilder#shareSnapshot(String) snaphotShot}
+ * when building clients.
+ *
+ * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses.
+ * @return The updated FileClientBuilder object.
+ * @throws NullPointerException If {@code pipeline} is {@code null}.
+ */
+ public FileClientBuilder pipeline(HttpPipeline pipeline) {
+ this.pipeline = Objects.requireNonNull(pipeline);
+ 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 builder, defaults to Configuration.NONE
+ * @param configuration configuration store
+ * @return the updated FileClientBuilder object
+ */
+ public FileClientBuilder configuration(Configuration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
- // fileUri, SharedKeyCredential, FileClientOptions ,withSnapshot
+ /**
+ * Sets the snapshot that the constructed clients will interact with. This snapshot must be linked to the share
+ * that has been specified in the builder.
+ *
+ * @param shareSnapshot Identifier of the snapshot
+ * @return the updated FileClientBuilder object
+ * @throws NullPointerException If {@code shareSnapshot} is {@code null}.
+ */
+ public FileClientBuilder shareSnapshot(String shareSnapshot) {
+ this.shareSnapshot = shareSnapshot;
+ return this;
+ }
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java b/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java
new file mode 100644
index 0000000000000..8cf9de8a7c250
--- /dev/null
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.storage.file;
+
+/*
+ * Gets the SDK information for this library component.
+ */
+class FileConfiguration {
+ //TODO: Eventually remove these hardcoded strings with https://github.com/Azure/azure-sdk-for-java/issues/3141
+ static final String NAME = "storage-file";
+ static final String VERSION = "1.0.0-SNAPSHOT";
+}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java
index 9c66716ca61c0..976be54d82381 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java
@@ -3,51 +3,362 @@
package com.azure.storage.file;
+import com.azure.core.http.HttpPipeline;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.core.implementation.util.ImplUtils;
+import com.azure.core.util.Context;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+import com.azure.storage.file.implementation.AzureFileStorageBuilder;
+import com.azure.storage.file.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.models.CorsRule;
+import com.azure.storage.file.models.DeleteSnapshotsOptionType;
import com.azure.storage.file.models.FileServiceProperties;
+import com.azure.storage.file.models.ListSharesIncludeType;
import com.azure.storage.file.models.ListSharesOptions;
+import com.azure.storage.file.models.ListSharesResponse;
+import com.azure.storage.file.models.ServicesListSharesSegmentResponse;
import com.azure.storage.file.models.ShareItem;
+import com.azure.storage.file.models.StorageErrorException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
-import java.util.Map;
-
-public class FileServiceAsyncClient {
+/**
+ * This class provides a client that contains all the operations for interacting with a file account in Azure Storage.
+ * Operations allowed by the client are creating, listing, and deleting shares and retrieving and updating properties
+ * of the account.
+ *
+ *
View {@link FileServiceClientBuilder this} for additional ways to construct the client.
+ *
+ * @see FileServiceClientBuilder
+ * @see FileServiceClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
+public final class FileServiceAsyncClient {
+ private final AzureFileStorageImpl client;
- FileServiceAsyncClient() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a FileServiceClient that sends requests to the storage account at {@code endpoint}.
+ * Each service call goes through the {@code httpPipeline}.
+ *
+ * @param endpoint URL for the Storage File service
+ * @param httpPipeline HttpPipeline that the HTTP requests and responses flow through
+ */
+ FileServiceAsyncClient(URL endpoint, HttpPipeline httpPipeline) {
+ this.client = new AzureFileStorageBuilder().pipeline(httpPipeline)
+ .url(endpoint.toString())
+ .build();
}
- public static FileServiceClientBuilder asyncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * @return the getFileServiceUrl of the Storage File service
+ */
+ public String getFileServiceUrl() {
+ return client.url();
}
- public String url() {
- throw new UnsupportedOperationException();
+ /**
+ * Constructs a ShareAsyncClient that interacts with the specified share.
+ *
+ *
If the share doesn't exist in the storage account {@link ShareAsyncClient#create() create} in the client will
+ * need to be called before interaction with the share can happen.
+ *
+ * @param shareName Name of the share
+ * @return a ShareAsyncClient that interacts with the specified share
+ */
+ public ShareAsyncClient getShareAsyncClient(String shareName) {
+ return new ShareAsyncClient(client, shareName);
}
- public ShareAsyncClient getShareClient(String shareName) {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all shares in the storage account without their metadata or snapshots.
+ *
+ *
Code Samples
+ *
+ *
List all shares in the account
+ *
+ * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.listShares}
+ *
+ * @return {@link ShareItem Shares} in the storage account without their metadata or snapshots
+ */
+ public Flux listShares() {
+ return listShares(null);
}
+ /**
+ * Lists the shares in the Storage account that pass the options filter.
+ *
+ *
Set starts with name filter using {@link ListSharesOptions#prefix(String) prefix} to filter shares that are
+ * listed.
+ *
+ *
Pass true to {@link ListSharesOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for
+ * the shares.
+ *
+ *
Pass true to {@link ListSharesOptions#includeSnapshots(boolean) includeSnapshots} to have snapshots of the
+ * shares listed.
+ *
+ *
Code Samples
+ *
+ *
List all shares that begin with "azure"
+ *
+ *
+ * client.listShares(new ListSharesOptions().prefix("azure"))
+ * .subscribe(result -> System.out.printf("Share %s exists in the account", result.name()));
+ *
+ *
+ *
List all shares including their snapshots and metadata
+ *
+ * @return Storage account File service properties
+ */
public Mono> getProperties() {
- throw new UnsupportedOperationException();
+ return client.services().getPropertiesWithRestResponseAsync(Context.NONE)
+ .map(response -> new SimpleResponse<>(response, response.value()));
+ }
+
+ /**
+ * Sets the properties for the storage account's File service. The properties range from storage analytics and
+ * metric to CORS (Cross-Origin Resource Sharing).
+ *
+ * To maintain the CORS in the Queue service pass a {@code null} value for {@link FileServiceProperties#cors() CORS}.
+ * To disable all CORS in the Queue service pass an empty list for {@link FileServiceProperties#cors() CORS}.
+ *
+ *
Code Sample
+ *
+ *
Clear CORS in the File service
+ *
+ *
+ * FileServiceProperties properties = client.getProperties().block().value();
+ * properties.cors(Collections.emptyList());
+ *
+ * client.setProperties(properties)
+ * .subscribe(response -> System.out.printf("Setting File service properties completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param properties Storage account File service properties
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException When one of the following is true
+ *
+ *
A CORS rule is missing one of its fields
+ *
More than five CORS rules will exist for the Queue service
{@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
+ *
+ */
+ public Mono setProperties(FileServiceProperties properties) {
+ return client.services().setPropertiesWithRestResponseAsync(properties, Context.NONE)
+ .map(VoidResponse::new);
}
- public Mono setProperties() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a share in the storage account with the specified name and returns a ShareAsyncClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the share "test"
+ *
+ * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.createShare#string}
+ *
+ * @param shareName Name of the share
+ * @return A response containing the ShareAsyncClient and the status of creating the share.
+ * @throws StorageErrorException If a share with the same name already exists
+ */
+ public Mono> createShare(String shareName) {
+ return createShare(shareName, null, null);
}
- public Mono> createShare(String shareName, Map metadata, int quotaInGB) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a share in the storage account with the specified name, metadata, and quota and returns a ShareAsyncClient to
+ * interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the share "test" with metadata "share:metadata"
+ *
+ *
+ * client.createShare("test", Collections.singletonMap("share", "metadata"), null)
+ * .subscribe(response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Create the share "test" with a quota of 10 GB
+ *
+ *
+ * client.createShare("test", null, 10)
+ * .subscribe(response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param shareName Name of the share
+ * @param metadata Optional. Metadata to associate with the share
+ * @param quotaInGB Optional. Maximum size the share is allowed to grow to in GB. This must be greater than 0 and
+ * less than or equal to 5120. The default value is 5120.
+ * @return A response containing the ShareAsyncClient and the status of creating the share.
+ * @throws StorageErrorException If a share with the same name already exists or {@code quotaInGB} is outside the
+ * allowed range.
+ */
+ public Mono> createShare(String shareName, Map metadata, Integer quotaInGB) {
+ ShareAsyncClient shareAsyncClient = new ShareAsyncClient(client, shareName);
+
+ return shareAsyncClient.create(metadata, quotaInGB)
+ .map(response -> new SimpleResponse<>(response, shareAsyncClient));
}
+ /**
+ * Deletes the share in the storage account with the given name
+ *
+ *
Code Samples
+ *
+ *
Delete the share "test"
+ *
+ * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.deleteShare#string}
+ *
+ * @param shareName Name of the share
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist
+ */
+ public Mono deleteShare(String shareName) {
+ return deleteShare(shareName, null);
+ }
+
+ /**
+ * Deletes the specific snapshot of the share in the storage account with the given name. Snapshot are identified
+ * by the time they were created.
+ *
+ *
Code Samples
+ *
+ *
Delete the snapshot of share "test" that was created at midnight
+ *
+ *
+ * OffsetDateTime midnight = OffsetDateTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC));
+ * client.deleteShare("test", midnight.toString())
+ * .subscribe(response -> System.out.printf("Deleting the snapshot completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param shareName Name of the share
+ * @param shareSnapshot Identifier of the snapshot
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist or the snapshot doesn't exist
+ */
public Mono deleteShare(String shareName, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ DeleteSnapshotsOptionType deleteSnapshots = null;
+ if (ImplUtils.isNullOrEmpty(shareSnapshot)) {
+ deleteSnapshots = DeleteSnapshotsOptionType.fromString(DeleteSnapshotsOptionType.INCLUDE.toString());
+ }
+ return client.shares().deleteWithRestResponseAsync(shareName, shareSnapshot, null, deleteSnapshots, Context.NONE)
+ .map(VoidResponse::new);
}
+
+// static Response mapToResponse(Response> response, T value) {
+// return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), value);
+// }
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java
index fd808bf1d6fde..ecab93cc76507 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java
@@ -1,52 +1,278 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
package com.azure.storage.file;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+import com.azure.storage.file.models.CorsRule;
import com.azure.storage.file.models.FileServiceProperties;
import com.azure.storage.file.models.ListSharesOptions;
import com.azure.storage.file.models.ShareItem;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
+import com.azure.storage.file.models.StorageErrorException;
import java.util.Map;
-public class FileServiceClient {
-
+/**
+ * This class provides a client that contains all the operations for interacting with a file account in Azure Storage.
+ * Operations allowed by the client are creating, listing, and deleting shares and retrieving and updating properties
+ * of the account.
+ *
+ *
View {@link FileServiceClientBuilder this} for additional ways to construct the client.
+ *
+ * @see FileServiceClientBuilder
+ * @see FileServiceAsyncClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
+public final class FileServiceClient {
private final FileServiceAsyncClient client;
- FileServiceClient() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a FileServiceClient that wraps a FileServiceAsyncClient and blocks requests.
+ *
+ * @param client FileServiceAsyncClient that is used to send requests
+ */
+ FileServiceClient(FileServiceAsyncClient client) {
+ this.client = client;
}
- public static FileServiceClientBuilder syncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * @return the getFileServiceUrl of the Storage File service
+ */
+ public String getFileServiceUrl() {
+ return client.getFileServiceUrl();
}
- public String url() {
- throw new UnsupportedOperationException();
+ /**
+ * Constructs a ShareClient that interacts with the specified share.
+ *
+ *
If the share doesn't exist in the storage account {@link ShareClient#create() create} in the client will
+ * need to be called before interaction with the share can happen.
+ *
+ * @param shareName Name of the share
+ * @return a ShareClient that interacts with the specified share
+ */
+ public ShareClient getShareClient(String shareName) {
+ return new ShareClient(client.getShareAsyncClient(shareName));
}
- public ShareClient getShareClient(String shareName) {
- throw new UnsupportedOperationException();
+ /**
+ * Lists all shares in the storage account without their metadata or snapshots.
+ *
+ *
Code Samples
+ *
+ *
List all shares in the account
+ *
+ * {@codesnippet com.azure.storage.file.fileServiceClient.listShares}
+ *
+ * @return {@link ShareItem Shares} in the storage account without their metadata or snapshots
+ */
+ public Iterable listShares() {
+ return listShares(null);
+ }
+
+ /**
+ * Lists the shares in the Storage account that pass the options filter.
+ *
+ *
Set starts with name filter using {@link ListSharesOptions#prefix(String) prefix} to filter shares that are
+ * listed.
+ *
+ *
Pass true to {@link ListSharesOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for
+ * the shares.
+ *
+ *
Pass true to {@link ListSharesOptions#includeSnapshots(boolean) includeSnapshots} to have snapshots of the
+ * shares listed.
+ *
+ *
Code Samples
+ *
+ *
List all shares that begin with "azure"
+ *
+ *
+ * for (ShareItem result : client.listShares(new ListSharesOptions().prefix("azure"))) {
+ * System.out.printf("Share %s exists in the account", result.name());
+ * }
+ *
+ *
+ *
List all shares including their snapshots and metadata
+ *
+ *
+ * for (ShareItem result : client.listShares(new ListSharesOptions().includeMetadata(true).includeSnapshots(true))) {
+ * System.out.printf("Share %s, Is Snapshot? %b, Metadata: %s", result.name(), result.snapshot() != null, result.metadata());
+ * }
+ *
+ *
+ * @param options Options for listing shares
+ * @return {@link ShareItem Shares} in the storage account that satisfy the filter requirements
+ */
+ public Iterable listShares(ListSharesOptions options) {
+ return client.listShares(options).toIterable();
+ }
+
+ /**
+ * Retrieves the properties of the storage account's File service. The properties range from storage analytics and
+ * metrics to CORS (Cross-Origin Resource Sharing).
+ *
+ *
+ *
+ * @return Storage account File service properties
+ */
+ public Response getProperties() {
+ return client.getProperties().block();
}
- public Flux listShares(ListSharesOptions options) {
- throw new UnsupportedOperationException();
+ /**
+ * Sets the properties for the storage account's File service. The properties range from storage analytics and
+ * metric to CORS (Cross-Origin Resource Sharing).
+ *
+ * To maintain the CORS in the Queue service pass a {@code null} value for {@link FileServiceProperties#cors() CORS}.
+ * To disable all CORS in the Queue service pass an empty list for {@link FileServiceProperties#cors() CORS}.
+ *
+ *
+ *
+ * @param properties Storage account File service properties
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException When one of the following is true
+ *
+ *
A CORS rule is missing one of its fields
+ *
More than five CORS rules will exist for the Queue service
{@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
+ *
+ */
+ public VoidResponse setProperties(FileServiceProperties properties) {
+ return client.setProperties(properties).block();
}
- public Mono> getProperties() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a share in the storage account with the specified name and returns a ShareClient to interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the share with share name of "myshare"
+ * {@codesnippet com.azure.storage.file.fileServiceClient.createShare#string}
+ *
+ * @param shareName Name of the share
+ * @return A response containing the ShareClient and the status of creating the share.
+ * @throws StorageErrorException If a share with the same name already exists
+ */
+ public Response createShare(String shareName) {
+ return createShare(shareName, null, null);
}
- public Mono setProperties() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a share in the storage account with the specified name and metadata and returns a ShareClient to
+ * interact with it.
+ *
+ *
Code Samples
+ *
+ *
Create the share "test" with metadata "share:metadata"
+ *
+ *
+ * Response<ShareClient> response = client.createShare("test", Collections.singletonMap("share", "metadata"), null);
+ * System.out.printf("Creating the share completed with status code %d", response.statusCode());
+ *
+ *
+ *
Create the share "test" with a quota of 10 GB
+ *
+ *
+ * Response<ShareClient> response = client.createShare("test", null, 10)
+ * System.out.printf("Creating the share completed with status code %d", response.statusCode());
+ *
+ *
+ * @param shareName Name of the share
+ * @param metadata Optional. Metadata to associate with the share
+ * @param quotaInGB Optional. Maximum size the share is allowed to grow to in GB. This must be greater than 0 and
+ * less than or equal to 5120. The default value is 5120.
+ * @return A response containing the ShareClient and the status of creating the share.
+ * @throws StorageErrorException If a share with the same name already exists or {@code quotaInGB} is outside the
+ * allowed range.
+ */
+ public Response createShare(String shareName, Map metadata, Integer quotaInGB) {
+ ShareClient shareClient = getShareClient(shareName);
+ return new SimpleResponse<>(shareClient.create(metadata, quotaInGB), shareClient);
}
- public Mono> createShare(String shareName, Map metadata, int quotaInGB) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the share in the storage account with the given name
+ *
+ *
Code Samples
+ *
+ *
Delete the share "test"
+ *
+ * {@codesnippet com.azure.storage.file.fileServiceClient.deleteShare#string}
+ *
+ * @param shareName Name of the share
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist
+ */
+ public VoidResponse deleteShare(String shareName) {
+ return deleteShare(shareName, null);
}
- public Mono deleteShare(String shareName, String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Deletes the specific snapshot of the share in the storage account with the given name. Snapshot are identified
+ * by the time they were created.
+ *
+ *
Code Samples
+ *
+ *
Delete the snapshot of share "test" that was created at midnight
+ *
+ *
+ * OffsetDateTime midnight = OffsetDateTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC));
+ * VoidResponse response = client.deleteShare("test", midnight.toString());
+ * System.out.printf("Deleting the snapshot completed with status code %d", response.statusCode());
+ *
+ *
+ * @param shareName Name of the share
+ * @param shareSnapshot Identifier of the snapshot
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist or the snapshot doesn't exist
+ */
+ public VoidResponse deleteShare(String shareName, String shareSnapshot) {
+ return client.deleteShare(shareName, shareSnapshot).block();
}
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java
index 01148e8af57f5..d909c95295002 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java
@@ -3,7 +3,309 @@
package com.azure.storage.file;
-public class FileServiceClientBuilder {
+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.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.RetryPolicy;
+import com.azure.core.http.policy.UserAgentPolicy;
+import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders;
+import com.azure.core.util.configuration.Configuration;
+import com.azure.core.util.configuration.ConfigurationManager;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+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;
- // connection string, FileClientOptions, Uri, SharedKeyCredential
+/**
+ * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link FileServiceClient FileServiceClients}
+ * and {@link FileServiceAsyncClient FileServiceAsyncClients}, calling {@link FileServiceClientBuilder#buildClient() buildClient}
+ * constructs an instance of FileServiceClient and calling {@link FileServiceClientBuilder#buildAsyncClient() buildAsyncClient}
+ * constructs an instance of FileServiceAsyncClient.
+ *
+ *
The client needs the endpoint of the Azure Storage File service and authorization credential.
+ * {@link FileServiceClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder a
+ * {@link SASTokenCredential} that authorizes the client.
+ *
+ *
Instantiating a synchronous FileService Client with SAS token
If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may
+ * be set using {@link FileServiceClientBuilder#credential(SASTokenCredential) credential}.
Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential
+ * a connection string from the Storage File service must be used. Set the SharedKeyCredential with
+ * {@link FileServiceClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and
+ * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.
+ *
+ *
Instantiating a synchronous FileService Client with connection string.
Instantiating an Asynchronous FileService Client with connection string.
+ * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.instantiation.connectionstring}
+ *
+ * @see FileServiceClient
+ * @see FileServiceAsyncClient
+ * @see SASTokenCredential
+ * @see SharedKeyCredential
+ */
+public final class FileServiceClientBuilder {
+ private static final String ACCOUNT_NAME = "accountname";
+ private final List policies;
+
+ private URL endpoint;
+ private SASTokenCredential sasTokenCredential;
+ private SharedKeyCredential sharedKeyCredential;
+ private HttpClient httpClient;
+ private HttpPipeline pipeline;
+ private HttpLogDetailLevel logLevel;
+ private RetryPolicy retryPolicy;
+ private Configuration configuration;
+
+ /**
+ * Creates a builder instance that is able to configure and construct {@link FileServiceClient FileServiceClients}
+ * and {@link FileServiceAsyncClient FileServiceAsyncClients}.
+ */
+ public FileServiceClientBuilder() {
+ retryPolicy = new RetryPolicy();
+ logLevel = HttpLogDetailLevel.NONE;
+ policies = new ArrayList<>();
+ configuration = ConfigurationManager.getConfiguration();
+ }
+
+ /**
+ * Creates a {@link FileServiceAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is
+ * called a new instance of {@link FileServiceAsyncClient} is created.
+ *
+ *
+ * If {@link FileServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link FileServiceClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link FileServiceAsyncClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A FileServiceAsyncClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null}.
+ * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public FileServiceAsyncClient buildAsyncClient() {
+ Objects.requireNonNull(endpoint);
+
+ if (pipeline != null) {
+ return new FileServiceAsyncClient(endpoint, pipeline);
+ }
+
+ if (sasTokenCredential == null && sharedKeyCredential == null) {
+ throw new IllegalArgumentException("Credentials are required for authorization");
+ }
+
+ // Closest to API goes first, closest to wire goes last.
+ final List policies = new ArrayList<>();
+
+ policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration));
+ policies.add(new RequestIdPolicy());
+ policies.add(new AddDatePolicy());
+
+ if (sharedKeyCredential != null) {
+ policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
+ } else {
+ policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
+ }
+
+ HttpPolicyProviders.addBeforeRetryPolicies(policies);
+
+ policies.add(retryPolicy);
+
+ policies.addAll(this.policies);
+ HttpPolicyProviders.addAfterRetryPolicies(policies);
+ policies.add(new HttpLoggingPolicy(logLevel));
+
+ HttpPipeline pipeline = HttpPipeline.builder()
+ .policies(policies.toArray(new HttpPipelinePolicy[0]))
+ .httpClient(httpClient)
+ .build();
+
+ return new FileServiceAsyncClient(endpoint, pipeline);
+ }
+
+ /**
+ * Creates a {@link FileServiceClient} based on options set in the builder. Every time {@code buildClient()} is
+ * called a new instance of {@link FileServiceClient} is created.
+ *
+ *
+ * If {@link FileServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
+ * {@link FileServiceClientBuilder#endpoint(String) endpoint} are used to create the
+ * {@link FileServiceClient client}. All other builder settings are ignored.
+ *
+ *
+ * @return A FileServiceClient with the options set from the builder.
+ * @throws NullPointerException If {@code endpoint} is {@code null}.
+ * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set.
+ */
+ public FileServiceClient buildClient() {
+ return new FileServiceClient(buildAsyncClient());
+ }
+
+ /**
+ * Sets the endpoint for the Azure Storage File instance that the client will interact with.
+ *
+ *
Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String) fromQuery} in an
+ * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.
+ *
+ * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from.
+ * @return the updated FileServiceClientBuilder object
+ * @throws IllegalArgumentException If {@code endpoint} isn't a proper URL
+ */
+ public FileServiceClientBuilder endpoint(String endpoint) {
+ Objects.requireNonNull(endpoint);
+ try {
+ URL fullURL = new URL(endpoint);
+ this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost());
+
+ // Attempt to get the SAS token from the URL passed
+ SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery());
+ if (credential != null) {
+ this.sasTokenCredential = credential;
+ }
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("The Azure Storage File Service endpoint url is malformed.");
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the {@link SASTokenCredential} used to authenticate requests sent to the Queue service.
+ *
+ * @param credential SAS token credential generated from the Storage account that authorizes requests
+ * @return the updated FileServiceClientBuilder object
+ * @throws NullPointerException If {@code credential} is {@code null}.
+ */
+ public FileServiceClientBuilder credential(SASTokenCredential credential) {
+ this.sasTokenCredential = Objects.requireNonNull(credential);
+ return this;
+ }
+
+ /**
+ * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the
+ * File service.
+ *
+ * @param connectionString Connection string from the Access Keys section in the Storage account
+ * @return the updated FileServiceClientBuilder object
+ * @throws NullPointerException If {@code connectionString} is {@code null}.
+ */
+ public FileServiceClientBuilder connectionString(String connectionString) {
+ Objects.requireNonNull(connectionString);
+ this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString);
+ getEndPointFromConnectionString(connectionString);
+ return this;
+ }
+
+ private void getEndPointFromConnectionString(String connectionString) {
+ Map connectionStringPieces = new HashMap<>();
+ for (String connectionStringPiece : connectionString.split(";")) {
+ String[] kvp = connectionStringPiece.split("=", 2);
+ connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]);
+ }
+ String accountName = connectionStringPieces.get(ACCOUNT_NAME);
+ try {
+ this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName));
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. "
+ + "Connection String: %s", connectionString));
+ }
+ }
+
+ /**
+ * Sets the HTTP client to use for sending and receiving requests to and from the service.
+ *
+ * @param httpClient The HTTP client to use for requests.
+ * @return The updated FileServiceClientBuilder object.
+ * @throws NullPointerException If {@code httpClient} is {@code null}.
+ */
+ public FileServiceClientBuilder httpClient(HttpClient httpClient) {
+ this.httpClient = Objects.requireNonNull(httpClient);
+ return this;
+ }
+
+ /**
+ * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}.
+ *
+ * @param pipelinePolicy The retry policy for service requests.
+ * @return The updated FileServiceClientBuilder object.
+ * @throws NullPointerException If {@code pipelinePolicy} is {@code null}.
+ */
+ public FileServiceClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) {
+ Objects.requireNonNull(pipelinePolicy);
+ this.policies.add(pipelinePolicy);
+ return this;
+ }
+
+ /**
+ * Sets the logging level for HTTP requests and responses.
+ *
+ * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses.
+ * @return The updated FileServiceClientBuilder object.
+ */
+ public FileServiceClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) {
+ this.logLevel = logLevel;
+ return this;
+ }
+
+ /**
+ * Sets the HTTP pipeline to use for the service client.
+ *
+ * If {@code pipeline} is set, all other settings are ignored, aside from {@link FileServiceClientBuilder#endpoint(String) endpoint}
+ * when building clients.
+ *
+ * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses.
+ * @return The updated FileServiceClientBuilder object.
+ * @throws NullPointerException If {@code pipeline} is {@code null}.
+ */
+ public FileServiceClientBuilder pipeline(HttpPipeline pipeline) {
+ this.pipeline = Objects.requireNonNull(pipeline);
+ return this;
+ }
+
+ /**
+ * Sets the configuration store that is used during construction of the service client.
+ *
+ * The default configuration store is a clone of the {@link ConfigurationManager#getConfiguration() global
+ * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction.
+ *
+ * @param configuration The configuration store used to
+ * @return The updated FileServiceClientBuilder object.
+ * @throws NullPointerException If {@code configuration} is {@code null}.
+ */
+ public FileServiceClientBuilder configuration(Configuration configuration) {
+ this.configuration = Objects.requireNonNull(configuration);
+ return this;
+ }
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/README.md b/storage/client/file/src/main/java/com/azure/storage/file/README.md
new file mode 100644
index 0000000000000..92fdab027d946
--- /dev/null
+++ b/storage/client/file/src/main/java/com/azure/storage/file/README.md
@@ -0,0 +1,446 @@
+# Azure File client library for Java
+The Server Message Block (SMB) protocol is the preferred file share protocol used on-premises today.
+The Microsoft Azure File service enables customers to leverage the availability and scalability of Azure’s Cloud Infrastructure as a Service (IaaS) SMB without having to rewrite SMB client applications.
+
+Files stored in Azure File service shares are accessible via the SMB protocol, and also via REST APIs.
+The File service offers the following four resources: the storage account, shares, directories, and files.
+Shares provide a way to organize sets of files and also can be mounted as an SMB file share that is hosted in the cloud.
+
+[Source code][source_code] | [Package (Maven)][package] | [API reference documentation][api_documentation] | [Product documentation][azconfig_docs]
+
+## Getting started
+
+### Prerequisites
+
+- [Java Development Kit (JDK)][jdk] with version 8 or above
+- [Azure Subscription][azure_subscription]
+- [Create Strorage Account][storage_account]
+
+### Adding the package to your product
+
+```xml
+
+ com.azure
+ azure-storage
+ 12.0.0
+
+```
+
+### Create a Storage Account
+To create a Storage Account you can use the Azure Portal or [Azure CLI][azure_cli].
+
+```Powershell
+az group create \
+ --name storage-resource-group \
+ --location westus
+```
+
+### Authenticate the client
+
+In order to interact with the Storage service (File Service, Share, Directory, MessageId, File) you'll need to create an instance of the Service Client class.
+To make this possible you'll need the Account SAS (shared access signature) string of Storage account. Learn more at [SAS Token][sas_token]
+
+#### Get Credentials
+
+- **SAS Token**
+
+a. Use the [Azure CLI][azure_cli] snippet below to get the SAS token from the Storage account.
+
+```Powershell
+az storage file generate-sas
+ --name {account name}
+ --expiry {date/time to expire SAS token}
+ --permission {permission to grant}
+ --connection-string {connection string of the storage account}
+```
+
+```Powershell
+CONNECTION_STRING=
+
+az storage file generate-sas
+ --name javasdksas
+ --expiry 2019-06-05
+ --permission rpau
+ --connection-string $CONNECTION_STRING
+```
+b. Alternatively, get the Account SAS Token from the Azure Portal.
+```
+Go to your storage account -> Shared access signature -> Click on Generate SAS and connection string
+```
+
+- **Shared Key Credential**
+
+a. Use account name and account key. Account name is your storage account name.
+```
+// Here is where we get the key
+Go to your storage account -> Access keys -> Key 1/ Key 2 -> Key
+```
+b. Use the connection string
+```
+// Here is where we get the key
+Go to your storage account -> Access Keys -> Keys 1/ Key 2 -> Connection string
+```
+## Key concepts
+### URL format
+Files are addressable using the following URL format:
+```
+https://.file.core.windows.net/
+```
+The following URL addresses a queue in the diagram:
+```
+https://myaccount.file.core.windows.net/images-to-download
+```
+
+#### Resource URI Syntax
+For the storage account, the base URI for queue operations includes the name of the account only:
+```
+https://myaccount.file.core.windows.net
+```
+For file, the base URI includes the name of the account and the name of the directory/file:
+```
+https://myaccount.file.core.windows.net/myshare/mydirectorypath/myfile
+```
+
+### Handling Exceptions
+```java
+TODO
+```
+
+### Resource Names
+The URI to reference a share, directory or file must be unique. Within a given storage account, every share must have a unique name. Every file within a given share or directory must also have a unique name within that share or directory.
+
+If you attempt to create a share, directory, or file with a name that violates naming rules, the request will fail with status code 400 (Bad Request).
+
+### Share Names
+The rules for File service share names are more restrictive than what is prescribed by the SMB protocol for SMB share names, so that the Blob and File services can share similar naming conventions for containers and shares. The naming restrictions for shares are as follows:
+
+1. A share name must be a valid DNS name.
+1. Share names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
+1. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in share names.
+1. All letters in a share name must be lowercase.
+1. Share names must be from 3 through 63 characters long.
+
+### Directory and File Names
+The Azure File service naming rules for directory and file names are as follows:
+
+1. Directory and file names are case-preserving and case-insensitive.
+1. Directory and file component names must be no more than 255 characters in length.
+1. Directory names cannot end with the forward slash character (/). If provided, it will be automatically removed.
+1. File names must not end with the forward slash character (/).
+1. Reserved URL characters must be properly escaped.
+1. The following characters are not allowed: " \ / : | < > * ?
+1. Illegal URL path characters not allowed. Code points like \uE000, while valid in NTFS filenames, are not valid Unicode characters. In addition, some ASCII or Unicode characters, like control characters (0x00 to 0x1F, \u0081, etc.), are also not allowed. For rules governing Unicode strings in HTTP/1.1 see [RFC 2616, Section 2.2: Basic Rules][RFC_URL_1] and [RFC 3987][RFL_URL_2].
+1. The following file names are not allowed: LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, PRN, AUX, NUL, CON, CLOCK$, dot character (.), and two dot characters (..).
+
+### Metadata Names
+Metadata for a share or file resource is stored as name-value pairs associated with the resource. Directories do not have metadata. Metadata names must adhere to the naming rules for [C# identifiers][C_identifiers].
+
+Note that metadata names preserve the case with which they were created, but are case-insensitive when set or read. If two or more metadata headers with the same name are submitted for a resource, the Azure File service returns status code 400 (Bad Request).
+
+### File Services
+The File Service REST API provides operations on accounts and manage file service properties. It allows the operations of listing and deleting shares, getting and setting file service properties.
+Once you have the SASToken, you can construct the file service client with `${accountName}`, `${sasToken}`
+
+```
+String fileServiceURL = String.format("https://%s.file.core.windows.net/%s", accountName, sasToken);
+FileServiceClient fileServiceClient = FileServiceClient.builder().endpoint(fileServiceURL).build();
+```
+
+### Share
+The share resource includes metadata and properties for that share. It allows the opertions of creating, creating snapshot, deleting shares, getting share properties, setting metadata, getting and setting ACL (Access policy).
+Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${sasToken}`
+
+```
+String shareURL = String.format("https://%s.file.core.windows.net/%s%s", accountName, shareName, sasToken);
+ShareClient shareClient = ShareClient.builder().endpoint(shareURL).build();
+```
+
+### Directory
+ The directory resource includes the properties for that directory. It allows the operations of creating, listing, deleting directories or subdirectories or files, getting properties, setting metadata, listing and force closing the handles.
+ Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${directoryPath}`, `${sasToken}`
+
+ ```
+ String directoryURL = String.format("https://%s.file.core.windows.net/%s%s", accountName, shareName, directoryPath, sasToken);
+ DirectoryClient directoryClient = DirectoryClient.builder().endpoint(directoryURL).build();
+ ```
+### File
+ The file resource includes the properties for that file. It allows the operations of creating, uploading, copying, downloading, deleting files or range of the files, getting properties, setting metadata, listing and force closing the handles.
+ Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${directoryPath}`, `${fileName}`, `${sasToken}`
+
+ ```
+ String fileURL = String.format("https://%s.file.core.windows.net/%s/%s/%s", accountName, shareName, directoryPath, fileName, sasToken);
+ FileClient fileClient = FileClient.builder().endpoint(fileURL).build();
+ ```
+
+## Examples
+
+The following sections provide several code snippets covering some of the most common Configuration Service tasks, including:
+- [Create a Share](#Create-a-share)
+- [Create a snapshot on Share](#Create-a-snapshot-on-share)
+- [Create a Directory](#Create-a-directory)
+- [Create a Subdirectory](#Create-a-subdirectory)
+- [Create a File](#Create-a-file)
+- [List all Shares](#List-all-shares)
+- [List all Subdirectories and Files](#List-all-subdirectories-and-files)
+- [List all ranges on file](#List-all-ranges-on-file)
+- [Delete a Share](#Delete-a-share)
+- [Delete a Directory](#Delete-a-directory)
+- [Delete a Subdirectory](#Delete-a-subdirectory)
+- [Delete a File](#Delete-a-file)
+- [Copy a File](#Copy-a-file)
+- [Abort copy a File](#Abort-copy-a-file)
+- [Upload data to Storage File](#Upload-data-to-storage)
+- [Upload file to Storage File](#Upload-file-to-storage)
+- [Download data from Storage File](#Download-data-from-storage)
+- [Download file from Storage File](#Download-file-from-storage)
+- [Get a File Service property](#Get-a-file-service-property)
+- [Set a File Service property](#set-a-file-service-property)
+- [Set a Share metadata](#Set-a-share-metadata)
+- [Get a Share access policy](#Get-a-share-access-policy)
+- [Set a Share access policy](#Set-a-share-access-policy)
+- [Get handles on Directory and File](#Get-handles-on-directory-file)
+- [Force close handles on handle id](#Force-close-handles-on-handle-id)
+- [Set quota on Share](#Set-quota-on-share)
+- [Set file httpHeaders](#Set-file-httpheaders)
+
+### Create a share
+Create a share in the Storage Account. Throws StorageErrorException If the share fails to be created.
+Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) with share name of ${shareName} = "testshare"
+
+```Java
+fileServiceClient.createShare(shareName);
+```
+
+### Create a snapshot on Share
+Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) with share name of `${shareName} = "testshare"`
+
+```Java
+ShareClient shareClient = fileServiceClient.getShareClient(shareName);
+shareClient.createSnapshot();
+```
+
+### Create a directory
+Taking the [`${shareClient}](#Create-snapshot-on-share) initialized above, [`${shareClient}`](#Share) with directory name of `${dirName} = "testdir"`
+
+```Java
+shareClient.createDirectory(dirName);
+```
+
+### Create a subdirectory
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) with the subdirectory name of `${subDirName}="testsubdir"`
+
+```Java
+directoryClient.createSubDirectory(subDirName);
+```
+
+### Create a File
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) with the file name of `${fileName}="testfile"`
+
+```Java
+directoryClient.createFile(fileName);
+```
+
+### List all Shares
+Taking the fileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services)
+
+```Java
+fileServiceClient.listShares();
+```
+
+### Create all subdirectories and files
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory)
+
+```Java
+directoryClient.listFilesAndDirectories();
+```
+
+### List all ranges on file
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File)
+
+```Java
+fileClient.ranges();
+```
+
+### Delete a share
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share)
+
+```Java
+shareClient.delete();
+```
+
+### Delete a directory
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) with directory name of `${dirName} = "testdir"`
+
+```Java
+shareClient.deleteDirectory(dirName)
+```
+
+### Delete a subdirectory
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) with the subdirectory name of `${subDirName}="testsubdir"`
+
+```Java
+directoryClient.deleteSubDirectory(subDirName)
+```
+
+### Delete a file
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) with the file name of `${fileName}="testfile"`
+
+```Java
+directoryClient.deleteFile(fileName)
+```
+
+### Copy a file
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the source URL of `${sourceURL}="https://myaccount.file.core.windows.net/myshare/myfile"`
+
+```Java
+Response copyInfoResponse = fileClient.startCopy(sourceURL, null);
+```
+
+### Abort copy a file
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the copy info response returned above `${copyId}=[copyInfoResponse](#Copy-a-file).value().copyId()`.
+
+```Java
+fileClient.abortCopy(copyId);
+```
+
+### Upload data to storage
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the data `${data}=Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8));`
+
+```Java
+fileClient.upload(data, data.readableBytes());
+```
+
+### Upload file to storage
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the file of filePath `${filePath}="/mydir/myfile"`
+```Java
+fileClient.uploadFromFile(filePath, );
+```
+
+### Download data from file range
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the file range of `${fileRange}=new FileRnage(1024, 2048)`
+```Java
+fileClient.downloadWithProperties(fileRange, false);
+```
+
+### Download file from storage
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) and download to the file of filePath `${filePath}="/mydir/myfile"`
+```Java
+fileClient.downloadToFile(filePath);
+```
+
+### Get a file service properties
+Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services)
+
+```Java
+fileServiceClient.getProperties();
+```
+
+### Set a file service properties
+Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) with `${fileServiceProperties}=new FileServiceProperties()`
+
+```Java
+fileServiceClient.setProperties(fileServiceProperties);
+```
+
+### Set a share metadata
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) with metadata of `${metadata}=Collections.singletonMap("directory", "metadata")`
+
+```Java
+shareClient.setMetadata(metadata);
+```
+
+### Get a share access policy
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share)
+
+```Java
+shareClient.getAccessPolicy();
+```
+
+### Set a share access policy
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) with list of permissions `${permissions}=Arrays.asList(= Arrays.asList(new SignedIdentifier()))`
+
+```Java
+shareClient.setAccessPolicy(permissions);
+```
+
+### Get handles on directory file
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory)
+
+```Java
+Iterable handleItems = directoryClient.getHandles(null, true);
+```
+
+### Force close handles on handle id
+Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) and the handle id returned above `${handleId}=[handleItems](#Get-handles-on-directory-file).iterator().next().handleId()`
+
+```Java
+directoryClient.forceCloseHandles(handleId);
+```
+
+### Set quota on share
+Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) with `${quotaOnGB}=1`
+
+```Java
+shareClient.setQuota(quotaOnGB);
+```
+
+### Set file httpheaders
+Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with httpHeaders `${httpHeaders}=new FileHTTPHeaders().fileContentType("text/plain")`
+
+```Java
+fileClient.setHttpHeaders(httpHeaders);
+```
+
+## Troubleshooting
+
+## General
+
+When you interact with file using this Java client library, errors returned by the service correspond to the same HTTP status codes returned for [REST API][storage_file_rest] requests. For example, if you try to retrieve a share that doesn't exist in your Storage Account, a `404` error is returned, indicating `Not Found`.
+
+## Next steps
+
+### More Samples
+- FileServiceSample
+- ShareSample
+- DirectorySample
+- FileSample
+- AsyncSample
+
+[Quickstart: Create a Java Spring app with App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-java-spring-app)
+
+## Contributing
+This project welcomes contributions and suggestions. Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit https://cla.microsoft.com.
+
+When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines.html).
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+
+[source_code]: to-be-continue
+[package]: to-be-continue
+[api_documentation]: https://docs.microsoft.com/en-us/rest/api/storageservices/file-service-rest-api
+[storage_docs]: https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction
+[jdk]: https://docs.microsoft.com/en-us/java/azure/java-supported-jdk-runtime?view=azure-java-stable
+[maven]: https://maven.apache.org/
+[azure_subscription]: https://azure.microsoft.com/en-us/free/
+[storage_account]: https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal
+[azure_cli]: https://docs.microsoft.com/cli/azure
+[sas_token]: https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1
+[RFC_URL_1]: https://www.ietf.org/rfc/rfc2616.txt
+[RFL_URL_2]: https://www.ietf.org/rfc/rfc3987.txt
+[C_identifiers]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/
+[storage_file_rest]: https://docs.microsoft.com/en-us/rest/api/storageservices/file-service-error-codes
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java
index d6d1326ba9545..45730904cf54e 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java
@@ -3,81 +3,506 @@
package com.azure.storage.file;
+import com.azure.core.http.HttpPipeline;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.core.implementation.DateTimeRfc1123;
+import com.azure.core.util.Context;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
+import com.azure.storage.file.implementation.AzureFileStorageBuilder;
+import com.azure.storage.file.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.models.ShareCreateSnapshotHeaders;
+import com.azure.storage.file.models.ShareGetPropertiesHeaders;
import com.azure.storage.file.models.ShareInfo;
import com.azure.storage.file.models.ShareProperties;
import com.azure.storage.file.models.ShareSnapshotInfo;
import com.azure.storage.file.models.ShareStatistics;
+import com.azure.storage.file.models.SharesCreateSnapshotResponse;
+import com.azure.storage.file.models.SharesGetPropertiesResponse;
+import com.azure.storage.file.models.SharesGetStatisticsResponse;
import com.azure.storage.file.models.SignedIdentifier;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
+import com.azure.storage.file.models.StorageErrorException;
+import java.net.URL;
+import java.time.OffsetDateTime;
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 share in Azure Storage Share.
+ * Operations allowed by the client are creating and deleting the share, creating snapshots for the share, creating and
+ * deleting directories in the share and retrieving and updating properties metadata and access policies of the share.
+ *
+ *
View {@link ShareClientBuilder this} for additional ways to construct the client.
+ *
+ * @see ShareClientBuilder
+ * @see ShareClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class ShareAsyncClient {
+ private final AzureFileStorageImpl client;
+ private final String shareName;
+ private final String shareSnapshot;
+
+ /**
+ * Creates a ShareAsyncClient that sends requests to the storage share at {@link AzureFileStorageImpl#url() endpoint}.
+ * Each service call goes through the {@link HttpPipeline pipeline} in the {@code client}.
+ *
+ * @param client Client that interacts with the service interfaces
+ * @param shareName Name of the share
+ */
+ ShareAsyncClient(AzureFileStorageImpl client, String shareName) {
+ this.shareName = shareName;
+ this.shareSnapshot = null;
- ShareAsyncClient() {
- throw new UnsupportedOperationException();
+ this.client = new AzureFileStorageBuilder().pipeline(client.httpPipeline())
+ .url(client.url())
+ .version(client.version())
+ .build();
}
- public static ShareClientBuilder asyncBuilder() {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a ShareAsyncClient that sends requests to the storage share at {@code endpoint}.
+ * Each service call goes through the {@code httpPipeline}.
+ *
+ * @param endpoint URL for the Storage File service
+ * @param httpPipeline HttpPipeline that the HTTP requests and response flow through
+ * @param shareName Name of the share
+ * @param shareSnapshot Optional. Specific snapshot of the share
+ */
+ ShareAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String shareSnapshot) {
+ this.shareName = shareName;
+ this.shareSnapshot = shareSnapshot;
+
+ this.client = new AzureFileStorageBuilder().pipeline(httpPipeline)
+ .url(endpoint.toString())
+ .build();
}
- public String url() {
- throw new UnsupportedOperationException();
+ /**
+ * @return the getShareUrl of the storage file service
+ */
+ public String getShareUrl() {
+ return client.url();
}
+
+ /**
+ * Constructs a {@link DirectoryAsyncClient} that interacts with the root directory in the share.
+ *
+ *
If the directory doesn't exist in the share {@link DirectoryAsyncClient#create(Map) create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @return a {@link DirectoryAsyncClient} that interacts with the root directory in the share
+ */
public DirectoryAsyncClient getRootDirectoryClient() {
- throw new UnsupportedOperationException();
+ return getDirectoryClient("");
}
+ /**
+ * Constructs a {@link DirectoryAsyncClient} that interacts with the specified directory.
+ *
+ *
If the directory doesn't exist in the share {@link DirectoryAsyncClient#create(Map) create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @param directoryName Name of the directory
+ * @return a {@link DirectoryAsyncClient} that interacts with the directory in the share
+ */
public DirectoryAsyncClient getDirectoryClient(String directoryName) {
- throw new UnsupportedOperationException();
+ return new DirectoryAsyncClient(client, shareName, directoryName, shareSnapshot);
}
- public Mono> create(Map metadata, int quotaInGB) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates the share in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Create the share
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.create}
+ *
+ * @return A response containing information about the share and the status its creation.
+ * @throws StorageErrorException If the share already exists with different metadata
+ */
+ public Mono> create() {
+ return create(null, null);
}
+ /**
+ * Creates the share in the storage account with the specified metadata and quota.
+ *
+ *
Code Samples
+ *
+ *
Create the share with metadata "share:metadata"
+ *
+ *
+ * client.createShare(Collections.singletonMap("share", "metadata"), null)
+ * .subscribe(response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Create the share with a quota of 10 GB
+ *
+ *
+ * client.createShare(null, 10)
+ * .subscribe(response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Optional. Metadata to associate with the share
+ * @param quotaInGB Optional. Maximum size the share is allowed to grow to in GB. This must be greater than 0 and
+ * less than or equal to 5120. The default value is 5120.
+ * @return A response containing information about the share and the status its creation.
+ * @throws StorageErrorException If the share already exists with different metadata or {@code quotaInGB} is outside the
+ * allowed range.
+ */
+ public Mono> create(Map metadata, Integer quotaInGB) {
+ return client.shares().createWithRestResponseAsync(shareName, null, metadata, quotaInGB, Context.NONE)
+ .map(this::mapToShareInfoResponse);
+ }
+
+ /**
+ * Creates a snapshot of the share with the same metadata associated to the share at the time of creation.
+ *
+ *
Code Samples
+ *
+ *
Create a snapshot
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.createSnapshot}
+ *
+ * @return A response containing information about the snapshot of share.
+ * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is
+ * in progress for the share
+ */
+ public Mono> createSnapshot() {
+ return createSnapshot(null);
+ }
+
+ /**
+ * Creates a snapshot of the share with the metadata that was passed associated to the snapshot.
+ *
+ *
Code Samples
+ *
+ *
Create a snapshot with metadata "snapshot:metadata"
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.createSnapshot#map}
+ *
+ * @param metadata Optional. Metadata to associate with the snapshot. If {@code null} the metadata of the share
+ * will be copied to the snapshot.
+ * @return A response containing information about the snapshot of share.
+ * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is
+ * in progress for the share
+ */
public Mono> createSnapshot(Map metadata) {
- throw new UnsupportedOperationException();
+ return client.shares().createSnapshotWithRestResponseAsync(shareName, null, metadata, Context.NONE)
+ .map(this::mapCreateSnapshotResponse);
}
+ /**
+ * Deletes the share in the storage account
+ *
+ *
Code Samples
+ *
+ *
Delete the share
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.delete}
+ *
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist
+ */
+ public Mono delete() {
+ return delete(null);
+ }
+
+ /**
+ * Deletes the specific snapshot of the share in the storage account. Snapshot are identified by the time they
+ * were created.
+ *
+ *
Code Samples
+ *
+ *
Delete the snapshot of share that was created at midnight
+ *
+ *
+ * OffsetDateTime midnight = OffsetDateTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC));
+ * client.deleteShare(midnight.toString())
+ * .subscribe(response -> System.out.printf("Deleting the snapshot completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param shareSnapshot Identifier of the snapshot
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist or the snapshot doesn't exist
+ */
public Mono delete(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ return client.shares().deleteWithRestResponseAsync(shareName, shareSnapshot, null, null, Context.NONE)
+ .map(VoidResponse::new);
+ }
+
+ /**
+ * Retrieves the properties of the share, these include the metadata associated to it and the quota that the share
+ * is restricted to.
+ *
+ *
+ *
+ * @return the properties of the share
+ * @throws StorageErrorException If the share doesn't exist
+ */
+ public Mono> getProperties() {
+ return getProperties(null);
}
+ /**
+ * Retrieves the properties of a specific snapshot of the share, these include the metadata associated to it and
+ * the quota that the share is restricted to.
+ *
+ *
Code Samples
+ *
+ *
Retrieve the properties from the snapshot at midnight
+ *
+ * @param shareSnapshot Identifier of the snapshot
+ * @return the properties of the share snapshot
+ * @throws StorageErrorException If the share or snapshot doesn't exist
+ */
public Mono> getProperties(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ return client.shares().getPropertiesWithRestResponseAsync(shareName, shareSnapshot, null, Context.NONE)
+ .map(this::mapGetPropertiesResponse);
}
+ /**
+ * Sets the maximum size in GB that the share is allowed to grow.
+ *
+ *
Code Samples
+ *
+ *
Set the quota to 1024 GB
+ *
+ *
+ * client.setQuota(1024)
+ * .subscribe(response -> System.out.printf("Setting the share quota completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param quotaInGB Size in GB to limit the share's growth. The quota in GB must be between 1 and 5120.
+ * @return information about the share
+ * @throws StorageErrorException If the share doesn't exist or {@code quotaInGB} is outside the allowed bounds
+ */
public Mono> setQuota(int quotaInGB) {
- throw new UnsupportedOperationException();
+ return client.shares().setQuotaWithRestResponseAsync(shareName, null, quotaInGB, Context.NONE)
+ .map(this::mapToShareInfoResponse);
}
+ /**
+ * Sets the user-defined metadata to associate to the share.
+ *
+ *
If {@code null} is passed for the metadata it will clear the metadata associated to the share.
+ *
+ *
Code Samples
+ *
+ *
Set the metadata to "share:updatedMetadata"
+ *
+ *
+ * client.setMetadata(Collections.singletonMap("share", "updatedMetadata"))
+ * .subscribe(response -> System.out.printf("Setting the share metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ *
Clear the metadata of the share
+ *
+ *
+ * client.setMetadata(null)
+ * .subscribe(response -> System.out.printf("Clearing the share metadata completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param metadata Metadata to set on the share, if null is passed the metadata for the share is cleared
+ * @return information about the share
+ * @throws StorageErrorException If the share doesn't exist or the metadata contains invalid keys
+ */
public Mono> setMetadata(Map metadata) {
- throw new UnsupportedOperationException();
+ return client.shares().setMetadataWithRestResponseAsync(shareName, null, metadata, Context.NONE)
+ .map(this::mapToShareInfoResponse);
}
- public Flux listAccessPolicy() {
- throw new UnsupportedOperationException();
+ /**
+ * Retrieves stored access policies specified for the share.
+ *
+ *
+ *
+ * @param permissions Access policies to set on the queue
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist, a stored access policy doesn't have all fields filled out,
+ * or the share will have more than five policies.
+ */
public Mono> setAccessPolicy(List permissions) {
- throw new UnsupportedOperationException();
+ return client.shares().setAccessPolicyWithRestResponseAsync(shareName, permissions, null, Context.NONE)
+ .map(this::mapToShareInfoResponse);
}
+ /**
+ * Retrieves storage statistics about the share.
+ *
+ *
Code Samples
+ *
+ *
Retrieve the storage statistics
+ *
+ *
+ * client.getStatistics()
+ * .subscribe(response -> System.out.printf("The share is using %d GB", response.value().getShareUsageInGB()));
+ *
+ *
+ * @return the storage statistics of the share
+ */
public Mono> getStatistics() {
- throw new UnsupportedOperationException();
+ return client.shares().getStatisticsWithRestResponseAsync(shareName, Context.NONE)
+ .map(this::mapGetStatisticsResponse);
+ }
+
+ /**
+ * Creates the directory in the share with the given name.
+ *
+ *
Code Samples
+ *
+ *
Create the directory "mydirectory"
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.createDirectory#string}
+ * *
+ * @param directoryName Name of the directory
+ * @return A response containing a {@link DirectoryAsyncClient} to interact with the created directory and the
+ * status of its creation.
+ * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of
+ * being deleted, or the parent directory for the new directory doesn't exist
+ */
+ public Mono> createDirectory(String directoryName) {
+ return createDirectory(directoryName, null);
}
+ /**
+ * Creates the directory in the share with the given name and associates the passed metadata to it.
+ *
+ *
Code Samples
+ *
+ *
Create the directory "documents" with metadata "directory:metadata"
+ *
+ *
+ * client.createDirectory("documents", Collections.singletonMap("directory", "metadata"))
+ * .subscribe(response -> System.out.printf("Creating the directory completed with status code %d", response.statusCode()));
+ *
+ *
+ * @param directoryName Name of the directory
+ * @param metadata Optional. Metadata to associate with the directory
+ * @return A response containing a {@link DirectoryAsyncClient} to interact with the created directory and the
+ * status of its creation.
+ * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of
+ * being deleted, the parent directory for the new directory doesn't exist, or the metadata is using an illegal
+ * key name
+ */
public Mono> createDirectory(String directoryName, Map metadata) {
- throw new UnsupportedOperationException();
+ DirectoryAsyncClient directoryAsyncClient = getDirectoryClient(directoryName);
+ return directoryAsyncClient.create(metadata).map(response -> new SimpleResponse<>(response, directoryAsyncClient));
}
+ /**
+ * Deletes the specified directory in the share.
+ *
+ *
Code Samples
+ *
+ *
Delete the directory "empty"
+ *
+ * {@codesnippet com.azure.storage.file.shareAsyncClient.deleteDirectory#string}
+ *
+ * @param directoryName Name of the directory
+ * @return A response that only contains headers and response status code
+ * @throws StorageErrorException If the share doesn't exist or the directory isn't empty
+ */
public Mono deleteDirectory(String directoryName) {
- throw new UnsupportedOperationException();
+ return getDirectoryClient(directoryName).delete().map(VoidResponse::new);
+ }
+
+ private Response mapToShareInfoResponse(Response> response) {
+ String eTag = response.headers().value("ETag");
+ OffsetDateTime lastModified = new DateTimeRfc1123(response.headers().value("Last-Modified")).dateTime();
+
+ return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), new ShareInfo(eTag, lastModified));
+ }
+
+ private Response mapCreateSnapshotResponse(SharesCreateSnapshotResponse response) {
+ ShareCreateSnapshotHeaders headers = response.deserializedHeaders();
+ ShareSnapshotInfo snapshotInfo = new ShareSnapshotInfo(headers.snapshot(), headers.eTag(), headers.lastModified());
+
+ return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), snapshotInfo);
+ }
+
+ private Response mapGetPropertiesResponse(SharesGetPropertiesResponse response) {
+ ShareGetPropertiesHeaders headers = response.deserializedHeaders();
+ ShareProperties shareProperties = new ShareProperties().quota(headers.quota())
+ .etag(headers.eTag())
+ .lastModified(headers.lastModified())
+ .metadata(headers.metadata());
+
+ return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), shareProperties);
+ }
+
+ private Response mapGetStatisticsResponse(SharesGetStatisticsResponse response) {
+ ShareStatistics shareStatistics = new ShareStatistics(response.value().shareUsageBytes() / 1024);
+
+ return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), shareStatistics);
}
}
diff --git a/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java b/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java
index 17ad31b4aa504..8ca702d3e3fb6 100644
--- a/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java
+++ b/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java
@@ -1,82 +1,417 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
package com.azure.storage.file;
import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.http.rest.VoidResponse;
+import com.azure.storage.common.credentials.SASTokenCredential;
+import com.azure.storage.common.credentials.SharedKeyCredential;
import com.azure.storage.file.models.ShareInfo;
import com.azure.storage.file.models.ShareProperties;
import com.azure.storage.file.models.ShareSnapshotInfo;
import com.azure.storage.file.models.ShareStatistics;
import com.azure.storage.file.models.SignedIdentifier;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
+import com.azure.storage.file.models.StorageErrorException;
import java.util.List;
import java.util.Map;
+/**
+ * This class provides a client that contains all the operations for interacting with a share in Azure Storage Share.
+ * Operations allowed by the client are creating and deleting the share, creating snapshots for the share, creating and
+ * deleting directories in the share and retrieving and updating properties metadata and access policies of the share.
+ *
+ *
View {@link ShareClientBuilder this} for additional ways to construct the client.
+ *
+ * @see ShareClientBuilder
+ * @see ShareAsyncClient
+ * @see SharedKeyCredential
+ * @see SASTokenCredential
+ */
public class ShareClient {
-
private final ShareAsyncClient client;
- ShareClient() {
- throw new UnsupportedOperationException();
- }
-
- public static ShareClientBuilder syncBuilder() {
- throw new UnsupportedOperationException();
+ ShareClient(ShareAsyncClient client) {
+ this.client = client;
}
- public String url() {
- throw new UnsupportedOperationException();
+ /**
+ * @return the getShareUrl of the storage file service
+ */
+ public String getShareUrl() {
+ return client.getShareUrl();
}
+ /**
+ * Constructs a {@link DirectoryClient} that interacts with the root directory in the share.
+ *
+ *
If the directory doesn't exist in the share {@link DirectoryClient#create(Map) create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @return a {@link DirectoryClient} that interacts with the root directory in the share
+ */
public DirectoryClient getRootDirectoryClient() {
- throw new UnsupportedOperationException();
+ return getDirectoryClient("");
}
+ /**
+ * Constructs a {@link DirectoryClient} that interacts with the specified directory.
+ *
+ *
If the directory doesn't exist in the share {@link DirectoryClient#create(Map) create} in the client will
+ * need to be called before interaction with the directory can happen.
+ *
+ * @param directoryName Name of the directory
+ * @return a {@link DirectoryClient} that interacts with the directory in the share
+ */
public DirectoryClient getDirectoryClient(String directoryName) {
- throw new UnsupportedOperationException();
+ return new DirectoryClient(client.getDirectoryClient(directoryName));
}
- public Mono> create(Map metadata, int quotaInGB) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates the share in the storage account.
+ *
+ *
Code Samples
+ *
+ *
Create the share
+ *
+ * {@codesnippet com.azure.storage.file.shareClient.create}
+ *
+ * @return A response containing information about the share and the status its creation.
+ * @throws StorageErrorException If the share already exists with different metadata
+ */
+ public Response create() {
+ return create(null, null);
}
- public Mono> createSnapshot(Map metadata) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates the share in the storage account with the specified metadata and quota.
+ *
+ *
Code Samples
+ *
+ *
Create the share with metadata "share:metadata"
+ *
+ *
+ * Response<ShareInfo> response = client.createShare(Collections.singletonMap("share", "metadata"), null);
+ * System.out.printf("Creating the share completed with status code %d", response.statusCode());
+ *
+ *
+ *
Create the share with a quota of 10 GB
+ *
+ *
+ * Response<ShareInfo> response = client.createShare(null, 10);
+ * System.out.printf("Creating the share completed with status code %d", response.statusCode());
+ *
+ *
+ * @param metadata Optional. Metadata to associate with the share
+ * @param quotaInGB Optional. Maximum size the share is allowed to grow to in GB. This must be greater than 0 and
+ * less than or equal to 5120. The default value is 5120.
+ * @return A response containing information about the share and the status its creation.
+ * @throws StorageErrorException If the share already exists with different metadata or {@code quotaInGB} is outside the
+ * allowed range.
+ */
+ public Response create(Map metadata, Integer quotaInGB) {
+ return client.create(metadata, quotaInGB).block();
}
- public Mono delete(String shareSnapshot) {
- throw new UnsupportedOperationException();
+ /**
+ * Creates a snapshot of the share with the same metadata associated to the share at the time of creation.
+ *
+ *