Implement inter-plugin communication system + add some showcase of it

This commit is contained in:
ChronosX88 2020-09-28 21:49:09 +04:00
parent a829042581
commit bd74068d6f
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
11 changed files with 139 additions and 68 deletions

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Reflection;
namespace Zirconium.Core.Plugins.IPC
{
public class ExportedIPCMethod
{
public object Service { get; set; }
public string MethodName { get; set; }
public MethodInfo Method { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Zirconium.Core.Plugins.IPC
{
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ExportedIPCMethodAttribute : System.Attribute
{
public string MethodName { get; private set; }
public ExportedIPCMethodAttribute(string name)
{
this.MethodName = name;
}
}
}

View File

@ -0,0 +1,53 @@
using System.Threading.Tasks;
using System.Linq;
using System;
using System.Collections.Generic;
using Zirconium.Utils;
namespace Zirconium.Core.Plugins.IPC
{
public class IPCRouter
{
private IDictionary<string, IList<ExportedIPCMethod>> methodTable = new Dictionary<string, IList<ExportedIPCMethod>>();
public void RegisterIPCService(string pluginName, object service)
{
Type t = service.GetType();
var exportedMethods = t.GetMethods()
.Where(m => m.GetCustomAttributes(typeof(ExportedIPCMethodAttribute), false).Length > 0)
.ToArray();
foreach (var m in exportedMethods)
{
var attr = (ExportedIPCMethodAttribute)m.GetCustomAttributes(typeof(ExportedIPCMethodAttribute), false).First();
var exportedMethod = new ExportedIPCMethod();
exportedMethod.Service = service;
exportedMethod.MethodName = attr.MethodName;
exportedMethod.Method = m;
if (methodTable.GetValueOrDefault(pluginName, null) == null)
{
methodTable[pluginName] = new List<ExportedIPCMethod>();
}
methodTable[pluginName].Add(exportedMethod);
}
}
public Task<dynamic> MakeRequest(string pluginName, string methodName, dynamic paramsObject)
{
return Task.Factory.StartNew<dynamic>(() =>
{
var method = methodTable[pluginName].Where(x => x.MethodName == methodName).FirstOrDefault();
var returnValue = method.Method.Invoke(method.Service, new object[] {paramsObject});
return returnValue;
});
}
public Task MakeNotif(string pluginName, string methodName, dynamic paramsObject)
{
return Task.Factory.StartNew(() =>
{
var method = methodTable[pluginName].Where(x => x.MethodName == methodName).FirstOrDefault();
method.Method.Invoke(method.Service, paramsObject);
});
}
}
}

View File

@ -7,7 +7,5 @@ namespace Zirconium.Core.Plugins.Interfaces
void Initialize(IPluginHostAPI hostModuleAPI); void Initialize(IPluginHostAPI hostModuleAPI);
void PreInitialize(IPluginManager pluginManager); void PreInitialize(IPluginManager pluginManager);
string GetPluginUniqueName(); string GetPluginUniqueName();
dynamic GetExportedAPI();
Type[] GetExportedTypes();
} }
} }

View File

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Zirconium.Core.Models; using Zirconium.Core.Models;
namespace Zirconium.Core.Plugins.Interfaces namespace Zirconium.Core.Plugins.Interfaces
@ -17,5 +18,8 @@ namespace Zirconium.Core.Plugins.Interfaces
dynamic GetSettings(string pluginName); dynamic GetSettings(string pluginName);
void ProvideAuth(IAuthProvider provider); void ProvideAuth(IAuthProvider provider);
IExposedSessionManager GetSessionManager(); IExposedSessionManager GetSessionManager();
void RegisterIPCService(IPluginAPI plugin, dynamic service);
Task<dynamic> MakeIPCRequest(string pluginName, string methodName, dynamic paramsObject);
Task MakeIPCNotification(string pluginName, string methodName, dynamic paramsObject);
} }
} }

View File

@ -2,6 +2,6 @@ namespace Zirconium.Core.Plugins.Interfaces
{ {
public interface IPluginManager public interface IPluginManager
{ {
dynamic Depends(IPluginAPI currentPlugin, string pluginName); void Depends(IPluginAPI currentPlugin, string pluginName);
} }
} }

View File

