// Copyright (c) 2017 schick Informatik using System; using System.Collections.Generic; using log4net; using bsmd.database; using System.ServiceModel.Activation; using System.ServiceProcess; using System.Timers; using System.IO; namespace bsmd.LockingService { [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class LockingService : IService { private static readonly ILog _log = LogManager.GetLogger(typeof(LockingService)); private static readonly Dictionary lockDict = new Dictionary(); private static readonly Timer staleTimer = new Timer(5000); // alle 5 Sekunden prüfen private const int staleTimeoutSeconds = 120; // wenn sie eine Anwendung 2 Minuten nicht meldet, werden die Locks freigegeben static LockingService() { staleTimer.Elapsed += StaleTimer_Elapsed; } private static void StaleTimer_Elapsed(object sender, ElapsedEventArgs e) { lock (lockDict) { List deleteList = new List(); foreach (Guid key in lockDict.Keys) if ((DateTime.Now - lockDict[key].lockAquired).TotalSeconds > staleTimeoutSeconds) deleteList.Add(key); foreach (Guid key in deleteList) { _log.WarnFormat("Stale remove message core id {0}, User {1}", key, lockDict[key].userId); lockDict.Remove(key); } } } private readonly ILog log = LogManager.GetLogger(typeof(LockingService)); #region Implementation IService public Guid Lock(Guid messageCoreId, Guid userId) { Guid result = Guid.Empty; lock (lockDict) { if (!lockDict.ContainsKey(messageCoreId)) { LockEntry le = new LockEntry(); le.lockAquired = DateTime.Now; le.userId = userId; lockDict.Add(messageCoreId, le); } else { if (lockDict[messageCoreId].userId == userId) { // this means "refresh" lockDict[messageCoreId].lockAquired = DateTime.Now; } else { // lock taken by somebody else.. result = lockDict[messageCoreId].userId; } } } return result; } public void Unlock(Guid messageCoreId, Guid userId) { lock(lockDict) { if(lockDict.ContainsKey(messageCoreId)) { if (lockDict[messageCoreId].userId == userId) lockDict.Remove(messageCoreId); } } } public void LockRefresh(List currentLocks, Guid userId) { foreach (Guid messageCoreId in currentLocks) this.Lock(messageCoreId, userId); } public void Log(string msg, string host, Guid userId) { log.Info(string.Format("{0} {1}:{2}", host, userId, msg)); } public List GetLocks() { List result = new List(); lock (lockDict) { foreach (Guid messageCoreId in lockDict.Keys) { CoreLock coreLock = new CoreLock(); coreLock.CoreId = messageCoreId; coreLock.UserId = lockDict[messageCoreId].userId; result.Add(coreLock); } } return result; } public ServerStatus GetStatus() { ServerStatus serverStatus = new ServerStatus(); // Test if processes are running ServiceController[] services = ServiceController.GetServices(); foreach (ServiceController serviceController in services) { switch (serviceController.ServiceName) { case "NSW Report Generator": serverStatus.Report = (int)serviceController.Status; break; case "NSWSendService": serverStatus.Transmitter = (int)serviceController.Status; break; case "ExcelReadService": serverStatus.Excel = (int)serviceController.Status; break; default: break; } } // collect file Names // TODO: evtl. könnte es Sinn ergeben, wenn man diese Daten z.B. nur alle 10 Sekunden erzeugt und // dann allen Aufrufern liefert. serverStatus.IMPFiles = new List(); string impPath = Path.Combine(Properties.Settings.Default.TransmitterRoot, "IMP"); if(Directory.Exists(impPath)) { foreach(string file in Directory.GetFiles(impPath)) serverStatus.IMPFiles.Add(Path.GetFileNameWithoutExtension(file)); } serverStatus.READYFiles = new List(); string readyPath = Path.Combine(Properties.Settings.Default.TransmitterRoot, "READY"); if (Directory.Exists(readyPath)) { foreach (string file in Directory.GetFiles(readyPath)) serverStatus.READYFiles.Add(Path.GetFileNameWithoutExtension(file)); } serverStatus.CORRUPTFiles = new List(); string corruptPath = Path.Combine(Properties.Settings.Default.TransmitterRoot, "CORRUPT"); if (Directory.Exists(corruptPath)) { foreach (string file in Directory.GetFiles(corruptPath)) serverStatus.CORRUPTFiles.Add(Path.GetFileNameWithoutExtension(file)); } return serverStatus; } public bool RestoreFromFile(string filename) { bool result = false; string readyPath = Path.Combine(Properties.Settings.Default.TransmitterRoot, "READY"); string filePath = string.Format("{0}\\{1}.xml", readyPath, filename); if(File.Exists(filePath)) { _log.InfoFormat("Restoring declaraction class from {0}", filePath); // TBD result = true; } else { _log.ErrorFormat("Restore file {0} does not exist", filePath); } return result; } #endregion #region class LockEntry internal class LockEntry { public DateTime lockAquired = DateTime.Now; public Guid userId; } #endregion } }