ln.application/service/ApplicationServiceBase.cs

154 lines
4.7 KiB
C#

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<Type> dependingServiceTypes = new HashSet<Type>();
public ApplicationServiceBase(String serviceName)
{
ServiceName = serviceName;
ServiceStateText = "Initialized";
}
public void Dispose()
{
ServiceStateText = "Disposed";
}
protected void DependOnService<S>() => dependingServiceTypes.Add(typeof(S));
public T Dependency<T>() 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);
}
}
}
}