创建一个控制台应用程序Program.cs:
class Program
{
#region 变量
private static JobStore jobStore;
private static ITrigger trigger;
private static IJobDetail jobDetail;
private static TimeZoneInfo expectedTimeZone;
private static TimeZoneInfo chinaTimeZone;
private static JobKey jobKey = new JobKey("test");
private static CronTriggerImpl retrievedCronTrigger;
private static DateTimeOffset expectedNextFireTime;
#endregion
private static void Main(string[] args)
{
Console.WriteLine("------------------------------------------------");
//1.首先创建一个作业调度池
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = schedulerFactory.GetScheduler();
try
{
do
{
//启动 scheduler
scheduler.Start();
//StoreCronTrigger();
StoreDailyIntervalTrigger();
// 将job和trigger进行绑定,并告知 quartz 调度器用trigger去执行job
scheduler.ScheduleJob(jobDetail, trigger);
//IList list = jobStore.GetTriggersForJob(jobKey);
Console.WriteLine("Quartz服务成功启动!");
System.Threading.Thread.Sleep(300000);
} while (true);
}
catch (Exception)
{
scheduler.Shutdown();
}
Console.ReadKey();
}
public static void StoreCronTrigger()
{
expectedTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
// 计算任务的开始时间,DateBuilder.evenMinuteDate方法是取下一个整数分钟
// DateBuilder.evenMinuteDate(new Date()); 当前时间的下一分钟假如在执行到这一句时,时间为17:23:22,那么runTime就是17:24:00
//该函数返回最近的整点分钟时间。
DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTime.Now); //DateTimeOffset.UtcNow
//返回的时间是给定的时间的整数倍。
//DateBuilder.nextGivenSecondDate(null, 15);
//系统时间:Tue Mar 10 09:09:27 CST 2015
//开始时间:Tue Mar 10 09:09:30 CST 2015 30秒正好是15的倍数
DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(DateTime.Now, 1);
jobStore = new JobStore();
jobStore.ClearAllSchedulingData();
//定义job和自定义的TestJob进行绑定
jobDetail = JobBuilder.Create().WithIdentity(jobKey).Build();
//定义cronTrigger 一个即时触发的触发器,(每隔10秒进行重复执行)
trigger = TriggerBuilder.Create()
.ForJob(jobKey)
.WithCronSchedule("0/10 * * * * ? *").StartNow()
//.WithCronSchedule("0/10 * * * * ? *").StartAt(runTime)
//.WithCronSchedule("0/10 * * * * ? *", x => x.InTimeZone(expectedTimeZone)) // 0 0 5 ? * *
.Build();
//保存到mongodb
jobStore.StoreJobAndTrigger(jobDetail, (IOperableTrigger) trigger);
}
public static void StoreDailyIntervalTrigger()
{
TimeZoneInfo chinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTime.Now);
jobStore = new JobStore();
jobStore.ClearAllSchedulingData();
jobDetail = JobBuilder.Create().WithIdentity("test").Build();
trigger = TriggerBuilder.Create()
.ForJob(jobKey)
//.WithDailyTimeIntervalSchedule(x => x.InTimeZone(chinaTimeZone).OnEveryDay() //创建并定义触发器的规则(每天执行一次时间为:时:分)
.WithDailyTimeIntervalSchedule(a => a.WithIntervalInSeconds(30).OnEveryDay()
.StartingDailyAt(new TimeOfDay(12, 00))
.EndingDailyAt(new TimeOfDay(17, 50))).Build();
//保存jobDetail,trigger数据到mongodb
jobStore.StoreJobAndTrigger(jobDetail, (IOperableTrigger) trigger);
}
public static void TestCronTrigger()
{
retrievedCronTrigger = (CronTriggerImpl) jobStore.GetTriggersForJob(jobKey).Single();
retrievedCronTrigger.TimeZone.ShouldEqual(expectedTimeZone);
//-----------------------------------------------------------------------------------
retrievedCronTrigger.Triggered(null);
var nextFireTimeUtc = retrievedCronTrigger.GetNextFireTimeUtc();
nextFireTimeUtc.ShouldEqual(expectedNextFireTime);
}
public static void TestDailyIntervalTrigger()
{
var triggersForJob = jobStore.GetTriggersForJob(jobKey).ToList();
triggersForJob.Count.ShouldEqual(1);
//-----------------------------------------------------------------------------------
var trigger = (DailyTimeIntervalTriggerImpl) jobStore.GetTriggersForJob(jobKey).Single();
trigger.TimeZone.ShouldEqual(chinaTimeZone);
//-----------------------------------------------------------------------------------
trigger.StartTimeOfDay.ShouldEqual(new TimeOfDay(10, 10));
trigger.EndTimeOfDay.ShouldEqual(new TimeOfDay(10, 20));
//------------------------------------------------------------------------------------------
trigger.Triggered(null);
trigger.GetNextFireTimeUtc().ShouldEqual(expectedNextFireTime);
}
}
TestJob.cs代码如下:
public class TestJob: IJob
{
public static int count=0;
public void Execute(IJobExecutionContext context)
{
Console.WriteLine(string.Format("执行{0}次了!时间:"+DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"), ++count));
}
}
JobStore.cs核心类:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Threading;
using Common.Logging;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;
namespace Quartz.Impl.MongoDB
{
public class JobStore : IJobStore
{
private readonly object lockObject = new object();
private TimeSpan misfireThreshold = TimeSpan.FromSeconds(5);
private ISchedulerSignaler signaler;
private readonly ILog log;
private MongoDatabase database;
private string instanceId;
private string instanceName;
private MongoCollection Calendars
{
get { return this.database.GetCollection(instanceName + ".Calendars"); }
}
private MongoCollection Jobs
{
get { return this.database.GetCollection(instanceName + ".Jobs"); }
}
private MongoCollection Triggers
{
get { return this.database.GetCollection(instanceName + ".Triggers"); }
}
private MongoCollection PausedTriggerGroups
{
get { return this.database.GetCollection(instanceName + ".PausedTriggerGroups"); }
}
private MongoCollection PausedJobGroups
{
get { return this.database.GetCollection(instanceName + ".PausedJobGroups"); }
}
private MongoCollection BlockedJobs
{
get { return this.database.GetCollection(instanceName + ".BlockedJobs"); }
}
private MongoCollection Schedulers
{
get { return this.database.GetCollection(instanceName + ".Schedulers"); }
}
public static string DefaultConnectionString { get; set; }
///
/// 初始化 类的新实例。
///
public JobStore()
{
log = LogManager.GetLogger(GetType());
string connectionString = ConfigurationManager.ConnectionStrings["quartznet-mongodb"] != null
? ConfigurationManager.ConnectionStrings["quartznet-mongodb"].ConnectionString
: DefaultConnectionString;
//如果没有连接字符串使用,然后抛出一个异常中止建立
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ApplicationException("Connection string is missing for the MongoDB job store.");
}
lock (lockObject)
{
var urlBuilder = new MongoUrlBuilder(connectionString);
var client = new MongoClient(urlBuilder.ToMongoUrl());
this.database = client.GetServer().GetDatabase("quartznet_tests");
this.instanceName = "Quartz";
//client = new MongoClient(ConfigurationManager.ConnectionStrings["mongoDB"].ConnectionString);
//this.database = client.GetDatabase(databaseName);
//this.collectionName = collectionName;
}
}
///
/// 初始化 类。
///
static JobStore()
{
var myConventions = new ConventionPack();
var idConvention = new NamedIdMemberConvention("Id", "Key");
myConventions.Add(idConvention);
ConventionRegistry.Register(
"quartz-net-mongodb",
myConventions,
t => t.FullName.StartsWith("Quartz.")
);
BsonSerializer.RegisterSerializer(
typeof (JobKey),
new JobKeySerializer()
);
BsonSerializer.RegisterSerializer(
typeof (TriggerKey),
new TriggerKeySerializer()
);
BsonSerializer.RegisterSerializer(
typeof (JobDetailImpl),
new JobDetailImplSerializer()
);
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.SetDiscriminator("JobDetailImpl");
});
BsonSerializer.RegisterSerializer(
typeof (JobDataMap),
new JobDataMapSerializer()
);
BsonSerializer.RegisterSerializer(
typeof (DateTimeOffset),
new DateTimeOffsetSerializer()
);
BsonSerializer.RegisterGenericSerializerDefinition(typeof (Collection.ISet<>), typeof (SetSerializer<>));
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.MapField(x => x.Name);
cm.MapField(x => x.Group);
cm.MapField(x => x.JobName);
cm.MapField(x => x.JobGroup);
cm.MapField(x => x.JobKey);
cm.MapField(x => x.Name);
cm.MapField(x => x.Group);
cm.MapField(x => x.Description);
cm.MapField(x => x.CalendarName);
cm.MapField(x => x.JobDataMap);
cm.MapField(x => x.MisfireInstruction);
cm.MapField(x => x.FireInstanceId);
cm.MapField(x => x.EndTimeUtc);
cm.MapField(x => x.StartTimeUtc);
cm.MapField(x => x.Priority);
cm.SetIsRootClass(true);
});
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.MapField("complete");
cm.MapField("nextFireTimeUtc");
cm.MapField("previousFireTimeUtc");
cm.SetIgnoreExtraElements(true);
});
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.MapField(x => x.CronExpressionString);
cm.MapField(x => x.TimeZone);
cm.MapField("nextFireTimeUtc");
cm.MapField("previousFireTimeUtc");
cm.MapField(x => x.TimeZone).SetSerializer(new TimeZoneInfoSerializer());
cm.SetIgnoreExtraElements(true);
});
BsonSerializer.RegisterSerializer(typeof (TimeOfDay), new TimeOfDaySerializer());
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.MapField("complete");
cm.MapField("nextFireTimeUtc");
cm.MapField("previousFireTimeUtc");
cm.MapField(x => x.TimeZone).SetSerializer(new TimeZoneInfoSerializer());
cm.SetIgnoreExtraElements(true);
});
BsonClassMap.RegisterClassMap(cm =>
{
cm.AutoMap();
cm.MapField("complete");
cm.MapField("nextFireTimeUtc");
cm.MapField("previousFireTimeUtc");
cm.SetIgnoreExtraElements(true);
});
}
[TimeSpanParseRule(TimeSpanParseRule.Milliseconds)]
public virtual TimeSpan MisfireThreshold
{
get { return misfireThreshold; }
set
{
if (value.TotalMilliseconds < 1)
{
throw new ArgumentException("Misfirethreashold must be larger than 0");
}
misfireThreshold = value;
}
}
private static long ftrCtr = SystemTime.UtcNow().Ticks;
///
///获取fired触发记录id。
///
/// fired触发记录id.
protected virtual string GetFiredTriggerRecordId()
{
long value = Interlocked.Increment(ref ftrCtr);
return Convert.ToString(value, CultureInfo.InvariantCulture);
}
public virtual void Initialize(ITypeLoadHelper loadHelper, ISchedulerSignaler s)
{
signaler = s;
Log.Info("MongoDB JobStore initialized.");
}
public virtual void SchedulerStarted()
{
// nothing to do
}
public void SchedulerPaused()
{
this.Schedulers.Update(
Query.EQ("_id", this.instanceId),
Update.Set("State", "Paused"));
}
public void SchedulerResumed()
{
this.Schedulers.Update(
Query.EQ("_id", this.instanceId),
Update.Set("State", "Resuming"));
}
public virtual void Shutdown()
{
this.Schedulers.Remove(
Query.EQ("_id", this.instanceId));
this.Triggers.Update(
Query.EQ("SchedulerInstanceId", this.instanceId),
Update.Unset("SchedulerInstanceId")
.Set("State", "Waiting"));
}
public virtual bool SupportsPersistence
{
get { return true; }
}
public void ClearAllSchedulingData()
{
lock (lockObject)
{
// unschedule jobs (delete triggers)
this.Triggers.RemoveAll();
this.PausedTriggerGroups.RemoveAll();
// delete jobs
this.Jobs.RemoveAll();
this.BlockedJobs.RemoveAll();
this.PausedJobGroups.RemoveAll();
// delete calendars
this.Calendars.RemoveAll();
}
}
protected ILog Log
{
get { return log; }
}
public virtual void StoreJobAndTrigger(IJobDetail newJob, IOperableTrigger newTrigger)
{
StoreJob(newJob, false);
StoreTrigger(newTrigger, false);
}
public virtual bool IsJobGroupPaused(string groupName)
{
var result = this.PausedJobGroups.FindOneByIdAs(groupName);
return !result.IsBsonNull;
}
public virtual bool IsTriggerGroupPaused(string groupName)
{
var result = this.PausedTriggerGroups.FindOneByIdAs(groupName);
return !result.IsBsonNull;
}
public virtual void StoreJob(IJobDetail newJob, bool replaceExisting)
{
bool repl = false;
lock (lockObject)
{
if (this.CheckExists(newJob.Key))
{
if (!replaceExisting)
{
throw new ObjectAlreadyExistsException(newJob);
}
repl = true;
}
if (!repl)
{
// try insert new
this.Jobs.Insert(newJob.ToBsonDocument());
}
else
{
// force upsert
this.Jobs.Save(newJob.ToBsonDocument());
}
}
}
public void StoreJobsAndTriggers(IDictionary> triggersAndJobs,
bool replace)
{
var dictionary = new Dictionary>();
foreach (var triggersAndJob in triggersAndJobs)
{
dictionary.Add(triggersAndJob.Key, triggersAndJob.Value.ToList());
}
StoreJobsAndTriggers(new Dictionary>(dictionary), replace);
}
public virtual bool RemoveJob(JobKey jobKey)
{
bool found;
lock (lockObject)
{
// keep separated to clean up any staled trigger
IList triggersForJob = this.GetTriggersForJob(jobKey);
foreach (IOperableTrigger trigger in triggersForJob)
{
this.RemoveTrigger(trigger.Key);
}
found = this.CheckExists(jobKey);
if (found)
{
this.Jobs.Remove(
Query.EQ("_id", jobKey.ToBsonDocument()));
this.BlockedJobs.Remove(
Query.EQ("_id", jobKey.ToBsonDocument()));
var others = this.Jobs.FindAs(
Query.EQ("Group", jobKey.Group));
if (others.Count() == 0)
{
this.PausedJobGroups.Remove(
Query.EQ("_id", jobKey.Group));
}
}
}
return found;
}
public bool RemoveJobs(IList jobKeys)
{
bool allFound = true;
lock (lockObject)
{
foreach (JobKey key in jobKeys)
{
allFound = RemoveJob(key) && allFound;
}
}
return allFound;
}
public bool RemoveTriggers(IList triggerKeys)
{
bool allFound = true;
lock (lockObject)
{
foreach (TriggerKey key in triggerKeys)
{
allFound = RemoveTrigger(key) && allFound;
}
}
return allFound;
}
public void StoreJobsAndTriggers(IDictionary> triggersAndJobs, bool replace)
{
lock (lockObject)
{
// make sure there are no collisions...
if (!replace)
{
foreach (IJobDetail job in triggersAndJobs.Keys)
{
if (CheckExists(job.Key))
{
throw new ObjectAlreadyExistsException(job);
}
foreach (ITrigger trigger in triggersAndJobs[job])
{
if (CheckExists(trigger.Key))
{
throw new ObjectAlreadyExistsException(trigger);
}
}
}
}
// do bulk add...
foreach (IJobDetail job in triggersAndJobs.Keys)
{
StoreJob(job, true);
foreach (ITrigger trigger in triggersAndJobs[job])
{
StoreTrigger((IOperableTrigger) trigger, true);
}
}
}
}
public virtual bool RemoveTrigger(TriggerKey triggerKey)
{
return RemoveTrigger(triggerKey, true);
}
public virtual void StoreTrigger(IOperableTrigger newTrigger, bool replaceExisting)
{
lock (lockObject)
{
if (this.CheckExists(newTrigger.Key))
{
if (!replaceExisting)
{
throw new ObjectAlreadyExistsException(newTrigger);
}
// don't delete orphaned job, this trigger has the job anyways
this.RemoveTrigger(newTrigger.Key, false);
}
if (this.RetrieveJob(newTrigger.JobKey) == null)
{
throw new JobPersistenceException("The job (" + newTrigger.JobKey +
") referenced by the trigger does not exist.");
}
var document = newTrigger.ToBsonDocument();
string state = "Waiting";
if (this.PausedTriggerGroups.FindOneByIdAs(newTrigger.Key.Group) != null
|| this.PausedJobGroups.FindOneByIdAs(newTrigger.JobKey.Group) != null)
{
state = "Paused";
if (this.BlockedJobs.FindOneByIdAs(newTrigger.JobKey.ToBsonDocument()) != null)
{
state = "PausedAndBlocked";
}
}
else if (this.BlockedJobs.FindOneByIdAs(newTrigger.JobKey.ToBsonDocument()) != null)
{
state = "Blocked";
}
document.Add("State", state);
this.Triggers.Save(document);
}
}
public virtual bool RemoveTrigger(TriggerKey key, bool removeOrphanedJob)
{
bool found;
lock (lockObject)
{
var trigger = this.RetrieveTrigger(key);
found = trigger != null;
if (found)
{
this.Triggers.Remove(
Query.EQ("_id", trigger.Key.ToBsonDocument()));
if (removeOrphanedJob)
{
IJobDetail jobDetail = this.RetrieveJob(trigger.JobKey);
IList trigs = this.GetTriggersForJob(jobDetail.Key);
if ((trigs == null
|| trigs.Count == 0)
&& !jobDetail.Durable)
{
if (this.RemoveJob(jobDetail.Key))
{
signaler.NotifySchedulerListenersJobDeleted(jobDetail.Key);
}
}
}
}
}
return found;
}
public virtual bool ReplaceTrigger(TriggerKey triggerKey, IOperableTrigger newTrigger)
{
bool found;
lock (lockObject)
{
IOperableTrigger oldTrigger = this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument());
found = oldTrigger != null;
if (found)
{
if (!oldTrigger.JobKey.Equals(newTrigger.JobKey))
{
throw new JobPersistenceException(
"New trigger is not related to the same job as the old trigger.");
}
this.RemoveTrigger(triggerKey);
try
{
this.StoreTrigger(newTrigger, false);
}
catch (JobPersistenceException)
{
this.StoreTrigger(oldTrigger, false); // put previous trigger back...
throw;
}
}
}
return found;
}
public virtual IJobDetail RetrieveJob(JobKey jobKey)
{
lock (lockObject)
{
return this.Jobs
.FindOneByIdAs(jobKey.ToBsonDocument());
}
}
public virtual IOperableTrigger RetrieveTrigger(TriggerKey triggerKey)
{
lock (lockObject)
{
return this.Triggers
.FindOneByIdAs(triggerKey.ToBsonDocument());
}
}
public bool CheckExists(JobKey jobKey)
{
lock (lockObject)
{
return this.Jobs.FindOneByIdAs(jobKey.ToBsonDocument()) != null;
}
}
public bool CheckExists(TriggerKey triggerKey)
{
lock (lockObject)
{
return this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument()) != null;
}
}
public virtual TriggerState GetTriggerState(TriggerKey triggerKey)
{
lock (lockObject)
{
BsonDocument triggerState = this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument());
if (triggerState.IsBsonNull)
{
return TriggerState.None;
}
if (triggerState["State"] == "Complete")
{
return TriggerState.Complete;
}
if (triggerState["State"] == "Paused")
{
return TriggerState.Paused;
}
if (triggerState["State"] == "PausedAndBlocked")
{
return TriggerState.Paused;
}
if (triggerState["State"] == "Blocked")
{
return TriggerState.Blocked;
}
if (triggerState["State"] == "Error")
{
return TriggerState.Error;
}
return TriggerState.Normal;
}
}
public virtual void StoreCalendar(string name, ICalendar calendar, bool replaceExisting,
bool updateTriggers)
{
CalendarWrapper calendarWrapper = new CalendarWrapper()
{
Name = name,
Calendar = calendar
};
lock (lockObject)
{
if (this.Calendars.FindOneByIdAs(name) != null
&& replaceExisting == false)
{
throw new ObjectAlreadyExistsException(string.Format(CultureInfo.InvariantCulture,
"Calendar with name '{0}' already exists.", name));
}
this.Calendars.Save(calendarWrapper);
if (updateTriggers)
{
var triggers = this.Triggers.FindAs(Query.EQ("CalendarName", name));
foreach (IOperableTrigger trigger in triggers)
{
trigger.UpdateWithNewCalendar(calendar, MisfireThreshold);
this.Triggers.Save(trigger);
}
}
}
}
public virtual bool RemoveCalendar(string calName)
{
if (this.Triggers.FindAs(Query.EQ("CalendarName", calName)) != null)
{
throw new JobPersistenceException("Calender cannot be removed if it is referenced by a Trigger!");
}
this.Calendars.Remove(
Query.EQ("_id", calName));
return true;
}
public virtual ICalendar RetrieveCalendar(string calName)
{
lock (lockObject)
{
CalendarWrapper calendarWrapper = this.Calendars
.FindOneByIdAs(calName);
if (calendarWrapper != null)
{
return calendarWrapper.Calendar;
}
return null;
}
}
public virtual int GetNumberOfJobs()
{
lock (lockObject)
{
return (int) this.Jobs.Count();
}
}
public virtual int GetNumberOfTriggers()
{
lock (lockObject)
{
return (int) this.Triggers.Count();
}
}
public virtual int GetNumberOfCalendars()
{
lock (lockObject)
{
return (int) this.Calendars.Count();
}
}
public virtual Collection.ISet GetJobKeys(GroupMatcher matcher)
{
lock (lockObject)
{
var result = this.Jobs
.FindAs(
Query.EQ("Group", matcher.CompareToValue))
.Select(j => j.Key);
return new Collection.HashSet(result);
}
}
public virtual IList GetCalendarNames()
{
lock (lockObject)
{
return this.Calendars
.Distinct("Name")
.Select(g => g.AsString)
.ToList();
}
}
public virtual Collection.ISet GetTriggerKeys(GroupMatcher matcher)
{
lock (lockObject)
{
var result = this.Triggers
.FindAs(
Query.EQ("Group", matcher.CompareToValue))
.Select(t => t.Key);
return new Collection.HashSet(result);
}
}
public virtual IList GetJobGroupNames()
{
lock (lockObject)
{
return this.Jobs
.Distinct("Group")
.Select(g => g.AsString)
.ToList();
}
}
public virtual IList GetTriggerGroupNames()
{
lock (lockObject)
{
return this.Triggers
.Distinct("Group")
.Select(g => g.AsString)
.ToList();
}
}
public virtual IList GetTriggersForJob(JobKey jobKey)
{
lock (lockObject)
{
return this.Triggers
.FindAs(
Query.EQ("JobKey", jobKey.ToBsonDocument()))
.ToList();
}
}
public virtual void PauseTrigger(TriggerKey triggerKey)
{
lock (lockObject)
{
this.Triggers.Update(
Query.And(
Query.EQ("_id", triggerKey.ToBsonDocument()),
Query.EQ("State", "Blocked")),
Update.Set("State", "PausedAndBlocked"));
this.Triggers.Update(
Query.And(
Query.EQ("_id", triggerKey.ToBsonDocument()),
Query.NE("State", "Blocked")),
Update.Set("State", "Paused"));
}
}
public virtual Collection.ISet PauseTriggers(GroupMatcher matcher)
{
IList pausedGroups;
lock (lockObject)
{
pausedGroups = new List();
StringOperator op = matcher.CompareWithOperator;
if (op == StringOperator.Equality)
{
this.PausedTriggerGroups.Save(
new BsonDocument(
new BsonElement("_id", matcher.CompareToValue)));
pausedGroups.Add(matcher.CompareToValue);
}
else
{
IList groups = this.GetTriggerGroupNames();
foreach (string group in groups)
{
if (op.Evaluate(group, matcher.CompareToValue))
{
this.PausedTriggerGroups.Save(
new BsonDocument(
new BsonElement("_id", matcher.CompareToValue)));
pausedGroups.Add(matcher.CompareToValue);
}
}
}
foreach (string pausedGroup in pausedGroups)
{
Collection.ISet keys =
this.GetTriggerKeys(GroupMatcher.GroupEquals(pausedGroup));
foreach (TriggerKey key in keys)
{
this.PauseTrigger(key);
}
}
}
return new Collection.HashSet(pausedGroups);
}
public virtual void PauseJob(JobKey jobKey)
{
lock (lockObject)
{
IList triggersForJob = this.GetTriggersForJob(jobKey);
foreach (IOperableTrigger trigger in triggersForJob)
{
this.PauseTrigger(trigger.Key);
}
}
}
public virtual IList PauseJobs(GroupMatcher matcher)
{
List pausedGroups = new List();
lock (lockObject)
{
StringOperator op = matcher.CompareWithOperator;
if (op == StringOperator.Equality)
{
this.PausedJobGroups.Save(
new BsonDocument(
new BsonElement("_id", matcher.CompareToValue)));
pausedGroups.Add(matcher.CompareToValue);
}
else
{
IList groups = this.GetJobGroupNames();
foreach (string group in groups)
{
if (op.Evaluate(group, matcher.CompareToValue))
{
this.PausedJobGroups.Save(
new BsonDocument(
new BsonElement("_id", matcher.CompareToValue)));
pausedGroups.Add(matcher.CompareToValue);
}
}
}
foreach (string groupName in pausedGroups)
{
foreach (JobKey jobKey in GetJobKeys(GroupMatcher.GroupEquals(groupName)))
{
IList triggers = this.GetTriggersForJob(jobKey);
foreach (IOperableTrigger trigger in triggers)
{
this.PauseTrigger(trigger.Key);
}
}
}
}
return pausedGroups;
}
public virtual void ResumeTrigger(TriggerKey triggerKey)
{
lock (lockObject)
{
IOperableTrigger trigger = this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument());
// does the trigger exist?
if (trigger == null)
{
return;
}
BsonDocument triggerState = this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument());
// if the trigger is not paused resuming it does not make sense...
if (triggerState["State"] != "Paused" &&
triggerState["State"] != "PausedAndBlocked")
{
return;
}
if (this.BlockedJobs.FindOneByIdAs(trigger.JobKey.ToBsonDocument()) != null)
{
triggerState["State"] = "Blocked";
}
else
{
triggerState["State"] = "Waiting";
}
this.ApplyMisfire(trigger);
this.Triggers.Save(triggerState);
}
}
public virtual IList ResumeTriggers(GroupMatcher matcher)
{
Collection.ISet groups = new Collection.HashSet();
lock (lockObject)
{
Collection.ISet keys = this.GetTriggerKeys(matcher);
foreach (TriggerKey triggerKey in keys)
{
groups.Add(triggerKey.Group);
IOperableTrigger trigger = this.Triggers.FindOneByIdAs(triggerKey.ToBsonDocument());
var pausedJobGroup = this.PausedJobGroups.FindOneByIdAs(trigger.JobKey.Group);
if (pausedJobGroup != null)
{
continue;
}
this.ResumeTrigger(triggerKey);
}
foreach (String group in groups)
{
this.PausedTriggerGroups.Remove(
Query.EQ("_id", group));
}
}
return new List(groups);
}
public virtual void ResumeJob(JobKey jobKey)
{
lock (lockObject)
{
IList triggersForJob = GetTriggersForJob(jobKey);
foreach (IOperableTrigger trigger in triggersForJob)
{
this.ResumeTrigger(trigger.Key);
}
}
}
public virtual Collection.ISet ResumeJobs(GroupMatcher matcher)
{
Collection.ISet resumedGroups = new Collection.HashSet();
lock (lockObject)
{
Collection.ISet keys = GetJobKeys(matcher);
foreach (string pausedJobGroup in this.PausedJobGroups.FindAllAs())
{
if (matcher.CompareWithOperator.Evaluate(pausedJobGroup, matcher.CompareToValue))
{
resumedGroups.Add(pausedJobGroup);
}
}
this.PausedTriggerGroups.Remove(
Query.All("_id", new BsonArray(resumedGroups)));
foreach (JobKey key in keys)
{
IList triggers = GetTriggersForJob(key);
foreach (IOperableTrigger trigger in triggers)
{
ResumeTrigger(trigger.Key);
}
}
}
return resumedGroups;
}
public virtual void PauseAll()
{
lock (lockObject)
{
IList triggerGroupNames = GetTriggerGroupNames();
foreach (string groupName in triggerGroupNames)
{
this.PauseTriggers(GroupMatcher.GroupEquals(groupName));
}
}
}
public virtual void ResumeAll()
{
lock (lockObject)
{
// TODO need a match all here!
this.PausedJobGroups.RemoveAll();
IList triggerGroupNames = this.GetTriggerGroupNames();
foreach (string groupName in triggerGroupNames)
{
this.ResumeTriggers(GroupMatcher.GroupEquals(groupName));
}
}
}
protected virtual bool ApplyMisfire(IOperableTrigger trigger)
{
DateTimeOffset misfireTime = SystemTime.UtcNow();
if (MisfireThreshold > TimeSpan.Zero)
{
misfireTime = misfireTime.AddMilliseconds(-1*MisfireThreshold.TotalMilliseconds);
}
DateTimeOffset? tnft = trigger.GetNextFireTimeUtc();
if (!tnft.HasValue || tnft.Value > misfireTime
|| trigger.MisfireInstruction == MisfireInstruction.IgnoreMisfirePolicy)
{
return false;
}
ICalendar cal = null;
if (trigger.CalendarName != null)
{
cal = this.RetrieveCalendar(trigger.CalendarName);
}
signaler.NotifyTriggerListenersMisfired(trigger);
trigger.UpdateAfterMisfire(cal);
this.StoreTrigger(trigger, true);
if (!trigger.GetNextFireTimeUtc().HasValue)
{
this.Triggers.Update(
Query.EQ("_id", trigger.Key.ToBsonDocument()),
Update.Set("State", "Complete"));
signaler.NotifySchedulerListenersFinalized(trigger);
}
else if (tnft.Equals(trigger.GetNextFireTimeUtc()))
{
return false;
}
return true;
}
public virtual IList AcquireNextTriggers(DateTimeOffset noLaterThan, int maxCount,
TimeSpan timeWindow)
{
lock (lockObject)
{
this.Schedulers.Save(new BsonDocument()
.SetElement(new BsonElement("_id", this.instanceId))
.SetElement(new BsonElement("Expires", (SystemTime.Now() + new TimeSpan(0, 10, 0)).UtcDateTime))
.SetElement(new BsonElement("State", "Running")));
this.Schedulers.Remove(
Query.LT("Expires", SystemTime.Now().UtcDateTime));
IEnumerable activeInstances = this.Schedulers.Distinct("_id");
this.Triggers.Update(
Query.NotIn("SchedulerInstanceId", activeInstances),
Update.Unset("SchedulerInstanceId")
.Set("State", "Waiting"));
List result = new List();
Collection.ISet acquiredJobKeysForNoConcurrentExec = new Collection.HashSet();
DateTimeOffset? firstAcquiredTriggerFireTime = null;
var candidates = this.Triggers.FindAs(
Query.And(
Query.EQ("State", "Waiting"),
Query.LTE("nextFireTimeUtc", (noLaterThan + timeWindow).UtcDateTime)))
.OrderBy(t => t.GetNextFireTimeUtc()).ThenByDescending(t => t.Priority);
foreach (IOperableTrigger trigger in candidates)
{
if (trigger.GetNextFireTimeUtc() == null)
{
continue;
}
if (firstAcquiredTriggerFireTime != null
&& trigger.GetNextFireTimeUtc() > (firstAcquiredTriggerFireTime.Value + timeWindow))
{
break;
}
if (this.ApplyMisfire(trigger))
{
if (trigger.GetNextFireTimeUtc() == null
|| trigger.GetNextFireTimeUtc() > noLaterThan + timeWindow)
{
continue;
}
}
JobKey jobKey = trigger.JobKey;
IJobDetail job = this.Jobs.FindOneByIdAs(jobKey.ToBsonDocument());
if (job.ConcurrentExecutionDisallowed)
{
if (acquiredJobKeysForNoConcurrentExec.Contains(jobKey))
{
continue; // go to next trigger in store.
}
else
{
acquiredJobKeysForNoConcurrentExec.Add(jobKey);
}
}
trigger.FireInstanceId = this.GetFiredTriggerRecordId();
var acquired = this.Triggers.FindAndModify(
Query.And(
Query.EQ("_id", trigger.Key.ToBsonDocument()),
Query.EQ("State", "Waiting")),
SortBy.Null,
Update.Set("State", "Acquired")
.Set("SchedulerInstanceId", this.instanceId)
.Set("FireInstanceId", trigger.FireInstanceId));
if (acquired.ModifiedDocument != null)
{
result.Add(trigger);
if (firstAcquiredTriggerFireTime == null)
{
firstAcquiredTriggerFireTime = trigger.GetNextFireTimeUtc();
}
}
if (result.Count == maxCount)
{
break;
}
}
return result;
}
}
public virtual void ReleaseAcquiredTrigger(IOperableTrigger trigger)
{
lock (lockObject)
{
this.Triggers.Update(
Query.EQ("_id", trigger.Key.ToBsonDocument()),
Update.Unset("SchedulerInstanceId")
.Set("State", "Waiting"));
}
}
public virtual IList TriggersFired(IList triggers)
{
lock (lockObject)
{
List results = new List();
foreach (IOperableTrigger trigger in triggers)
{
// was the trigger deleted since being acquired?
if (this.Triggers.FindOneByIdAs(trigger.Key.ToBsonDocument()) == null)
{
continue;
}
// was the trigger completed, paused, blocked, etc. since being acquired?
BsonDocument triggerState = this.Triggers.FindOneByIdAs(trigger.Key.ToBsonDocument());
if (triggerState["State"] != "Acquired")
{
continue;
}
ICalendar cal = null;
if (trigger.CalendarName != null)
{
cal = this.RetrieveCalendar(trigger.CalendarName);
if (cal == null)
{
continue;
}
}
DateTimeOffset? prevFireTime = trigger.GetPreviousFireTimeUtc();
// call triggered on our copy, and the scheduler's copy
trigger.Triggered(cal);
var document = trigger.ToBsonDocument();
document.Add("State", "Executing");
this.Triggers.Save(document);
TriggerFiredBundle bndle = new TriggerFiredBundle(this.RetrieveJob(trigger.JobKey),
trigger,
cal,
false, SystemTime.UtcNow(),
trigger.GetPreviousFireTimeUtc(), prevFireTime,
trigger.GetNextFireTimeUtc());
IJobDetail job = bndle.JobDetail;
if (job.ConcurrentExecutionDisallowed)
{
var jobTriggers = this.GetTriggersForJob(job.Key);
IEnumerable triggerKeys = jobTriggers
.Where(t => !t.Key.Equals(trigger.Key))
.Select(t => t.Key.ToBsonDocument());
this.Triggers.Update(
Query.And(
Query.In("_id", triggerKeys),
Query.EQ("State", "Waiting")),
Update.Set("State", "Blocked"));
this.Triggers.Update(
Query.And(
Query.In("_id", triggerKeys),
Query.EQ("State", "Paused")),
Update.Set("State", "PausedAndBlocked"));
this.BlockedJobs.Save(
new BsonDocument(
new BsonElement("_id", job.Key.ToBsonDocument())));
}
results.Add(new TriggerFiredResult(bndle));
}
return results;
}
}
public virtual void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail,
SchedulerInstruction triggerInstCode)
{
lock (lockObject)
{
this.ReleaseAcquiredTrigger(trigger);
// It's possible that the job is null if:
// 1- it was deleted during execution
// 2- RAMJobStore is being used only for volatile jobs / triggers
// from the JDBC job store
if (jobDetail.PersistJobDataAfterExecution)
{
this.Jobs.Update(
Query.EQ("_id", jobDetail.Key.ToBsonDocument()),
Update.Set("JobDataMap", jobDetail.JobDataMap.ToBsonDocument()));
}
if (jobDetail.ConcurrentExecutionDisallowed)
{
IList jobTriggers = this.GetTriggersForJob(jobDetail.Key);
IEnumerable triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument());
this.Triggers.Update(
Query.And(
Query.In("_id", triggerKeys),
Query.EQ("State", "Blocked")),
Update.Set("State", "Waiting"));
this.Triggers.Update(
Query.And(
Query.In("_id", triggerKeys),
Query.EQ("State", "PausedAndBlocked")),
Update.Set("State", "Paused"));
signaler.SignalSchedulingChange(null);
}
this.BlockedJobs.Remove(
Query.EQ("_id", jobDetail.Key.ToBsonDocument()));
if (triggerInstCode == SchedulerInstruction.DeleteTrigger)
{
log.Debug("Deleting trigger");
DateTimeOffset? d = trigger.GetNextFireTimeUtc();
if (!d.HasValue)
{
d = trigger.GetNextFireTimeUtc();
if (!d.HasValue)
{
this.RemoveTrigger(trigger.Key);
}
else
{
log.Debug("Deleting cancelled - trigger still active");
}
}
else
{
this.RemoveTrigger(trigger.Key);
signaler.SignalSchedulingChange(null);
}
}
else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete)
{
this.Triggers.Update(
Query.EQ("_id", trigger.Key.ToBsonDocument()),
Update.Set("State", "Complete"));
signaler.SignalSchedulingChange(null);
}
else if (triggerInstCode == SchedulerInstruction.SetTriggerError)
{
Log.Info(string.Format(CultureInfo.InvariantCulture, "Trigger {0} set to ERROR state.", trigger.Key));
this.Triggers.Update(
Query.EQ("_id", trigger.Key.ToBsonDocument()),
Update.Set("State", "Error"));
signaler.SignalSchedulingChange(null);
}
else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError)
{
Log.Info(string.Format(CultureInfo.InvariantCulture, "All triggers of Job {0} set to ERROR state.",
trigger.JobKey));
IList jobTriggers = this.GetTriggersForJob(jobDetail.Key);
IEnumerable triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument());
this.Triggers.Update(
Query.In("_id", triggerKeys),
Update.Set("State", "Error"));
signaler.SignalSchedulingChange(null);
}
else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete)
{
IList jobTriggers = this.GetTriggersForJob(jobDetail.Key);
IEnumerable triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument());
this.Triggers.Update(
Query.In("_id", triggerKeys),
Update.Set("State", "Complete"));
signaler.SignalSchedulingChange(null);
}
}
}
public virtual string InstanceId
{
set { this.instanceId = value; }
}
public virtual string InstanceName
{
set { this.instanceName = value; }
}
public int ThreadPoolSize
{
set { }
}
public long EstimatedTimeToReleaseAndAcquireTrigger
{
get { return 200; }
}
public bool Clustered
{
get { return true; }
}
public virtual Collection.ISet GetPausedTriggerGroups()
{
return new Collection.HashSet(this.PausedTriggerGroups.FindAllAs());
}
}
}
结果如图:
Mongodb数据库: