/trunk/Kona.Infrastructure/Extensibility/Plugins/Plugin.cs
C# | 301 lines | 213 code | 39 blank | 49 comment | 49 complexity | 3a805ea5a9bd74a860b0a34673e9f5ba MD5 | raw file
Possible License(s): BSD-3-Clause
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Web.Compilation;
- using System.Reflection;
- using System.Linq;
- using System.Web.UI.WebControls;
- using System.Web.Mvc;
- using System.Web.UI;
- using System.Web;
- using System.IO;
-
- namespace Kona.Infrastructure {
-
- public class Plugin {
- // TODO: ensure thread safety.
- // TODO: make sure the scope of the cache here is small enough.
- private static List<Plugin> _plugins = new List<Plugin>();
- private static Hashtable _methods = Hashtable.Synchronized(new Hashtable());
- private static Hashtable _cached = Hashtable.Synchronized(new Hashtable());
- // InitializePlugins needs to be reentrant
- private static bool s_inited;
- private static object s_initLock = new object();
- private static IList<PluginSetting> _settings;
-
- //settings
- PluginSetting _setting;
- public PluginSetting Settings {
- get {
- _setting=Plugin._settings.SingleOrDefault(x => x.PluginName.Equals(this.PluginName, StringComparison.InvariantCultureIgnoreCase));
- return _setting ?? new PluginSetting();
- }
- }
-
- public bool IsEnabled {
- get {
- bool result = false;
- //the plugin is enabled if
- //1) the settings have been filled in
- //2) and the user says it is
- if (this.Settings.IsEnabled) {
- //make sure each setting is set
- result= this.Settings.IsEnabled;
-
- //TODO: Figure out a check for required values
-
-
- }
-
- return result;
- }
-
- }
- public string PluginName {
- get {
- return this.GetType().Name;
- }
- }
- public string Code {
- get {
- return FilePath.GetFileText();
- }
-
- }
- string _filePath = "";
- public string FilePath {
- get {
- if (string.IsNullOrEmpty(_filePath)) {
- string directoryRoot = HttpContext.Current.Server.MapPath("~/App_Code");
- _filePath=this.GetType().Name.LocateFilePath(directoryRoot);
- }
- return _filePath;
- }
- set {
- _filePath = value;
- }
- }
-
- public static void LoadSettings() {
- ObjectStore store = new ObjectStore();
- _settings=store.GetList<PluginSetting>("PluginSetting");
-
- }
-
- public static void ValidateSetting(string pluginName, string settingName, object value) {
- var plugin = Plugins.Where(x => x.GetType().Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault();
- if (plugin != null) {
-
- PropertyInfo prop = plugin.GetType().GetProperty(settingName);
-
- //see if it can be assigned this value
- if (prop != null) {
- //set the value to see if it can be coerced
- try {
- value.ChangeType(prop.PropertyType);
- } catch {
- throw new InvalidOperationException("Can't assign " + value.ToString() + " to " + prop.PropertyType.Name);
- }
- } else {
- throw new InvalidOperationException("No setting found to validate with that name");
- }
-
- } else {
- throw new InvalidOperationException("No plugin found to validate with that name");
- }
- }
-
- /// <summary>
- /// Optionally override this in a plug-in to add an initialization step.
- /// </summary>
- public virtual void Initialize() { }
-
-
- //initializes based on App_Code
- public static IList<Plugin> InitializePlugins() {
- var codeAssemblies = System.Web.Compilation.BuildManager.CodeAssemblies;
- return InitializePlugins(codeAssemblies);
- }
-
- public object GetSetting(string name) {
- //see if this setting is presetn
- object result = null;
- if (this.Settings.Settings.ContainsKey(name))
- result = this.Settings.Settings[name];
-
- return result;
- }
-
-
- //TODO: Fix this to be less arrow-y
- /// <summary>
- /// Discovers and initializes the plug-ins.
- /// </summary>
- public static IList<Plugin> InitializePlugins(IList codeAssemblies) {
- if (s_inited)
- return null;
- LoadSettings();
- lock (s_initLock) {
- if (!s_inited) {
- if (codeAssemblies != null) {
- foreach (Assembly assembly in codeAssemblies) {
- if (!assembly.FullName.StartsWith("System") & !assembly.FullName.StartsWith("Microsoft.VisualStudio")) {
- try {
- foreach (var type in assembly.GetTypes()) {
- if (typeof(Plugin).IsAssignableFrom(type) && type != typeof(Plugin)) {
- var plugin = (Plugin)Activator.CreateInstance(type);
- plugin.Initialize();
- _plugins.Add(plugin);
- }
- }
- }
- catch (Exception x) {
- if (x is System.Reflection.ReflectionTypeLoadException) {
- var newX = x as System.Reflection.ReflectionTypeLoadException;
- throw new InvalidOperationException(newX.LoaderExceptions[0].Message);
- }
- else {
- throw;
- }
- }
- }
- }
- }
- s_inited = true;
- }
- }
- return _plugins;
- }
-
- /// <summary>
- /// The list of installed plug-ins.
- /// </summary>
- public static Plugin[] Plugins {
- get {
- return _plugins.ToArray();
- }
- }
- public static TResult Execute<TResult>(string pluginName, string operation, params object[] input) {
-
- MethodInfo method = null;
- TResult result = default(TResult);
-
- //find the plugin
- var plugin = _plugins.Where(x => x.GetType().Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault();
-
- if (plugin != null) {
- method = plugin.GetType().GetMethods().FirstOrDefault(x => x.Name == operation);
-
- if (method != null) {
- result = (TResult)method.Invoke(plugin, input);
- }
- //load the first
- }
- return result;
- }
-
- public static TResult ExecuteFirst<TResult>(string operation, params object[] input) {
-
- MethodInfo method = null;
- Plugin foundPlugin=null;
- TResult result = default(TResult);
- foreach (var plugin in _plugins) {
- method = plugin.GetType().GetMethods().FirstOrDefault(x => x.Name == operation);
- if (method != null){
- foundPlugin=plugin;
- break;
- }
- }
-
- if (method != null) {
- result = (TResult)method.Invoke(foundPlugin, input);
-
- }
- //load the first
-
- return result;
-
- }
-
- /// <summary>
- /// Gets a method that calls all the plug-ins of the provided signature.
- /// </summary>
- /// <typeparam name="T">A delegate type describing the expected signature.</typeparam>
- /// <returns>A method that calls all the plug-ins with the signature of T.</returns>
- public static T GetMethod<T>(string operation) where T : class {
- var operationMethods = _methods[operation] as Hashtable;
- var operationCached = _cached[operation] as Hashtable;
- if (operationMethods == null) {
- operationMethods = Hashtable.Synchronized(new Hashtable());
- _methods.Add(operation, operationMethods);
- operationCached = Hashtable.Synchronized(new Hashtable());
- _cached.Add(operation, operationCached);
- }
- var processDelegate = (Delegate)operationMethods[typeof(T)];
- if (processDelegate == null) {
- if (operationCached[typeof(T)] == null) {
- foreach (var plugin in _plugins) {
- var pluginType = plugin.GetType();
- var processMethod = pluginType.GetMethod(operation,
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
- if (processMethod == null) continue;
- var newDelegate = Delegate.CreateDelegate(typeof(T), plugin, processMethod, false);
- if (newDelegate != null) {
- if (processDelegate == null) {
- processDelegate = newDelegate;
- }
- else {
- processDelegate = Delegate.Combine(processDelegate, newDelegate);
- }
- }
- }
- operationMethods[typeof(T)] = processDelegate;
- operationCached[typeof(T)] = true;
- }
- }
- return (T)(object)processDelegate;
- }
-
- /// <summary>
- /// Feeds the provided input through all plug-ins that have T [operation](string, T)
- /// as their signature, where [operation] is the contents of the operation parameter.
- /// </summary>
- /// <typeparam name="T">The type of the input to process.</typeparam>
- /// <param name="operation">The key of the operation to perform.</param>
- /// <param name="input">The object to process.</param>
- /// <returns>The processed object.</returns>
- public static T Process<T>(string operation, T input) where T : class {
- var methods = GetMethod<Func<T, T>>(operation);
- if (methods != null) {
- foreach (Func<T, T> pluginMethod in methods.GetInvocationList()) {
- if (input == null) return null;
- input = pluginMethod(input);
- }
- }
- return input;
- }
-
- /// <summary>
- /// Puts the provided object through all plug-ins with signature
- /// bool Process(string, T). The return value is false if any of the
- /// plug-ins returned false, and true otherwise. This is the pattern
- /// to use for pluggable validation logic for example.
- /// </summary>
- /// <typeparam name="T">The type of the input to process.</typeparam>
- /// <param name="operation">The key of the operation to perform.</param>
- /// <param name="input">The object to process.</param>
- /// <returns>False if any of the plug-ins returned false.</returns>
- public static bool IsTrue<T>(string operation, T input) {
- Delegate methods = GetMethod<Func<T, bool>>(operation);
- if (methods == null) return true;
- foreach (var pluginMethod in methods.GetInvocationList()) {
- if (!(bool)pluginMethod.DynamicInvoke(input)) {
- return false;
- }
- }
- return true;
- }
-
-
- }
- }