Skip to content

Commit

Permalink
Improve plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang committed Dec 1, 2019
1 parent 4a07a63 commit f6a7be0
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/neo/Consensus/ConsensusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ private void InitializeConsensus(byte viewNumber)

private void Log(string message, LogLevel level = LogLevel.Info)
{
Plugin.Log(nameof(ConsensusService), level, message);
Utility.Log(nameof(ConsensusService), level, message);
}

private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message)
Expand Down
2 changes: 1 addition & 1 deletion src/neo/Plugins/LogLevel.cs → src/neo/LogLevel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Neo.Plugins
namespace Neo
{
public enum LogLevel : byte
{
Expand Down
3 changes: 2 additions & 1 deletion src/neo/NeoSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public NeoSystem(string storageEngine = null)
this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store));
this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this));
this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this));
Plugin.NotifyPluginsLoadedAfterSystemConstructed();
foreach (var plugin in Plugin.Plugins)
plugin.OnPluginsLoaded();
}

public void Dispose()
Expand Down
4 changes: 2 additions & 2 deletions src/neo/Plugins/IP2PPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Neo.Plugins
{
public interface IP2PPlugin
{
bool OnP2PMessage(Message message);
bool OnConsensusMessage(ConsensusPayload payload);
bool OnP2PMessage(Message message) => true;
bool OnConsensusMessage(ConsensusPayload payload) => true;
}
}
6 changes: 3 additions & 3 deletions src/neo/Plugins/IPersistencePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ namespace Neo.Plugins
{
public interface IPersistencePlugin
{
void OnPersist(StoreView snapshot, IReadOnlyList<ApplicationExecuted> applicationExecutedList);
void OnCommit(StoreView snapshot);
bool ShouldThrowExceptionFromCommit(Exception ex);
void OnPersist(StoreView snapshot, IReadOnlyList<ApplicationExecuted> applicationExecutedList) { }
void OnCommit(StoreView snapshot) { }
bool ShouldThrowExceptionFromCommit(Exception ex) => false;
}
}
148 changes: 78 additions & 70 deletions src/neo/Plugins/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,35 @@
using System.Linq;
using System.Reflection;
using System.Threading;
using static System.IO.Path;

namespace Neo.Plugins
{
public abstract class Plugin : IDisposable
{
public static readonly List<Plugin> Plugins = new List<Plugin>();
private static readonly List<ILogPlugin> Loggers = new List<ILogPlugin>();
internal static readonly List<ILogPlugin> Loggers = new List<ILogPlugin>();
internal static readonly Dictionary<string, IStoragePlugin> Storages = new Dictionary<string, IStoragePlugin>();
internal static readonly List<IRpcPlugin> RpcPlugins = new List<IRpcPlugin>();
internal static readonly List<IPersistencePlugin> PersistencePlugins = new List<IPersistencePlugin>();
internal static readonly List<IP2PPlugin> P2PPlugins = new List<IP2PPlugin>();
internal static readonly List<IMemoryPoolTxObserverPlugin> TxObserverPlugins = new List<IMemoryPoolTxObserverPlugin>();

private static readonly string pluginsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins");
public static readonly string PluginsDirectory = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins");
private static readonly FileSystemWatcher configWatcher;

private static int suspend = 0;

protected static NeoSystem System { get; private set; }
public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json");
public virtual string Name => GetType().Name;
public string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName);
protected static NeoSystem System { get; private set; }
public virtual Version Version => GetType().Assembly.GetName().Version;
public virtual string ConfigFile => Path.Combine(pluginsPath, GetType().Assembly.GetName().Name, "config.json");

static Plugin()
{
if (Directory.Exists(pluginsPath))
if (Directory.Exists(PluginsDirectory))
{
configWatcher = new FileSystemWatcher(pluginsPath, "*.json")
configWatcher = new FileSystemWatcher(PluginsDirectory)
{
EnableRaisingEvents = true,
IncludeSubdirectories = true,
Expand All @@ -58,74 +59,108 @@ protected Plugin()
Configure();
}

public abstract void Configure();

protected virtual void OnPluginsLoaded()
protected virtual void Configure()
{
}

private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e)
{
foreach (var plugin in Plugins)
switch (GetExtension(e.Name))
{
if (plugin.ConfigFile == e.FullPath)
{
plugin.Configure();
plugin.Log($"Reloaded config for {plugin.Name}");
case ".json":
Plugins.FirstOrDefault(p => p.ConfigFile == e.FullPath)?.Configure();
break;
}
case ".dll":
if (e.ChangeType != WatcherChangeTypes.Created) return;
if (GetDirectoryName(e.FullPath) != PluginsDirectory) return;
LoadPlugin(Assembly.Load(File.ReadAllBytes(e.FullPath)));
break;
}
}

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains(".resources"))
return null;

Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
return assembly;

AssemblyName an = new AssemblyName(args.Name);
string filename = an.Name + ".dll";

try
{
return Assembly.LoadFrom(filename);
}
catch (Exception ex)
{
Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to resolve assembly or its dependency: {ex.Message}");
return null;
}
}

