/pServiceBus 3.0.0/Src/PServiceBus.RabbitMQ/Provider.cs

http://pservicebus.codeplex.com · C# · 292 lines · 247 code · 45 blank · 0 comment · 37 complexity · 7a314cdf56e34546b9055ead46dfedc8 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using RabbitMQ.Client;
  6. using System.Text.RegularExpressions;
  7. using RabbitMQ.Client.Exceptions;
  8. using System.Runtime.Serialization.Formatters.Binary;
  9. using System.IO;
  10. using PServiceBus.Core.Runtime;
  11. using PServiceBus.Core.Runtime.Extensions;
  12. using PServiceBus.Core.Logger;
  13. using PServiceBus.Core.Interface;
  14. using System.Collections;
  15. using System.Collections.Concurrent;
  16. namespace PServiceBus.RabbitMQ {
  17. public sealed class Provider<TObject> : IHaveEndpoint, IObjectProvider<TObject> where TObject : class {
  18. private object _lockObject = new object();
  19. private static ConcurrentDictionary<string, IConnection> _connections = new ConcurrentDictionary<string, IConnection>();
  20. private static ConcurrentDictionary<string, IModel> _models = new ConcurrentDictionary<string, IModel>();
  21. private static ConcurrentDictionary<string, ProviderFactory> _factories = new ConcurrentDictionary<string, ProviderFactory>();
  22. private static ConcurrentDictionary<string, object> _providers = new ConcurrentDictionary<string, object>();
  23. public static Provider<TObject> GetProvider(string endpoint, bool appendTypeNameToEndpoint = false, string endpointAlias = null) {
  24. return _providers.GetOrAdd(endpoint, key =>
  25. new Provider<TObject> { Endpoint = endpoint, IDFunc = x => Guid.NewGuid(),
  26. EndpointAlias = endpointAlias, AppendTypeNameToEndpoint = appendTypeNameToEndpoint }) as Provider<TObject>;
  27. }
  28. private void Query(Action<IModel, string, string> action) {
  29. lock (_lockObject) {
  30. var factory = ConnectionProviderFactory;
  31. CreateQueue(Model, factory.ExchangeName, factory.Queue);
  32. action(Model, factory.ExchangeName, factory.Queue);
  33. }
  34. }
  35. internal class ProviderFactory {
  36. public ConnectionFactory ConnectionFactory { get; set; }
  37. public string ExchangeName { get; set; }
  38. public string Queue { get; set; }
  39. }
  40. private IConnection GetConnection(string endpoint, bool appendTypeNameToEndpoint, string endpointAlias = null) {
  41. string key = String.Concat(endpoint, endpointAlias ?? string.Empty);
  42. var connection = _connections.GetOrAdd(key, k => {
  43. return GetConnectionFactory(Endpoint, AppendTypeNameToEndpoint, EndpointAlias).ConnectionFactory.CreateConnection();
  44. });
  45. if (connection == null || !connection.IsOpen)
  46. connection = _connections.AddOrUpdate(key, k => GetConnectionFactory(Endpoint, AppendTypeNameToEndpoint, EndpointAlias).ConnectionFactory.CreateConnection(),
  47. (k, c) => GetConnectionFactory(Endpoint, AppendTypeNameToEndpoint, EndpointAlias).ConnectionFactory.CreateConnection());
  48. return connection;
  49. }
  50. private IModel GetModel(string endpoint, bool appendTypeNameToEndpoint, string endpointAlias = null) {
  51. string key = String.Concat(endpoint, endpointAlias ?? string.Empty);
  52. return _models.GetOrAdd(key, k => GetConnection(endpoint, appendTypeNameToEndpoint, endpointAlias).CreateModel());
  53. }
  54. private IConnection Connection {
  55. get { return GetConnection(Endpoint, AppendTypeNameToEndpoint, EndpointAlias); }
  56. }
  57. private IModel Model {
  58. get { return GetModel(Endpoint, AppendTypeNameToEndpoint, EndpointAlias); }
  59. }
  60. private ProviderFactory GetConnectionFactory(string endpoint, bool appendTypeNameToEndpoint, string endpointAlias = null) {
  61. var key = String.Concat(endpoint, endpointAlias ?? string.Empty);
  62. return _factories.GetOrAdd(key, k =>
  63. {
  64. var path = AppendTypeNameToEndpoint ? typeof(TObject).FullName.Replace(".", "_") : string.Empty;
  65. path += (EndpointAlias ?? string.Empty);
  66. endpoint += (endpoint.EndsWith("/") ? string.Empty : "/") + path;
  67. var uri = EndpointUri.Parse(endpoint);
  68. var queue = uri.Path;
  69. var exchangeName = "ex" + queue;
  70. var factory = new ConnectionFactory() { HostName = uri.Host, UserName = uri.Username, Password = uri.Password, Port = uri.Port };
  71. return new ProviderFactory() { ConnectionFactory = factory, ExchangeName = exchangeName, Queue = queue };
  72. });
  73. }
  74. private ProviderFactory ConnectionProviderFactory {
  75. get {
  76. return GetConnectionFactory(Endpoint, AppendTypeNameToEndpoint, EndpointAlias);
  77. }
  78. }
  79. public void Clear() {
  80. Query((ch, exchangeName, queue) => ch.QueuePurge(Endpoint));
  81. }
  82. private bool _queueCreated = false;
  83. private bool CreateQueue(IModel ch, string exchangeName, string queue) {
  84. var success = true;
  85. if (_queueCreated) return success;
  86. try {
  87. ch.ExchangeDeclare(exchangeName, ExchangeType.Fanout, true);
  88. ch.QueueDeclare(queue, true, false, false, null);
  89. ch.QueueBind(queue, exchangeName, queue, null);
  90. _queueCreated = true;
  91. } catch { success = false; }
  92. return success;
  93. }
  94. private TObject GetObjectByID(string id, bool deleteObjectOnRead) {
  95. var obj = default(TObject);
  96. try {
  97. lock (_lockObject) {
  98. Query((ch, exchangeName, queue) =>
  99. {
  100. while (true) {
  101. try {
  102. var result = ch.BasicGet(queue, !deleteObjectOnRead);
  103. if (result == null) break;
  104. var body = result.Body.Deserialize<TObject>();
  105. var prop = result.BasicProperties;
  106. if (body != null && (id == null || (id != null && prop.MessageId == id))) {
  107. if (deleteObjectOnRead) ch.BasicAck(result.DeliveryTag, false);
  108. obj = body;
  109. break;
  110. }
  111. } catch (Exception) { break; }
  112. }
  113. });
  114. }
  115. }
  116. catch {
  117. }
  118. return obj;
  119. }
  120. #region IObjectProvider<TObject> Members
  121. public bool Refresh(TObject obj) {
  122. throw new NotImplementedException();
  123. }
  124. public bool Add(IEnumerable<TObject> list) {
  125. var success = true;
  126. try {
  127. Query((ch, exchangeName, queue) =>
  128. {
  129. foreach (var data in list) {
  130. var prop = ch.CreateBasicProperties();
  131. prop.SetPersistent(true);
  132. prop.DeliveryMode = 2;
  133. prop.MessageId = IDFunc(data).ToString();
  134. ch.BasicPublish(exchangeName, queue, prop, data.Serialize());
  135. }
  136. });
  137. } catch {
  138. success = false;
  139. }
  140. return success;
  141. }
  142. public bool Add(TObject obj) {
  143. var success = true;
  144. try {
  145. Query((ch, exchangeName, queue) =>
  146. {
  147. var prop = ch.CreateBasicProperties();
  148. prop.SetPersistent(true);
  149. prop.DeliveryMode = 2;
  150. prop.MessageId = IDFunc(obj).ToString();
  151. ch.BasicPublish(exchangeName, queue, prop, obj.Serialize());
  152. });
  153. } catch {
  154. success = false;
  155. }
  156. return success;
  157. }
  158. public void Delete() {
  159. Query((ch, exchangeName, queue) =>
  160. {
  161. try {
  162. ch.QueueUnbind(queue, exchangeName, queue, null);
  163. ch.ExchangeDelete(exchangeName, true);
  164. ch.QueueDelete(queue, true, false);
  165. } catch {
  166. throw;
  167. }
  168. });
  169. }
  170. public bool Delete(IEnumerable<TObject> list) {
  171. var success = true;
  172. foreach (var obj in list)
  173. success &= GetObjectByID(IDFunc(obj).ToString(), true) != null;
  174. return success;
  175. }
  176. public bool Delete(IEnumerable<Func<TObject, bool>> conditions) {
  177. var success = true;
  178. foreach (var condition in conditions) {
  179. var deleteObj = this.FirstOrDefault(obj => obj != null && condition(obj));
  180. success &= deleteObj != null
  181. && GetObjectByID(IDFunc(deleteObj).ToString(), true) != null;
  182. }
  183. return success;
  184. }
  185. public bool Delete(Func<TObject, bool> condition) {
  186. var deleteObj = this.FirstOrDefault(obj => obj != null && condition(obj));
  187. return deleteObj != null
  188. && GetObjectByID(IDFunc(deleteObj).ToString(), true) != null;
  189. }
  190. public bool Exists(Func<TObject, bool> condition) {
  191. return Get(condition) != null;
  192. }
  193. public TObject Get(Func<TObject, bool> condition) {
  194. return this.FirstOrDefault(obj => condition(obj));
  195. }
  196. public bool Exists(TObject obj) {
  197. return GetObjectByID(IDFunc(obj).ToString(), false) != null;
  198. }
  199. public TObject Get(Guid id) {
  200. return GetObjectByID(id.ToString(), false);
  201. }
  202. public bool Delete(TObject obj) {
  203. return GetObjectByID(IDFunc(obj).ToString(), true) != null;
  204. }
  205. public string Endpoint { get; set; }
  206. public string EndpointAlias { get; set; }
  207. public bool AppendTypeNameToEndpoint { get; set; }
  208. public bool DeleteObjectOnRead { get; set; }
  209. public Func<TObject, Guid> IDFunc { get; set; }
  210. #endregion
  211. #region IEnumerable<TObject> Members
  212. public IEnumerator<TObject> GetEnumerator() {
  213. var factory = ConnectionProviderFactory;
  214. CreateQueue(Model, factory.ExchangeName, factory.Queue);
  215. var body = default(TObject);
  216. while (true) {
  217. try {
  218. var result = Model.BasicGet(factory.Queue, !DeleteObjectOnRead);
  219. if (result != null)
  220. MethodHelper.Try(() => body = result.Body.Deserialize<TObject>());
  221. else break;
  222. if (DeleteObjectOnRead) Model.BasicAck(result.DeliveryTag, false);
  223. } catch (Exception) { break; }
  224. if (body != null) yield return body;
  225. }
  226. }
  227. #endregion
  228. #region IEnumerable Members
  229. IEnumerator IEnumerable.GetEnumerator() {
  230. return GetEnumerator();
  231. }
  232. #endregion
  233. #region IDisposable Members
  234. public void Dispose() {
  235. MethodHelper.Try(() => {
  236. foreach (var kv in _models) kv.Value.Close();
  237. });
  238. MethodHelper.Try(() => {
  239. foreach (var kv in _connections) kv.Value.Close();
  240. });
  241. _models.Clear();
  242. _connections.Clear();
  243. _factories.Clear();
  244. }
  245. #endregion
  246. }
  247. }