PageRenderTime 339ms CodeModel.GetById 38ms RepoModel.GetById 3ms app.codeStats 0ms

/src/MassTransit.QuartzIntegration/ScheduleMessageConsumer.cs

http://github.com/MassTransit/MassTransit
C# | 210 lines | 157 code | 53 blank | 0 comment | 18 complexity | 0f84324926b2a600afcc87346db9a82e MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. namespace MassTransit.QuartzIntegration
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Xml.Linq;
  10. using Context;
  11. using Newtonsoft.Json;
  12. using Newtonsoft.Json.Linq;
  13. using Quartz;
  14. using Quartz.Util;
  15. using Scheduling;
  16. using Serialization;
  17. public class ScheduleMessageConsumer :
  18. IConsumer<ScheduleMessage>,
  19. IConsumer<ScheduleRecurringMessage>
  20. {
  21. readonly IScheduler _scheduler;
  22. public ScheduleMessageConsumer(IScheduler scheduler)
  23. {
  24. _scheduler = scheduler;
  25. }
  26. public async Task Consume(ConsumeContext<ScheduleMessage> context)
  27. {
  28. var correlationId = context.Message.CorrelationId.ToString("N");
  29. var jobKey = new JobKey(correlationId);
  30. var jobDetail = await CreateJobDetail(context, context.Message.Destination, jobKey).ConfigureAwait(false);
  31. var triggerKey = new TriggerKey(correlationId);
  32. var trigger = TriggerBuilder.Create()
  33. .ForJob(jobDetail)
  34. .StartAt(context.Message.ScheduledTime)
  35. .WithSchedule(SimpleScheduleBuilder.Create().WithMisfireHandlingInstructionFireNow())
  36. .WithIdentity(triggerKey)
  37. .Build();
  38. if (await _scheduler.CheckExists(trigger.Key, context.CancellationToken).ConfigureAwait(false))
  39. await _scheduler.UnscheduleJob(trigger.Key, context.CancellationToken).ConfigureAwait(false);
  40. await _scheduler.ScheduleJob(jobDetail, trigger, context.CancellationToken).ConfigureAwait(false);
  41. LogContext.Debug?.Log("Scheduled: {Key} {Schedule}", jobKey, trigger.GetNextFireTimeUtc());
  42. }
  43. public async Task Consume(ConsumeContext<ScheduleRecurringMessage> context)
  44. {
  45. var jobKey = new JobKey(context.Message.Schedule.ScheduleId, context.Message.Schedule.ScheduleGroup);
  46. var jobDetail = await CreateJobDetail(context, context.Message.Destination, jobKey).ConfigureAwait(false);
  47. var triggerKey = new TriggerKey("Recurring.Trigger." + context.Message.Schedule.ScheduleId, context.Message.Schedule.ScheduleGroup);
  48. var trigger = CreateTrigger(context.Message.Schedule, jobDetail, triggerKey);
  49. if (await _scheduler.CheckExists(triggerKey, context.CancellationToken).ConfigureAwait(false))
  50. await _scheduler.UnscheduleJob(triggerKey, context.CancellationToken).ConfigureAwait(false);
  51. await _scheduler.ScheduleJob(jobDetail, trigger, context.CancellationToken).ConfigureAwait(false);
  52. LogContext.Debug?.Log("Scheduled: {Key} {Schedule}", jobKey, trigger.GetNextFireTimeUtc());
  53. }
  54. ITrigger CreateTrigger(RecurringSchedule schedule, IJobDetail jobDetail, TriggerKey triggerKey)
  55. {
  56. var tz = TimeZoneInfo.Local;
  57. if (!string.IsNullOrWhiteSpace(schedule.TimeZoneId) && schedule.TimeZoneId != tz.Id)
  58. tz = TimeZoneUtil.FindTimeZoneById(schedule.TimeZoneId);
  59. var triggerBuilder = TriggerBuilder.Create()
  60. .ForJob(jobDetail)
  61. .WithIdentity(triggerKey)
  62. .StartAt(schedule.StartTime)
  63. .WithDescription(schedule.Description)
  64. .WithCronSchedule(schedule.CronExpression, x =>
  65. {
  66. x.InTimeZone(tz);
  67. switch (schedule.MisfirePolicy)
  68. {
  69. case MissedEventPolicy.Skip:
  70. x.WithMisfireHandlingInstructionDoNothing();
  71. break;
  72. case MissedEventPolicy.Send:
  73. x.WithMisfireHandlingInstructionFireAndProceed();
  74. break;
  75. }
  76. });
  77. if (schedule.EndTime.HasValue)
  78. triggerBuilder.EndAt(schedule.EndTime);
  79. return triggerBuilder.Build();
  80. }
  81. static async Task<IJobDetail> CreateJobDetail(ConsumeContext context, Uri destination, JobKey jobKey, Guid? tokenId = default)
  82. {
  83. string body = Encoding.UTF8.GetString(context.ReceiveContext.GetBody());
  84. var mediaType = context.ReceiveContext.ContentType?.MediaType;
  85. if (JsonMessageSerializer.JsonContentType.MediaType.Equals(mediaType, StringComparison.OrdinalIgnoreCase))
  86. body = TranslateJsonBody(body, destination.ToString());
  87. else if (XmlMessageSerializer.XmlContentType.MediaType.Equals(mediaType, StringComparison.OrdinalIgnoreCase))
  88. body = TranslateXmlBody(body, destination.ToString());
  89. else
  90. throw new InvalidOperationException("Only JSON and XML messages can be scheduled");
  91. var builder = JobBuilder.Create<ScheduledMessageJob>()
  92. .RequestRecovery(true)
  93. .WithIdentity(jobKey)
  94. .UsingJobData("Destination", ToString(destination))
  95. .UsingJobData("ResponseAddress", ToString(context.ResponseAddress))
  96. .UsingJobData("FaultAddress", ToString(context.FaultAddress))
  97. .UsingJobData("Body", body)
  98. .UsingJobData("ContentType", mediaType);
  99. if (context.MessageId.HasValue)
  100. builder = builder.UsingJobData("MessageId", context.MessageId.Value.ToString());
  101. if (context.CorrelationId.HasValue)
  102. builder = builder.UsingJobData("CorrelationId", context.CorrelationId.Value.ToString());
  103. if (context.ConversationId.HasValue)
  104. builder = builder.UsingJobData("ConversationId", context.ConversationId.Value.ToString());
  105. if (context.InitiatorId.HasValue)
  106. builder = builder.UsingJobData("InitiatorId", context.InitiatorId.Value.ToString());
  107. if (context.RequestId.HasValue)
  108. builder = builder.UsingJobData("RequestId", context.RequestId.Value.ToString());
  109. if (context.ExpirationTime.HasValue)
  110. builder = builder.UsingJobData("ExpirationTime", context.ExpirationTime.Value.ToString("O"));
  111. if (tokenId.HasValue)
  112. builder = builder.UsingJobData("TokenId", tokenId.Value.ToString("N"));
  113. var headers = context.Headers.GetAll();
  114. if (headers.Any())
  115. builder = builder.UsingJobData("HeadersAsJson", JsonConvert.SerializeObject(headers));
  116. var jobDetail = builder
  117. .Build();
  118. return jobDetail;
  119. }
  120. static string ToString(Uri uri)
  121. {
  122. return uri?.ToString() ?? "";
  123. }
  124. static string TranslateJsonBody(string body, string destination)
  125. {
  126. var envelope = JObject.Parse(body);
  127. envelope["destinationAddress"] = destination;
  128. var message = envelope["message"];
  129. var payload = message["payload"];
  130. var payloadType = message["payloadType"];
  131. envelope["message"] = payload;
  132. envelope["messageType"] = payloadType;
  133. return JsonConvert.SerializeObject(envelope, Formatting.Indented);
  134. }
  135. static string TranslateXmlBody(string body, string destination)
  136. {
  137. using (var reader = new StringReader(body))
  138. {
  139. var document = XDocument.Load(reader);
  140. var envelope = (from e in document.Descendants("envelope") select e).Single();
  141. var destinationAddress = (from a in envelope.Descendants("destinationAddress") select a).Single();
  142. var message = (from m in envelope.Descendants("message") select m).Single();
  143. IEnumerable<XElement> messageType = (from mt in envelope.Descendants("messageType") select mt);
  144. var payload = (from p in message.Descendants("payload") select p).Single();
  145. IEnumerable<XElement> payloadType = (from pt in message.Descendants("payloadType") select pt);
  146. message.Remove();
  147. messageType.Remove();
  148. destinationAddress.Value = destination;
  149. message = new XElement("message");
  150. message.Add(payload.Descendants());
  151. envelope.Add(message);
  152. envelope.Add(payloadType.Select(x => new XElement("messageType", x.Value)));
  153. return document.ToString(SaveOptions.DisableFormatting);
  154. }
  155. }
  156. }
  157. }