From 58f89014f5059f5c7cc9ba2823afedb4a06e1b2e Mon Sep 17 00:00:00 2001 From: Leon Aquitaine Date: Wed, 11 Oct 2023 23:26:31 -0400 Subject: [PATCH 1/5] Added GetParentTypes --- Zen.Base/Extension/Reflection.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Zen.Base/Extension/Reflection.cs b/Zen.Base/Extension/Reflection.cs index 18cd00ef..4df34317 100644 --- a/Zen.Base/Extension/Reflection.cs +++ b/Zen.Base/Extension/Reflection.cs @@ -111,6 +111,33 @@ public static T GetObject(this IDictionary dict, Dictionary GetParentTypes(this Type type) + { + // https://stackoverflow.com/a/18375526/1845714 + + // is there any base type? + if (type == null) + { + yield break; + } + + // return all implemented or inherited interfaces + foreach (var i in type.GetInterfaces()) + { + yield return i; + } + + // return all inherited types + var currentBaseType = type.BaseType; + while (currentBaseType != null) + { + yield return currentBaseType; + currentBaseType = currentBaseType.BaseType; + } + } + + + /// /// Gets the method ext. /// From a971f42d7986c9c8a733099a8e4ad6690b844694 Mon Sep 17 00:00:00 2001 From: Leon Aquitaine Date: Wed, 11 Oct 2023 23:27:08 -0400 Subject: [PATCH 2/5] Initial MessageQueue generic processor --- Zen.MessageQueue/Configuration.cs | 35 ++++++++++++++ Zen.MessageQueue/Current.cs | 8 ++++ Zen.MessageQueue/Queue.cs | 46 +++++++++++++++++++ .../Shared/IMessageQueueBundle.cs | 9 ++++ .../Shared/MessageQueuePrimitive.cs | 11 +++++ Zen.MessageQueue/Zen.MessageQueue.csproj | 11 +++++ 6 files changed, 120 insertions(+) create mode 100644 Zen.MessageQueue/Configuration.cs create mode 100644 Zen.MessageQueue/Current.cs create mode 100644 Zen.MessageQueue/Queue.cs create mode 100644 Zen.MessageQueue/Shared/IMessageQueueBundle.cs create mode 100644 Zen.MessageQueue/Shared/MessageQueuePrimitive.cs create mode 100644 Zen.MessageQueue/Zen.MessageQueue.csproj diff --git a/Zen.MessageQueue/Configuration.cs b/Zen.MessageQueue/Configuration.cs new file mode 100644 index 00000000..a1e9e349 --- /dev/null +++ b/Zen.MessageQueue/Configuration.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Options; +using Zen.Base.Common; +using Zen.Base.Extension; +using Zen.Base.Module.Service; + +namespace Zen.MessageQueue +{ + public class Configuration : IConfigureOptions + { + private readonly IOptions _options; + + public Configuration(IOptions options) => _options = options.Value; + + public void Configure(IOptions options) + { + _options.CopyMembersTo(options); + } + + public interface IOptions + { + int HttpPort { get; set; } + int HttpsPort { get; set; } + } + + [IoCIgnore] + public class Options : AutoOptions { } + + [Priority(Level = -99)] + public class AutoOptions : IOptions // If nothing else is defined, AutoOptions kicks in. + { + public int HttpPort { get; set; } = 5000; + public int HttpsPort { get; set; } = 5001; + } + } +} \ No newline at end of file diff --git a/Zen.MessageQueue/Current.cs b/Zen.MessageQueue/Current.cs new file mode 100644 index 00000000..99e62a55 --- /dev/null +++ b/Zen.MessageQueue/Current.cs @@ -0,0 +1,8 @@ +namespace Zen.MessageQueue +{ + public static class Current + { + + public readonly static Configuration.IOptions Options = Base.Configuration.GetSettings(new Configuration.Options(), "MessageQueue"); + } +} \ No newline at end of file diff --git a/Zen.MessageQueue/Queue.cs b/Zen.MessageQueue/Queue.cs new file mode 100644 index 00000000..7b42d140 --- /dev/null +++ b/Zen.MessageQueue/Queue.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Zen.Base.Extension; +using Zen.Base.Module.Service; +using Zen.MessageQueue.Shared; + +namespace Zen.MessageQueue +{ + + public delegate void MessageReceivedHandler(object item); + + public static class Queue + { + private static readonly IMessageQueueBundle DefaultBundle = (IMessageQueueBundle)IoC.GetClassesByInterface(false)?.First()?.CreateInstance(); + + private static readonly Dictionary _cache = new Dictionary(); + + public static event MessageReceivedHandler Receive; + + + public static void RegisterType(this T targetObject) + { + + if (DefaultBundle == null) + throw new InvalidOperationException("No Message Queue adapter specificed. Try adding a Zen.Module.MQ.* reference."); + + var type = typeof(T); + + if (_cache.ContainsKey(type)) return; + MessageQueuePrimitive adapter = DefaultBundle.AdapterType.CreateGenericInstance>(); + + _cache[typeof(T)] = adapter; + + adapter.Receive += (item) => + { + Receive?.Invoke(item); + }; + } + + public static void Send(this T targetObject) + { + ((MessageQueuePrimitive)_cache[typeof(T)]).Send(targetObject); + } + } +} diff --git a/Zen.MessageQueue/Shared/IMessageQueueBundle.cs b/Zen.MessageQueue/Shared/IMessageQueueBundle.cs new file mode 100644 index 00000000..fdcd363c --- /dev/null +++ b/Zen.MessageQueue/Shared/IMessageQueueBundle.cs @@ -0,0 +1,9 @@ +using System; + +namespace Zen.MessageQueue.Shared +{ + public interface IMessageQueueBundle + { + Type AdapterType { get; set; } + } +} diff --git a/Zen.MessageQueue/Shared/MessageQueuePrimitive.cs b/Zen.MessageQueue/Shared/MessageQueuePrimitive.cs new file mode 100644 index 00000000..77f0129f --- /dev/null +++ b/Zen.MessageQueue/Shared/MessageQueuePrimitive.cs @@ -0,0 +1,11 @@ +namespace Zen.MessageQueue.Shared +{ + public delegate void MessageReceivedHandler(T item); + + public abstract class MessageQueuePrimitive + { + public virtual void Send(T item) { } + public virtual event MessageReceivedHandler Receive; + + } +} \ No newline at end of file diff --git a/Zen.MessageQueue/Zen.MessageQueue.csproj b/Zen.MessageQueue/Zen.MessageQueue.csproj new file mode 100644 index 00000000..6e2ac88b --- /dev/null +++ b/Zen.MessageQueue/Zen.MessageQueue.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + From 3061fe969164a660d4847099375185ed73afbadf Mon Sep 17 00:00:00 2001 From: Leon Aquitaine Date: Wed, 11 Oct 2023 23:27:17 -0400 Subject: [PATCH 3/5] Cleanup --- Zen.Base/Module/Data/Adapter/DataAdapterPrimitive.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Zen.Base/Module/Data/Adapter/DataAdapterPrimitive.cs b/Zen.Base/Module/Data/Adapter/DataAdapterPrimitive.cs index 17c0459b..eb39ac49 100644 --- a/Zen.Base/Module/Data/Adapter/DataAdapterPrimitive.cs +++ b/Zen.Base/Module/Data/Adapter/DataAdapterPrimitive.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq.Expressions; using Zen.Base.Module.Data.Connection; -using Zen.Base.Module.Log; namespace Zen.Base.Module.Data.Adapter { From 77f3635f803d90503cd57deb7e1e6281104d13ac Mon Sep 17 00:00:00 2001 From: Leon Aquitaine Date: Wed, 11 Oct 2023 23:30:13 -0400 Subject: [PATCH 4/5] initial RabbitMQ support --- Zen.Module.MQ.RabbitMQ/Configuration.cs | 44 +++++++++++++++ Zen.Module.MQ.RabbitMQ/RabbitMQAdapter.cs | 56 +++++++++++++++++++ .../RabbitMQDefaultBundle.cs | 12 ++++ .../Zen.Module.MQ.RabbitMQ.csproj | 16 ++++++ 4 files changed, 128 insertions(+) create mode 100644 Zen.Module.MQ.RabbitMQ/Configuration.cs create mode 100644 Zen.Module.MQ.RabbitMQ/RabbitMQAdapter.cs create mode 100644 Zen.Module.MQ.RabbitMQ/RabbitMQDefaultBundle.cs create mode 100644 Zen.Module.MQ.RabbitMQ/Zen.Module.MQ.RabbitMQ.csproj diff --git a/Zen.Module.MQ.RabbitMQ/Configuration.cs b/Zen.Module.MQ.RabbitMQ/Configuration.cs new file mode 100644 index 00000000..b426a111 --- /dev/null +++ b/Zen.Module.MQ.RabbitMQ/Configuration.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Options; +using Zen.Base.Common; +using Zen.Base.Module.Service; + +namespace Zen.Module.MQ.RabbitMQ +{ + public class Configuration : IConfigureOptions + { + private readonly IOptions _options; + + public Configuration(IOptions options) => _options = options.Value; + + public void Configure(Options options) + { + options.HostName = _options.HostName; + } + + public interface IOptions + { + string HostName { get; set; } + bool Durable { get; set; } + bool Exclusive { get; set; } + bool AutoDelete { get; set; } + } + + [IoCIgnore] + public class Options : IOptions + { + public string HostName { get; set; } + public bool Durable { get; set; } + public bool Exclusive { get; set; } + public bool AutoDelete { get; set; } + } + + [Priority(Level = -99)] + public class AutoOptions : IOptions // If nothing else is defined, AutoOptions kicks in. + { + public string HostName { get; set; } = @"localhost"; + public bool Durable { get; set; } = true; + public bool Exclusive { get; set; } = false; + public bool AutoDelete { get; set; } = true; + } + } +} diff --git a/Zen.Module.MQ.RabbitMQ/RabbitMQAdapter.cs b/Zen.Module.MQ.RabbitMQ/RabbitMQAdapter.cs new file mode 100644 index 00000000..372959ef --- /dev/null +++ b/Zen.Module.MQ.RabbitMQ/RabbitMQAdapter.cs @@ -0,0 +1,56 @@ +using RabbitMQ.Client; +using Zen.Base; +using Zen.MessageQueue.Shared; +using Zen.Base.Extension; +using System.Text; +using RabbitMQ.Client.Events; +using System.Linq; +using System.Collections.Generic; + +namespace Zen.Module.MQ.RabbitMQ +{ + public class RabbitMQAdapter : MessageQueuePrimitive + { + private readonly Configuration.IOptions _options; + private readonly IModel _channel; + private readonly string _queueName; + private readonly List _categories; + + public RabbitMQAdapter() + { + _options = new Configuration.Options().GetSettings("MessageQueue:RabbitMQ"); + + var factory = new ConnectionFactory { HostName = _options.HostName }; + var connection = factory.CreateConnection(); + _channel = connection.CreateModel(); + + _categories = typeof(T).GetParentTypes().Select(i => i.Name).ToList(); + _queueName = typeof(T).FullName; + + + _channel.QueueDeclare(_queueName, durable: _options.Durable, exclusive: _options.Exclusive, autoDelete: _options.AutoDelete); + + var consumer = new EventingBasicConsumer(_channel); + + consumer.Received += (model, ea) => + { + var body = ea.Body.ToArray(); + var item = Encoding.UTF8.GetString(body).FromJson(); + + Receive?.Invoke(item); + }; + + _channel.BasicConsume(queue: _queueName, autoAck: true, consumer: consumer); + } + + public override event MessageReceivedHandler Receive; + + public override void Send(T item) + { + var payload = item.ToJson(); + var body = Encoding.UTF8.GetBytes(payload); + + _channel.BasicPublish(exchange: string.Empty, routingKey: _queueName, basicProperties: null, body: body); + } + } +} \ No newline at end of file diff --git a/Zen.Module.MQ.RabbitMQ/RabbitMQDefaultBundle.cs b/Zen.Module.MQ.RabbitMQ/RabbitMQDefaultBundle.cs new file mode 100644 index 00000000..71641238 --- /dev/null +++ b/Zen.Module.MQ.RabbitMQ/RabbitMQDefaultBundle.cs @@ -0,0 +1,12 @@ +using System; +using Zen.Base.Common; +using Zen.MessageQueue.Shared; + +namespace Zen.Module.MQ.RabbitMQ +{ + [Priority(Level = -2)] + public class RabbitMQDefaultBundle : IMessageQueueBundle + { + public Type AdapterType { get; set; } = typeof(RabbitMQAdapter<>); + } +} \ No newline at end of file diff --git a/Zen.Module.MQ.RabbitMQ/Zen.Module.MQ.RabbitMQ.csproj b/Zen.Module.MQ.RabbitMQ/Zen.Module.MQ.RabbitMQ.csproj new file mode 100644 index 00000000..66589bc1 --- /dev/null +++ b/Zen.Module.MQ.RabbitMQ/Zen.Module.MQ.RabbitMQ.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + From 3b856b75a7cb3e46ae173d64a3e2a447d5fedc87 Mon Sep 17 00:00:00 2001 From: Leon Aquitaine Date: Wed, 11 Oct 2023 23:30:44 -0400 Subject: [PATCH 5/5] Added Docker sample references --- Samples/Sample05-Docker/Program.cs | 11 ++++++++ .../Properties/launchSettings.json | 2 ++ .../Sample05-Docker/Sample05-Docker.csproj | 6 ++--- Samples/Sample05-Docker/appsettings.json | 9 +++++-- Zen.sln | 14 +++++++++- docker-compose.yml | 26 ++++++++++++++++--- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/Samples/Sample05-Docker/Program.cs b/Samples/Sample05-Docker/Program.cs index a8e86066..6a90fb78 100644 --- a/Samples/Sample05-Docker/Program.cs +++ b/Samples/Sample05-Docker/Program.cs @@ -1,3 +1,4 @@ +using MongoDB.Bson; using Zen.Web.Host; namespace Sample05_Docker @@ -6,7 +7,17 @@ public class Program { public static void Main(string[] args) { + Zen.MessageQueue.Queue.RegisterType("string"); + Zen.MessageQueue.Queue.Send("string"); + Zen.MessageQueue.Queue.Receive += (model) => + { + Zen.Base.Log.Add(model.ToJson()); + }; + + Builder.Start(args); + + } } } diff --git a/Samples/Sample05-Docker/Properties/launchSettings.json b/Samples/Sample05-Docker/Properties/launchSettings.json index 3e44b5d8..fb8373ea 100644 --- a/Samples/Sample05-Docker/Properties/launchSettings.json +++ b/Samples/Sample05-Docker/Properties/launchSettings.json @@ -25,6 +25,8 @@ "ASPNETCORE_URLS": "https://+:443;http://+:80" }, "publishAllPorts": true, + "httpPort": 80, + "sslPort": 443, "useSSL": true } }, diff --git a/Samples/Sample05-Docker/Sample05-Docker.csproj b/Samples/Sample05-Docker/Sample05-Docker.csproj index abf4e088..912c6b29 100644 --- a/Samples/Sample05-Docker/Sample05-Docker.csproj +++ b/Samples/Sample05-Docker/Sample05-Docker.csproj @@ -16,14 +16,12 @@ + + - - - - diff --git a/Samples/Sample05-Docker/appsettings.json b/Samples/Sample05-Docker/appsettings.json index 6374f733..b793fd66 100644 --- a/Samples/Sample05-Docker/appsettings.json +++ b/Samples/Sample05-Docker/appsettings.json @@ -11,8 +11,13 @@ "EnableHtml5": false }, "Database": { - "MongoDB1": { - "ConnectionString": "mongodb://db-1:27017/zen-samples" + "MongoDB": { + "ConnectionString": "mongodb://db:27017/zen-samples" + } + }, + "MessageQueue": { + "RabbitMQ": { + "HostName": "mq" } }, "AllowedHosts": "*" diff --git a/Zen.sln b/Zen.sln index ff348de9..7e706a9c 100644 --- a/Zen.sln +++ b/Zen.sln @@ -83,10 +83,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zen.Provider.OpenId", "Zen. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zen.Provider.Discord", "Zen.Provider.Discord\Zen.Provider.Discord.csproj", "{480AC12F-C560-41A8-800F-5B2E3B4A2748}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample05-Docker", "Samples\Sample05-Docker\Sample05-Docker.csproj", "{C77E403D-ADAD-4613-B19C-C603EA98DCA3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample05-Docker", "Samples\Sample05-Docker\Sample05-Docker.csproj", "{C77E403D-ADAD-4613-B19C-C603EA98DCA3}" EndProject Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{B7D3FBD5-FA0E-42D8-8871-22F50394ADBC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zen.MessageQueue", "Zen.MessageQueue\Zen.MessageQueue.csproj", "{AA3CEC1D-AB1C-46C4-8F9A-BEADEEE3E149}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zen.Module.MQ.RabbitMQ", "Zen.Module.MQ.RabbitMQ\Zen.Module.MQ.RabbitMQ.csproj", "{52F92880-A5A2-44ED-AA24-D2A7F0DA0201}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -257,6 +261,14 @@ Global {B7D3FBD5-FA0E-42D8-8871-22F50394ADBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7D3FBD5-FA0E-42D8-8871-22F50394ADBC}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7D3FBD5-FA0E-42D8-8871-22F50394ADBC}.Release|Any CPU.Build.0 = Release|Any CPU + {AA3CEC1D-AB1C-46C4-8F9A-BEADEEE3E149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA3CEC1D-AB1C-46C4-8F9A-BEADEEE3E149}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA3CEC1D-AB1C-46C4-8F9A-BEADEEE3E149}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA3CEC1D-AB1C-46C4-8F9A-BEADEEE3E149}.Release|Any CPU.Build.0 = Release|Any CPU + {52F92880-A5A2-44ED-AA24-D2A7F0DA0201}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52F92880-A5A2-44ED-AA24-D2A7F0DA0201}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52F92880-A5A2-44ED-AA24-D2A7F0DA0201}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52F92880-A5A2-44ED-AA24-D2A7F0DA0201}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docker-compose.yml b/docker-compose.yml index 891beefc..02795f4d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,23 +4,41 @@ services: sample05-docker: image: ${DOCKER_REGISTRY-}sample05docker ports: - - 5000:5000 + - 5000:80 + - 5001:443 build: context: . dockerfile: Samples/Sample05-Docker/Dockerfile - links: - - "db:database" networks: - frontend - backend - db: + depends_on: + - "database" + - "messagequeue" + + database: image: mongo:latest + hostname: "db" + labels: + NAME: "MongoDB" ports: - 27017:27017 volumes: - ./data:/data/db networks: - backend + + messagequeue: # login guest:guest + image: rabbitmq:3-management + hostname: "mq" + labels: + NAME: "rabbitmq" + ports: + - "5672:5672" + - "15672:15672" + networks: + - backend + networks: frontend: backend: