using System; using System.Threading; using ln.logging; using System.Collections.Generic; using System.Linq; using System.Security.Authentication.ExtendedProtection.Configuration; namespace ln.application.service { public abstract class ApplicationServiceBase : IDisposable { public int TimeOut { get; set; } = 30000; public String ServiceName { get; protected set; } public virtual bool IsAlive => ((ServiceThread != null) && ServiceThread.IsAlive); public virtual bool IsReady => IsAlive && ready; bool ready; public bool StopRequested { get; protected set; } public Thread ServiceThread { get; protected set; } public string ServiceStateText { get; protected set; } public Application CurrentApplication { get; protected set; } public Type[] DependingServiceTypes => dependingServiceTypes.ToArray(); HashSet dependingServiceTypes = new HashSet(); public ApplicationServiceBase(String serviceName) { ServiceName = serviceName; ServiceStateText = "Initialized"; } public void Dispose() { ServiceStateText = "Disposed"; } protected void DependOnService() => dependingServiceTypes.Add(typeof(S)); public T Dependency() where T : ApplicationServiceBase { T serviceBase = (T)CurrentApplication.ServiceContainer[typeof(T)].ServiceBase; if (serviceBase == null) throw new EntryPointNotFoundException(String.Format("depending service not found: {0}",typeof(T).ToString())); return serviceBase; } public virtual void ServiceMain(Application application) { Ready(); while (!StopRequested) { lock (Thread.CurrentThread) { Monitor.Wait(Thread.CurrentThread); } } } public virtual bool Start(Application application, String[] arguments) { if (IsAlive) throw new NotSupportedException("Service is already alive"); CurrentApplication = application; foreach (Type dep in dependingServiceTypes) { ServiceDefinition depService = CurrentApplication.ServiceContainer[dep]; if (!depService.IsAlive) { CurrentApplication.ServiceContainer.Start(depService); if (!depService.IsAlive) { Logging.Log(LogLevel.ERROR, "service {0} could not start depending service {1}", ServiceName, depService.ServiceClassName); return false; } } lock (depService.ServiceBase) { if (!depService.ServiceBase.IsReady) { Monitor.Wait(depService.ServiceBase, TimeOut); if (!depService.ServiceBase.IsReady) { Logging.Log(LogLevel.ERROR, "service {0} was waiting too long for depending service {1}", ServiceName, depService.ServiceClassName); return false; } } } } ready = false; StopRequested = false; ServiceThread = new Thread(() => ServiceMain(application)); ServiceThread.Start(); return true; } public virtual bool Stop() => Stop(false); public virtual bool Stop(bool force) { if (IsAlive) { StopRequested = true; lock (ServiceThread) { Monitor.PulseAll(ServiceThread); } ServiceThread.Join(TimeOut>>1); if (ServiceThread.ThreadState == ThreadState.WaitSleepJoin) { ServiceThread.Interrupt(); } ServiceThread.Join(TimeOut >> 1); if (IsAlive && force) { Logging.Log(LogLevel.INFO, "Service did not shutdown, enforcing {0}",ServiceName); ServiceThread.Abort(); ServiceThread.Join(TimeOut); } if (!ServiceThread.IsAlive) CurrentApplication = null; ready = false; return !ServiceThread.IsAlive; } return true; } protected void Ready() { lock (this) { ready = true; Monitor.PulseAll(this); } } } }