using System; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Reflection; using System.Threading; using ZeroLevel.Network; namespace ZeroLevel.Services.Applications { public abstract class BaseZeroService : IZeroService { private readonly ZeroServiceInfo _serviceInfo = new ZeroServiceInfo(); private readonly IExchange _exhange; protected IExchange Exchange => _exhange; public ZeroServiceInfo ServiceInfo => _serviceInfo; public string Name { get { return _serviceInfo.Name; } private set { _serviceInfo.Name = value; } } public string Key { get { return _serviceInfo.ServiceKey; } private set { _serviceInfo.ServiceKey = value; } } public string Version { get { return _serviceInfo.Version; } private set { _serviceInfo.Version = value; } } public string Group { get { return _serviceInfo.ServiceGroup; } private set { _serviceInfo.ServiceGroup = value; } } public string Type { get { return _serviceInfo.ServiceType; } private set { _serviceInfo.ServiceType = value; } } public ZeroServiceStatus Status => _state; private ZeroServiceStatus _state; protected BaseZeroService() { Name = GetType().Name; _exhange = new Exchange(this); } protected BaseZeroService(string name) { Name = name; _exhange = new Exchange(this); } protected abstract void StartAction(); protected abstract void StopAction(); #region Config private const string DEFAULT_GROUP_NAME = "__default_group__"; private const string DEFAULT_TYPE_NAME = "__default_type__"; public void ReadServiceInfo() { this.Name = ReadName(); this.Key = ReadKey(); this.Version = ReadVersion(); this.Group = ReadServiceGroup(); this.Type = ReadServiceType(); } public void ReadServiceInfo(IConfigurationSet set) { this.Name = ReadName(set); this.Key = ReadKey(set); this.Version = ReadVersion(set); this.Group = ReadServiceGroup(set); this.Type = ReadServiceType(set); } private string ReadName(IConfigurationSet set = null) { return FindInConfig(set, new[] { "ServiceName", "AppName" }, string.Empty, "service") ?? this.GetType().Name; } private string ReadKey(IConfigurationSet set = null) { return FindInConfig(set, new[] { "ServiceKey", "AppKey" }, string.Empty, "service"); } private string ReadVersion(IConfigurationSet set = null) { return FindInConfig(set, new[] { "Version", "AppVersion" }, string.Empty, "service") ?? "1.0"; } private string ReadServiceGroup(IConfigurationSet set = null) { return FindInConfig(set, new[] { "DiscoveryGroup", "ServiceGroup" }, string.Empty, "service") ?? DEFAULT_GROUP_NAME; } private string ReadServiceType(IConfigurationSet set = null) { return FindInConfig(set, new[] { "DiscoveryType", "ServiceType" }, string.Empty, "service") ?? DEFAULT_TYPE_NAME; } protected T FindInConfig(IConfigurationSet set, string[] keys, params string[] sections) { var configSet = set ?? Configuration.DefaultSet; foreach (var section in sections) { if (string.IsNullOrWhiteSpace(section)) { foreach (var key in keys) { if (configSet.Default.Contains(key)) { return configSet.Default.First(key); } } } else if (configSet.ContainsSection(section)) { foreach (var key in keys) { if (configSet[section].Contains(key)) { return configSet[section].First(key); } } } } return default(T); } #endregion Config #region Network public void UseDiscovery() { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { _exhange.UseDiscovery(); } } public void UseDiscovery(string endpoint) { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { _exhange.UseDiscovery(endpoint); } } public void UseDiscovery(IPEndPoint endpoint) { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { _exhange.UseDiscovery(endpoint); } } public IRouter UseHost() { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { return _exhange.UseHost(); } return BaseSocket.NullRouter; } public IRouter UseHost(int port) { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { return _exhange.UseHost(port); } return BaseSocket.NullRouter; } public IRouter UseHost(IPEndPoint endpoint) { if (_state == ZeroServiceStatus.Running || _state == ZeroServiceStatus.Initialized) { return _exhange.UseHost(endpoint); } return BaseSocket.NullRouter; } #region Autoregistration inboxes private static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo, object target) { Func getType; var isAction = methodInfo.ReturnType.Equals((typeof(void))); if (isAction) { getType = Expression.GetActionType; } else { getType = Expression.GetFuncType; } if (methodInfo.IsStatic) { return Delegate.CreateDelegate(delegateType, methodInfo); } return Delegate.CreateDelegate(delegateType, target, methodInfo.Name); } public void AutoregisterInboxes(IServer server) { var type = server.GetType(); // Search router registerinbox methods with inbox name var register_methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)? .Where(mi => mi.Name.Equals("RegisterInbox", StringComparison.Ordinal) && mi.GetParameters().First().ParameterType == typeof(string)); var register_message_handler = register_methods.First(mi => mi.IsGenericMethod == false); var register_message_handler_with_msg = register_methods.First(mi => { if (mi.IsGenericMethod) { var paremeters = mi.GetParameters().ToArray(); if (paremeters.Length == 2 && paremeters[1].ParameterType.IsAssignableToGenericType(typeof(MessageHandler<>))) { return true; } } return false; }); var register_request_handler_without_msg = register_methods.First(mi => { if (mi.IsGenericMethod) { var paremeters = mi.GetParameters().ToArray(); if (paremeters.Length == 2 && paremeters[1].ParameterType.IsAssignableToGenericType(typeof(RequestHandler<>))) { return true; } } return false; }); var register_request_handler = register_methods.First(mi => { if (mi.IsGenericMethod) { var paremeters = mi.GetParameters().ToArray(); if (paremeters.Length == 2 && paremeters[1].ParameterType.IsAssignableToGenericType(typeof(RequestHandler<,>))) { return true; } } return false; }); MethodInfo[] methods = this. GetType(). GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Instance); foreach (MethodInfo mi in methods) { try { foreach (Attribute attr in Attribute.GetCustomAttributes(mi, typeof(ExchangeAttribute))) { var args = mi.GetParameters().ToArray(); if (attr.GetType() == typeof(ExchangeMainHandlerAttribute)) { if (args.Length == 1) { var handler = CreateDelegate(typeof(MessageHandler), mi, this); register_message_handler.Invoke(server, new object[] { BaseSocket.DEFAULT_MESSAGE_INBOX, handler }); } else { var handler = CreateDelegate(typeof(MessageHandler<>).MakeGenericType(args[1].ParameterType), mi, this); MethodInfo genericMethod = register_message_handler_with_msg.MakeGenericMethod(args[1].ParameterType); genericMethod.Invoke(server, new object[] { BaseSocket.DEFAULT_MESSAGE_INBOX, handler }); } } else if (attr.GetType() == typeof(ExchangeHandlerAttribute)) { if (args.Length == 1) { var handler = CreateDelegate(typeof(MessageHandler), mi, this); register_message_handler.Invoke(server, new object[] { (attr as ExchangeHandlerAttribute).Inbox, handler }); } else { var handler = CreateDelegate(typeof(MessageHandler<>).MakeGenericType(args[1].ParameterType), mi, this); MethodInfo genericMethod = register_message_handler_with_msg.MakeGenericMethod(args[1].ParameterType); genericMethod.Invoke(server, new object[] { (attr as ExchangeHandlerAttribute).Inbox, handler }); } } else if (attr.GetType() == typeof(ExchangeMainReplierAttribute)) { var returnType = mi.ReturnType; var genArgType = args[1].ParameterType; MethodInfo genericMethod = register_request_handler.MakeGenericMethod(genArgType, returnType); var requestHandler = CreateDelegate(typeof(RequestHandler<,>).MakeGenericType(args[1].ParameterType, returnType), mi, this); genericMethod.Invoke(server, new object[] { BaseSocket.DEFAULT_REQUEST_INBOX, requestHandler }); } else if (attr.GetType() == typeof(ExchangeReplierAttribute)) { var returnType = mi.ReturnType; var genArgType = args[1].ParameterType; MethodInfo genericMethod = register_request_handler.MakeGenericMethod(genArgType, returnType); var requestHandler = CreateDelegate(typeof(RequestHandler<,>).MakeGenericType(args[1].ParameterType, returnType), mi, this); genericMethod.Invoke(server, new object[] { (attr as ExchangeReplierAttribute).Inbox, requestHandler }); } else if (attr.GetType() == typeof(ExchangeMainReplierWithoutArgAttribute)) { var returnType = mi.ReturnType; MethodInfo genericMethod = register_request_handler_without_msg.MakeGenericMethod(returnType); var requestHandler = CreateDelegate(typeof(RequestHandler<>).MakeGenericType(returnType), mi, this); genericMethod.Invoke(server, new object[] { BaseSocket.DEFAULT_REQUEST_WITHOUT_ARGS_INBOX, requestHandler }); } else if (attr.GetType() == typeof(ExchangeReplierWithoutArgAttribute)) { var returnType = mi.ReturnType; MethodInfo genericMethod = register_request_handler_without_msg.MakeGenericMethod(returnType); var requestHandler = CreateDelegate(typeof(RequestHandler<>).MakeGenericType(returnType), mi, this); genericMethod.Invoke(server, new object[] { (attr as ExchangeReplierWithoutArgAttribute).Inbox, requestHandler }); } } } catch (Exception ex) { Log.Debug($"[ZExchange] Can't register method {mi.Name} as inbox handler. {ex}"); } } } #endregion #endregion #region Service control public void Start() { if (_state == ZeroServiceStatus.Initialized) { try { StartAction(); _state = ZeroServiceStatus.Running; Log.Debug($"[{Name}] Service started"); } catch (Exception ex) { Log.Fatal(ex, $"[{Name}] Failed to start service"); } } } public void Stop() { if (_state == ZeroServiceStatus.Running) { try { StopAction(); Log.Debug($"[{Name}] Service stopped"); } catch (Exception ex) { Log.Fatal(ex, $"[{Name}] Failed to stop service"); } } _state = ZeroServiceStatus.Stopped; } public void WaitForStatus(ZeroServiceStatus status) { var start = DateTime.UtcNow; while (this.Status != status) { Thread.Sleep(1500); } } public void WaitForStatus(ZeroServiceStatus status, TimeSpan period) { var start = DateTime.UtcNow; while (this.Status != status && (DateTime.UtcNow - start) < period) { Thread.Sleep(1500); } } public void WaitWhileStatus(ZeroServiceStatus status) { var start = DateTime.UtcNow; while (this.Status == status) { Thread.Sleep(1500); } } public void WaitWhileStatus(ZeroServiceStatus status, TimeSpan period) { var start = DateTime.UtcNow; while (this.Status == status && (DateTime.UtcNow - start) < period) { Thread.Sleep(1500); } } #endregion public void Dispose() { if (_state != ZeroServiceStatus.Disposed) { _state = ZeroServiceStatus.Disposed; _exhange.Dispose(); } } } }