using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Timers; using e_suite.Scheduler.Jobs; using e_suite.Scheduler.Scheduler; using eSuite.Core.Clock; namespace e_suite.Scheduler; public class TimedHostedService : IHostedService { private readonly List _jobs = []; private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly IClock _clock; private readonly System.Timers.Timer _timer; public TimedHostedService(ILogger logger, IServiceProvider serviceProvider, IClock clock ) { _timer = new System.Timers.Timer { Interval = 10000, AutoReset = true, }; _timer.Elapsed += ExecuteTimer; _logger = logger; _serviceProvider = serviceProvider; _clock = clock; } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Timed Background Service is starting."); RegisterJobs(); _timer.Enabled = true; ExecuteTimer(_timer, null!); return Task.CompletedTask; } private void RegisterJobs() { RegisterJob(); RegisterJob(); } private void RegisterJob() where T : IJob { var schedule = GetSchedule(typeof(T)); var scheduledJob = new ScheduledJob(_clock) { Schedule = schedule, #pragma warning disable CS8601 //Null reference is checked for so no need to worry about it here. Job = _serviceProvider.GetService(typeof(T)) as IJob, #pragma warning restore CS8601 LastExecuted = _clock.GetNow }; if (scheduledJob.Job == null) throw new ArgumentNullException(nameof(scheduledJob.Job), "job cannot be null"); _jobs.Add(scheduledJob); } private static ISchedule GetSchedule(Type type) { var attributes = type.GetCustomAttributes(false); foreach (var attribute in attributes) { if (attribute is ISchedule schedule) return schedule; } throw new InvalidOperationException($"{type.FullName} does not contain a valid Schedule attribute"); } private void ExecuteTimer(object? sender, ElapsedEventArgs e) { var now = _clock.GetNow; var taskList = new List(); foreach (var job in _jobs.Where(job => job.NextScheduledExecution <= now)) { job.LastExecuted = now; taskList.Add(ExecuteJobAsync(job.Job)); } Task.WhenAll(taskList).GetAwaiter().GetResult(); } private async Task ExecuteJobAsync(IJob job) { _logger.LogInformation("Executing {FullName}", job.GetType().FullName); await job.ExecuteAsync(); _logger.LogInformation("Executing {FullName} - Completed", job.GetType().FullName); } public Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("Timed Background Service is stopping."); _jobs.Clear(); return Task.CompletedTask; } }