/src/Rebus.Snoop/Listeners/MsmqInteraction.cs

https://github.com/asgerhallas/Rebus · C# · 256 lines · 223 code · 33 blank · 0 comment · 5 complexity · 737d2bb431507d21e4ae67cf0abf55e5 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Messaging;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using GalaSoft.MvvmLight.Messaging;
  9. using Newtonsoft.Json;
  10. using Rebus.Shared;
  11. using Rebus.Snoop.Events;
  12. using Rebus.Snoop.ViewModel.Models;
  13. using Message = Rebus.Snoop.ViewModel.Models.Message;
  14. namespace Rebus.Snoop.Listeners
  15. {
  16. public class MsmqInteraction
  17. {
  18. public MsmqInteraction()
  19. {
  20. Messenger.Default.Register(this, (MachineAdded newMachineCreated) => LoadQueues(newMachineCreated.Machine));
  21. Messenger.Default.Register(this, (ReloadQueuesRequested request) => LoadQueues(request.Machine));
  22. Messenger.Default.Register(this, (ReloadMessagesRequested request) => LoadMessages(request.Queue));
  23. Messenger.Default.Register(this, (MoveMessagesToSourceQueueRequested request) => MoveMessagesToSourceQueues(request.MessagesToMove));
  24. }
  25. void MoveMessagesToSourceQueues(IEnumerable<Message> messagesToMove)
  26. {
  27. Task.Factory
  28. .StartNew(() =>
  29. {
  30. var canBeMoved = messagesToMove
  31. .Where(m => m.Headers.ContainsKey(Headers.SourceQueue));
  32. var result = new
  33. {
  34. Moved = new List<Message>(),
  35. Failed = new List<Tuple<Message, string>>(),
  36. };
  37. foreach (var message in canBeMoved)
  38. {
  39. try
  40. {
  41. MoveMessage(message);
  42. result.Moved.Add(message);
  43. }
  44. catch (Exception e)
  45. {
  46. result.Failed.Add(new Tuple<Message, string>(message, e.ToString()));
  47. }
  48. }
  49. return result;
  50. })
  51. .ContinueWith(t =>
  52. {
  53. var result = t.Result;
  54. if (result.Failed.Any())
  55. {
  56. var details = string.Join(Environment.NewLine, result.Failed.Select(f => string.Format("Id {0}: {1}", f.Item1.Id, f.Item2)));
  57. return NotificationEvent.Fail(details, "{0} messages moved - {1} move operations failed", result.Moved.Count, result.Failed.Count);
  58. }
  59. return NotificationEvent.Success("{0} messages moved", result.Moved.Count);
  60. })
  61. .ContinueWith(t => Messenger.Default.Send(t.Result), Context.UiThread);
  62. }
  63. void MoveMessage(Message message)
  64. {
  65. var sourceQueuePath = message.QueuePath;
  66. var destinationQueuePath = MsmqUtil.GetFullPath(message.Headers[Headers.SourceQueue]);
  67. using (var transaction = new MessageQueueTransaction())
  68. {
  69. transaction.Begin();
  70. try
  71. {
  72. var sourceQueue = new MessageQueue(sourceQueuePath) { MessageReadPropertyFilter = DefaultFilter() };
  73. var destinationQueue = new MessageQueue(destinationQueuePath);
  74. var msmqMessage = sourceQueue.ReceiveById(message.Id, transaction);
  75. destinationQueue.Send(msmqMessage, transaction);
  76. transaction.Commit();
  77. }
  78. catch
  79. {
  80. transaction.Abort();
  81. throw;
  82. }
  83. }
  84. Messenger.Default.Send(new MessageMoved(message, sourceQueuePath, destinationQueuePath));
  85. }
  86. void LoadMessages(Queue queue)
  87. {
  88. Task.Factory
  89. .StartNew(() =>
  90. {
  91. var messageQueue = new MessageQueue(queue.QueuePath);
  92. messageQueue.MessageReadPropertyFilter = DefaultFilter();
  93. var list = new List<Message>();
  94. using (var enumerator = messageQueue.GetMessageEnumerator2())
  95. {
  96. while (enumerator.MoveNext())
  97. {
  98. var message = enumerator.Current;
  99. list.Add(GenerateMessage(message, queue.QueuePath));
  100. }
  101. }
  102. return new { Messages = list };
  103. })
  104. .ContinueWith(t =>
  105. {
  106. if (!t.IsFaulted)
  107. {
  108. var result = t.Result;
  109. queue.SetMessages(result.Messages);
  110. return NotificationEvent.Success("{0} messages loaded from {1}",
  111. result.Messages.Count,
  112. queue.QueueName);
  113. }
  114. var details = t.Exception.ToString();
  115. return NotificationEvent.Fail(details, "Could not load messages from {0}: {1}",
  116. queue.QueueName,
  117. t.Exception);
  118. }, Context.UiThread)
  119. .ContinueWith(t => Messenger.Default.Send(t.Result), Context.UiThread);
  120. }
  121. static MessagePropertyFilter DefaultFilter()
  122. {
  123. return new MessagePropertyFilter
  124. {
  125. Label = true,
  126. ArrivedTime = true,
  127. Extension = true,
  128. Body = true,
  129. Id = true,
  130. };
  131. }
  132. Message GenerateMessage(System.Messaging.Message message, string queuePath)
  133. {
  134. try
  135. {
  136. var headers = TryDeserializeHeaders(message);
  137. return new Message
  138. {
  139. Label = message.Label,
  140. Time = message.ArrivedTime,
  141. Headers = headers,
  142. Bytes = TryDetermineMessageSize(message),
  143. Body = TryDecodeBody(message, headers),
  144. Id = message.Id,
  145. QueuePath = queuePath,
  146. };
  147. }
  148. catch (Exception e)
  149. {
  150. return new Message
  151. {
  152. Body = string.Format(@"Message could not be properly decoded:
  153. {0}", e)
  154. };
  155. }
  156. }
  157. int TryDetermineMessageSize(System.Messaging.Message message)
  158. {
  159. try
  160. {
  161. return (int)message.BodyStream.Length;
  162. }
  163. catch (Exception e)
  164. {
  165. return -1;
  166. }
  167. }
  168. string TryDecodeBody(System.Messaging.Message message, Dictionary<string, string> headers)
  169. {
  170. if (headers.ContainsKey(Headers.Encoding))
  171. {
  172. var encoding = headers[Headers.Encoding];
  173. var encoder = Encoding.GetEncoding(encoding);
  174. using (var reader = new BinaryReader(message.BodyStream))
  175. {
  176. var bytes = reader.ReadBytes((int)message.BodyStream.Length);
  177. var str = encoder.GetString(bytes);
  178. return str;
  179. }
  180. }
  181. return "(message encoding not specified)";
  182. }
  183. Dictionary<string, string> TryDeserializeHeaders(System.Messaging.Message message)
  184. {
  185. try
  186. {
  187. var headersAsJsonString = Encoding.UTF7.GetString(message.Extension);
  188. var headers = JsonConvert.DeserializeObject<Dictionary<string, string>>(headersAsJsonString);
  189. return headers ?? new Dictionary<string, string>();
  190. }
  191. catch
  192. {
  193. return new Dictionary<string, string>();
  194. }
  195. }
  196. void LoadQueues(Machine machine)
  197. {
  198. Task.Factory
  199. .StartNew(() =>
  200. {
  201. var privateQueues = MessageQueue.GetPrivateQueuesByMachine(machine.MachineName);
  202. return privateQueues;
  203. })
  204. .ContinueWith(t =>
  205. {
  206. if (!t.IsFaulted)
  207. {
  208. var queues = t.Result
  209. .Select(queue => new Queue(queue));
  210. machine.SetQueues(queues);
  211. return NotificationEvent.Success("{0} queues loaded from {1}",
  212. t.Result.Length,
  213. machine.MachineName);
  214. }
  215. var details = t.Exception.ToString();
  216. return NotificationEvent.Fail(details, "Could not load queues from {0}: {1}",
  217. machine.MachineName, t.Exception.Message);
  218. }, Context.UiThread)
  219. .ContinueWith(t => Messenger.Default.Send(t.Result), Context.UiThread);
  220. }
  221. }
  222. }