/// <summary> /// Deletes the specified blob. /// </summary> /// <param name="containerName">The target blob container name from which the blob will be deleted.</param> /// <param name="blobName">The custom name associated with the blob.</param> /// <returns>True if the blob was deleted, otherwise false.</returns> public bool Delete(string containerName, string blobName) { Guard.ArgumentNotNullOrEmptyString(containerName, "containerName"); Guard.ArgumentNotNullOrEmptyString(blobName, "blobName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(containerName, blobName); try { var container = this.blobStorage.GetContainerReference(CloudUtility.GetSafeContainerName(containerName)); var cloudBlob = container.GetBlobReference(blobName); return(this.retryPolicy.ExecuteAction <bool>(() => { return cloudBlob.DeleteIfExists(); })); } catch (StorageClientException ex) { // If blob doesn't exist, do not re-throw the exception, simply return a null reference. if (!(ex.ErrorCode == StorageErrorCode.ContainerNotFound || ex.ErrorCode == StorageErrorCode.BlobNotFound || ex.ErrorCode == StorageErrorCode.ResourceNotFound)) { throw; } } finally { TraceManager.CloudStorageComponent.TraceOut(callToken); } return(false); }
/// <summary> /// Gets the approximate number of items in the specified queue. /// </summary> /// <param name="queueName">The name of the queue to be queried.</param> /// <returns>The approximate number of items in the queue.</returns> public int GetCount(string queueName) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(queueName); int messageCount = 0; try { var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); messageCount = this.retryPolicy.ExecuteAction <int>(() => { return(queue.RetrieveApproximateMessageCount()); }); return(messageCount); } catch (StorageClientException ex) { if (ex.ErrorCode == StorageErrorCode.ResourceNotFound || ex.ExtendedErrorInformation.ErrorCode == QueueErrorCodeStrings.QueueNotFound) { return(0); } throw; } finally { TraceManager.CloudStorageComponent.TraceOut(callToken, messageCount); } }
/// <summary> /// Deletes the specified blob container. /// </summary> /// <param name="containerName">The name of the blob container to be deleted.</param> /// <returns>True if the container existed and was successfully deleted; otherwise false.</returns> public bool DeleteContainer(string containerName) { Guard.ArgumentNotNullOrEmptyString(containerName, "containerName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(containerName); try { var container = this.blobStorage.GetContainerReference(CloudUtility.GetSafeContainerName(containerName)); return(this.retryPolicy.ExecuteAction <bool>(() => { container.Delete(); return true; })); } catch (StorageClientException ex) { if (ex.ErrorCode == StorageErrorCode.ContainerNotFound || ex.ErrorCode == StorageErrorCode.ResourceNotFound) { return(false); } throw; } finally { TraceManager.CloudStorageComponent.TraceOut(callToken); } }
/// <summary> /// Deletes the specified queue. /// </summary> /// <param name="queueName">The name of the queue to be deleted.</param> /// <returns>True if the queue name has been actually deleted, otherwise False.</returns> public bool DeleteQueue(string queueName) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(queueName); try { // Must delete the associated blob storage in which large messages are persisted. LargeQueueMessageInfo largeMsgInfo = LargeQueueMessageInfo.Create(queueName); this.overflowStorage.DeleteContainer(largeMsgInfo.ContainerName); // Now proceed with deleting the queue. var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); return(this.retryPolicy.ExecuteAction <bool>(() => { if (queue.Exists()) { queue.Delete(); return true; } return false; })); } catch (Exception ex) { TraceManager.CloudStorageComponent.TraceError(ex); return(false); } finally { TraceManager.CloudStorageComponent.TraceOut(callToken); } }
/// <summary> /// Creates a new queue. /// </summary> /// <param name="queueName">The name of a new queue.</param> /// <returns>A flag indicating whether or the queue did not exist and was therefore created.</returns> public bool CreateQueue(string queueName) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(queueName); try { var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); return(this.retryPolicy.ExecuteAction <bool>(() => { return queue.CreateIfNotExist(); })); } catch (StorageClientException ex) { if (ex.ErrorCode == StorageErrorCode.ContainerAlreadyExists || ex.ErrorCode == StorageErrorCode.ResourceAlreadyExists) { return(false); } throw; } finally { TraceManager.CloudStorageComponent.TraceOut(callToken); } }
/// <summary> /// Puts a single message on a queue. /// </summary> /// <typeparam name="T">The type of the payload associated with the message.</typeparam> /// <param name="queueName">The target queue name on which message will be placed.</param> /// <param name="message">The payload to be put into a queue.</param> public void Put <T>(string queueName, T message) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); Guard.ArgumentNotNull(message, "message"); // Obtain a reference to the queue by its name. The name will be validated against compliance with storage resource names. var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); CloudQueueMessage queueMessage = null; // Allocate a memory buffer into which messages will be serialized prior to being put on a queue. using (MemoryStream dataStream = new MemoryStream(Convert.ToInt32(CloudQueueMessage.MaxMessageSize))) { // Perform serialization of the message data into the target memory buffer. this.dataSerializer.Serialize(message, dataStream); // Reset the position in the buffer as we will be reading its content from the beginning. dataStream.Seek(0, SeekOrigin.Begin); // First, determine whether the specified message can be accommodated on a queue. if (CloudUtility.IsAllowedQueueMessageSize(dataStream.Length)) { queueMessage = new CloudQueueMessage(dataStream.ToArray()); } else { // Create an instance of a large queue item metadata message. LargeQueueMessageInfo queueMsgInfo = LargeQueueMessageInfo.Create(queueName); // Persist the stream of data that represents a large message into the overflow message store. this.overflowStorage.Put <Stream>(queueMsgInfo.ContainerName, queueMsgInfo.BlobReference, dataStream); // Invoke the Put operation recursively to enqueue the metadata message. Put <LargeQueueMessageInfo>(queueName, queueMsgInfo); } } // Check if a message is available to be put on a queue. if (queueMessage != null) { Put(queue, queueMessage); } }
/// <summary> /// Clears all messages from the specified queue. /// </summary> /// <param name="queueName">The name of the queue that will be emptied.</param> public void Clear(string queueName) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(queueName); try { var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); this.retryPolicy.ExecuteAction(() => { if (queue.Exists()) { queue.Clear(); } }); } finally { TraceManager.CloudStorageComponent.TraceOut(callToken); } }
/// <summary> /// Gets the number of blobs in the specified container. /// </summary> /// <param name="containerName">The name of the blob container to be queried.</param> /// <returns>The number of blobs in the container.</returns> public int GetCount(string containerName) { Guard.ArgumentNotNullOrEmptyString(containerName, "containerName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(containerName); int blobCount = 0; try { var container = this.blobStorage.GetContainerReference(CloudUtility.GetSafeContainerName(containerName)); blobCount = this.retryPolicy.ExecuteAction <int>(() => { var blobList = container.ListBlobs(new BlobRequestOptions() { BlobListingDetails = BlobListingDetails.None, RetryPolicy = RetryPolicies.NoRetry(), UseFlatBlobListing = true }); return(blobList != null ? blobList.Count() : 0); }); return(blobCount); } catch (StorageClientException ex) { // If blob doesn't exist, do not re-throw the exception, simply return a null reference. if (!(ex.ErrorCode == StorageErrorCode.ContainerNotFound || ex.ErrorCode == StorageErrorCode.BlobNotFound || ex.ErrorCode == StorageErrorCode.ResourceNotFound)) { throw; } return(0); } finally { TraceManager.CloudStorageComponent.TraceOut(callToken, blobCount); } }
private KeyValuePair <BlobProperties, T> GetBlob <T>(string containerName, string blobName) { Guard.ArgumentNotNullOrEmptyString(containerName, "containerName"); Guard.ArgumentNotNullOrEmptyString(blobName, "blobName"); var callToken = TraceManager.CloudStorageComponent.TraceIn(containerName, blobName); MemoryStream blobData = null; object blobObject = null; bool isBlobAsStream = IsStreamType(typeof(T)); try { var container = this.blobStorage.GetContainerReference(CloudUtility.GetSafeContainerName(containerName)); var cloudBlob = container.GetBlobReference(blobName); blobData = new MemoryStream(); this.retryPolicy.ExecuteAction(() => { blobData.Seek(0, SeekOrigin.Begin); cloudBlob.DownloadToStream(blobData); }); blobData.Seek(0, SeekOrigin.Begin); // If blob type is a stream, just return the memory stream. if (isBlobAsStream) { blobObject = blobData; } else { // Otherwise, deserialize the underlying object from the blob data. blobObject = this.dataSerializer.Deserialize(blobData, typeof(T)); } return(new KeyValuePair <BlobProperties, T>(cloudBlob.Properties, (T)blobObject)); } catch (StorageClientException ex) { // If blob doesn't exist, do not re-throw the exception, simply return a null reference. if (!(ex.ErrorCode == StorageErrorCode.ContainerNotFound || ex.ErrorCode == StorageErrorCode.BlobNotFound || ex.ErrorCode == StorageErrorCode.ResourceNotFound)) { throw; } } finally { // Should dispose the working stream unless it must be returned to the caller. if (blobData != null && !isBlobAsStream) { blobData.Dispose(); } TraceManager.CloudStorageComponent.TraceOut(callToken); } // Always return a non-null name/value pair, however do return a null reference to both the blob and its properties. return(new KeyValuePair <BlobProperties, T>()); }
/// <summary> /// Puts a blob into the underlying storage and returns its eTag value. /// If the blob with the same name already exists, overwrite behavior can be applied. /// </summary> /// <typeparam name="T">The type of the payload associated with the blob.</typeparam> /// <param name="containerName">The target blob container name into which a blob will be stored.</param> /// <param name="blobName">The custom name associated with the blob.</param> /// <param name="blob">The blob's payload.</param> /// <param name="overwrite">The flag indicating whether or not overwriting the existing blob is permitted.</param> /// <param name="expectedEtag">The parameters holding the blob's expected eTag value.</param> /// <param name="actualEtag">The output parameters holding the blob's actual eTag value.</param> /// <returns>True if the blob was successfully put into the specified container, otherwise false.</returns> private bool Put <T>(string containerName, string blobName, T blob, bool overwrite, string expectedEtag, out string actualEtag) { Guard.ArgumentNotNullOrEmptyString(containerName, "containerName"); Guard.ArgumentNotNullOrEmptyString(blobName, "blobName"); Guard.ArgumentNotNull(blob, "blob"); var callToken = TraceManager.CloudStorageComponent.TraceIn(containerName, blobName, overwrite, expectedEtag); // Verify whether or not the specified blob is already of type Stream. Stream blobStream = IsStreamType(blob.GetType()) ? blob as Stream : null; Stream blobData = null; actualEtag = null; try { // Are we dealing with a stream already? If yes, just use it as is. if (blobStream != null) { blobData = blobStream; } else { // The specified blob is something else rather than a Stream, we perform serialization of T into a new stream instance. blobData = new MemoryStream(); this.dataSerializer.Serialize(blob, blobData); } var container = this.blobStorage.GetContainerReference(CloudUtility.GetSafeContainerName(containerName)); StorageErrorCode lastErrorCode = StorageErrorCode.None; Func <string> uploadAction = () => { var cloudBlob = container.GetBlobReference(blobName); return(UploadBlob(cloudBlob, blobData, overwrite, expectedEtag)); }; try { // First attempt - perform upload and let the UploadBlob method handle any retry conditions. string eTag = uploadAction(); if (!String.IsNullOrEmpty(eTag)) { actualEtag = eTag; return(true); } } catch (StorageClientException ex) { lastErrorCode = ex.ErrorCode; if (!(lastErrorCode == StorageErrorCode.ContainerNotFound || lastErrorCode == StorageErrorCode.ResourceNotFound || lastErrorCode == StorageErrorCode.BlobAlreadyExists)) { // Anything other than "not found" or "already exists" conditions will be considered as a runtime error. throw; } } if (lastErrorCode == StorageErrorCode.ContainerNotFound) { // Failover action #1: create the target container and try again. This time, use a retry policy to wrap calls to the UploadBlob method. string eTag = this.retryPolicy.ExecuteAction <string>(() => { CreateContainer(containerName); return(uploadAction()); }); return(!String.IsNullOrEmpty(actualEtag = eTag)); } if (lastErrorCode == StorageErrorCode.BlobAlreadyExists && overwrite) { // Failover action #2: Overwrite was requested but BlobAlreadyExists has still been returned. Delete the original blob and try to upload again. string eTag = this.retryPolicy.ExecuteAction <string>(() => { var cloudBlob = container.GetBlobReference(blobName); cloudBlob.DeleteIfExists(); return(uploadAction()); }); return(!String.IsNullOrEmpty(actualEtag = eTag)); } } finally { // Only dispose the blob data stream if it was newly created. if (blobData != null && null == blobStream) { blobData.Dispose(); } TraceManager.CloudStorageComponent.TraceOut(callToken, actualEtag); } return(false); }
/// <summary> /// Gets a collection of messages from the specified queue and applies the specified visibility timeout. /// </summary> /// <typeparam name="T">The type of the payload associated with the message.</typeparam> /// <param name="queueName">The name of the source queue.</param> /// <param name="count">The number of messages to retrieve.</param> /// <param name="visibilityTimeout">The timeout during which retrieved messages will remain invisible on the queue.</param> /// <returns>The list of messages retrieved from the specified queue.</returns> public IEnumerable <T> Get <T>(string queueName, int count, TimeSpan visibilityTimeout) { Guard.ArgumentNotNullOrEmptyString(queueName, "queueName"); Guard.ArgumentNotZeroOrNegativeValue(count, "count"); try { var queue = this.queueStorage.GetQueueReference(CloudUtility.GetSafeContainerName(queueName)); IEnumerable <CloudQueueMessage> queueMessages = this.retryPolicy.ExecuteAction <IEnumerable <CloudQueueMessage> >(() => { return(queue.GetMessages(Math.Min(count, CloudQueueMessage.MaxNumberOfMessagesToPeek), visibilityTimeout)); }); if (queueMessages != null) { Type queueMsgType = typeof(T); var messages = new List <T>(count); foreach (var queueMsg in queueMessages) { object msgObject = DeserializeQueueMessage(queueMsg, queueMsgType); // Construct an in-flight message object describing the queued message. InflightMessageInfo inflightMessage = new InflightMessageInfo() { QueueName = queue.Name }; // Register the queued message so that it can be located later on. inflightMessage.QueueMessages.Add(queueMsg); if (msgObject is LargeQueueMessageInfo) { // Looks like we are dealing a large message that needs to be fetched from blob storage. inflightMessage.LargeMessageMetadata = msgObject as LargeQueueMessageInfo; // Attempt to retrieve the actual message data from the blob storage. T msgData = this.overflowStorage.Get <T>(inflightMessage.LargeMessageMetadata.ContainerName, inflightMessage.LargeMessageMetadata.BlobReference); // If blob was found, add it to the list of messages being fetched and returned. if (msgData != null) { messages.Add(msgData); msgObject = msgData; } else { // We should skip the current message as it's data has not been located. continue; } } else { messages.Add((T)msgObject); } // Update the in-flight collection, add a new item if necessary, otherwise perform a thread-safe update. this.inflightMessages.AddOrUpdate(msgObject, inflightMessage, (key, oldValue) => { oldValue.QueueMessages.Clear(); return(inflightMessage); }); } return(messages); } } catch (Exception ex) { // We intend to never fail when fetching messages from a queue. We will still need to report on a exception and return an empty list. TraceManager.CloudStorageComponent.TraceError(ex); } // By default, return an empty collection of messages if the above code path didn't return a result. return(new T[0]); }