Initial Commit

master
Harald Wolff 2019-08-03 13:49:06 +02:00
commit fd109766a8
8 changed files with 605 additions and 0 deletions

41
.gitignore vendored 100644
View File

@ -0,0 +1,41 @@
# Autosave files
*~
# build
[Oo]bj/
[Bb]in/
packages/
TestResults/
# globs
Makefile.in
*.DS_Store
*.sln.cache
*.suo
*.cache
*.pidb
*.userprefs
*.usertasks
config.log
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.user
*.tar.gz
tarballs/
test-results/
Thumbs.db
.vs/
# Mac bundle stuff
*.dmg
*.app
# resharper
*_Resharper.*
*.Resharper
# dotCover
*.dotCover

127
Application.cs 100644
View File

@ -0,0 +1,127 @@
using System;
using ln.http;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using ln.logging;
using ln.types;
using System.Net;
using ln.application.service;
using ln.http.resources;
namespace ln.application
{
public class Application : IApplicationInterface
{
public ArgumentContainer Arguments { get; protected set; }
public HTTPServer HttpServer { get; private set; }
public ServiceContainer ServiceContainer { get; }
public ResourceApplication HTTPApplication { get; private set; }
public MemoryLogger MemoryLogger { get; }
public Application()
{
MemoryLogger = new MemoryLogger(8192);
Logger.Default.Backends.Add(MemoryLogger);
Arguments = new ArgumentContainer()
.Add((char)0, "log-level", "INFO")
.Add('l', "http-listen","0.0.0.0")
.Add('p', "http-port", "8080");
ServiceContainer = new ServiceContainer(this);
}
public virtual void PrepareStart()
{
}
public void Start(String[] arguments)
{
Arguments.Parse(arguments);
Logger.ConsoleLogger.MaxLogLevel = (LogLevel)Enum.Parse(typeof(LogLevel), Arguments["log-level"].Value);
/* Startup Sequence */
Logging.Log(LogLevel.INFO, "Calling PrepareStart()");
PrepareStart();
/* HTTP Server */
Logging.Log(LogLevel.INFO, "Start: HTTPServer on {0}:{1}", Arguments['l'].Value, Arguments['p'].IntegerValue);
HttpServer = new HTTPServer();
HttpServer.AddEndpoint(new System.Net.IPEndPoint(
IPAddress.Parse(Arguments['l'].Value),
Arguments['p'].IntegerValue
));
HttpServer.Start();
HTTPApplication = new ResourceApplication();
HttpServer.DefaultApplication = HTTPApplication;
/* Application Services */
foreach (ServiceDefinition serviceDefinition in ServiceContainer)
{
if (serviceDefinition.AutoStart)
ServiceContainer.Start(serviceDefinition);
}
}
public void Stop()
{
Logging.Log(LogLevel.INFO, "Application shutdown requested");
/* Application Services */
foreach (ServiceDefinition serviceDefinition in ServiceContainer)
{
if (serviceDefinition.IsAlive)
{
ServiceContainer.Stop(serviceDefinition);
if (serviceDefinition.IsAlive)
throw new Exception("application service could not be stopped");
}
}
/* HTTP Server */
Logging.Log(LogLevel.INFO, "Stopping HTTP server");
HttpServer.Stop();
}
/*
* Plugin Classes
*/
Dictionary<Type, List<object>> pluginInstances = new Dictionary<Type, List<object>>();
public void RegisterPluginInstance<PC>(PC pluginInstance) => RegisterPluginInstance(typeof(PC),pluginInstance);
public void RegisterPluginInstance(Type type,object pluginInstance)
{
if (!pluginInstances.ContainsKey(type))
pluginInstances.Add(type, new List<object>());
if (!pluginInstances[type].Contains(pluginInstance))
pluginInstances[type].Add(pluginInstance);
}
public IEnumerable<PC> GetPluginInstances<PC>()
{
Type pluginType = typeof(PC);
if (!pluginInstances.ContainsKey(pluginType))
return new PC[0];
return pluginInstances[pluginType].Select((ut) => (PC)ut);
}
public IEnumerable GetPluginInstances(Type pluginType)
{
if (!pluginInstances.ContainsKey(pluginType))
return new object[0];
return pluginInstances[pluginType];
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ln.application.service;
using ln.http.resources;
using ln.http;
using ln.types;
namespace ln.application
{
public interface IApplicationInterface
{
ArgumentContainer Arguments { get; }
HTTPServer HttpServer { get; }
ResourceApplication HTTPApplication { get; }
ServiceContainer ServiceContainer { get; }
}
}

View File

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("ln.application")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{44AA3A50-7214-47F2-9D60-6FF34C0FE6D3}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>ln.application</RootNamespace>
<AssemblyName>ln.application</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Application.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IApplicationInterface.cs" />
<Compile Include="service\ServiceDefinition.cs" />
<Compile Include="service\ServiceContainer.cs" />
<Compile Include="service\ApplicationServiceBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.types\ln.types.csproj">
<Project>{8D9AB9A5-E513-4BA7-A450-534F6456BF28}</Project>
<Name>ln.types</Name>
</ProjectReference>
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
<Project>{D471A566-9FB6-41B2-A777-3C32874ECD0E}</Project>
<Name>ln.logging</Name>
</ProjectReference>
<ProjectReference Include="..\ln.http.resources\ln.http.resources.csproj">
<Project>{F9086FE4-8925-42FF-A59C-607341604293}</Project>
<Name>ln.http.resources</Name>
</ProjectReference>
<ProjectReference Include="..\ln.http\ln.http.csproj">
<Project>{CEEEEB41-3059-46A2-A871-2ADE22C013D9}</Project>
<Name>ln.http</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="service\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,108 @@
using System;
using System.Threading;
using ln.logging;
using System.Collections.Generic;
using System.Linq;
namespace ln.application.service
{
public abstract class ApplicationServiceBase : IDisposable
{
public int TimeOut { get; set; } = 10000;
public String ServiceName { get; protected set; }
public virtual bool IsAlive => ((ServiceThread != null) && ServiceThread.IsAlive);
public bool StopRequested { get; protected set; }
public Thread ServiceThread { get; protected set; }
public string ServiceStateText { get; protected set; }
public IApplicationInterface CurrentApplicationInterface { 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)CurrentApplicationInterface.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(IApplicationInterface applicationInterface) => throw new NotImplementedException();
public virtual bool Start(IApplicationInterface applicationInterface, String[] arguments)
{
if (IsAlive)
throw new NotSupportedException("Service is already alive");
CurrentApplicationInterface = applicationInterface;
foreach (Type dep in dependingServiceTypes)
{
ServiceDefinition depService = CurrentApplicationInterface.ServiceContainer[dep];
if (!depService.IsAlive)
{
CurrentApplicationInterface.ServiceContainer.Start(depService);
if (!depService.IsAlive)
{
Logging.Log(LogLevel.ERROR, "service {0} could not start depending service {1}", ServiceName, depService.ServiceClassName);
return false;
}
}
}
StopRequested = false;
ServiceThread = new Thread(() => ServiceMain(applicationInterface));
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);
if (IsAlive && force)
{
Logging.Log(LogLevel.INFO, "Service did not shutdown, enforcing {0}",ServiceName);
ServiceThread.Abort();
ServiceThread.Join(TimeOut);
}
if (!ServiceThread.IsAlive)
CurrentApplicationInterface = null;
return !ServiceThread.IsAlive;
}
return true;
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ln.application.service
{
public class ServiceContainer : IEnumerable<ServiceDefinition>
{
public Application Application { get; }
HashSet<ServiceDefinition> serviceDefinitions = new HashSet<ServiceDefinition>();
public ServiceContainer(Application application)
{
Application = application;
}
public void Add(ServiceDefinition serviceDefinition)
{
serviceDefinitions.Add(serviceDefinition);
}
public void Remove(ServiceDefinition serviceDefinition)
{
serviceDefinitions.Remove(serviceDefinition);
}
public ServiceDefinition this[string serviceClassName]
{
get
{
foreach (ServiceDefinition serviceDefinition in serviceDefinitions)
{
if (serviceDefinition.ServiceClassName.Equals(serviceClassName))
return serviceDefinition;
}
throw new KeyNotFoundException();
}
}
public ServiceDefinition this[string assemblyName,string serviceClassName]
{
get
{
foreach (ServiceDefinition serviceDefinition in serviceDefinitions)
{
if (serviceDefinition.ServiceClassName.Equals(serviceClassName) && Object.Equals(serviceDefinition.AssemblyName,assemblyName))
return serviceDefinition;
}
throw new KeyNotFoundException();
}
}
public ServiceDefinition this[Type serviceType]
{
get
{
return this[serviceType.Assembly.FullName,serviceType.FullName];
}
}
public void Start(ServiceDefinition serviceDefinition)
{
if (!serviceDefinitions.Contains(serviceDefinition))
throw new NotSupportedException("ServiceDefinition not known to ServiceContainer");
ServiceDefinition sd = this[serviceDefinition.AssemblyName, serviceDefinition.ServiceClassName];
sd.Start(this.Application);
}
public void Stop(ServiceDefinition serviceDefinition)
{
if (!serviceDefinitions.Contains(serviceDefinition))
throw new NotSupportedException("ServiceDefinition not known to ServiceContainer");
ServiceDefinition sd = this[serviceDefinition.AssemblyName, serviceDefinition.ServiceClassName];
sd.Stop();
}
public void Load(ServiceDefinition serviceDefinition)
{
if (!serviceDefinitions.Contains(serviceDefinition))
throw new NotSupportedException("ServiceDefinition not known to ServiceContainer");
ServiceDefinition sd = this[serviceDefinition.AssemblyName, serviceDefinition.ServiceClassName];
sd.Load();
}
public void Unload(ServiceDefinition serviceDefinition)
{
if (!serviceDefinitions.Contains(serviceDefinition))
throw new NotSupportedException("ServiceDefinition not known to ServiceContainer");
ServiceDefinition sd = this[serviceDefinition.AssemblyName, serviceDefinition.ServiceClassName];
sd.Unload();
}
public IEnumerator<ServiceDefinition> GetEnumerator()
{
return ((IEnumerable<ServiceDefinition>)serviceDefinitions).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<ServiceDefinition>)serviceDefinitions).GetEnumerator();
}
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.Reflection;
using ln.logging;
using System.IO;
using System.Linq;
namespace ln.application.service
{
public class ServiceDefinition
{
public String AssemblyName { get; }
public String ServiceClassName { get; }
public bool AutoStart { get; set; }
public ApplicationServiceBase ServiceBase { get; private set; }
public bool IsLoaded => ServiceBase != null;
public bool IsAlive => IsLoaded && ServiceBase.IsAlive;
Assembly serviceAssembly;
Type serviceType;
public ServiceDefinition(String assemblyName, string serviceClassName)
{
AssemblyName = assemblyName;
ServiceClassName = serviceClassName;
}
public ServiceDefinition(string serviceClassName)
{
AssemblyName = null;
ServiceClassName = serviceClassName;
}
public static ServiceDefinition From<S>() => From<S>(true);
public static ServiceDefinition From<S>(bool autoStart)
{
Type st = typeof(S);
ServiceDefinition sd = new ServiceDefinition(
st.Assembly.FullName,
st.FullName
);
sd.AutoStart = autoStart;
return sd;
}
public void Load()
{
if (IsLoaded)
throw new NotSupportedException(String.Format("Service {0} [{1}] is already loaded",ServiceClassName,AssemblyName));
if (AssemblyName != null)
serviceAssembly = Assembly.Load(AssemblyName);
serviceType = FinalAssembly.GetType(ServiceClassName);
if (serviceType == null)
throw new EntryPointNotFoundException(String.Format("could not get type {0}",ServiceClassName));
if (!serviceType.IsSubclassOf(typeof(ApplicationServiceBase)))
throw new NotSupportedException(String.Format("application service type must derive from ApplicationServiceBase"));
ServiceBase = (ApplicationServiceBase)Activator.CreateInstance(serviceType);
}
public void Unload() => Unload(false);
public void Unload(bool force)
{
if (!IsLoaded)
throw new NotSupportedException(String.Format("Service {0} [{1}] is not loaded", ServiceClassName, AssemblyName));
if (!force && IsAlive)
throw new NotSupportedException(String.Format("Service {0} [{1}] still alive", ServiceClassName, AssemblyName));
ServiceBase.Dispose();
ServiceBase = null;
serviceType = null;
serviceAssembly = null;
}
public void Start(IApplicationInterface applicationInterface)
{
if (!IsLoaded)
Load();
Logging.Log(LogLevel.INFO, "service: starting {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
if (ServiceBase.Start(applicationInterface,new string[0]))
Logging.Log(LogLevel.INFO, "service: started {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
else
Logging.Log(LogLevel.ERROR, "service: failed {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
}
public void Stop()
{
if (IsAlive)
{
Logging.Log(LogLevel.INFO, "service: stopping {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
if (ServiceBase.Stop())
Logging.Log(LogLevel.INFO, "service: stopped {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
else
Logging.Log(LogLevel.ERROR, "service: stopfail {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
}
}
private Assembly FinalAssembly => serviceAssembly != null ? serviceAssembly : Assembly.GetEntryAssembly();
public override int GetHashCode()
{
return (AssemblyName != null ? AssemblyName.GetHashCode() : 0) ^ ServiceClassName.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is ServiceDefinition)
{
ServiceDefinition other = obj as ServiceDefinition;
return Object.Equals(AssemblyName, other.AssemblyName) && ServiceClassName.Equals(other.ServiceClassName);
}
return false;
}
}
}