using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Spi;
namespace Ombi.Helpers
{
public class OmbiQuartz
{
protected IScheduler _scheduler { get; set; }
public static IScheduler Scheduler => Instance._scheduler;
private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
// Singleton
protected static OmbiQuartz _instance;
///
/// Singleton
///
public static OmbiQuartz Instance => _instance ?? (_instance = new OmbiQuartz());
protected OmbiQuartz() : this(true)
{
}
///
/// Allows tests to skip the async scheduler initialisation and supply their own scheduler,
/// since Init() runs async void and would otherwise race with (and overwrite) a test scheduler.
///
protected OmbiQuartz(bool init)
{
if (init)
{
Init();
}
}
private async void Init()
{
_scheduler = await new StdSchedulerFactory().GetScheduler();
}
public IScheduler UseJobFactory(IJobFactory jobFactory)
{
Scheduler.JobFactory = jobFactory;
return Scheduler;
}
public static async Task IsJobRunning(string jobName)
{
var running = await Scheduler.GetCurrentlyExecutingJobs();
return running.Any(x => x.JobDetail.Key.Name.Equals(jobName, StringComparison.InvariantCultureIgnoreCase));
}
public async Task AddJob(string name, string group, string cronExpression, Dictionary jobData = null)
where T : IJob
{
var jobBuilder = JobBuilder.Create()
.WithIdentity(new JobKey(name, group));
if (jobData != null)
{
foreach (var o in jobData)
{
jobBuilder.UsingJobData(o.Key, o.Value);
}
}
if (!cronExpression.HasValue())
{
jobBuilder.StoreDurably(true);
}
var job = jobBuilder.Build();
if (cronExpression.HasValue())
{
ITrigger jobTrigger = TriggerBuilder.Create()
.WithIdentity(name + "Trigger", group)
.WithCronSchedule(cronExpression,
x => x.WithMisfireHandlingInstructionFireAndProceed())
.ForJob(name, group)
.StartNow()
.Build();
await Scheduler.ScheduleJob(job, jobTrigger);
}
else
{
await Scheduler.AddJob(job, true);
}
}
public static async Task TriggerJob(string jobName, string group)
{
await TriggerJobIfNotRunning(jobName, group);
}
///
/// Triggers the job unless it is already running. Returns whether the job was triggered,
/// so callers that depend on the job running (e.g. a resync after wiping data) can react.
///
public static async Task TriggerJobIfNotRunning(string jobName, string group, IDictionary data = null)
{
await _semaphore.WaitAsync();
try
{
if (await IsJobRunning(jobName))
{
return false;
}
if (data != null)
{
await Scheduler.TriggerJob(new JobKey(jobName, group), new JobDataMap(data));
}
else
{
await Scheduler.TriggerJob(new JobKey(jobName, group));
}
return true;
}
finally
{
_semaphore.Release();
}
}
public static async Task TriggerJob(string jobName, string group, IDictionary data)
{
await Scheduler.TriggerJob(new JobKey(jobName, group), new JobDataMap(data));
}
public static async Task Start()
{
await Scheduler.Start();
}
}
}