You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

QuartzScheduler.cs 10 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Collections.Specialized;
  6. using Quartz.Impl;
  7. using Quartz;
  8. using System.Data;
  9. using System.Configuration;
  10. using System.Collections;
  11. using System.Windows.Forms;
  12. using System.IO;
  13. using System.Xml.Linq;
  14. using Quartz.Impl.Matchers;
  15. using Quartz.Impl.Triggers;
  16. using Quartz.Util;
  17. namespace ClickForensics.Quartz.Manager
  18. {
  19. public class QuartzScheduler
  20. {
  21. public QuartzScheduler(string server, int port, string scheduler)
  22. {
  23. Address = string.Format("tcp://{0}:{1}/{2}", server, port, scheduler);
  24. _schedulerFactory = new StdSchedulerFactory(getProperties(Address));
  25. try
  26. {
  27. _scheduler = _schedulerFactory.GetScheduler();
  28. }
  29. catch (SchedulerException)
  30. {
  31. MessageBox.Show("Unable to connect to the specified server", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  32. }
  33. }
  34. public string Address { get; private set; }
  35. private NameValueCollection getProperties(string address)
  36. {
  37. NameValueCollection properties = new NameValueCollection();
  38. properties["quartz.scheduler.instanceName"] = "RemoteClient";
  39. properties["quartz.scheduler.proxy"] = "true";
  40. properties["quartz.threadPool.threadCount"] = "0";
  41. properties["quartz.scheduler.proxy.address"] = address;
  42. return properties;
  43. }
  44. public IScheduler GetScheduler()
  45. {
  46. return _scheduler;
  47. }
  48. public DataTable GetJobs()
  49. {
  50. DataTable table = new DataTable();
  51. table.Columns.Add("GroupName");
  52. table.Columns.Add("JobName");
  53. table.Columns.Add("JobDescription");
  54. table.Columns.Add("TriggerName");
  55. table.Columns.Add("TriggerGroupName");
  56. table.Columns.Add("TriggerType");
  57. table.Columns.Add("TriggerState");
  58. table.Columns.Add("NextFireTime");
  59. table.Columns.Add("PreviousFireTime");
  60. var jobGroups = GetScheduler().GetJobGroupNames();
  61. foreach (string group in jobGroups)
  62. {
  63. var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
  64. var jobKeys = GetScheduler().GetJobKeys(groupMatcher);
  65. foreach (var jobKey in jobKeys)
  66. {
  67. var detail = GetScheduler().GetJobDetail(jobKey);
  68. var triggers = GetScheduler().GetTriggersOfJob(jobKey);
  69. foreach (ITrigger trigger in triggers)
  70. {
  71. DataRow row = table.NewRow();
  72. row["GroupName"] = group;
  73. row["JobName"] = jobKey.Name;
  74. row["JobDescription"] = detail.Description;
  75. row["TriggerName"] = trigger.Key.Name;
  76. row["TriggerGroupName"] = trigger.Key.Group;
  77. row["TriggerType"] = trigger.GetType().Name;
  78. row["TriggerState"] = GetScheduler().GetTriggerState(trigger.Key);
  79. DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
  80. if (nextFireTime.HasValue)
  81. {
  82. row["NextFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
  83. }
  84. DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
  85. if (previousFireTime.HasValue)
  86. {
  87. row["PreviousFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
  88. }
  89. table.Rows.Add(row);
  90. }
  91. }
  92. }
  93. return table;
  94. }
  95. private ISchedulerFactory _schedulerFactory;
  96. private IScheduler _scheduler;
  97. public void ScheduleOneTimeJob(Type jobType, JobDataMap dataMap, int clientID)
  98. {
  99. string name = string.Format("{0}-{1}", jobType.Name, clientID);
  100. string group = clientID.ToString();
  101. IJobDetail jobDetail = JobBuilder.
  102. Create().
  103. OfType(jobType).
  104. WithIdentity(name, group).
  105. WithDescription("One time job").
  106. UsingJobData(dataMap).Build();
  107. ITrigger trigger = TriggerBuilder.
  108. Create().
  109. ForJob(jobDetail).
  110. WithIdentity(name, group).
  111. WithSchedule(SimpleScheduleBuilder.Create().WithRepeatCount(0).WithInterval(TimeSpan.Zero)).
  112. StartNow().Build();
  113. GetScheduler().ScheduleJob(jobDetail, trigger);
  114. }
  115. public DataTable GetRunningJobs()
  116. {
  117. DataTable table = new DataTable();
  118. table.Columns.Add("JobName", typeof(string));
  119. table.Columns.Add("RunTime", typeof(int));
  120. try
  121. {
  122. var contexts = GetScheduler().GetCurrentlyExecutingJobs();
  123. foreach (var context in contexts)
  124. {
  125. DataRow row = table.NewRow();
  126. row["JobName"] = context.JobDetail.Key.Name;
  127. row["RunTime"] = (DateTime.Now.ToUniversalTime() - ((DateTimeOffset)context.FireTimeUtc).DateTime).TotalMinutes;
  128. table.Rows.Add(row);
  129. }
  130. }
  131. catch (Exception ex)
  132. {
  133. //TODO: Let the user know we couldn't load the running jobs.
  134. }
  135. return table;
  136. }
  137. public void BackupToFile(System.IO.FileInfo file)
  138. {
  139. IScheduler scheduler = GetScheduler();
  140. var jobGroupNames = scheduler.GetJobGroupNames();
  141. List<IJobDetail> jobDetails = new List<IJobDetail>();
  142. foreach (var jobGroup in jobGroupNames)
  143. {
  144. var groupMatcher = GroupMatcher<JobKey>.GroupContains(jobGroup);
  145. var jobKeys = scheduler.GetJobKeys(groupMatcher);
  146. foreach (var jobKey in jobKeys)
  147. {
  148. jobDetails.Add(scheduler.GetJobDetail(jobKey));
  149. }
  150. }
  151. writeToFile(file, jobDetails);
  152. }
  153. private void writeToFile(System.IO.FileInfo file, List<IJobDetail> jobDetails)
  154. {
  155. using (StreamWriter writer = file.CreateText())
  156. {
  157. XNamespace ns = "http://quartznet.sourceforge.net/JobSchedulingData";
  158. XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes")
  159. , new XElement(ns + "quartz"
  160. , new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance")
  161. , new XAttribute("version", "1.0")
  162. , new XAttribute("overwrite-existing-jobs", "true")
  163. )
  164. );
  165. foreach (IJobDetail detail in jobDetails)
  166. {
  167. doc.Root.Add(
  168. new XElement(ns + "job"
  169. , new XElement(ns + "job-detail"
  170. , new XElement(ns + "name", detail.Key.Name)
  171. , new XElement(ns + "group", detail.Key.Group)
  172. , new XElement(ns + "description", detail.Description)
  173. , new XElement(ns + "job-type", detail.JobType.FullName + "," + detail.JobType.Assembly.FullName)
  174. //TODO: Apparently volatile is no longer available. Check.
  175. //, new XElement(ns + "volatile", detail.Volatile)
  176. , new XElement(ns + "durable", detail.Durable)
  177. , new XElement(ns + "recover", detail.RequestsRecovery)
  178. , getJobDataMap(ns, detail.JobDataMap)
  179. )
  180. , getTriggers(ns, detail)
  181. )
  182. );
  183. }
  184. writer.Write(doc);
  185. writer.Flush();
  186. writer.Close();
  187. }
  188. }
  189. private XElement getJobDataMap(XNamespace ns, JobDataMap jobDataMap)
  190. {
  191. XElement map = new XElement(ns + "job-data-map");
  192. foreach (var key in jobDataMap.GetKeys())
  193. {
  194. map.Add(new XElement(ns + "entry"
  195. , new XElement(ns + "key", key)
  196. , new XElement(ns + "value", jobDataMap[key])
  197. )
  198. );
  199. }
  200. return map;
  201. }
  202. private XElement[] getTriggers(XNamespace ns, IJobDetail detail)
  203. {
  204. var triggers = _scheduler.GetTriggersOfJob(detail.Key);
  205. XElement[] elements = new XElement[triggers.Count];
  206. int i = 0;
  207. foreach (var trigger in triggers)
  208. {
  209. elements[i] = new XElement(ns + "trigger");
  210. if (triggers[i] is SimpleTriggerImpl)
  211. {
  212. elements[i].Add(getSimpleTrigger(ns, (SimpleTriggerImpl)triggers[i]));
  213. }
  214. else if (triggers[i] is CronTriggerImpl)
  215. {
  216. elements[i].Add(getCronTrigger(ns, (CronTriggerImpl)triggers[i]));
  217. }
  218. i++;
  219. }
  220. return elements;
  221. }
  222. private XElement getCronTrigger(XNamespace ns, CronTriggerImpl trigger)
  223. {
  224. XElement cronTrigger = new XElement(ns + "cron");
  225. addCommonTriggerData(ns, cronTrigger, trigger);
  226. cronTrigger.Add(
  227. new XElement(ns + "cron-expression", trigger.CronExpressionString)
  228. );
  229. return cronTrigger;
  230. }
  231. private void addCommonTriggerData(XNamespace ns, XElement rootTriggerElement, AbstractTrigger trigger)
  232. {
  233. rootTriggerElement.Add(
  234. new XElement(ns + "name", trigger.Key.Name)
  235. , new XElement(ns + "group", trigger.Key.Group)
  236. , new XElement(ns + "description", trigger.Description)
  237. , new XElement(ns + "misfire-instruction", getMisfireInstructionText(trigger))
  238. //, new XElement(ns + "volatile", trigger.Volatile)
  239. , new XElement(ns + "job-name", trigger.JobName)
  240. , new XElement(ns + "job-group", trigger.JobGroup)
  241. );
  242. }
  243. private string getMisfireInstructionText(AbstractTrigger trigger)
  244. {
  245. if (trigger is CronTriggerImpl)
  246. {
  247. return getCronTriggerMisfireInstructionText(trigger.MisfireInstruction);
  248. }
  249. return getSimpleTriggerMisfireInstructionText(trigger.MisfireInstruction);
  250. }
  251. private string getSimpleTriggerMisfireInstructionText(int misfireInstruction)
  252. {
  253. switch (misfireInstruction)
  254. {
  255. case 0:
  256. return "SmartPolicy";
  257. case 1:
  258. return "FireNow";
  259. case 2:
  260. return "RescheduleNowWithExistingRepeatCount";
  261. case 3:
  262. return "RescheduleNowWithRemainingRepeatCount";
  263. case 4:
  264. return "RescheduleNextWithRemainingCount";
  265. case 5:
  266. return "RescheduleNextWithExistingCount";
  267. default:
  268. throw new ArgumentOutOfRangeException(string.Format("{0} is not a supported misfire instruction for SimpleTrigger See Quartz.MisfireInstruction for more details.", misfireInstruction));
  269. }
  270. }
  271. private string getCronTriggerMisfireInstructionText(int misfireInstruction)
  272. {
  273. switch (misfireInstruction)
  274. {
  275. case 0:
  276. return "SmartPolicy";
  277. case 1:
  278. return "FireOnceNow";
  279. case 2:
  280. return "DoNothing";
  281. default:
  282. throw new ArgumentOutOfRangeException(string.Format("{0} is not a supported misfire instruction for CronTrigger See Quartz.MisfireInstruction for more details.", misfireInstruction));
  283. }
  284. }
  285. private XElement getSimpleTrigger(XNamespace ns, SimpleTriggerImpl trigger)
  286. {
  287. XElement simpleTrigger = new XElement(ns + "simple");
  288. addCommonTriggerData(ns, simpleTrigger, trigger);
  289. simpleTrigger.Add(
  290. new XElement(ns + "repeat-count", trigger.RepeatCount)
  291. , new XElement(ns + "repeat-interval", trigger.RepeatInterval.Milliseconds)
  292. );
  293. return simpleTrigger;
  294. }
  295. }
  296. }