@ -1,7 +1,10 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using Zirconium.Core.Models; using Zirconium.Core.Models;
using Zirconium.Core.Plugins.Interfaces; using Zirconium.Core.Plugins.Interfaces;
using Zirconium.Core.Plugins.IPC;
namespace Zirconium.Core.Plugins namespace Zirconium.Core.Plugins
{ {
@ -9,11 +12,13 @@ namespace Zirconium.Core.Plugins
{ {
private App _app; private App _app;
private Router _router; private Router _router;
private IPCRouter _ipcRouter;
public PluginHostAPI(App app, Router router) public PluginHostAPI(App app, Router router)
{ {
_router = router; _router = router;
_app = app; _app = app;
_ipcRouter = new IPCRouter();
} }
public IExposedSessionManager GetSessionManager() { public IExposedSessionManager GetSessionManager() {
@ -78,5 +83,18 @@ namespace Zirconium.Core.Plugins
{ {
_router.RemoveCoreEventHandler(handler.GetHandlingEventType(), handler); _router.RemoveCoreEventHandler(handler.GetHandlingEventType(), handler);
} }
public void RegisterIPCService(IPluginAPI plugin, dynamic service)
{
_ipcRouter.RegisterIPCService(plugin.GetPluginUniqueName(), service);
}
public Task<dynamic> MakeIPCRequest(string pluginName, string methodName, dynamic paramsObject) {
return _ipcRouter.MakeRequest(pluginName, methodName, paramsObject);
}
public Task MakeIPCNotification(string pluginName, string methodName, dynamic paramsObject) {
return _ipcRouter.MakeNotif(pluginName, methodName, paramsObject);
}
} }
} }

View File

@ -8,6 +8,7 @@ using McMaster.NETCore.Plugins;
using Zirconium.Core.Logging; using Zirconium.Core.Logging;
using Zirconium.Core.Models; using Zirconium.Core.Models;
using Zirconium.Core.Plugins.Interfaces; using Zirconium.Core.Plugins.Interfaces;
using Zirconium.Core.Plugins.IPC;
using Zirconium.Utils; using Zirconium.Utils;
namespace Zirconium.Core.Plugins namespace Zirconium.Core.Plugins
@ -73,7 +74,8 @@ namespace Zirconium.Core.Plugins
typeof(IC2SMessageHandler), typeof(IC2SMessageHandler),
typeof(ICoreEventHandler), typeof(ICoreEventHandler),
typeof(BaseMessage), typeof(BaseMessage),
typeof(CoreEvent) typeof(CoreEvent),
typeof(ExportedIPCMethodAttribute)
}, },
config => config.PreferSharedTypes = true config => config.PreferSharedTypes = true
); );
@ -92,28 +94,18 @@ namespace Zirconium.Core.Plugins
// This assumes the implementation of IPlugin has a parameterless constructor // This assumes the implementation of IPlugin has a parameterless constructor
plugin = (IPluginAPI)Activator.CreateInstance(pluginType); plugin = (IPluginAPI)Activator.CreateInstance(pluginType);
Logging.Log.Debug($"Created plugin instance '{plugin.GetPluginUniqueName()}'."); Logging.Log.Debug($"Created plugin instance '{plugin.GetPluginUniqueName()}'.");
var exportedTypes = plugin.GetExportedTypes();
if (exportedTypes != null)
{
foreach (var type in exportedTypes)
{
var path = new Uri(type.Module.Assembly.CodeBase).LocalPath;
AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
}
}
plugin.PreInitialize(this); plugin.PreInitialize(this);
plugin.Initialize(_pluginHostAPI); plugin.Initialize(_pluginHostAPI);
} }
return plugin; return plugin;
} }
public dynamic Depends(IPluginAPI currentPlugin, string pluginName) public void Depends(IPluginAPI currentPlugin, string pluginName)
{ {
var dependantPlugin = _plugins.GetValueOrDefault(pluginName, null); var dependantPlugin = _plugins.GetValueOrDefault(pluginName, null);
if (dependantPlugin != null) return dependantPlugin.GetExportedAPI(); if (dependantPlugin != null) return;
this.LoadPlugin(pluginName); this.LoadPlugin(pluginName);
dependantPlugin = _plugins[pluginName]; dependantPlugin = _plugins[pluginName];
return dependantPlugin.GetExportedAPI();
} }
} }
} }

View File

