using System; using System.Linq; using System.Threading; using Hangfire; using LeafWeb.Core.DAL; using LeafWeb.Core.Entities; using LeafWeb.Core.Remote; using NLog; namespace LeafWeb.Web.Services { public class PiscalQueueManager : IDisposable { private readonly DataService _dataService; private readonly PiscalService _piscalService; private readonly EmailNotificationService _emailService; public PiscalQueueManager(DataService dataService, PiscalService piscalService, EmailNotificationService emailService) { _dataService = dataService; _piscalService = piscalService; _emailService = emailService; } public PiscalQueueManager() : this(new DataService(), new PiscalService(), new EmailNotificationService()) {} private static readonly object ProcessQueueLock = new object(); public void ProcessQueue() { var logger = LogManager.GetCurrentClassLogger(); if (Monitor.TryEnter(ProcessQueueLock)) { logger.Trace("ProcessQueue entered"); try { ProcessRunning(logger); ProcessQueue(logger); } finally { logger.Trace("ProcessQueue exit"); Monitor.Exit(ProcessQueueLock); } } else { logger.Trace("ProcessQueue locked, queue already processing"); } } private void ProcessQueue(ILogger logger) { var runningLeafInputs = _dataService.GetLeafInputs(LeafInputStatusType.Running).ToList(); if (runningLeafInputs.Any()) return; var pending = _dataService .GetLeafInputs(LeafInputStatusType.Pending) .OrderBy(l => l.StatusHistory.Min(sh => sh.DateTime)) .FirstOrDefault(); if (pending == null) return; logger.Info("LeafInputFile: {0}, Start", pending.Id); try { _piscalService.Run(pending); } catch (PiscalClientException ex) { logger.Error("LeafInputFile: {0}, ProcessQueue Exception: {1}", pending.Id, ex.Message); _dataService.SetLeafInputStatus(pending, LeafInputStatusType.Exception, "Error occurred starting LeafInput", ex.Message); logger.Info("LeafInputFile: {0}, Cleanup", pending.Id); _piscalService.Cleanup(pending); // TODO: re-queue? //_dataService.SetLeafInputFileStatus(queuedFile, LeafInputStatusType.Queued, "Re-queuing LeafInput"); } _dataService.SetLeafInputStatus(pending, LeafInputStatusType.Running); } private void ProcessRunning(ILogger logger) { var running = _dataService.GetLeafInputs(LeafInputStatusType.Running).ToList(); foreach (var leafInput in running) { try { var status = _piscalService.GetStatus(leafInput); switch (status) { case PiscalStatus.NotStarted: logger.Warn("LeafInputFile: {0}, Not Started, re-queueing", leafInput.Id); // if it's not started, try to requeue the process - unusual state _dataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Pending); break; case PiscalStatus.Running: logger.Trace("LeafInputFile: {0}, Running", leafInput.Id); // continue running break; case PiscalStatus.Complete: logger.Info("LeafInputFile: {0}, Complete", leafInput.Id); // collect the leaf output var leafOutputFiles = _piscalService.RetrieveOutputFiles(leafInput).ToList(); foreach (var outputFile in leafOutputFiles) _dataService.AddLeafOutputFile(outputFile); logger.Info("LeafInputFile: {0}, output files: {1}", leafInput.Id, string.Join(", ", leafOutputFiles.Select(o => o.Filename))); // update db _dataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Complete); BackgroundJob.Enqueue(() => _emailService.SendLeafWebComplete(leafInput.Id)); // remove working data from the server logger.Info("LeafInputFile: {0}, Cleanup", leafInput.Id); _piscalService.Cleanup(leafInput); break; } } catch (PiscalClientException ex) { logger.Error("LeafInputFile: {0}, ProcessRunning Exception: {1}", leafInput.Id, ex.Message); _dataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Exception, "Error occurred processing LeafInput", ex.Message); // TODO: Send email logger.Info("LeafInputFile: {0}, Cleanup", leafInput.Id); _piscalService.Cleanup(leafInput); // TODO: re-queue ? } } } public void Dispose() { _dataService.Dispose(); } } }