com.microsoft.windowsazure.services.blob.client.CloudPageBlob 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.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
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.OperationContext;
import com.microsoft.windowsazure.services.core.storage.StorageException;
import com.microsoft.windowsazure.services.core.storage.utils.Base64;
import com.microsoft.windowsazure.services.core.storage.utils.Utility;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;
/**
* Represents a Windows Azure page blob.
*/
public final class CloudPageBlob extends CloudBlob {
/**
* Creates an instance of the CloudPageBlob
class using the specified relative URI and storage service
* client.
*
* @param uri
* A java.net.URI
object that represents the relative URI to the blob, beginning with the
* container name.
*
* @throws StorageException
* If a storage service error occurred.
*/
public CloudPageBlob(final URI uri) throws StorageException {
super(BlobType.PAGE_BLOB);
Utility.assertNotNull("blobAbsoluteUri", uri);
this.uri = uri;
this.parseURIQueryStringAndVerify(uri, null, Utility.determinePathStyleFromUri(uri, null));;
}
/**
* Creates an instance of the CloudPageBlob
class by copying values from another page blob.
*
* @param otherBlob
* A CloudPageBlob
object that represents the page blob to copy.
*
* @throws StorageException
* If a storage service error occurred.
*/
public CloudPageBlob(final CloudPageBlob otherBlob) throws StorageException {
super(otherBlob);
}
/**
* Creates an instance of the CloudPageBlob
class using the specified URI and cloud blob client.
*
* @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.
*
* @throws StorageException
* If a storage service error occurred.
*/
public CloudPageBlob(final URI uri, final CloudBlobClient client) throws StorageException {
super(BlobType.PAGE_BLOB, uri, client);
}
/**
* Creates an instance of the CloudPageBlob
class using the specified URI, cloud blob client, and cloud
* blob container.
*
* @param uri
* A java.net.URI
object that represents the relative 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.
*/
public CloudPageBlob(final URI uri, final CloudBlobClient client, final CloudBlobContainer container)
throws StorageException {
super(BlobType.PAGE_BLOB, uri, client, container);
}
/**
* Creates an instance of the CloudPageBlob
class using the specified URI, snapshot ID, and cloud blob
* client.
*
* @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.
*/
public CloudPageBlob(final URI uri, final String snapshotID, final CloudBlobClient client) throws StorageException {
super(BlobType.PAGE_BLOB, uri, snapshotID, client);
}
/**
* Clears pages from a page blob.
*
* Calling clearPages
releases the storage space used by the specified pages. Pages that have been
* cleared are no longer tracked as part of the page blob, and no longer incur a charge against the storage account.
*
* @param offset
* The offset, in bytes, at which to begin clearing pages. This value must be a multiple of 512.
* @param length
* The length, in bytes, of the data range to be cleared. This value must be a multiple of 512.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public void clearPages(final long offset, final long length) throws StorageException, IOException {
this.clearPages(offset, length, null, null, null);
}
/**
* Clears pages from a page blob using the specified lease ID, request options, and operation context.
*
* Calling clearPages
releases the storage space used by the specified pages. Pages that have been
* cleared are no longer tracked as part of the page blob, and no longer incur a charge against the storage account.
*
* @param offset
* The offset, in bytes, at which to begin clearing pages. This value must be a multiple of 512.
* @param length
* The length, in bytes, of the data range to be cleared. This value must be a multiple of 512.
* @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 void clearPages(final long offset, final long length, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException {
if (offset % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page start offset must be multiple of 512!");
}
if (length % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page data must be multiple of 512!");
}
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
options.applyDefaults(this.blobServiceClient);
final PageProperties pageProps = new PageProperties();
pageProps.setPageOperation(PageOperationType.CLEAR);
pageProps.getRange().setStartOffset(offset);
pageProps.getRange().setEndOffset(offset + length - 1);
this.putPagesInternal(pageProps, null, length, null, accessCondition, options, opContext);
}
/**
* Creates a page blob.
*
* @param length
* The size, in bytes, of the page blob.
*
* @throws IllegalArgumentException
* If the length is not a multiple of 512.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public void create(final long length) throws StorageException {
this.create(length, null, null, null);
}
/**
* Creates a page blob using the specified request options and operation context.
*
* @param length
* The size, in bytes, of the page blob.
* @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 IllegalArgumentException
* If the length is not a multiple of 512.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public void create(final long length, final AccessCondition accessCondition, BlobRequestOptions options,
OperationContext opContext) throws StorageException {
assertNoWriteOperationForSnapshot();
if (length % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page blob length must be multiple of 512.");
}
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.put(blob.getTransformedAddress(opContext), this
.getRequestOptions().getTimeoutIntervalInMs(), blob.properties, BlobType.PAGE_BLOB, length,
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_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
blob.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Returns a collection of page ranges and their starting and ending byte offsets.
*
* The start and end byte offsets for each page range are inclusive.
*
* @return An ArrayList
object that represents the set of page ranges and their starting and ending
* byte offsets.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public ArrayList downloadPageRanges() throws StorageException {
return this.downloadPageRanges(null, null, null);
}
/**
* Returns a collection of page ranges and their starting and ending byte offsets 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 An ArrayList
object that represents the set of page ranges and their starting and ending
* byte offsets.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public ArrayList downloadPageRanges(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 ArrayList execute(final CloudBlobClient client, final CloudBlob blob,
final OperationContext opContext) throws Exception {
final BlobRequestOptions blobOptions = (BlobRequestOptions) this.getRequestOptions();
final HttpURLConnection request = BlobRequest.getPageRanges(blob.getTransformedAddress(opContext),
blobOptions.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;
}
blob.updateEtagAndLastModifiedFromResponse(request);
blob.updateLengthFromResponse(request);
final GetPageRangesResponse response = new GetPageRangesResponse(request.getInputStream());
return response.getPageRanges();
}
};
return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(),
opContext);
}
/**
* Opens an input stream object to write data to the page blob.
*
* @param length
* The length, in bytes, of the stream to create. This value must be a multiple of 512.
*
* @return A {@link BlobOutputStream} object used to write data to the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public BlobOutputStream openOutputStream(final long length) throws StorageException {
return this.openOutputStream(length, null, null, null);
}
/**
* Opens an input stream object to write data to the page blob, using the specified lease ID, request options and
* operation context.
*
* @param length
* The length, in bytes, of the stream to create. This value must be a multiple of 512.
* @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 {@link BlobOutputStream} object used to write data to the blob.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public BlobOutputStream openOutputStream(final long length, 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);
if (options.getStoreBlobContentMD5()) {
throw new IllegalArgumentException("Blob Level MD5 is not supported for PageBlob");
}
return new BlobOutputStream(this, length, accessCondition, options, opContext);
}
/**
* Used for both uploadPages and clearPages.
*
* @param pageProperties
* The page properties.
* @param data
* The data to write.
* @param length
* The number of bytes to write.
* @param md5
* the MD5 hash for the data.
* @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
private void putPagesInternal(final PageProperties pageProperties, final byte[] data, final long length,
final String md5, final AccessCondition accessCondition, final BlobRequestOptions options,
final OperationContext opContext) throws StorageException {
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.putPage(blob.getTransformedAddress(opContext),
blobOptions.getTimeoutIntervalInMs(), pageProperties, accessCondition, blobOptions, opContext);
this.setConnection(request);
if (pageProperties.getPageOperation() == PageOperationType.UPDATE) {
if (blobOptions.getUseTransactionalContentMD5()) {
request.setRequestProperty(Constants.HeaderConstants.CONTENT_MD5, md5);
}
this.signRequest(client, request, length, null);
request.getOutputStream().write(data);
}
else {
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.updateEtagAndLastModifiedFromResponse(request);
return null;
}
};
ExecutionEngine
.executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext);
}
/**
* Uploads the source stream data to the page blob.
*
* @param sourceStream
* An IntputStream
object to read from.
* @param length
* The length, in bytes, of the stream data, must be non zero and a multiple of 512.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@Override
@DoesServiceRequest
public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException {
this.upload(sourceStream, length, null, null, null);
}
/**
* Uploads the source stream data to the page blob using the specified lease ID, request options, and operation
* context.
*
* @param sourceStream
* An IntputStream
object to read from.
* @param length
* The length, in bytes, of the stream data. This must be great than zero and a multiple of 512.
* @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.
*/
@Override
@DoesServiceRequest
public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition,
BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException {
assertNoWriteOperationForSnapshot();
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
options.applyDefaults(this.blobServiceClient);
if (length <= 0 || length % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page data must be multiple of 512!");
}
if (options.getStoreBlobContentMD5()) {
throw new IllegalArgumentException("Blob Level MD5 is not supported for PageBlob");
}
if (sourceStream.markSupported()) {
// Mark sourceStream for current position.
sourceStream.mark(Constants.MAX_MARK_LENGTH);
}
if (length <= 4 * Constants.MB) {
this.create(length, accessCondition, options, opContext);
this.uploadPages(sourceStream, 0, length, accessCondition, options, opContext);
}
else {
final OutputStream streamRef = this.openOutputStream(length, accessCondition, options, opContext);
Utility.writeToOutputStream(sourceStream, streamRef, length, false, false, null, opContext);
streamRef.close();
}
}
/**
* Uploads a range of contiguous pages, up to 4 MB in size, at the specified offset in the page blob.
*
* @param sourceStream
* An InputStream
object that represents the input stream to write to the page blob.
* @param offset
* The offset, in number of bytes, at which to begin writing the data. This value must be a multiple of
* 512.
* @param length
* The length, in bytes, of the data to write. This value must be a multiple of 512.
*
* @throws IllegalArgumentException
* If the offset or length are not multiples of 512, or if the length is greater than 4 MB.
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public void uploadPages(final InputStream sourceStream, final long offset, final long length)
throws StorageException, IOException {
this.uploadPages(sourceStream, offset, length, null, null, null);
}
/**
* Uploads a range of contiguous pages, up to 4 MB in size, at the specified offset in the page blob, using the
* specified lease ID, request options, and operation context.
*
* @param sourceStream
* An InputStream
object that represents the input stream to write to the page blob.
* @param offset
* The offset, in number of bytes, at which to begin writing the data. This value must be a multiple of
* 512.
* @param length
* The length, in bytes, of the data to write. This value must be a multiple of 512.
* @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 IllegalArgumentException
* If the offset or length are not multiples of 512, or if the length is greater than 4 MB.
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public void uploadPages(final InputStream sourceStream, final long offset, final long length,
final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
throws StorageException, IOException {
if (offset % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page start offset must be multiple of 512!");
}
if (length % BlobConstants.PAGE_SIZE != 0) {
throw new IllegalArgumentException("Page data must be multiple of 512!");
}
if (length > 4 * Constants.MB) {
throw new IllegalArgumentException("Max write size is 4MB. Please specify a smaller range.");
}
assertNoWriteOperationForSnapshot();
if (opContext == null) {
opContext = new OperationContext();
}
if (options == null) {
options = new BlobRequestOptions();
}
options.applyDefaults(this.blobServiceClient);
final PageProperties pageProps = new PageProperties();
pageProps.setPageOperation(PageOperationType.UPDATE);
pageProps.getRange().setStartOffset(offset);
pageProps.getRange().setEndOffset(offset + length - 1);
final byte[] data = new byte[(int) length];
String md5 = null;
int count = 0;
long total = 0;
while (total < length) {
count = sourceStream.read(data, 0, (int) Math.min(length - total, Integer.MAX_VALUE));
total += count;
}
if (options.getUseTransactionalContentMD5()) {
try {
final MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(data, 0, data.length);
md5 = Base64.encode(digest.digest());
}
catch (final NoSuchAlgorithmException e) {
// This wont happen, throw fatal.
throw Utility.generateNewUnexpectedStorageException(e);
}
}
this.putPagesInternal(pageProps, data, length, md5, accessCondition, options, opContext);
}
}