public virtual void Dispose()
{
}

protected IConfigurationSection GetConfiguration()
{
return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build().GetSection("PluginConfiguration");
}

internal static void LoadPlugins(NeoSystem system)
private static void LoadPlugin(Assembly assembly)
{
System = system;
if (!Directory.Exists(pluginsPath)) return;
foreach (string filename in Directory.EnumerateFiles(pluginsPath, "*.dll", SearchOption.TopDirectoryOnly))
foreach (Type type in assembly.ExportedTypes)
{
var file = File.ReadAllBytes(filename);
Assembly assembly = Assembly.Load(file);
foreach (Type type in assembly.ExportedTypes)
if (!type.IsSubclassOf(typeof(Plugin))) continue;
if (type.IsAbstract) continue;

ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
try
{
constructor?.Invoke(null);
}
catch (Exception ex)
{
if (!type.IsSubclassOf(typeof(Plugin))) continue;
if (type.IsAbstract) continue;

ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
try
{
constructor?.Invoke(null);
}
catch (Exception ex)
{
Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}");
}
Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}");
}
}
}

internal static void NotifyPluginsLoadedAfterSystemConstructed()
internal static void LoadPlugins(NeoSystem system)
{
foreach (var plugin in Plugins)
plugin.OnPluginsLoaded();
System = system;
if (!Directory.Exists(PluginsDirectory)) return;
List<Assembly> assemblies = new List<Assembly>();
foreach (string filename in Directory.EnumerateFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly))
{
try
{
assemblies.Add(Assembly.Load(File.ReadAllBytes(filename)));
}
catch { }
}
foreach (Assembly assembly in assemblies)
{
LoadPlugin(assembly);
}
}

protected void Log(string message, LogLevel level = LogLevel.Info)
{
Log($"{nameof(Plugin)}:{Name}", level, message);
Utility.Log($"{nameof(Plugin)}:{Name}", level, message);
}

public static void Log(string source, LogLevel level, string message)
protected virtual bool OnMessage(object message)
{
foreach (ILogPlugin plugin in Loggers)
plugin.Log(source, level, message);
return false;
}

protected virtual bool OnMessage(object message) => false;
internal protected virtual void OnPluginsLoaded()
{
}

protected static bool ResumeNodeStartup()
{
Expand All @@ -148,32 +183,5 @@ protected static void SuspendNodeStartup()
Interlocked.Increment(ref suspend);
System.SuspendNodeStartup();
}

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains(".resources"))
return null;

Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
return assembly;

AssemblyName an = new AssemblyName(args.Name);
string filename = an.Name + ".dll";

try
{
return Assembly.LoadFrom(filename);
}
catch (Exception ex)
{
Log(nameof(Plugin), LogLevel.Error, $"Failed to resolve assembly or its dependency: {ex.Message}");
return null;
}
}

public virtual void Dispose()
{
}
}
}
7 changes: 7 additions & 0 deletions src/neo/Utility.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Configuration;
using Neo.Cryptography.ECC;
using Neo.Plugins;
using Neo.SmartContract;
using Neo.Wallets;
using System;
Expand Down Expand Up @@ -72,5 +73,11 @@ public static IConfigurationRoot LoadConfig(string config)
.AddJsonFile(configFile, true)
.Build();
}

public static void Log(string source, LogLevel level, string message)
{
foreach (ILogPlugin plugin in Plugin.Loggers)
plugin.Log(source, level, message);
}
}
}
2 changes: 1 addition & 1 deletion tests/neo.UnitTests/Ledger/UT_MemoryPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Neo.UnitTests.Ledger
{
internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin
{
public override void Configure() { }
protected override void Configure() { }
public void TransactionAdded(Transaction tx) { }
public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable<Transaction> transactions) { }
}
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/Plugins/TestLogPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ public TestLogPlugin() : base() { }

public string Output { set; get; }

public override void Configure() { }
protected override void Configure() { }

public new void Log(string source, LogLevel level, string message)
void ILogPlugin.Log(string source, LogLevel level, string message)
{
Output = source + "_" + level.ToString() + "_" + message;
}
Expand Down
8 changes: 0 additions & 8 deletions tests/neo.UnitTests/Plugins/UT_Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ public void TestSendMessage()
}
}

[TestMethod]
public void TestNotifyPluginsLoadedAfterSystemConstructed()
{
var pp = new TestLogPlugin();
Action action = () => Plugin.NotifyPluginsLoadedAfterSystemConstructed();
action.Should().NotThrow();
}

[TestMethod]
public void TestResumeNodeStartupAndSuspendNodeStartup()
{
Expand Down

0 comments on commit f6a7be0

Please sign in to comment.