/// <summary> /// Remove a message from a Q after it has been processed, with success or failure /// message contains the owning Q to go to /// </summary> /// <param name="message"></param> /// <returns></returns> public bool RemoveFromQ(IFatpipeMessage message) { if (message == null) { throw new ArgumentNullException("message"); } bool result = true; try { this.fpmDal.RemoveMessageFromQueue(message); } catch (Exception exception) { LoggerFactory.Logger.Error("FatpipeManager.RemoveFromQueue", EventId.BizpipeRemoveFromQueue, "Failed to delete message (Id: {0}, PopReceipt: {1}) from queue {2}. Error: {3}", message.Header.QueueMessageId, message.Header.QueueMessagePopReceipt, message.Header.QueueName, exception.ToString()); result = false; } return(result); }
public bool SaveSuspendedMessage(IFatpipeMessage message) { string location = "FpmDal->SaveSuspendedMessage"; bool result = true; SuspendedMessageEntity suspendedMessage = null; //TODO remove the return statement // don't want any message to be suspended return(true); try { Stopwatch watch = Stopwatch.StartNew(); suspendedMessage = new SuspendedMessageEntity(message.Header.TenantIdentifier, message.Header.Identifier); suspendedMessage.Timestamp = DateTime.UtcNow; suspendedMessage.SuspendedMessageBlobReference = message.Header.Identifier; if (message.Status != null) { suspendedMessage.ErrorMessage = string.Format(CultureInfo.InvariantCulture, "{0}. Message suspended after {1} retries. Error: {2}", message.Status.ProcessingResult, message.Status.NumberOfRetryAttempts, message.Status.ErrorDescription); } IOutboundFatpipeMessage outboundMessage = message as IOutboundFatpipeMessage; if (outboundMessage != null) { if (outboundMessage.RoutingInfo != null) { suspendedMessage.PartnerIdentifier = outboundMessage.RoutingInfo.PartnerId; } } this.suspendedTableServiceContext.AddObject(MaargConfiguration.Instance[MaargConfiguration.SuspendedMessageTableName], suspendedMessage); //save to suspended table and blob DataServiceResponse response = this.suspendedTableServiceContext.SaveChangesWithRetries(); // The entry in the Q is the message blob name using (Stream stream = BizpipeMesssageStreaming.WriteBizpipeMessageToStream(message)) { CloudBlob blob = this.suspendedContainer.GetBlobReference(suspendedMessage.SuspendedMessageBlobReference); blob.UploadFromStream(stream); } watch.Stop(); LoggerFactory.Logger.Debug(location, "Message {0} saved to suspended queue in {1} ms.", message.Header.Identifier, watch.ElapsedMilliseconds); } catch (Exception exception) { LoggerFactory.Logger.Error(location, EventId.BizpipeSaveSuspendMessage , "Exception encountered during suspended message operation: {0}." , exception.ToString()); result = false; } return(result); }
public IFatpipeMessage GetSuspendedMessage(string messageIdentifier) { string location = "FpmDal->GetSuspendedMessage"; IFatpipeMessage result = null; SuspendedMessageEntity suspendedMessage = null; try { Stopwatch watch = Stopwatch.StartNew(); suspendedMessage = (from e in this.suspendedTableServiceContext.CreateQuery <SuspendedMessageEntity>(MaargConfiguration.Instance[MaargConfiguration.SuspendedMessageTableName]) where e.RowKey == messageIdentifier select e).FirstOrDefault(); if (suspendedMessage != null) { IFatpipeMessage message = null; CloudBlob blob = this.suspendedContainer.GetBlobReference(suspendedMessage.SuspendedMessageBlobReference); using (MemoryStream stream = new MemoryStream()) { blob.DownloadToStream(stream); stream.Position = 0; message = BizpipeMesssageStreaming.CreateBizpipeMessageFromStream(stream, this.fpm); } if (!string.IsNullOrEmpty(suspendedMessage.PartnerIdentifier)) { IOutboundFatpipeMessage outboundMessage = this.fpm.CreateNewOutboundMessage(); outboundMessage.Header = message.Header; outboundMessage.Body = message.Body; outboundMessage.Status = message.Status; outboundMessage.RoutingInfo = new RoutingInfo(); outboundMessage.RoutingInfo.PartnerId = suspendedMessage.PartnerIdentifier; outboundMessage.RoutingInfo.TransportType = TransportType.Suspend; result = outboundMessage; } else { result = message; } } watch.Stop(); LoggerFactory.Logger.Debug(location, "Message {0} retrieved from suspended queue in {1} ms.", messageIdentifier, watch.ElapsedMilliseconds); } catch (Exception exception) { LoggerFactory.Logger.Error(location, EventId.BizpipeGetSuspendMessage , "Error getting the suspended message {0}: {1}." , messageIdentifier , exception.ToString()); } return(result); }
/// <summary> /// Used by OutboundTransportManager /// /// 1. DeQ bunch of messages from OutboundQueue /// 2. Transport each of them /// 3. RemoveFromQ each of them or suspend them as appropriate /// </summary> /// <param name="count">Number of messages to DeQ</param> /// <returns></returns> public List <IOutboundFatpipeMessage> DeQFromOutgoingQ(int count) { int iter = 0; IFatpipeMessage msg = null; List <IOutboundFatpipeMessage> msgList = new List <IOutboundFatpipeMessage>(); while (iter < count) { msg = fpmDal.Dequeue(Constants.QueueType.OUTBOUND); if (msg == null) { break; } IOutboundFatpipeMessage message = msg as IOutboundFatpipeMessage; string partnerId; bool flag = msg.Header.Context.TryGetValue("PartnerId", out partnerId); if (flag) { RoutingInfo routingInfo = new RoutingInfo(partnerId, TransportType.None); flag = msg.Header.Context.TryGetValue("TransportType", out partnerId); routingInfo.TransportType = (TransportType)Enum.Parse(typeof(TransportType), partnerId, true); message.RoutingInfo = routingInfo; } msgList.Add(message); iter++; } return(msgList); }
public IFatpipeMessage Clone(Stream bodyStream) { IFatpipeMessage msg = this.fpm.CreateNewMessage(); msg.Body.Body = bodyStream; return(msg); }
public IFatpipeMessage Dequeue(Constants.QueueType type) { string location = "FpmDal->Dequeue"; IFatpipeMessage fatpipeMessage = null; bool inbound = (type == Constants.QueueType.INBOUND); CloudQueue queue = inbound ? this.incomingQ : this.outgoingQ; CloudBlobContainer container = inbound ? this.inContainer : this.outContainer; try { Stopwatch watch = Stopwatch.StartNew(); CloudQueueMessage entry = queue.GetMessage(this.visibilityTimeout); if (entry != null) { watch.Stop(); LoggerFactory.Logger.Info(location + (inbound ? "->B2BEngineReadingFromIncomingQ" : "OutboundTransportReadingFromOutgoingQ"), "timeInMs={0}, entry={1}", watch.ElapsedMilliseconds, entry.AsString); byte[] buffer = null; bool isInlineContent = inbound ? GetContent(entry, out buffer) : false; MemoryStream blobStream = null; if (isInlineContent) { blobStream = new MemoryStream(buffer); } else { watch = Stopwatch.StartNew(); string blobName = entry.AsString; CloudBlob blob = container.GetBlobReference(blobName); BlobRequestOptions options = new BlobRequestOptions(); blobStream = new MemoryStream(); blob.DownloadToStream(blobStream); watch.Stop(); LoggerFactory.Logger.Info(location + (inbound ? "->B2BEngineReadingFromIncomingBlob" : "OutboundTransportReadingFromOutgoingBlob"), "timeInMs={0}, blob={1}", watch.ElapsedMilliseconds, blobName); } blobStream.Position = 0; //msg = DecodeBlobPayload(blobStream, this.fpm); fatpipeMessage = BizpipeMesssageStreaming.CreateBizpipeMessageFromStream(blobStream, this.fpm); // set the QueueName and the queue message identifiers fatpipeMessage.Header.QueueName = (inbound == true) ? incomingQ.Name : outgoingQ.Name; fatpipeMessage.Header.QueueMessageId = entry.Id; fatpipeMessage.Header.QueueMessagePopReceipt = entry.PopReceipt; fatpipeMessage.Status.NumberOfRetryAttempts = entry.DequeueCount; } } catch (Exception ex) { LoggerFactory.Logger.Warning(location, EventId.BizpipeDequeueMessage , "Exception encountered during Dequeue operation: {0}", ex.ToString()); } return(fatpipeMessage); }
public bool RetryOrSuspendMessage(IFatpipeMessage message) { if (message.Status.NumberOfRetryAttempts >= this.maximumNumberOfRetryAttempts) { return(this.SuspendMessage(message)); } //Message will become visible again after the preset period of invisibility; no need to add another visibility setting. return(true); }
public bool Dequeue(ref IFatpipeMessage msg) { CloudQueueMessage entry = incomingQ.GetMessage(); string blobName = entry.AsString; CloudBlob blob = container.GetBlobReference(blobName); //blob.DownloadToStream( return(true); }
public void RemoveMessageFromQueue(IFatpipeMessage message) { string location = "FpmDal->RemoveMessageFromQueue"; if (message == null) { throw new ArgumentNullException("message"); } if (!string.IsNullOrEmpty(message.Header.QueueName)) { CloudQueue queue = null; if (message.Header.QueueName == this.incomingQ.Name) { queue = this.incomingQ; } if (message.Header.QueueName == this.outgoingQ.Name) { queue = this.outgoingQ; } if (queue != null) { try { queue.DeleteMessage(message.Header.QueueMessageId, message.Header.QueueMessagePopReceipt); } catch (Exception exception) { LoggerFactory.Logger.Error(location, EventId.BizpipeRemoveMessage , "Error deleting message (Id = {0}, PopReceipt = {1}) from queue {2}: {3}.", message.Header.QueueMessageId, message.Header.QueueMessagePopReceipt, queue.Name, exception.ToString()); throw; } } else { LoggerFactory.Logger.Error(location, EventId.BizpipeStrayMessage , "Queue message is attached to queue {0} which is neither the inbound ({1}) nor the outbound ({2}) queue.", message.Header.QueueName, this.incomingQ.Name, this.outgoingQ.Name); } } }
public bool Enqueue(IFatpipeMessage msg) { // store the entire message in Blob string blobName = msg.Header.Identifier; CloudBlob blob = container.GetBlobReference(blobName); // Only the body is being uploaded for now..change it to upload the entire msg blob.UploadFromStream(msg.Body.Body); CloudQueueMessage entry = new CloudQueueMessage(blobName); incomingQ.AddMessage(entry); return(true); }
public IEnumerable <IFatpipeMessage> ListSuspendedMessages(string tenantIdentifier) { string location = "FpmDal->ListSuspendedMessage"; List <IFatpipeMessage> result = new List <IFatpipeMessage>(); try { Stopwatch watch = Stopwatch.StartNew(); string tableName = MaargConfiguration.Instance[MaargConfiguration.SuspendedMessageTableName]; var suspendedMessages = ( from e in this.suspendedTableServiceContext.CreateQuery <SuspendedMessageEntity>(tableName) where e.PartitionKey == tenantIdentifier select e ).AsTableServiceQuery <SuspendedMessageEntity>(); foreach (var suspendedMessage in suspendedMessages) { IFatpipeMessage message = this.GetSuspendedMessage(suspendedMessage.RowKey); if (message != null) { result.Add(message); } } watch.Stop(); LoggerFactory.Logger.Info(location, "Listed suspended messages for tenant {0} in {1} ms.", tenantIdentifier, watch.ElapsedMilliseconds); } catch (Exception exception) { LoggerFactory.Logger.Error(location, EventId.BizpipeListSuspendMessages , "Error listing suspended messages for tenant {0}: {1}." , tenantIdentifier , exception.ToString()); throw; } return(result); }
/// <summary> /// Used by B2BProtocolEngine, it's a 3 step process. /// /// 1. First acquire lock (this method), so that other workers don't see the message for a period of time /// 2. Process message and write to OutboundQ or SuspendQ /// 3. Remove the message from Q /// /// </summary> /// <param name="count">Number of messages to DeQ</param> /// <returns></returns> public List <IFatpipeMessage> DeQFromIncomingQ(int count) { int iter = 0; IFatpipeMessage msg = null; List <IFatpipeMessage> msgList = new List <IFatpipeMessage>(); while (iter < count) { msg = fpmDal.Dequeue(Constants.QueueType.INBOUND); if (msg == null) { break; } msgList.Add(msg); iter++; } return(msgList); }
/// <summary> /// Has the logic to convert a FatpipeMessage into a Stream object that represents the storage logic /// </summary> /// <param name="msg"></param> /// <returns></returns> public static Stream WriteBizpipeMessageToStream(IFatpipeMessage message) { IList <KeyValuePair <string, string> > headerList = new List <KeyValuePair <string, string> >(); headerList.Add(new KeyValuePair <string, string>(MsgId, message.Header.Identifier)); headerList.Add(new KeyValuePair <string, string>(TenantId, message.Header.TenantIdentifier)); headerList.Add(new KeyValuePair <string, string>(CorrelationId, message.Header.CorrelationId)); if (message.Header.Context != null) { foreach (KeyValuePair <string, string> kvpair in message.Header.Context) { headerList.Add(new KeyValuePair <string, string>(kvpair.Key, kvpair.Value)); } } Stream headerStream = StreamHelper.SerializeHeadersToStream(headerList, true); Stream bodyStream = message.Body.Body; return(new ConcatenatingStream(headerStream, bodyStream, null)); }
public static IFatpipeMessage CreateBizpipeMessageFromStream(Stream stream, IFatpipeManager manager) { IFatpipeMessage message = manager.CreateNewMessage(); IList <KeyValuePair <string, string> > headerList; message.Body.Body = StreamHelper.DeserializeStreamToHeadersAndBody(stream, out headerList); MessageHeader header = new MessageHeader(string.Empty, string.Empty); message.Header = header; IDictionary <string, string> context = header.Context; foreach (KeyValuePair <string, string> keyValuePair in headerList) { string key = keyValuePair.Key; if (string.Compare(key, MsgId, StringComparison.OrdinalIgnoreCase) == 0) { header.Identifier = keyValuePair.Value; } else if (string.Compare(key, TenantId, StringComparison.OrdinalIgnoreCase) == 0) { header.TenantIdentifier = keyValuePair.Value; } else if (string.Compare(key, CorrelationId, StringComparison.OrdinalIgnoreCase) == 0) { header.CorrelationId = keyValuePair.Value; } else { context[keyValuePair.Key] = keyValuePair.Value; } } return(message); }
//Write message reference to permanent store, not Queue public bool SuspendMessage(IFatpipeMessage message) { bool result = true; string location = "FatpipeManager.SuspendMessage"; try { //delete from queue this.fpmDal.RemoveMessageFromQueue(message); //save to suspended store result = this.fpmDal.SaveSuspendedMessage(message); } catch (Exception exception) { LoggerFactory.Logger.Error(location, EventId.BizpipeSuspendMessage, "Failed to suspend message {0}: {1}.", message.Header.Identifier, exception.ToString()); result = false; } return(result); }
public bool Enqueue(IFatpipeMessage msg, Constants.QueueType type) { string location = "FpmDal->Enqueue"; Stopwatch watch; try { // store the entire message in Blob string blobName = msg.Header.Identifier; CloudBlob blob; bool inbound = (type == Constants.QueueType.INBOUND); Stream blobStream = BizpipeMesssageStreaming.WriteBizpipeMessageToStream(msg); // The entry in the Q is the message blob name CloudQueueMessage entry = new CloudQueueMessage(blobName); //SetQueueBodyData(entry, blobStream); if (inbound) { //Measure and write to blob storage watch = Stopwatch.StartNew(); blob = inContainer.GetBlobReference(blobName); blob.UploadFromStream(blobStream); watch.Stop(); LoggerFactory.Logger.Info(location + "->SaveToIncomingBlob", "timeInMs={0} blobName={1}", watch.ElapsedMilliseconds, blobName); //Measure and write to incomingQ watch = Stopwatch.StartNew(); incomingQ.AddMessage(entry); watch.Stop(); LoggerFactory.Logger.Info(location + "->WriteToIncomingQ", "timeInMs={0} entry={1}", watch.ElapsedMilliseconds, blobName); } else { //Measure and write to blob storage watch = Stopwatch.StartNew(); blob = outContainer.GetBlobReference(blobName); blob.UploadFromStream(blobStream); watch.Stop(); LoggerFactory.Logger.Info(location + "->B2BEngineWritingToOutgoingBlob", "timeInMs={0}, blob={1}", watch.ElapsedMilliseconds, blobName); //Measure and write to incomingQ watch = Stopwatch.StartNew(); outgoingQ.AddMessage(entry); watch.Stop(); LoggerFactory.Logger.Info(location + "->B2BEngineWritingToOutgoingQ", "timeInMs={0}, entry={1}", watch.ElapsedMilliseconds, blobName); } // set the QueueName and the queue message identifiers msg.Header.QueueName = (inbound == true) ? incomingQ.Name : outgoingQ.Name; msg.Header.QueueMessageId = entry.Id; msg.Header.QueueMessagePopReceipt = entry.PopReceipt; } catch (Exception ex) { LoggerFactory.Logger.Warning(location, EventId.BizpipeEnqueueMessage , "Exception encountered during Enqueue operation: {0}", ex.ToString()); return(false); } return(true); }
/// <summary> /// This is a very interesting method. It provides optics into what's going on in the system. /// Logic will be specific to agent. /// Refer to CorrelationId in message which stitches multiple messages together /// Eg. EDI Message M1, received, by AS2 /// M1 (correlationId), split into Ms1, Ms2, Ms3, Ms4, by B2BProtocolEngine /// Ms1, sent, by HTTP /// Ms2, sent, by AS2 /// Ms2, suspended, by AS2 /// Common operations are /// messageM1, Received, 4/9/2012, By https://maarg.com, WebRole1, WebRoleId1234, ServerCloudMaarg1 /// /// This is a very interesting subject and implementation can be very intelligent. /// We will start with a simple one though /// </summary> /// <param name="message"></param> /// <param name="agent"></param> /// <returns></returns> public bool WriteBusinessTransactionToStore(IFatpipeMessage message, IMessageHandlerAgent agent) { return(false); }