161 lines
5.3 KiB
C#
161 lines
5.3 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
using CertMgr.Core.Jobs;
|
|
using CertMgr.Core.Log;
|
|
using CertMgr.Core.Utils;
|
|
|
|
namespace CertMgr.Core;
|
|
|
|
internal sealed class JobRegistry
|
|
{
|
|
private readonly Dictionary<string, JobDescriptor> _items;
|
|
|
|
public JobRegistry()
|
|
{
|
|
_items = new Dictionary<string, JobDescriptor>();
|
|
}
|
|
|
|
public IReadOnlyList<string> Names => _items.Keys.ToList();
|
|
|
|
public Task LoadAsync(CancellationToken cancellationToken)
|
|
{
|
|
CLog.Info("Loading job registry...");
|
|
|
|
if (TryGetAssemblyTypes(Assembly.GetExecutingAssembly(), out Type[] types))
|
|
{
|
|
foreach (Type type in types)
|
|
{
|
|
if (type.IsAbstract)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
JobUtils.TryGetJobName(type, out string? jobName, out string? errorMessage);
|
|
TryGetGenericTypes(type, out Type? settingsType, out Type? resultType);
|
|
if (jobName != null && settingsType != null)
|
|
{
|
|
if (_items.TryGetValue(jobName, out JobDescriptor? alreadyRegisteredType))
|
|
{
|
|
CLog.Error("Job '{0}' has multiple implementations. Types '{0}' and '{1}'. First win.", jobName, alreadyRegisteredType.JobType.ToString(false), type.ToString(false));
|
|
}
|
|
else
|
|
{
|
|
_items.Add(jobName, new JobDescriptor(type, settingsType, resultType));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (jobName == null && settingsType != null)
|
|
{
|
|
if (errorMessage != null)
|
|
{
|
|
CLog.Error(errorMessage);
|
|
}
|
|
else
|
|
{
|
|
CLog.Error("Failed to get name of job from type '{0}'", type.ToString(false));
|
|
}
|
|
}
|
|
if (jobName != null && settingsType == null)
|
|
{
|
|
CLog.Error("Job '{0}' has job-name '{1}' but the class doesn't inherit from '{2}'", type.ToString(false), jobName, typeof(Job<>).ToString(false));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CLog.Info("Loading job registry... done (found {0} jobs)", _items.Count);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public bool TryGet(string name, [NotNullWhen(true)] out JobDescriptor? jobDescriptor)
|
|
{
|
|
jobDescriptor = null;
|
|
|
|
if (_items.TryGetValue(name, out JobDescriptor? descriptor))
|
|
{
|
|
jobDescriptor = descriptor;
|
|
}
|
|
|
|
return jobDescriptor != null;
|
|
}
|
|
|
|
private bool TryGetGenericTypes(Type type, [NotNullWhen(true)] out Type? settingType, out Type? resultType)
|
|
{
|
|
settingType = null;
|
|
resultType = null;
|
|
|
|
for (Type t = type; t != null && type != typeof(object); t = t.BaseType!)
|
|
{
|
|
if (t.IsGenericType)
|
|
{
|
|
Type[] genericTypes = t.GetGenericArguments();
|
|
Type currentGenericType = t.GetGenericTypeDefinition();
|
|
if (currentGenericType == typeof(Job<,>))
|
|
{
|
|
if (typeof(JobSettings).IsAssignableFrom(genericTypes[0]))
|
|
{
|
|
settingType = genericTypes[0];
|
|
resultType = genericTypes[1];
|
|
}
|
|
else
|
|
{
|
|
settingType = genericTypes[1];
|
|
resultType = genericTypes[0];
|
|
}
|
|
}
|
|
else if (currentGenericType == typeof(JobBase<>))
|
|
{
|
|
if (typeof(JobSettings).IsAssignableFrom(genericTypes[0]))
|
|
{
|
|
settingType = genericTypes[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return settingType != null;
|
|
}
|
|
|
|
private bool TryGetAssemblyTypes(Assembly assembly, out Type[] types)
|
|
{
|
|
bool succeeded;
|
|
|
|
try
|
|
{
|
|
types = assembly.GetTypes();
|
|
succeeded = true;
|
|
}
|
|
catch (ReflectionTypeLoadException e)
|
|
{
|
|
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped(256);
|
|
StringBuilder sb = lease.Builder;
|
|
sb.AppendFormat("Failed to load assembly types. {0}: {1} (exceptions-count = {2}).{3}", e.GetType().ToString(false), e.Message, e.LoaderExceptions?.Length ?? -1, Environment.NewLine);
|
|
foreach (Exception? le in e.LoaderExceptions ?? Array.Empty<Exception>())
|
|
{
|
|
if (le == null)
|
|
{
|
|
continue;
|
|
}
|
|
sb.AppendFormat("\t{0}: {1}{2}", le.GetType().ToString(false), le.Message, Environment.NewLine);
|
|
}
|
|
CLog.Error(StringBuilderCache.GetStringAndRelease(sb));
|
|
|
|
types = Array.Empty<Type>();
|
|
succeeded = false;
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("count = {0}", _items.Count);
|
|
}
|
|
}
|
|
|