queryParameters = PathUtility.parseQueryString(completeUri.getQuery());
+
+ final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT);
+ if (snapshotIDs != null && snapshotIDs.length > 0) {
+ this.getShare().snapshotID = snapshotIDs[0];
+ }
}
/**
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java
index 318b949..da4f67b 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java
@@ -76,6 +76,11 @@ public final class CloudFileShare {
*/
private StorageUri storageUri;
+ /**
+ * Holds the snapshot ID.
+ */
+ String snapshotID;
+
/**
* Holds a reference to the associated service client.
*/
@@ -102,13 +107,14 @@ public final class CloudFileShare {
* @see Naming and Referencing Shares,
* Directories, Files, and Metadata
*/
- protected CloudFileShare(final String shareName, final CloudFileClient client) throws URISyntaxException,
+ public CloudFileShare(final String shareName, String snapshotID, final CloudFileClient client) throws URISyntaxException,
StorageException {
Utility.assertNotNull("client", client);
Utility.assertNotNull("shareName", shareName);
this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), shareName);
this.name = shareName;
+ this.snapshotID = snapshotID;
this.fileServiceClient = client;
}
@@ -145,6 +151,8 @@ public CloudFileShare(final StorageUri storageUri) throws StorageException {
* A java.net.URI
object that represents the absolute URI of the share.
* @param credentials
* A {@link StorageCredentials} object used to authenticate access.
+ * @param snapshotID
+ * A String
that represents the snapshot version, if applicable.
*
* @throws StorageException
* If a storage service error occurred.
@@ -200,6 +208,11 @@ public void create(FileRequestOptions options, OperationContext opContext) throw
opContext = new OperationContext();
}
+ assertNoSnapshot();
+ if (this.properties != null && this.properties.getShareQuota() != null) {
+ Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA);
+ }
+
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
@@ -340,6 +353,36 @@ public void delete() throws StorageException {
@DoesServiceRequest
public void delete(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException {
+ this.delete(DeleteShareSnapshotsOption.NONE, accessCondition, options, opContext);
+ }
+
+ /**
+ * Deletes the share using the specified snapshot and request options, and operation context.
+ *
+ * A share that has snapshots cannot be deleted unless the snapshots are also deleted. If a share has snapshots, use
+ * the {@link DeleteShareSnapshotsOption#INCLUDE_SNAPSHOTS} value
+ * in the deleteSnapshotsOption
parameter to include the snapshots when deleting the base share.
+ *
+ * @param deleteSnapshotsOption
+ * A {@link DeleteShareSnapshotsOption} object that indicates whether to delete only snapshots, or the share
+ * and its snapshots.
+ * @param accessCondition
+ * An {@link AccessCondition} object that represents the access conditions for the share.
+ * @param options
+ * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
+ * null
will use the default request options from the associated service client (
+ * {@link CloudFileClient}).
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation. This object
+ * is used to track requests to the storage service, and to provide additional runtime information about
+ * the operation.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ */
+ @DoesServiceRequest
+ public void delete(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
+ throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
@@ -347,12 +390,12 @@ public void delete(AccessCondition accessCondition, FileRequestOptions options,
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
- ExecutionEngine.executeWithRetry(this.fileServiceClient, this, deleteImpl(accessCondition, options),
+ ExecutionEngine.executeWithRetry(this.fileServiceClient, this, deleteImpl(deleteSnapshotsOption, accessCondition, options),
options.getRetryPolicyFactory(), opContext);
}
private StorageRequest deleteImpl(
- final AccessCondition accessCondition, final FileRequestOptions options) {
+ final DeleteShareSnapshotsOption deleteSnapshotsOption, final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@@ -361,7 +404,7 @@ private StorageRequest deleteImpl(
public HttpURLConnection buildRequest(
CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception {
return FileRequest.deleteShare(
- share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition);
+ share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition, share.snapshotID, deleteSnapshotsOption);
}
@Override
@@ -419,12 +462,45 @@ public boolean deleteIfExists() throws StorageException {
@DoesServiceRequest
public boolean deleteIfExists(AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
+ return this.deleteIfExists(DeleteShareSnapshotsOption.NONE, accessCondition, options, opContext);
+
+ }
+
+ /**
+ * Deletes the share if it exists, using the specified snapshot and request options, and operation context.
+ *
+ * A share that has snapshots cannot be deleted unless the snapshots are also deleted. If a share has snapshots, use
+ * the {@link DeleteShareSnapshotsOption#INCLUDE_SNAPSHOTS} value
+ * in the deleteSnapshotsOption
parameter to include the snapshots when deleting the base share.
+ *
+ * @param deleteSnapshotsOption
+ * A {@link DeleteShareSnapshotsOption} object that indicates whether to delete only snapshots, or the share
+ * and its snapshots.
+ * @param accessCondition
+ * An {@link AccessCondition} object that represents the access conditions for the share.
+ * @param options
+ * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
+ * null
will use the default request options from the associated service client (
+ * {@link CloudFileClient}).
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation. This object
+ * is used to track requests to the storage service, and to provide additional runtime information about
+ * the operation.
+ *
+ * @return true
if the share existed and was deleted; otherwise, false
.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ */
+ @DoesServiceRequest
+ public boolean deleteIfExists(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options,
+ OperationContext opContext) throws StorageException {
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
boolean exists = this.exists(true /* primaryOnly */, accessCondition, options, opContext);
if (exists) {
try {
- this.delete(accessCondition, options, opContext);
+ this.delete(deleteSnapshotsOption, accessCondition, options, opContext);
return true;
}
catch (StorageException e) {
@@ -499,7 +575,7 @@ public void setRequestLocationMode() {
public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
throws Exception {
return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()),
- options, context, accessCondition);
+ options, context, accessCondition, share.snapshotID);
}
@Override
@@ -567,6 +643,8 @@ public FileSharePermissions downloadPermissions(AccessCondition accessCondition,
opContext = new OperationContext();
}
+ assertNoSnapshot();
+
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
@@ -626,6 +704,136 @@ public FileSharePermissions postProcessResponse(HttpURLConnection connection,
return getRequest;
}
+ /**
+ * Creates a snapshot of the share.
+ *
+ * @return A CloudFileShare
object that represents the snapshot of the share.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ */
+ @DoesServiceRequest
+ public final CloudFileShare createSnapshot() throws StorageException {
+ return this
+ .createSnapshot(null /* metadata */, null /* accessCondition */, null /* options */, null /* opContext */);
+ }
+
+ /**
+ * Creates a snapshot of the file share using the specified request options and operation context.
+ *
+ * @param accessCondition
+ * An {@link AccessCondition} object that represents the access conditions for the share.
+ * @param options
+ * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
+ * null
will use the default request options from the associated service client (
+ * {@link CloudFileClient}).
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation. This object
+ * is used to track requests to the storage service, and to provide additional runtime information about
+ * the operation.
+ *
+ * @return A CloudFileShare
object that represents the snapshot of the file share.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ */
+ @DoesServiceRequest
+ public final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options,
+ OperationContext opContext) throws StorageException {
+ return this.createSnapshot(null /* metadata */, accessCondition, options, opContext);
+ }
+
+ /**
+ * Creates a snapshot of the file share using the specified request options and operation context.
+ *
+ * @param metadata
+ * A collection of name-value pairs defining the metadata of the snapshot, or null.
+ * @param accessCondition
+ * An {@link AccessCondition} object that represents the access conditions for the file share.
+ * @param options
+ * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
+ * null
will use the default request options from the associated service client (
+ * {@link CloudFileClient}).
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation. This object
+ * is used to track requests to the storage service, and to provide additional runtime information about
+ * the operation.
+ *
+ * @return A CloudFileShare
object that represents the snapshot of the file share.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ */
+ @DoesServiceRequest
+ public final CloudFileShare createSnapshot(final HashMap metadata,
+ final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
+ throws StorageException {
+ assertNoSnapshot();
+
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ opContext.initialize();
+ options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
+
+ return ExecutionEngine
+ .executeWithRetry(this.fileServiceClient, this,
+ this.createSnapshotImpl(metadata, accessCondition, options), options.getRetryPolicyFactory(),
+ opContext);
+ }
+
+ private StorageRequest createSnapshotImpl(
+ final HashMap metadata, final AccessCondition accessCondition,
+ final FileRequestOptions options) {
+ final StorageRequest putRequest =
+ new StorageRequest(
+ options, this.getStorageUri()) {
+
+ @Override
+ public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
+ throws Exception {
+ return FileRequest.snapshotShare(share.getTransformedAddress().getUri(this.getCurrentLocation()),
+ options, context, accessCondition);
+ }
+
+ @Override
+ public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) {
+ if (metadata != null) {
+ FileRequest.addMetadata(connection, metadata, context);
+ }
+ }
+
+ @Override
+ public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
+ throws Exception {
+ StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
+ }
+
+ @Override
+ public CloudFileShare preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
+ throws Exception {
+ if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
+ this.setNonExceptionedRetryableFailure(true);
+ return null;
+ }
+
+ final String snapshotTime = FileResponse.getSnapshotTime(this.getConnection());
+ CloudFileShare snapshot = new CloudFileShare(share.getName(), snapshotTime, client);
+ snapshot.setProperties(new FileShareProperties(share.properties));
+
+ // use the specified metadata if not null : otherwise share's metadata
+ snapshot.setMetadata(metadata != null ? metadata : share.metadata);
+
+ snapshot.updatePropertiesFromResponse(this.getConnection());
+
+ return snapshot;
+ }
+ };
+
+ return putRequest;
+ }
+
/**
* Queries the service for this share's {@link ShareStats}.
*
@@ -662,6 +870,8 @@ public ShareStats getStats(FileRequestOptions options, OperationContext opContex
opContext = new OperationContext();
}
+ assertNoSnapshot();
+
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
@@ -781,7 +991,7 @@ public void setRequestLocationMode() {
public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
throws Exception {
return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()),
- options, context, accessCondition);
+ options, context, accessCondition, share.snapshotID);
}
@Override
@@ -795,6 +1005,11 @@ public Boolean preProcessResponse(CloudFileShare share, CloudFileClient client,
throws Exception {
if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
share.updatePropertiesFromResponse(this.getConnection());
+ final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(),
+ client.isUsePathStyleUris());
+ share.metadata = attributes.getMetadata();
+ share.properties = attributes.getProperties();
+
return Boolean.valueOf(true);
}
else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
@@ -824,6 +1039,15 @@ private void updatePropertiesFromResponse(HttpURLConnection request) {
}
}
+ /**
+ * Asserts that the share is not a snapshot.
+ */
+ protected void assertNoSnapshot() {
+ if (isSnapshot()) {
+ throw new IllegalArgumentException(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT);
+ }
+ }
+
/**
* Returns a shared access signature for the share. Note this does not contain the leading "?".
*
@@ -931,6 +1155,8 @@ public void uploadMetadata() throws StorageException {
@DoesServiceRequest
public void uploadMetadata(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException {
+ assertNoSnapshot();
+
if (opContext == null) {
opContext = new OperationContext();
}
@@ -1019,6 +1245,12 @@ public final void uploadProperties() throws StorageException {
public final void uploadProperties(
AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException {
+ assertNoSnapshot();
+
+ if (this.properties != null && this.properties.getShareQuota() != null) {
+ Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA);
+ }
+
if (opContext == null) {
opContext = new OperationContext();
}
@@ -1100,6 +1332,8 @@ public void uploadPermissions(final FileSharePermissions permissions) throws Sto
@DoesServiceRequest
public void uploadPermissions(final FileSharePermissions permissions, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException {
+ assertNoSnapshot();
+
if (opContext == null) {
opContext = new OperationContext();
}
@@ -1200,7 +1434,14 @@ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCred
}
this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
-
+
+ final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery());
+
+ final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT);
+ if (snapshotIDs != null && snapshotIDs.length > 0) {
+ this.snapshotID = snapshotIDs[0];
+ }
+
final StorageCredentialsSharedAccessSignature parsedCredentials =
SharedAccessSignatureHelper.parseQuery(completeUri);
@@ -1237,6 +1478,26 @@ public URI getUri() {
return this.storageUri.getPrimaryUri();
}
+ /**
+ * Returns the snapshotID for this share.
+ *
+ * @return The snapshotID as a string for this share.
+ */
+ public final String getSnapshot() {
+ return this.snapshotID;
+ }
+
+ /**
+ * Indicates whether this share is a snapshot.
+ *
+ * @return true
if the share is a snapshot, otherwise false
.
+ *
+ * @see DeleteSnapshotsOption
+ */
+ public final boolean isSnapshot() {
+ return this.snapshotID != null;
+ }
+
/**
* Returns the list of URIs for all locations.
*
@@ -1246,6 +1507,24 @@ public StorageUri getStorageUri() {
return this.storageUri;
}
+ /**
+ * Returns the snapshot or shared access signature qualified URI for this share.
+ *
+ * @return A java.net.URI
object that represents the snapshot or shared access signature.
+ *
+ * @throws StorageException
+ * If a storage service error occurred.
+ * @throws URISyntaxException
+ * If the resource URI is invalid.
+ */
+ public final URI getQualifiedUri() throws URISyntaxException, StorageException {
+ if (this.isSnapshot()) {
+ return PathUtility.addToQuery(this.getUri(), String.format("sharesnapshot=%s", this.snapshotID));
+ }
+
+ return this.fileServiceClient.getCredentials().transformUri(this.getUri());
+ }
+
/**
* Returns the name of the share.
*
@@ -1293,7 +1572,7 @@ public void setMetadata(final HashMap metadata) {
* A {@link FileShareProperties} object that represents the properties being assigned to the
* share.
*/
- protected void setProperties(final FileShareProperties properties) {
+ public void setProperties(final FileShareProperties properties) {
this.properties = properties;
}
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/DeleteShareSnapshotsOption.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/DeleteShareSnapshotsOption.java
new file mode 100644
index 0000000..d6bce51
--- /dev/null
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/DeleteShareSnapshotsOption.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright Microsoft Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ */
+package com.microsoft.azure.storage.file;
+
+/**
+ * Specifies options when calling the delete share operation.
+ */
+public enum DeleteShareSnapshotsOption {
+
+ /**
+ * Specifies deleting the blob and its snapshots.
+ */
+ INCLUDE_SNAPSHOTS,
+
+ /**
+ * Specifies deleting the blob only. If the blob has snapshots, this option will result in an error from the
+ * service.
+ */
+ NONE
+}
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileOutputStream.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileOutputStream.java
index f768db8..ee8816f 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileOutputStream.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileOutputStream.java
@@ -19,6 +19,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Callable;
@@ -205,6 +206,8 @@ public void close() throws IOException {
}
catch (final StorageException e) {
throw Utility.initIOException(e);
+ } catch (URISyntaxException e) {
+ throw Utility.initIOException(e);
}
}
finally {
@@ -227,9 +230,10 @@ public void close() throws IOException {
*
* @throws StorageException
* An exception representing any error which occurred during the operation.
+ * @throws URISyntaxException
*/
@DoesServiceRequest
- private void commit() throws StorageException {
+ private void commit() throws StorageException, URISyntaxException {
if (this.options.getStoreFileContentMD5()) {
this.parentFileRef.getProperties().setContentMD5(Base64.encode(this.md5Digest.digest()));
}
@@ -287,7 +291,13 @@ public Void call() {
FileOutputStream.this.streamFaulted = true;
FileOutputStream.this.lastError = Utility.initIOException(e);
}
+ } catch (URISyntaxException e) {
+ synchronized (FileOutputStream.this.lastErrorLock) {
+ FileOutputStream.this.streamFaulted = true;
+ FileOutputStream.this.lastError = Utility.initIOException(e);
+ }
}
+
return null;
}
};
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java
index c119ce8..8edfd9a 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java
@@ -18,6 +18,7 @@
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.EnumSet;
import java.util.Map;
import com.microsoft.azure.storage.AccessCondition;
@@ -38,6 +39,8 @@ final class FileRequest {
private static final String RANGE_LIST_QUERY_ELEMENT_NAME = "rangelist";
+ private static final String SNAPSHOTS_QUERY_ELEMENT_NAME = "snapshots";
+
/**
* Generates a web request to abort a copy operation.
*
@@ -118,6 +121,23 @@ private static void addProperties(final HttpURLConnection request, FilePropertie
BaseRequest.addOptionalHeader(request, FileConstants.CONTENT_TYPE_HEADER, properties.getContentType());
}
+ /**
+ * Adds the share snapshot if present.
+ * Only for listing files and directories which requires a different query param.
+ *
+ * @param builder
+ * a query builder.
+ * @param snapshotVersion
+ * the share snapshot version to the query builder.
+ * @throws StorageException
+ */
+ public static void addShareSnapshot(final UriQueryBuilder builder, final String snapshotVersion)
+ throws StorageException {
+ if (snapshotVersion != null) {
+ builder.add(Constants.QueryConstants.SHARE_SNAPSHOT, snapshotVersion);
+ }
+ }
+
/**
* Creates a request to copy a file, Sign with 0 length.
*
@@ -268,14 +288,27 @@ public static HttpURLConnection deleteFile(final URI uri, final FileRequestOptio
* @throws IllegalArgumentException
*/
public static HttpURLConnection deleteShare(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, final AccessCondition accessCondition) throws IOException,
- URISyntaxException, StorageException {
+ final OperationContext opContext, final AccessCondition accessCondition, String snapshotVersion, DeleteShareSnapshotsOption deleteSnapshotsOption)
+ throws IOException, URISyntaxException, StorageException {
final UriQueryBuilder shareBuilder = getShareUriQueryBuilder();
+ FileRequest.addShareSnapshot(shareBuilder, snapshotVersion);
HttpURLConnection request = BaseRequest.delete(uri, fileOptions, shareBuilder, opContext);
if (accessCondition != null) {
accessCondition.applyConditionToRequest(request);
}
+ switch (deleteSnapshotsOption) {
+ case NONE:
+ // nop
+ break;
+ case INCLUDE_SNAPSHOTS:
+ request.setRequestProperty(Constants.HeaderConstants.DELETE_SNAPSHOT_HEADER,
+ Constants.HeaderConstants.INCLUDE_SNAPSHOTS_VALUE);
+ break;
+ default:
+ break;
+ }
+
return request;
}
@@ -329,6 +362,8 @@ public static HttpURLConnection getAcl(final URI uri, final FileRequestOptions f
* the operation.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
+ * @param snapshotVersion
+ * The snapshot version, if the share is a snapshot.
* @param offset
* The offset at which to begin returning content.
* @param count
@@ -345,7 +380,7 @@ public static HttpURLConnection getAcl(final URI uri, final FileRequestOptions f
* @throws IllegalArgumentException
*/
public static HttpURLConnection getFile(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, final AccessCondition accessCondition, final Long offset,
+ final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion, final Long offset,
final Long count, boolean requestRangeContentMD5) throws IOException, URISyntaxException, StorageException {
if (offset != null && requestRangeContentMD5) {
@@ -354,6 +389,7 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions
}
final UriQueryBuilder builder = new UriQueryBuilder();
+ FileRequest.addShareSnapshot(builder, snapshotVersion);
final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext);
request.setRequestMethod(Constants.HTTP_GET);
@@ -398,6 +434,8 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @return a HttpURLConnection to use to perform the operation.
+ * @param snapshotVersion
+ * the snapshot version to the query builder.
* @throws IOException
* if there is an error opening the connection
* @throws URISyntaxException
@@ -407,10 +445,10 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions
* @throws IllegalArgumentException
*/
public static HttpURLConnection getFileProperties(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, final AccessCondition accessCondition) throws StorageException,
+ final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion) throws StorageException,
IOException, URISyntaxException {
final UriQueryBuilder builder = new UriQueryBuilder();
- return getProperties(uri, fileOptions, opContext, accessCondition, builder);
+ return getProperties(uri, fileOptions, opContext, accessCondition, builder, snapshotVersion);
}
/**
@@ -438,10 +476,11 @@ public static HttpURLConnection getFileProperties(final URI uri, final FileReque
* @throws IllegalArgumentException
*/
public static HttpURLConnection getFileRanges(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, final AccessCondition accessCondition) throws StorageException,
+ final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion) throws StorageException,
IOException, URISyntaxException {
final UriQueryBuilder builder = new UriQueryBuilder();
+ addShareSnapshot(builder, snapshotVersion);
builder.add(Constants.QueryConstants.COMPONENT, RANGE_LIST_QUERY_ELEMENT_NAME);
final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext);
@@ -469,14 +508,17 @@ public static HttpURLConnection getFileRanges(final URI uri, final FileRequestOp
* the operation.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the share.
+ * @param snapshotVersion
+ * the snapshot version to the query builder.
* @return a HttpURLConnection configured for the operation.
* @throws StorageException
* */
public static HttpURLConnection getShareProperties(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, AccessCondition accessCondition) throws IOException, URISyntaxException,
+ final OperationContext opContext, AccessCondition accessCondition, final String snapshotVersion) throws IOException, URISyntaxException,
StorageException {
final UriQueryBuilder shareBuilder = getShareUriQueryBuilder();
- return getProperties(uri, fileOptions, opContext, accessCondition, shareBuilder);
+
+ return getProperties(uri, fileOptions, opContext, accessCondition, shareBuilder, snapshotVersion);
}
/**
@@ -559,12 +601,16 @@ private static UriQueryBuilder getDirectoryUriQueryBuilder() throws StorageExcep
* the operation.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the share.
+ * @param snapshotVersion
+ * the snapshot version to the query builder.
* @return a HttpURLConnection configured for the operation.
* @throws StorageException
* */
private static HttpURLConnection getProperties(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, AccessCondition accessCondition, final UriQueryBuilder builder)
+ final OperationContext opContext, AccessCondition accessCondition, final UriQueryBuilder builder,
+ String snapshotVersion)
throws IOException, URISyntaxException, StorageException {
+ addShareSnapshot(builder, snapshotVersion);
HttpURLConnection request = BaseRequest.getProperties(uri, fileOptions, builder, opContext);
if (accessCondition != null) {
@@ -591,7 +637,8 @@ private static HttpURLConnection getProperties(final URI uri, final FileRequestO
* @param listingContext
* A set of parameters for the listing operation.
* @param detailsIncluded
- * Additional details to return with the listing.
+ * A java.util.EnumSet
object that contains {@link ShareListingDetails} values that indicate
+ * whether share snapshots and/or metadata will be returned.
* @return a HttpURLConnection configured for the operation.
* @throws IOException
* @throws URISyntaxException
@@ -600,12 +647,28 @@ private static HttpURLConnection getProperties(final URI uri, final FileRequestO
*/
public static HttpURLConnection listShares(final URI uri, final FileRequestOptions fileOptions,
final OperationContext opContext, final ListingContext listingContext,
- final ShareListingDetails detailsIncluded) throws URISyntaxException, IOException, StorageException {
-
+ final EnumSet detailsIncluded) throws URISyntaxException, IOException, StorageException {
final UriQueryBuilder builder = BaseRequest.getListUriQueryBuilder(listingContext);
- if (detailsIncluded == ShareListingDetails.ALL || detailsIncluded == ShareListingDetails.METADATA) {
- builder.add(Constants.QueryConstants.INCLUDE, Constants.QueryConstants.METADATA);
+ if (detailsIncluded != null && detailsIncluded.size() > 0) {
+ final StringBuilder sb = new StringBuilder();
+ boolean started = false;
+
+ if (detailsIncluded.contains(ShareListingDetails.SNAPSHOTS)) {
+ started = true;
+ sb.append(SNAPSHOTS_QUERY_ELEMENT_NAME);
+ }
+
+ if (detailsIncluded.contains(ShareListingDetails.METADATA)) {
+ if (started)
+ {
+ sb.append(",");
+ }
+
+ sb.append(Constants.QueryConstants.METADATA);
+ }
+
+ builder.add(Constants.QueryConstants.INCLUDE, sb.toString());
}
final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext);
@@ -741,14 +804,16 @@ public static HttpURLConnection deleteDirectory(final URI uri, final FileRequest
* the operation.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the directory.
+ * @param snapshotVersion
+ * the snapshot version to the query builder.
* @return a HttpURLConnection configured for the operation.
* @throws StorageException
* */
public static HttpURLConnection getDirectoryProperties(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, AccessCondition accessCondition) throws IOException, URISyntaxException,
+ final OperationContext opContext, AccessCondition accessCondition, String snapshotVersion) throws IOException, URISyntaxException,
StorageException {
final UriQueryBuilder directoryBuilder = getDirectoryUriQueryBuilder();
- return getProperties(uri, fileOptions, opContext, accessCondition, directoryBuilder);
+ return getProperties(uri, fileOptions, opContext, accessCondition, directoryBuilder, snapshotVersion);
}
/**
@@ -767,6 +832,8 @@ public static HttpURLConnection getDirectoryProperties(final URI uri, final File
* the operation.
* @param listingContext
* A set of parameters for the listing operation.
+ * @param snapshotVersion
+ * the snapshot version to the query builder.
* @return a HttpURLConnection configured for the operation.
* @throws IOException
* @throws URISyntaxException
@@ -774,10 +841,11 @@ public static HttpURLConnection getDirectoryProperties(final URI uri, final File
* @throws IllegalArgumentException
*/
public static HttpURLConnection listFilesAndDirectories(final URI uri, final FileRequestOptions fileOptions,
- final OperationContext opContext, final ListingContext listingContext) throws URISyntaxException,
+ final OperationContext opContext, final ListingContext listingContext, String snapshotVersion) throws URISyntaxException,
IOException, StorageException {
final UriQueryBuilder builder = getDirectoryUriQueryBuilder();
+ addShareSnapshot(builder, snapshotVersion);
builder.add(Constants.QueryConstants.COMPONENT, Constants.QueryConstants.LIST);
if (listingContext != null) {
@@ -1099,6 +1167,49 @@ public static HttpURLConnection setFileMetadata(final URI uri, final FileRequest
return setMetadata(uri, fileOptions, opContext, accessCondition, null);
}
+ /**
+ * Constructs a HttpURLConnection to create a snapshot of the share.
+ *
+ * @param uri
+ * A java.net.URI
object that specifies the absolute URI.
+ * @param fileOptions
+ * A {@link FileRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudFileClient}.
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation. This object
+ * is used to track requests to the storage service, and to provide additional runtime information about
+ * the operation.
+ * @param accessCondition
+ * An {@link AccessCondition} object that represents the access conditions for the share.
+ * @return a HttpURLConnection to use to perform the operation.
+ * @throws IOException
+ * if there is an error opening the connection
+ * @throws URISyntaxException
+ * if the resource URI is invalid
+ * @throws StorageException
+ * an exception representing any error which occurred during the operation.
+ * @throws IllegalArgumentException
+ */
+ public static HttpURLConnection snapshotShare(final URI uri, final FileRequestOptions fileOptions,
+ final OperationContext opContext, final AccessCondition accessCondition) throws IOException,
+ URISyntaxException, StorageException {
+ final UriQueryBuilder builder = new UriQueryBuilder();
+ builder.add(Constants.QueryConstants.RESOURCETYPE, "share");
+ builder.add(Constants.QueryConstants.COMPONENT, Constants.QueryConstants.SNAPSHOT);
+ final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext);
+
+ request.setFixedLengthStreamingMode(0);
+ request.setDoOutput(true);
+ request.setRequestMethod(Constants.HTTP_PUT);
+
+ if (accessCondition != null) {
+ accessCondition.applyConditionToRequest(request);
+ }
+
+ return request;
+ }
+
/**
* Constructs a HttpURLConnection to set the file's properties, Sign with zero length specified.
*
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileResponse.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileResponse.java
index a7ce697..fe9a009 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileResponse.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileResponse.java
@@ -209,6 +209,17 @@ static Integer parseShareQuota(final HttpURLConnection request) {
return (shareQuota == -1) ? null : shareQuota;
}
+ /**
+ * Gets the snapshot ID from the request header.
+ *
+ * @param request
+ * The response from server.
+ * @return the snapshot ID from the request header.
+ */
+ public static String getSnapshotTime(final HttpURLConnection request) {
+ return request.getHeaderField(Constants.HeaderConstants.SNAPSHOT_ID_HEADER);
+ }
+
/**
* Private Default Ctor
*/
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileShareProperties.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileShareProperties.java
index c5e29c6..b658733 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileShareProperties.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileShareProperties.java
@@ -39,6 +39,27 @@ public final class FileShareProperties {
*/
private Integer shareQuota;
+ /**
+ * Creates an instance of the FileShareProperties
class.
+ */
+ public FileShareProperties() {
+ }
+
+ /**
+ * Creates an instance of the FileShareProperties
class by copying values from another
+ * FileShareProperties
instance.
+ *
+ * @param other
+ * A {@link FileShareProperties} object which represents the file share properties to copy.
+ */
+ public FileShareProperties(final FileShareProperties other) {
+ if (other != null) {
+ this.setEtag(other.getEtag());
+ this.setLastModified(other.getLastModified());
+ this.setShareQuota(other.getShareQuota());
+ }
+ }
+
/**
* Gets the ETag value of the share.
*
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java
index c73693d..3d3b9b9 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java
@@ -46,6 +46,7 @@ final class ShareListHandler extends DefaultHandler {
private final ListResponse response = new ListResponse();
private FileShareAttributes attributes;
private String shareName;
+ private String snapshotID;
private ShareListHandler(CloudFileClient serviceClient) {
this.serviceClient = serviceClient;
@@ -77,6 +78,7 @@ public void startElement(String uri, String localName, String qName, Attributes
if (FileConstants.SHARE_ELEMENT.equals(localName)) {
this.shareName = Constants.EMPTY_STRING;
+ this.snapshotID = null;
this.attributes = new FileShareAttributes();
}
}
@@ -105,6 +107,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc
CloudFileShare retShare = this.serviceClient.getShareReference(this.shareName);
retShare.setMetadata(this.attributes.getMetadata());
retShare.setProperties(this.attributes.getProperties());
+ retShare.snapshotID = this.snapshotID;
this.response.getResults().add(retShare);
}
@@ -134,6 +137,9 @@ else if (FileConstants.SHARE_ELEMENT.equals(parentNode)) {
if (Constants.NAME_ELEMENT.equals(currentNode)) {
this.shareName = value;
}
+ else if (Constants.QueryConstants.SNAPSHOT.equals(currentNode.toLowerCase())) {
+ this.snapshotID = value;
+ }
}
else if (Constants.PROPERTIES.equals(parentNode)) {
try {
diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java
index b9b54dd..14bb2c0 100644
--- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java
+++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java
@@ -18,10 +18,11 @@
* Specifies which details to include when listing the shares in this storage account.
*/
public enum ShareListingDetails {
+
/**
- * Specifies including all available details.
+ * Specifies including no additional details.
*/
- ALL(1),
+ NONE(0),
/**
* Specifies including share metadata.
@@ -29,9 +30,9 @@ public enum ShareListingDetails {
METADATA(1),
/**
- * Specifies including no additional details.
+ * Specifies listing share snapshots.
*/
- NONE(0);
+ SNAPSHOTS(2);
/**
* Returns the value of this enum.