I'm currently working on some kind of simplified wrapper around Quartz.net to be able to manage all registered and running in background jobs, display additional information about job execution progress, percentage, return some kind of messages about current job state, etc. If, for example, my current job is posting data to social media, I want to see which social media is data currently posted to, or if job is sending emails I want to see in real time how many email messages are currently generated, how many are already sent, how many left, etc. For this purpose I save data to IJobExecutionContext (context.JobDetail.JobDataMap)
in job Execute
method. Then use my manager to get all currently executing jobs:
public List<IJobExecutionContext> RunningJobs
{
get { return Scheduler.GetCurrentlyExecutingJobs().ToList(); }
}
and display details fetched from JobDataMap
of each IJobExecutionContext
. This kinda works fine. But the problem is that if, for example, job execution has failed I want to log (save) as much data as possible, including exception object itself, in JobDataMap
. But, the problem is, when job execution is completed context object is disposed. And I lost all of that data. So when I try to fetch all JobDetail of currently scheduled jobs:
public List<IJobDetail> AllJobs
{
get
{
var result = new List<IJobDetail>();
var jobGroups = Scheduler.GetJobGroupNames();
foreach(string group in jobGroups)
{
var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
var jobKeys = Scheduler.GetJobKeys(groupMatcher);
foreach(var jobKey in jobKeys)
{
var detail = Scheduler.GetJobDetail(jobKey);
result.Add(detail);
}
}
return result;
}
}
I can not see the data I saved during job execution to detail.JobDataMap
. But I still can see data saved when job was initialized/scheduled, before it actually being executed. As an option I can catch the exception an log it during the execution (which I am actually doing) but I want to mark this job as failed and display the cause of the fail in my Jobs detail view. Which takes data from my manager (which is like some kind of repository of my jobs) using List<IJobDetail> AllJobs
(implemented above).
So do I have to use some kind of additional job details storage to save this data during execution and then fetch all scheduled jobs and map this data by job ids or something. Which is not the solution I would prefer. Or it there actually a way to save data to JobDataMap on execution and access this data after execution?
After some research I've discovered that there are two types of jobs in Quartz: stateful and stateless (non stateful jobs).
Stateful job instances follow slightly different rules from regular Job instances. The key difference is that their associated JobDataMap is re-persisted after every execution of the job, thus preserving state for the next execution. The other difference is that stateful jobs are not allowed to execute concurrently, which means new triggers that occur before the completion of the execute(xx) method will be delayed.
So to achieve my goal I just have to decorate my job class with PersistJobDataAfterExecution
and DisallowConcurrentExecution
attributes which will make my job stateful and it will persist it's JobDataMap after execution.