简体   繁体   中英

Quartz Job Scheduler Not Firing Jobs (C#)

I've run into a rather perplexing issue with Quartz (version 2.2.4) in the C# project I'm working on, and in spite of my best efforts I haven't been able to resolve the issue.

The project requires several different tasks to be scheduled independently of one-another; this being the case I've written a class specifically to handle setting up jobs on a 24 hour cycle. The class is supposed to be instantiated once for each task that needs to be scheduled.

Below is the object in question:

namespace ProjectFront.Utilities {

public class QuartzScheduler {
    public const string QB_TRIGGER = "qbTrigger";
    public const string QB_JOB = "qbJob";
    public const string QB_GROUP = "qbGroup";

    private IScheduler scheduler;
    private ITrigger trigger;
    private IJobDetail job;
    private JobKey jobKey;

    public QuartzScheduler(IJob controller, string triggerId, string jobId, string jobGroup, int syncHour, int syncMinute) {
        StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
        schedulerFactory.Initialize();
        jobKey = new JobKey(jobId, jobGroup);
        syncHour = getHour(syncHour);
        syncMinute = getMin(syncMinute);
        scheduler = schedulerFactory.GetScheduler();
        scheduler.Start();       
        job = JobBuilder.Create(controller.GetType())
            .StoreDurably()
            .WithIdentity(jobKey)
            .Build();
        trigger = TriggerBuilder.Create()
             .WithIdentity(triggerId)
             .ForJob(jobKey)
             .StartNow()
             .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
             .Build();
        scheduler.ScheduleJob(job, trigger);
        scheduler.Start();
    }

    public void Reschedule(int syncHour, int syncMinute) {
        scheduler.Standby();
        syncHour = getHour(syncHour);
        syncMinute = getMin(syncMinute);
        TriggerKey triggerKey = trigger.Key;
        trigger = TriggerBuilder.Create()
            .WithIdentity(triggerKey)
            .ForJob(jobKey)
            .StartNow()
            .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
            .Build();
        scheduler.RescheduleJob(triggerKey, trigger);
        scheduler.Start();
    }

    public IScheduler GetScheduler() {
        return scheduler;
    }

    public ITrigger GetTrigger() {
        return trigger;
    }

    private int getHour(int num) {
        if (num < 4) {
            return num + 20;
        }
        return num - 4;
    }

    private int getMin(int num) {
        while (num >= 60) {
            num -= 60;
        }
        return num;
    }
}
}

I've written a couple of NUnit tests to make sure that everything in the object works. It passes two of them, but always fails the third; the job definitely exists, and can even be rescheduled, but silently fails to fire when TriggerJob() is called. Rescheduling it and then waiting for it to fire similarly fails to work.

These are the tests:

namespace UnitTestProject.BackEnd_UnitTests.Util {
public class ControllerStub : IJob{

    public ControllerStub() { }

    public void Execute(IJobExecutionContext context)
    {
        Console.WriteLine("Fired!");
        Console.WriteLine(DateTime.Now.ToString());
    }
}

[TestFixture]
public class QBSchedulerTest {
    public static int fired = 0;
    private QuartzScheduler target;
    private JobKey expectedJob;
    private ControllerStub dummy;

    [TestFixtureSetUp]
    public void fixture() {
        colAlias.LogManager.Adapter = new colAlias.Simple.TraceLoggerFactoryAdapter { Level = colAlias.LogLevel.Info };
        expectedJob = new JobKey(QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP);
    }

    [SetUp]
    public void setup() {
        dummy = new ControllerStub();
        target = new QuartzScheduler(dummy, QuartzScheduler.QB_TRIGGER, QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP, 0, 1);
    }

    [TearDown]
    public void teardown() {
        target.GetScheduler().Clear();
    }

    [Test]
    public void HasBeenScheduled() {
        Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
        Assert.IsTrue(target.GetScheduler().IsStarted);
        Assert.IsTrue(target.GetScheduler().GetJobGroupNames().Contains(QuartzScheduler.QB_GROUP));
        Assert.True(target.GetTrigger().JobKey.Equals(expectedJob));
        Assert.Null(target.GetTrigger().GetPreviousFireTimeUtc());
        Assert.IsTrue(target.GetTrigger().GetMayFireAgain());
    }

    [Test]
    public void CanBeRescheduled() {
        target.Reschedule(1, 0);
        string actual = target.GetTrigger().GetNextFireTimeUtc().ToString();
        Assert.IsTrue(actual.Contains("1:00:00 AM"));
    }

    [Test]
    public void CanBeExecuted() {
        DateTimeOffset expected = DateTimeOffset.Now;
        expected.AddMinutes(1);
        Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
        target.Reschedule(expected.Hour, expected.Minute);
        target.GetScheduler().TriggerJob(expectedJob);
        Thread.Sleep(60500);
        Assert.NotNull(target.GetScheduler().GetTrigger(new TriggerKey(QuartzScheduler.QB_TRIGGER)).GetPreviousFireTimeUtc());
    }
}
}

The only reason for the failure I've been able to find for why the job wouldn't activate has been that the scheduler can't find the default constructor. I've tried the tests with both the given method and with the default constructor, but the test fails anyway.

I'm at a loss as to why this could be. Can anyone provide possible suggestions?

EDIT: Making things stranger still, adding a Console.Write() statement to the Execute() method causes the message to appear in the console window, but the changes to the value of FakeController.fired don't persist, and GetTrigger().GetPreviousFiringTimeUtc() still returns null!

EDIT: (Updated code) I've narrowed the issue down to something with the construction of the Trigger object; target.GetScheduler.TriggerJob(expectedJob) will cause Console.Write() messages to appear (though the static variable doesn't change value). However, scheduling a job as shown will result in the trigger never firing.

The issue was with the construction of the trigger's scheduling mechanism not being written correctly. ChronScheduleBuilder was either not working correctly or else implemented in the wrong way.

The final solution was to replace it with a simple interval scheduler as follows:

trigger = TriggerBuilder
             .Create()
             .WithIdentity(triggerKey)
             .ForJob(jobKey)
             .WithSchedule(SimpleScheduleBuilder
                           .Create()
                           .WithIntervalInHours(24)
                           .RepeatForever())
             .StartAt(
                new DateTimeOffset(
                    new DateTime(
                        DateTimeOffset.Now.Year,
                        DateTimeOffset.Now.Month,
                        DateTimeOffset.Now.Day,
                        syncHour,
                        syncMinute,
                        0,
                        DateTimeKind.Local)))
             .Build();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM