com.microsoft.windowsazure.services.blob.client.CloudBlob Maven / Gradle / Ivy
Show all versions of microsoft-windowsazure-api Show documentation
/**
* 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.windowsazure.services.blob.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import com.microsoft.windowsazure.services.blob.core.storage.SharedAccessSignatureHelper;
import com.microsoft.windowsazure.services.core.storage.AccessCondition;
import com.microsoft.windowsazure.services.core.storage.Constants;
import com.microsoft.windowsazure.services.core.storage.DoesServiceRequest;
import com.microsoft.windowsazure.services.core.storage.LeaseStatus;
import com.microsoft.windowsazure.services.core.storage.OperationContext;
import com.microsoft.windowsazure.services.core.storage.RetryNoRetry;
import com.microsoft.windowsazure.services.core.storage.RetryPolicy;
import com.microsoft.windowsazure.services.core.storage.StorageCredentialsSharedAccessSignature;
import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
import com.microsoft.windowsazure.services.core.storage.StorageException;
import com.microsoft.windowsazure.services.core.storage.StorageExtendedErrorInformation;
import com.microsoft.windowsazure.services.core.storage.utils.Base64;
import com.microsoft.windowsazure.services.core.storage.utils.PathUtility;
import com.microsoft.windowsazure.services.core.storage.utils.StreamMd5AndLength;
import com.microsoft.windowsazure.services.core.storage.utils.UriQueryBuilder;
import com.microsoft.windowsazure.services.core.storage.utils.Utility;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.BaseResponse;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.LeaseAction;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;
/**
*
* Represents a Windows Azure blob. This is the base class for the {@link CloudBlockBlob} and {@link CloudPageBlob}
* classes.
*/
public abstract class CloudBlob implements ListBlobItem {
/**
* Holds the metadata for the blob.
*/
HashMap metadata;
/**
* Holds the properties of the blob.
*/
BlobProperties properties;
/**
* Holds the URI of the blob, Setting this is RESERVED for internal use.
*/
URI uri;
/**
* Holds the snapshot ID.
*/
String snapshotID;
/**
* Represents the state of the most recent or pending copy operation.
*/
CopyState copyState;
/**
* Holds the Blobs container Reference.
*/
private CloudBlobContainer container;
/**
* Represents the blob's directory.
*/
protected CloudBlobDirectory parent;
/**
* Holds the Blobs Name.
*/
private String name;
/**
* Represents the blob client.
*/
protected CloudBlobClient blobServiceClient;
/**
* Creates an instance of the CloudBlob
class.
*
* @param type
* the type of the blob.
*/
protected CloudBlob(final BlobType type) {
this.metadata = new HashMap();
this.properties = new BlobProperties(type);
}
/**
* Creates an instance of the CloudBlob
class using the specified URI and cloud blob client.
*
* @param type
* the type of the blob.
* @param uri
* A java.net.URI
object that represents the URI to the blob, beginning with the container
* name.
* @param client
* A {@link CloudBlobClient} object that specifies the endpoint for the Blob service.
*
* @throws StorageException
* If a storage service error occurred.
*/
protected CloudBlob(final BlobType type, final URI uri, final CloudBlobClient client) throws StorageException {
this(type);
Utility.assertNotNull("blobAbsoluteUri", uri);
this.blobServiceClient = client;
this.uri = uri;
this.parseURIQueryStringAndVerify(uri, client, client.isUsePathStyleUris());
}
/**
* Creates an instance of the CloudBlob
class using the specified URI, cloud blob client, and cloud
* blob container.
*
* @param type
* the type of the blob.
* @param uri
* A java.net.URI
object that represents the absolute URI to the blob, beginning with the
* container name.
* @param client
* A {@link CloudBlobClient} object that specifies the endpoint for the Blob service.
* @param container
* A {@link CloudBlobContainer} object that represents the container to use for the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
protected CloudBlob(final BlobType type, final URI uri, final CloudBlobClient client,
final CloudBlobContainer container) throws StorageException {
this(type, uri, client);
this.container = container;
}
/**
* Creates an instance of the CloudBlob
class using the specified URI, snapshot ID, and cloud blob
* client.
*
* @param type
* the type of the blob.
* @param uri
* A java.net.URI
object that represents the absolute URI to the blob, beginning with the
* container name.
* @param snapshotID
* A String
that represents the snapshot version, if applicable.
* @param client
* A {@link CloudBlobContainer} object that represents the container to use for the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
protected CloudBlob(final BlobType type, final URI uri, final String snapshotID, final CloudBlobClient client)
throws StorageException {
this(type, uri, client);
if (snapshotID != null) {
if (this.snapshotID != null) {
throw new IllegalArgumentException(
"Snapshot query parameter is already defined in the blobUri. Either pass in a snapshotTime parameter or use a full URL with a snapshot query parameter.");
}
else {
this.snapshotID = snapshotID;
}
}
}
/**
* Creates an instance of the CloudBlob
class by copying values from another blob.
*
* @param otherBlob
* A CloudBlob
object that represents the blob to copy.
*/
protected CloudBlob(final CloudBlob otherBlob) {
this.metadata = new HashMap();
this.properties = new BlobProperties(otherBlob.properties);
if (otherBlob.metadata != null) {
this.metadata = new HashMap();
for (final String key : otherBlob.metadata.keySet()) {
this.metadata.put(key, otherBlob.metadata.get(key));
}
}
this.snapshotID = otherBlob.snapshotID;
this.uri = otherBlob.uri;
this.container = otherBlob.container;
this.parent = otherBlob.parent;
this.blobServiceClient = otherBlob.blobServiceClient;
this.name = otherBlob.name;
this.copyState = otherBlob.copyState;
}
/**
* Acquires a new lease on the blob with the specified lease time and proposed lease ID.
*
* @param leaseTimeInSeconds
* Specifies the span of time for which to acquire the lease, in seconds.
* If null, an infinite lease will be acquired. If not null, the value must be greater than
* zero.
*
* @param proposedLeaseId
* A String
that represents the proposed lease ID for the new lease,
* or null if no lease ID is proposed.
*
* @return A String
that represents the lease ID.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final String acquireLease(final Integer leaseTimeInSeconds, final String proposedLeaseId)
throws StorageException {
return this.acquireLease(leaseTimeInSeconds, proposedLeaseId, null, null, null);
}
/**
* Acquires a new lease on the blob with the specified lease time, proposed lease ID, request
* options, and operation context.
*
* @param leaseTimeInSeconds
* Specifies the span of time for which to acquire the lease, in seconds.
* If null, an infinite lease will be acquired. If not null, the value must be greater than
* zero.
*
* @param proposedLeaseId
* A String
that represents the proposed lease ID for the new lease,
* or null if no lease ID is proposed.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
*
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client
* ({@link CloudBlobClient}).
*
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. The context
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A String
that represents the lease ID.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final String acquireLease(final Integer leaseTimeInSeconds, final String proposedLeaseId,
final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public String execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.ACQUIRE, leaseTimeInSeconds,
proposedLeaseId, null, accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.properties.setLeaseStatus(LeaseStatus.LOCKED);
return BlobResponse.getLeaseID(request, opContext);
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Asserts that the blob has the correct blob type specified in the blob attributes.
*
* @throws StorageException
* If an incorrect blob type is used.
*/
protected final void assertCorrectBlobType() throws StorageException {
if (this instanceof CloudBlockBlob && this.properties.getBlobType() != BlobType.BLOCK_BLOB) {
throw new StorageException(
StorageErrorCodeStrings.INCORRECT_BLOB_TYPE,
String.format(
"Incorrect Blob type, please use the correct Blob type to access a blob on the server. Expected %s, actual %s",
BlobType.BLOCK_BLOB, this.properties.getBlobType()),
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
if (this instanceof CloudPageBlob && this.properties.getBlobType() != BlobType.PAGE_BLOB) {
throw new StorageException(
StorageErrorCodeStrings.INCORRECT_BLOB_TYPE,
String.format(
"Incorrect Blob type, please use the correct Blob type to access a blob on the server. Expected %s, actual %s",
BlobType.PAGE_BLOB, this.properties.getBlobType()),
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
}
/**
* Breaks the lease and ensures that another client cannot acquire a new lease until the current lease period
* has expired.
*
* @param breakPeriodInSeconds
* Specifies the time to wait, in seconds, until the current lease is broken.
* If null, the break period is the remainder of the current lease, or zero for infinite leases.
*
* @return The time, in seconds, remaining in the lease period.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final long breakLease(final Integer breakPeriodInSeconds) throws StorageException {
return this.breakLease(breakPeriodInSeconds, null, null, null);
}
/**
* Breaks the existing lease, using the specified request options and operation context, and ensures that another
* client cannot acquire a new lease until the current lease period has expired.
*
* @param breakPeriodInSeconds
* Specifies the time to wait, in seconds, until the current lease is broken.
* If null, the break period is the remainder of the current lease, or zero for infinite leases.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client
* ({@link CloudBlobClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. The context
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return The time, in seconds, remaining in the lease period.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final long breakLease(final Integer breakPeriodInSeconds, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Long execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.BREAK, null, null,
breakPeriodInSeconds, accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return -1L;
}
blob.updateEtagAndLastModifiedFromResponse(request);
final String leaseTime = BlobResponse.getLeaseTime(request, opContext);
blob.properties.setLeaseStatus(LeaseStatus.UNLOCKED);
return Utility.isNullOrEmpty(leaseTime) ? -1L : Long.parseLong(leaseTime);
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Copies an existing blob's contents, properties, and metadata to this instance of the CloudBlob
* class.
*
* @param sourceBlob
* A CloudBlob
object that represents the source blob to copy.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void copyFromBlob(final CloudBlob sourceBlob) throws StorageException, URISyntaxException {
this.copyFromBlob(sourceBlob, null, null, null, null);
}
/**
* Copies an existing blob's contents, properties, and metadata to this instance of the CloudBlob
* class, using the specified access conditions, lease ID, request options, and operation context.
*
* @param sourceBlob
* A CloudBlob
object that represents the source blob to copy.
* @param sourceAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the source blob.
* @param destinationAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the destination blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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.
* @throws URISyntaxException
*
*/
@DoesServiceRequest
public final void copyFromBlob(final CloudBlob sourceBlob, final AccessCondition sourceAccessCondition,
final AccessCondition destinationAccessCondition, BlobRequestOptions options, OperationContext opContext)
throws StorageException, URISyntaxException {
this.copyFromBlob(sourceBlob.uri, sourceAccessCondition, destinationAccessCondition, options, opContext);
}
/**
* Copies an existing blob's contents, properties, and metadata to this instance of the CloudBlob
* class.
*
* @param source
* A URI
The URI of a source blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void copyFromBlob(final URI source) throws StorageException {
this.copyFromBlob(source, null, null, null, null);
}
/**
* Copies an existing blob's contents, properties, and metadata to a new blob, using the specified access
* conditions, lease ID, request options, and operation context.
*
* @param source
* A URI
The URI of a source blob.
* @param sourceAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the source blob.
* @param destinationAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the destination blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void copyFromBlob(final URI source, final AccessCondition sourceAccessCondition,
final AccessCondition destinationAccessCondition, BlobRequestOptions options, OperationContext opContext)
throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.copyFrom(blob.getTransformedAddress(opContext),
blobOptions.getTimeoutIntervalInMs(), source.toString(), blob.snapshotID,
sourceAccessCondition, destinationAccessCondition, blobOptions, opContext);
this.setConnection(request);
BlobRequest.addMetadata(request, blob.metadata, opContext);
this.signRequest(client, request, 0, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
blob.copyState = BaseResponse.getCopyState(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Aborts an ongoing blob copy operation.
*
* @param copyId
* A String
object that identifying the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void abortCopy(final String copyId) throws StorageException {
this.abortCopy(copyId, null, null, null);
}
/**
* Aborts an ongoing blob copy operation.
*
* @param copyId
* A String
object that identifying the copy operation.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void abortCopy(final String copyId, final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.abortCopy(blob.getTransformedAddress(opContext),
blobOptions.getTimeoutIntervalInMs(), copyId, accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Creates a snapshot of the blob.
*
* @return A CloudBlob
object that represents the snapshot of the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final CloudBlob createSnapshot() throws StorageException {
return this.createSnapshot(null, null, null);
}
/**
* Creates a snapshot of the blob using the specified request options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 CloudBlob
object that represents the snapshot of the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final CloudBlob createSnapshot(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
assertNoWriteOperationForSnapshot();
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public CloudBlob execute(final CloudBlobClient client, final CloudBlob blob,
final OperationContext opContext) throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.snapshot(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
CloudBlob snapshot = null;
final String snapshotTime = BlobResponse.getSnapshotTime(request, opContext);
if (blob instanceof CloudBlockBlob) {
snapshot = new CloudBlockBlob(blob.getUri(), snapshotTime, client);
}
else if (blob instanceof CloudPageBlob) {
snapshot = new CloudPageBlob(blob.getUri(), snapshotTime, client);
}
blob.updateEtagAndLastModifiedFromResponse(request);
return snapshot;
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Deletes the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void delete() throws StorageException {
this.delete(DeleteSnapshotsOption.NONE, null, null, null);
}
/**
* Deletes the blob using the specified snapshot and request options, and operation context.
*
* A blob that has snapshots cannot be deleted unless the snapshots are also deleted. If a blob has snapshots, use
* the {@link DeleteSnapshotsOption#DELETE_SNAPSHOTS_ONLY} or {@link DeleteSnapshotsOption#INCLUDE_SNAPSHOTS} value
* in the deleteSnapshotsOption
parameter to specify how the snapshots should be handled when the blob
* is deleted.
*
* @param deleteSnapshotsOption
* A {@link DeleteSnapshotsOption} object that indicates whether to delete only blobs, only snapshots, or
* both.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void delete(final DeleteSnapshotsOption deleteSnapshotsOption, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException {
Utility.assertNotNull("deleteSnapshotsOption", deleteSnapshotsOption);
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.delete(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.snapshotID, deleteSnapshotsOption,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, -1L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Deletes the blob if it exists.
*
* A blob that has snapshots cannot be deleted unless the snapshots are also deleted. If a blob has snapshots, use
* the {@link DeleteSnapshotsOption#DELETE_SNAPSHOTS_ONLY} or {@link DeleteSnapshotsOption#INCLUDE_SNAPSHOTS} value
* in the deleteSnapshotsOption
parameter to specify how the snapshots should be handled when the blob
* is deleted.
*
* @return true
if the blob was deleted; otherwise, false
.
*
* @throws StorageException
* If a storage service error occurred.
*
*/
@DoesServiceRequest
public final boolean deleteIfExists() throws StorageException {
return this.deleteIfExists(DeleteSnapshotsOption.NONE, null, null, null);
}
/**
* Deletes the blob if it exists, using the specified snapshot and request options, and operation context.
*
* A blob that has snapshots cannot be deleted unless the snapshots are also deleted. If a blob has snapshots, use
* the {@link DeleteSnapshotsOption#DELETE_SNAPSHOTS_ONLY} or {@link DeleteSnapshotsOption#INCLUDE_SNAPSHOTS} value
* in the deleteSnapshotsOption
parameter to specify how the snapshots should be handled when the blob
* is deleted.
*
* @param deleteSnapshotsOption
* A {@link DeleteSnapshotsOption} object that indicates whether to delete only blobs, only snapshots, or
* both.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 blob was deleted; otherwise, false
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final boolean deleteIfExists(final DeleteSnapshotsOption deleteSnapshotsOption,
final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
throws StorageException {
Utility.assertNotNull("deleteSnapshotsOption", deleteSnapshotsOption);
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Boolean execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.delete(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.snapshotID, deleteSnapshotsOption,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, -1L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED) {
return true;
}
else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
else {
this.setNonExceptionedRetryableFailure(true);
// return false instead of null to avoid SCA issues
return false;
}
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Downloads the contents of a blob to a stream.
*
* @param outStream
* An OutputStream
object that represents the target stream.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void download(final OutputStream outStream) throws StorageException, IOException {
this.download(outStream, null, null, null);
}
/**
* Downloads the contents of a blob to a stream using the specified request options and operation context.
*
* @param outStream
* An OutputStream
object that represents the target stream.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void download(final OutputStream outStream, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.get(blob.getTransformedAddress(opContext),
blobOptions.getTimeoutIntervalInMs(), blob.snapshotID, accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, -1L, null);
final InputStream streamRef = ExecutionEngine.getInputStream(request, opContext, this.getResult());
final String contentMD5 = request.getHeaderField(Constants.HeaderConstants.CONTENT_MD5);
final Boolean validateMD5 = !blobOptions.getDisableContentMD5Validation()
&& !Utility.isNullOrEmpty(contentMD5);
final String contentLength = request.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH);
final long expectedLength = Long.parseLong(contentLength);
final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(),
blob.snapshotID, opContext);
blob.properties = retrievedAttributes.getProperties();
blob.metadata = retrievedAttributes.getMetadata();
blob.copyState = retrievedAttributes.getCopyState();
ExecutionEngine.getResponseCode(this.getResult(), request, opContext);
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
validateMD5, this.getResult(), opContext);
if (descriptor.getLength() != expectedLength) {
throw new StorageException(
StorageErrorCodeStrings.OUT_OF_RANGE_INPUT,
"An incorrect number of bytes was read from the connection. The connection may have been closed",
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
if (validateMD5 && !contentMD5.equals(descriptor.getMd5())) {
throw new StorageException(StorageErrorCodeStrings.INVALID_MD5, String.format(
"Blob data corrupted (integrity check failed), Expected value is %s, retrieved %s",
contentMD5, descriptor.getMd5()), Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
return null;
}
};
try {
// Executed with no retries so that the first failure will move out
// to the read Stream.
ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, RetryNoRetry.getInstance(), opContext);
opContext.setIntermediateMD5(null);
}
catch (final StorageException ex) {
// Check if users has any retries specified, Or if the exception is retryable
final RetryPolicy dummyPolicy = options.getRetryPolicyFactory().createInstance(opContext);
if ((ex.getHttpStatusCode() == Constants.HeaderConstants.HTTP_UNUSED_306 && !ex.getErrorCode().equals(
StorageErrorCodeStrings.OUT_OF_RANGE_INPUT))
|| ex.getHttpStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED
|| !dummyPolicy.shouldRetry(0, impl.getResult().getStatusCode(), (Exception) ex.getCause(),
opContext).isShouldRetry()) {
opContext.setIntermediateMD5(null);
throw ex;
}
// Continuation, fail gracefully into a read stream. This needs to
// be outside the operation above as it would get retried resulting
// in a nested retry
// Copy access condition, and update etag. This will potentially replace the if match value, but not on the
// users object.
AccessCondition etagLockCondition = new AccessCondition();
etagLockCondition.setIfMatch(this.getProperties().getEtag());
if (accessCondition != null) {
etagLockCondition.setLeaseID(accessCondition.getLeaseID());
}
// 1. Open Read Stream
final BlobInputStream streamRef = this.openInputStream(etagLockCondition, options, opContext);
// Cache value indicating if we need
final boolean validateMd5 = streamRef.getValidateBlobMd5();
streamRef.setValidateBlobMd5(false);
streamRef.mark(Integer.MAX_VALUE);
try {
// 2. Seek to current position, this will disable read streams
// internal content md5 checks.
if (opContext.getCurrentOperationByteCount() > 0) {
// SCA will say this if a failure as we do not validate the
// return of skip.
// The Blob class repositioning a virtual pointer and will
// always skip the correct
// number of bytes.
streamRef.skip(opContext.getCurrentOperationByteCount());
}
// 3. Continue copying
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
validateMd5, null, opContext);
if (validateMd5 && !this.properties.getContentMD5().equals(descriptor.getMd5())) {
throw new StorageException(StorageErrorCodeStrings.INVALID_MD5, String.format(
"Blob data corrupted (integrity check failed), Expected value is %s, retrieved %s",
this.properties.getContentMD5(), descriptor.getMd5()),
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
}
catch (final IOException secondEx) {
opContext.setIntermediateMD5(null);
if (secondEx.getCause() != null && secondEx.getCause() instanceof StorageException) {
throw (StorageException) secondEx.getCause();
}
else {
throw secondEx;
}
}
}
}
/**
* Populates a blob's properties and metadata.
*
* This method populates the blob's system properties and user-defined metadata. Before reading a blob's properties
* or metadata, call this method or its overload to retrieve the latest values for the blob's properties and
* metadata from the Windows Azure storage service.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadAttributes() throws StorageException {
this.downloadAttributes(null, null, null);
}
/**
* Populates a blob's properties and metadata using the specified request options and operation context.
*
* This method populates the blob's system properties and user-defined metadata. Before reading a blob's properties
* or metadata, call this method or its overload to retrieve the latest values for the blob's properties and
* metadata from the Windows Azure storage service.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void downloadAttributes(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.getProperties(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.snapshotID, accessCondition, blobOptions,
opContext);
this.setConnection(request);
this.signRequest(client, request, -1L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
// Set attributes
final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(),
blob.snapshotID, opContext);
if (retrievedAttributes.getProperties().getBlobType() != blob.properties.getBlobType()) {
throw new StorageException(
StorageErrorCodeStrings.INCORRECT_BLOB_TYPE,
String.format(
"Incorrect Blob type, please use the correct Blob type to access a blob on the server. Expected %s, actual %s",
blob.properties.getBlobType(), retrievedAttributes.getProperties().getBlobType()),
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
blob.properties = retrievedAttributes.getProperties();
blob.metadata = retrievedAttributes.getMetadata();
blob.copyState = retrievedAttributes.getCopyState();
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Downloads a range of bytes from the blob to the given byte buffer.
*
* @param offset
* The byte offset to use as the starting point for the source.
* @param length
* The number of bytes to read.
* @param buffer
* The byte buffer, as an array of bytes, to which the blob bytes are downloaded.
* @param bufferOffet
* The byte offset to use as the starting point for the target.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadRange(final long offset, final int length, final byte[] buffer, final int bufferOffet)
throws StorageException {
this.downloadRangeInternal(offset, length, buffer, bufferOffet, null, null, null);
}
/**
* Downloads a range of bytes from the blob to the given byte buffer, using the specified request options and
* operation context.
*
* @param offset
* The byte offset to use as the starting point for the source.
* @param length
* The number of bytes to read.
* @param buffer
* The byte buffer, as an array of bytes, to which the blob bytes are downloaded.
* @param bufferOffet
* The byte offset to use as the starting point for the target.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void downloadRange(final long offset, final int length, final byte[] buffer, final int bufferOffet,
final AccessCondition accessCondition, final BlobRequestOptions options, final OperationContext opContext)
throws StorageException {
if (offset < 0 || length <= 0) {
throw new IndexOutOfBoundsException();
}
Utility.assertNotNull("buffer", buffer);
if (length + bufferOffet > buffer.length) {
throw new IndexOutOfBoundsException();
}
opContext.initialize();
this.downloadRangeInternal(offset, length, buffer, bufferOffet, accessCondition, options, opContext);
}
/**
* Downloads a range of bytes from the blob to the given byte buffer.
*
* @param blobOffset
* the offset of the blob to begin downloading at
* @param length
* the number of bytes to read
* @param buffer
* the byte buffer to write to.
* @param bufferOffset
* the offset in the byte buffer to begin writing.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* An object that specifies any additional options for the request
* @param opContext
* an object used to track the execution of the operation
* @throws StorageException
* an exception representing any error which occurred during the operation.
*/
@DoesServiceRequest
protected final void downloadRangeInternal(final long blobOffset, final int length, final byte[] buffer,
final int bufferOffset, final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (blobOffset < 0 || length <= 0) {
throw new IndexOutOfBoundsException();
}
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
options.applyDefaults(this.blobServiceClient);
if (options.getUseTransactionalContentMD5() && length > 4 * Constants.MB) {
throw new IllegalArgumentException(
"Cannot specify x-ms-range-get-content-md5 header on ranges larger than 4 MB. Either use a BlobReadStream via openRead, or disable TransactionalMD5 checking via the BlobRequestOptions.");
}
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.get(blob.getTransformedAddress(opContext),
blobOptions.getTimeoutIntervalInMs(), blob.snapshotID, blobOffset, length, accessCondition,
blobOptions, opContext);
this.setConnection(request);
if (blobOptions.getUseTransactionalContentMD5()) {
request.setRequestProperty(Constants.HeaderConstants.RANGE_GET_CONTENT_MD5, "true");
}
this.signRequest(client, request, -1L, null);
final InputStream sourceStream = ExecutionEngine.getInputStream(request, opContext, this.getResult());
int totalRead = 0;
int nextRead = buffer.length - bufferOffset;
int count = sourceStream.read(buffer, bufferOffset, nextRead);
while (count > 0) {
totalRead += count;
nextRead = buffer.length - (bufferOffset + totalRead);
if (nextRead == 0) {
// check for case where more data is returned
if (sourceStream.read(new byte[1], 0, 1) != -1) {
throw new StorageException(
StorageErrorCodeStrings.OUT_OF_RANGE_INPUT,
"An incorrect number of bytes was read from the connection. The connection may have been closed",
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
}
count = sourceStream.read(buffer, bufferOffset + totalRead, nextRead);
}
ExecutionEngine.getResponseCode(this.getResult(), request, opContext);
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_PARTIAL) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
// Do not update blob length and Content-MD5 in downloadRangeInternal API.
final long originalBlobLength = blob.properties.getLength();
final String originalContentMD5 = blob.properties.getContentMD5();
final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(),
blob.snapshotID, opContext);
blob.properties = retrievedAttributes.getProperties();
blob.metadata = retrievedAttributes.getMetadata();
blob.copyState = retrievedAttributes.getCopyState();
blob.properties.setContentMD5(originalContentMD5);
blob.properties.setLength(originalBlobLength);
final String contentLength = request.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH);
final long expectedLength = Long.parseLong(contentLength);
if (totalRead != expectedLength) {
throw new StorageException(
StorageErrorCodeStrings.OUT_OF_RANGE_INPUT,
"An incorrect number of bytes was read from the connection. The connection may have been closed",
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
if (blobOptions.getUseTransactionalContentMD5()) {
final String contentMD5 = request.getHeaderField(Constants.HeaderConstants.CONTENT_MD5);
try {
final MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(buffer, bufferOffset, length);
final String calculatedMD5 = Base64.encode(digest.digest());
if (!contentMD5.equals(calculatedMD5)) {
throw new StorageException(StorageErrorCodeStrings.INVALID_MD5, String.format(
"Blob data corrupted (integrity check failed), Expected value is %s, retrieved %s",
contentMD5, calculatedMD5), Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
}
catch (final NoSuchAlgorithmException e) {
// This wont happen, throw fatal.
throw Utility.generateNewUnexpectedStorageException(e);
}
}
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Checks to see if the blob exists.
*
* @return true
if the blob exists, other wise false
.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final boolean exists() throws StorageException {
return this.exists(null, null, null);
}
/**
* Checks to see if the blob exists, using the specified request options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 blob exists, other wise false
.
*
* @throws StorageException
* f a storage service error occurred.
*/
@DoesServiceRequest
public final boolean exists(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Boolean execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.getProperties(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.snapshotID, accessCondition, blobOptions,
opContext);
this.setConnection(request);
this.signRequest(client, request, -1L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(),
blob.snapshotID, opContext);
blob.properties = retrievedAttributes.getProperties();
blob.metadata = retrievedAttributes.getMetadata();
blob.copyState = retrievedAttributes.getCopyState();
return Boolean.valueOf(true);
}
else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return Boolean.valueOf(false);
}
else {
this.setNonExceptionedRetryableFailure(true);
// return false instead of null to avoid SCA issues
return false;
}
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Returns a shared access signature for the blob using the specified group policy identifier and operation context.
* Note this does not contain the leading "?".
*
* @param policy
* A SharedAccessPolicy
object that represents the access policy for the shared access
* signature.
* @param groupPolicyIdentifier
* A String
that represents the container-level access policy.
*
* @return A String
that represents the shared access signature.
*
* @throws IllegalArgumentException
* If the credentials are invalid or the blob is a snapshot.
* @throws InvalidKeyException
* If the credentials are invalid.
* @throws StorageException
* If a storage service error occurred.
*/
public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, final String groupPolicyIdentifier)
throws InvalidKeyException, StorageException {
if (!this.blobServiceClient.getCredentials().canCredentialsSignRequest()) {
throw new IllegalArgumentException(
"Cannot create Shared Access Signature unless the Account Key credentials are used by the BlobServiceClient.");
}
if (this.isSnapshot()) {
throw new IllegalArgumentException(
"Cannot create Shared Access Signature for snapshots. Perform the operation on the root blob instead.");
}
final String resourceName = this.getCanonicalName(true);
final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHash(policy,
groupPolicyIdentifier, resourceName, this.blobServiceClient, null);
final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignature(policy,
groupPolicyIdentifier, "b", signature);
return builder.toString();
}
/**
* Returns the canonical name of the blob in the format of
* /<account-name>/<container-name>/<blob-name>.
*
* This format is used by both Shared Access and Copy blob operations.
*
* @param ignoreSnapshotTime
* true
if the snapshot time is ignored; otherwise, false
.
*
* @return The canonical name in the format of /<account-name>/<container
* -name>/<blob-name>.
*/
String getCanonicalName(final boolean ignoreSnapshotTime) {
String canonicalName;
if (this.blobServiceClient.isUsePathStyleUris()) {
canonicalName = this.getUri().getRawPath();
}
else {
canonicalName = PathUtility.getCanonicalPathFromCredentials(this.blobServiceClient.getCredentials(), this
.getUri().getRawPath());
}
if (!ignoreSnapshotTime && this.snapshotID != null) {
canonicalName = canonicalName.concat("?snapshot=");
canonicalName = canonicalName.concat(this.snapshotID);
}
return canonicalName;
}
/**
* Returns the blob's container.
*
* @return A {@link CloudBlobContainer} object that represents the container of the blob.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
@Override
public final CloudBlobContainer getContainer() throws StorageException, URISyntaxException {
if (this.container == null) {
final URI containerURI = PathUtility.getContainerURI(this.getUri(),
this.blobServiceClient.isUsePathStyleUris());
this.container = new CloudBlobContainer(containerURI, this.blobServiceClient);
}
return this.container;
}
/**
* Returns the metadata for the blob.
*
* @return A java.util.HashMap
object that represents the metadata for the blob.
*/
public final HashMap getMetadata() {
return this.metadata;
}
/**
* Returns the name of the blob.
*
* @return A String
that represents the name of the blob.
*
* @throws URISyntaxException
* If the resource URI is invalid.
*/
public final String getName() throws URISyntaxException {
if (Utility.isNullOrEmpty(this.name)) {
this.name = PathUtility.getBlobNameFromURI(this.getUri(), this.blobServiceClient.isUsePathStyleUris());
}
return this.name;
}
/**
* Returns the blob item's parent.
*
* @return A {@link CloudBlobDirectory} object that represents the parent directory for the blob.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
@Override
public final CloudBlobDirectory getParent() throws URISyntaxException, StorageException {
if (this.parent == null) {
final URI parentURI = PathUtility.getParentAddress(this.getUri(),
this.blobServiceClient.getDirectoryDelimiter(), this.blobServiceClient.isUsePathStyleUris());
this.parent = new CloudBlobDirectory(parentURI, null, this.blobServiceClient);
}
return this.parent;
}
/**
* Returns the blob's properties.
*
* @return A {@link BlobProperties} object that represents the properties of the blob.
*/
public final BlobProperties getProperties() {
return this.properties;
}
/**
* Returns the blob's copy state.
*
* @return A {@link CopyState} object that represents the copy state of the blob.
*/
public CopyState getCopyState() {
return this.copyState;
}
/**
* Returns the snapshot or shared access signature qualified URI for this blob.
*
* @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("snapshot=%s", this.snapshotID));
}
else if (this.blobServiceClient.getCredentials() instanceof StorageCredentialsSharedAccessSignature) {
return this.blobServiceClient.getCredentials().transformUri(this.getUri());
}
else {
return this.getUri();
}
}
/**
* Returns the Blob service client associated with the blob.
*
* @return A {@link CloudBlobClient} object that represents the client.
*/
public final CloudBlobClient getServiceClient() {
return this.blobServiceClient;
}
/**
* Gets the Blob Snapshot ID.
*
* @return the Blob Snapshot ID.
*/
public final String getSnapshotID() {
return this.snapshotID;
}
/**
* Returns the transformed URI for the resource if the given credentials require transformation.
*
* @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 java.net.URI
object that represents the transformed URI.
*
* @throws IllegalArgumentException
* If the URI is not absolute.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
protected final URI getTransformedAddress(final OperationContext opContext) throws URISyntaxException,
StorageException {
if (this.blobServiceClient.getCredentials().doCredentialsNeedTransformUri()) {
if (this.getUri().isAbsolute()) {
return this.blobServiceClient.getCredentials().transformUri(this.getUri(), opContext);
}
else {
final StorageException ex = Utility.generateNewUnexpectedStorageException(null);
ex.getExtendedErrorInformation().setErrorMessage("Blob Object relative URIs not supported.");
throw ex;
}
}
else {
return this.getUri();
}
}
/**
* Returns the URI for this blob.
*
* @return A java.net.URI
object that represents the URI for the blob.
*/
@Override
public final URI getUri() {
return this.uri;
}
/**
* Indicates whether this blob is a snapshot.
*
* @return true
if the blob is a snapshot, otherwise false
.
*
* @see DeleteSnapshotsOption
*/
public final boolean isSnapshot() {
return this.snapshotID != null;
}
/**
* Opens a blob input stream to download the blob.
*
* Use {@link CloudBlobClient#setStreamMinimumReadSizeInBytes} to configure the read size.
*
* @return An InputStream
object that represents the stream to use for reading from the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final BlobInputStream openInputStream() throws StorageException {
return this.openInputStream(null, null, null);
}
/**
* Opens a blob input stream to download the blob using the specified request options and operation context.
*
* Use {@link CloudBlobClient#setStreamMinimumReadSizeInBytes} to configure the read size.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 An InputStream
object that represents the stream to use for reading from the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final BlobInputStream openInputStream(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
assertNoWriteOperationForSnapshot();
options.applyDefaults(this.blobServiceClient);
return new BlobInputStream(this, accessCondition, options, opContext);
}
/**
* Parse Uri for SAS (Shared access signature) information.
*
* Validate that no other query parameters are passed in. Any SAS information will be recorded as corresponding
* credentials instance. If existingClient is passed in, any SAS information found will not be supported. Otherwise
* a new client is created based on SAS information or as anonymous credentials.
*
* @param completeUri
* The complete Uri.
* @param existingClient
* The client to use.
* @param usePathStyleUris
* If true, path style Uris are used.
* @throws StorageException
* If a storage service error occurred.
* */
protected void parseURIQueryStringAndVerify(final URI completeUri, final CloudBlobClient existingClient,
final boolean usePathStyleUris) throws StorageException {
Utility.assertNotNull("resourceUri", completeUri);
if (!completeUri.isAbsolute()) {
final String errorMessage = String.format(
"Address '%s' is not an absolute address. Relative addresses are not permitted in here.",
completeUri.toString());
throw new IllegalArgumentException(errorMessage);
}
this.uri = PathUtility.stripURIQueryAndFragment(completeUri);
final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery());
final StorageCredentialsSharedAccessSignature sasCreds = SharedAccessSignatureHelper
.parseQuery(queryParameters);
final String[] snapshotIDs = queryParameters.get(BlobConstants.SNAPSHOT);
if (snapshotIDs != null && snapshotIDs.length > 0) {
this.snapshotID = snapshotIDs[0];
}
if (sasCreds == null) {
return;
}
final Boolean sameCredentials = existingClient == null ? false : Utility.areCredentialsEqual(sasCreds,
existingClient.getCredentials());
if (existingClient == null || !sameCredentials) {
try {
this.blobServiceClient = new CloudBlobClient(new URI(PathUtility.getServiceClientBaseAddress(
this.getUri(), usePathStyleUris)), sasCreds);
}
catch (final URISyntaxException e) {
throw Utility.generateNewUnexpectedStorageException(e);
}
}
if (existingClient != null && !sameCredentials) {
this.blobServiceClient
.setPageBlobStreamWriteSizeInBytes(existingClient.getPageBlobStreamWriteSizeInBytes());
this.blobServiceClient.setSingleBlobPutThresholdInBytes(existingClient.getSingleBlobPutThresholdInBytes());
this.blobServiceClient.setStreamMinimumReadSizeInBytes(existingClient.getStreamMinimumReadSizeInBytes());
this.blobServiceClient.setWriteBlockSizeInBytes(existingClient.getWriteBlockSizeInBytes());
this.blobServiceClient.setConcurrentRequestCount(existingClient.getConcurrentRequestCount());
this.blobServiceClient.setDirectoryDelimiter(existingClient.getDirectoryDelimiter());
this.blobServiceClient.setRetryPolicyFactory(existingClient.getRetryPolicyFactory());
this.blobServiceClient.setTimeoutInMs(existingClient.getTimeoutInMs());
}
}
void updateEtagAndLastModifiedFromResponse(HttpURLConnection request) {
String tempStr = request.getHeaderField(Constants.HeaderConstants.ETAG);
// ETag
if (!Utility.isNullOrEmpty(tempStr)) {
this.getProperties().setEtag(tempStr);
}
// Last Modified
if (0 != request.getLastModified()) {
final Calendar lastModifiedCalendar = Calendar.getInstance(Utility.LOCALE_US);
lastModifiedCalendar.setTimeZone(Utility.UTC_ZONE);
lastModifiedCalendar.setTime(new Date(request.getLastModified()));
this.getProperties().setLastModified(lastModifiedCalendar.getTime());
}
}
void updateLengthFromResponse(HttpURLConnection request) {
final String xContentLengthHeader = request.getHeaderField(BlobConstants.CONTENT_LENGTH_HEADER);
if (!Utility.isNullOrEmpty(xContentLengthHeader)) {
this.getProperties().setLength(Long.parseLong(xContentLengthHeader));
}
}
/**
* Releases the lease on the blob.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob. The LeaseID is
* required to be set on the AccessCondition.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void releaseLease(final AccessCondition accessCondition) throws StorageException {
this.releaseLease(accessCondition, null, null);
}
/**
* Releases the lease on the blob using the specified request options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.The LeaseID is
* required to be set on the AccessCondition.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void releaseLease(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
Utility.assertNotNull("accessCondition", accessCondition);
Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.RELEASE, null, null, null,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
blob.properties.setLeaseStatus(LeaseStatus.UNLOCKED);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Renews an existing lease.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob. The LeaseID is
* required to be set on the AccessCondition.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void renewLease(final AccessCondition accessCondition) throws StorageException {
this.renewLease(accessCondition, null, null);
}
/**
* Renews an existing lease using the specified request options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob. The LeaseID is
* required to be set on the AccessCondition.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void renewLease(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
Utility.assertNotNull("accessCondition", accessCondition);
Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.RENEW, null, null, null,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Changes the existing lease ID to the proposed lease ID.
*
* @param proposedLeaseId
* A String
that represents the proposed lease ID for the new lease,
* or null if no lease ID is proposed.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
* required to be set with an access condition.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void changeLease(final String proposedLeaseId, final AccessCondition accessCondition)
throws StorageException {
this.changeLease(proposedLeaseId, accessCondition, null, null);
}
/**
* Changes the existing lease ID to the proposed lease Id with the specified access conditions, request options,
* and operation context.
*
* @param proposedLeaseId
* A String
that represents the proposed lease ID for the new lease,
* or null if no lease ID is proposed.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
* required to be set with an access condition.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client
* ({@link CloudBlobClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. The context
* 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 final void changeLease(final String proposedLeaseId, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException {
Utility.assertNotNull("accessCondition", accessCondition);
Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.CHANGE, null, proposedLeaseId, null,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Sets the container for the blob.
*
* @param container
* A {@link CloudBlobContainer} object that represents the container being assigned to the blob.
*/
protected final void setContainer(final CloudBlobContainer container) {
this.container = container;
}
/**
* Sets the metadata for the blob.
*
* @param metadata
* A java.util.HashMap
object that contains the metadata being assigned to the blob.
*/
public final void setMetadata(final HashMap metadata) {
this.metadata = metadata;
}
/**
* Sets the properties for the blob.
*
* @param properties
* A {@link BlobProperties} object that represents the properties being assigned to the blob.
*/
protected final void setProperties(final BlobProperties properties) {
this.properties = properties;
}
/**
* Reserved for internal use.
*
* @param copyState
* the copyState to set
*/
public void setCopyState(final CopyState copyState) {
this.copyState = copyState;
}
/**
* Sets the blob snapshot ID.
*
* @param snapshotID
* A String
that represents the snapshot ID being assigned to the blob.
*/
public final void setSnapshotID(final String snapshotID) {
this.snapshotID = snapshotID;
}
/**
* Attempts to break the lease and ensure that another client cannot acquire a new lease until the current lease
* period has expired.
*
* @return Time, in seconds, remaining in the lease period, or -1 if the lease is already broken.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final long tryBreakLease() throws StorageException {
return this.tryBreakLease(null, null, null);
}
/**
* Attempts to breaks the lease using the specified request options and operation context, and ensure that another
* client cannot acquire a new lease until the current lease period has expired.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 Time, in seconds, remaining in the lease period, -1 if the lease is already broken.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final long tryBreakLease(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Long execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.lease(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), LeaseAction.BREAK, null, null, null,
accessCondition, blobOptions, opContext);
this.setConnection(request);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_CONFLICT) {
final StorageException potentialConflictException = StorageException.translateException(request,
null, opContext);
StorageExtendedErrorInformation extendedInfo = potentialConflictException
.getExtendedErrorInformation();
if (extendedInfo == null) {
// If we cant validate the error then the error must be surfaced to the user.
throw potentialConflictException;
}
if (!extendedInfo.getErrorCode().equals(StorageErrorCodeStrings.LEASE_ALREADY_BROKEN)) {
this.setException(potentialConflictException);
this.setNonExceptionedRetryableFailure(true);
}
return -1L;
}
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return -1L;
}
blob.updateEtagAndLastModifiedFromResponse(request);
final String leaseTime = BlobResponse.getLeaseTime(request, opContext);
return Utility.isNullOrEmpty(leaseTime) ? -1L : Long.parseLong(leaseTime);
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Uploads the source stream data to the blob.
*
* @param sourceStream
* An InputStream
object that represents the source stream to upload.
*
* @param length
* The length of the stream data in bytes, or -1 if unknown. The length must be greater than zero and a
* multiple of 512 for page blobs.
*
* @throws IOException
* If an I/O exception occurred.
*
* @throws StorageException
* If a storage service error occurred.
*
*/
@DoesServiceRequest
public abstract void upload(InputStream sourceStream, long length) throws StorageException, IOException;
/**
* Uploads the source stream data to the blob using the specified lease ID, request options, and operation context.
*
* @param sourceStream
* An InputStream
object that represents the source stream to upload.
* @param length
* The length of the stream data in bytes, or -1 if unknown. The length must be greater than zero and a
* multiple of 512 for page blobs.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public abstract void upload(InputStream sourceStream, long length, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException;
/**
* Uploads a blob in a single operation.
*
* @param sourceStream
* A InputStream
object that represents the source stream to upload.
* @param length
* The length, in bytes, of the stream, or -1 if unknown.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
protected final void uploadFullBlob(final InputStream sourceStream, final long length,
final AccessCondition accessCondition, final BlobRequestOptions options, final OperationContext opContext)
throws StorageException, IOException {
assertNoWriteOperationForSnapshot();
// Mark sourceStream for current position.
sourceStream.mark(Constants.MAX_MARK_LENGTH);
if (length < 0 || length > BlobConstants.MAX_SINGLE_UPLOAD_BLOB_SIZE_IN_BYTES) {
throw new IllegalArgumentException(String.format(
"Invalid stream length; stream must be between 0 and %s MB in length.",
BlobConstants.MAX_SINGLE_UPLOAD_BLOB_SIZE_IN_BYTES / Constants.MB));
}
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.put(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.properties, blob.properties.getBlobType(),
0, accessCondition, blobOptions, opContext);
this.setConnection(request);
BlobRequest.addMetadata(request, blob.metadata, opContext);
this.signRequest(client, request, length, null);
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(sourceStream,
request.getOutputStream(), length, true, false, null, opContext);
if (length != descriptor.getLength()) {
throw new StorageException(
StorageErrorCodeStrings.INVALID_INPUT,
"An incorrect stream length was specified, resulting in an authentication failure. Please specify correct length, or -1.",
HttpURLConnection.HTTP_FORBIDDEN, null, null);
}
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Uploads the blob's metadata to the storage service.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void uploadMetadata() throws StorageException {
this.uploadMetadata(null, null, null);
}
/**
* Uploads the blob's metadata to the storage service using the specified lease ID, request options, and operation
* context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void uploadMetadata(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
assertNoWriteOperationForSnapshot();
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.setMetadata(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), accessCondition, blobOptions, opContext);
this.setConnection(request);
BlobRequest.addMetadata(request, blob.metadata, opContext);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Updates the blob's properties to the storage service.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void uploadProperties() throws StorageException {
this.uploadProperties(null, null, null);
}
/**
* Updates the blob's properties using the specified lease ID, request options, and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the blob.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudBlobClient}).
* @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 final void uploadProperties(final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
assertNoWriteOperationForSnapshot();
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
opContext.initialize();
options.applyDefaults(this.blobServiceClient);
final StorageOperation impl = new StorageOperation(
options) {
@Override
public Void execute(final CloudBlobClient client, final CloudBlob blob, final OperationContext opContext)
throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.setProperties(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.properties, null, accessCondition,
blobOptions, opContext);
this.setConnection(request);
BlobRequest.addMetadata(request, blob.metadata, opContext);
this.signRequest(client, request, 0L, null);
ExecutionEngine.processRequest(request, opContext, this.getResult());
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Asserts that write operation is not done for snapshot.
*/
protected void assertNoWriteOperationForSnapshot() {
if (isSnapshot()) {
throw new IllegalArgumentException("Cannot perform this operation on a blob representing a snapshot.");
}
}
}