@ -1,8 +1,10 @@
using System.Collections.Immutable; using System;
using System.Collections.Immutable;
using System.Collections.Generic; using System.Collections.Generic;
using Zirconium.Core.Plugins.Interfaces; using Zirconium.Core.Plugins.Interfaces;
using MongoDB.Driver; using MongoDB.Driver;
using Zirconium.Core.Logging; using Zirconium.Core.Logging;
using Zirconium.Core.Plugins.IPC;
namespace MongoDBPlugin namespace MongoDBPlugin
{ {
@ -10,18 +12,6 @@ namespace MongoDBPlugin
{ {
private IMongoDatabase _database; private IMongoDatabase _database;
public dynamic GetExportedAPI()
{
return _database;
}
public System.Type[] GetExportedTypes()
{
// var mongoCore = Assembly.Load("MongoDB.Driver.Core");
// var mongoBson = Assembly.Load("MongoDB.Bson");
return new System.Type[] {typeof(IMongoDatabase)};
}
public string GetPluginUniqueName() public string GetPluginUniqueName()
{ {
return "MongoDB"; return "MongoDB";
@ -30,13 +20,13 @@ namespace MongoDBPlugin
public void Initialize(IPluginHostAPI pluginHostAPI) public void Initialize(IPluginHostAPI pluginHostAPI)
{ {
var settingsDynamic = pluginHostAPI.GetSettings(this); var settingsDynamic = pluginHostAPI.GetSettings(this);
var settings = (IImmutableDictionary<string, dynamic>) ((IDictionary<string, object>)settingsDynamic).ToImmutableDictionary(); var settings = (IImmutableDictionary<string, dynamic>)((IDictionary<string, object>)settingsDynamic).ToImmutableDictionary();
var host = (string) settings.GetValueOrDefault("Host"); var host = (string)settings.GetValueOrDefault("Host");
var port = (int) settings.GetValueOrDefault("Port"); var port = (int)settings.GetValueOrDefault("Port");
var user = (string) settings.GetValueOrDefault("User"); var user = (string)settings.GetValueOrDefault("User");
var password = (string) settings.GetValueOrDefault("Password"); var password = (string)settings.GetValueOrDefault("Password");
var database = (string) settings.GetValueOrDefault("Database"); var database = (string)settings.GetValueOrDefault("Database");
MongoClient client; MongoClient client;
if (user == null && password == null) if (user == null && password == null)
@ -51,8 +41,25 @@ namespace MongoDBPlugin
var db = client.GetDatabase(database); var db = client.GetDatabase(database);
Log.Info("MongoDB is connected"); Log.Info("MongoDB is connected");
_database = db; _database = db;
var ipcService = new IPCService(db);
pluginHostAPI.RegisterIPCService(this, ipcService);
} }
public void PreInitialize(IPluginManager pluginManager) { } public void PreInitialize(IPluginManager pluginManager) { }
class IPCService
{
private IMongoDatabase _db;
public IPCService(IMongoDatabase db) { _db = db; }
[ExportedIPCMethod("Insert")]
public void Insert(dynamic paramsObject) {
var colName = paramsObject.ColName;
var model = paramsObject.Model;
_db.GetCollection<dynamic>(colName).InsertOne(model);
Log.Debug("successfully inserted the document");
}
}
} }
} }

View File

@ -1,52 +1,29 @@
using Zirconium.Core.Plugins.Interfaces; using System.Dynamic;
using MongoDB.Driver; using Zirconium.Core.Plugins.Interfaces;
using Zirconium.Core.Logging;
using System;
namespace TestMongoDB namespace TestMongoDB
{ {
class Plugin : IPluginAPI class Plugin : IPluginAPI
{ {
private IMongoDatabase _database;
public dynamic GetExportedAPI()
{
return null;
}
public Type[] GetExportedTypes()
{
return null;
}
public string GetPluginUniqueName() public string GetPluginUniqueName()
{ {
return "TestMongoDB"; return "TestMongoDB";
} }
public void Initialize(IPluginHostAPI pluginHostAPI) public async void Initialize(IPluginHostAPI pluginHostAPI)
{ {
var tm = new TestModel(); var tm = new TestModel();
tm.ABC = "qweqowie"; tm.ABC = "qweqowie";
_database.GetCollection<TestModel>("test_model").InsertOne(tm); dynamic paramsObject = new ExpandoObject();
paramsObject.ColName = "test_model";
paramsObject.Model = tm;
await pluginHostAPI.MakeIPCRequest("MongoDB", "Insert", paramsObject);
} }
public void PreInitialize(IPluginManager pluginManager) public void PreInitialize(IPluginManager pluginManager)
{ {
var db = pluginManager.Depends(this, "MongoDB"); pluginManager.Depends(this, "MongoDB");
var receivedType = db.GetType();
Log.Debug(db.GetType().FullName);
Log.Debug(db.GetType().AssemblyQualifiedName);
foreach (Type i in db.GetType().GetInterfaces()) {
Log.Debug("DB object interface: " + i.AssemblyQualifiedName);
}
Log.Debug("Type info in current assembly context:");
var mongoDBType = typeof(IMongoDatabase);
Log.Debug(mongoDBType.FullName);
Log.Debug(mongoDBType.AssemblyQualifiedName);
Log.Debug(new Uri(mongoDBType.Module.Assembly.CodeBase).LocalPath);
Log.Debug($"Casting compatibility: {mongoDBType.IsAssignableFrom(db.GetType())}");
_database = (IMongoDatabase) db;
} }
} }

View File

@ -8,8 +8,6 @@
<ProjectReference Include="../../Zirconium/Zirconium.csproj"> <ProjectReference Include="../../Zirconium/Zirconium.csproj">
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference> </ProjectReference>
<ProjectReference Include="../MongoDB/MongoDB.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>