From 5576f65ef043d2f0addebb071f0739a033378afa Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 11:54:29 +0600 Subject: [PATCH] Updated DataType factory to use PluginTypeResolver and made the dictionary thread safe. Updated MacroEngineFactory to use PluginTypeResolver and made it thread safe. Have also updated it to only create one instance of each IMacroEngine per application which should increase performance a teeny bit, plus that's how it should be. --- src/Umbraco.Core/PluginTypeResolver.cs | 13 +++ .../Resolving/MultipleResolverBase.cs | 3 + src/Umbraco.Tests/DataTypeFactoryTests.cs | 63 +++++++++++++ .../EnumerableExtensionsTests.cs | 1 - src/Umbraco.Tests/PluginTypeResolverTests.cs | 12 ++- src/Umbraco.Tests/Umbraco.Tests.csproj | 5 + .../PluginTypeResolverExtensions.cs | 21 +++++ .../businesslogic/datatype/factory.cs | 56 ++++------- .../businesslogic/macro/MacroEngineFactory.cs | 92 +++++++------------ 9 files changed, 169 insertions(+), 97 deletions(-) create mode 100644 src/Umbraco.Tests/DataTypeFactoryTests.cs diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index 50df96c5c2..8440aadb02 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -104,6 +104,19 @@ namespace Umbraco.Core return instances; } + /// + /// Used to create an instance of the specified type based on the resolved/cached plugin types + /// + /// + /// + /// + /// + internal T CreateInstance(Type type, bool throwException = false) + { + var instances = CreateInstances(new[] {type}, throwException); + return instances.FirstOrDefault(); + } + private IEnumerable ResolveTypes(Func> finder) { using (var readLock = new UpgradeableReadLock(_lock)) diff --git a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs b/src/Umbraco.Core/Resolving/MultipleResolverBase.cs index 8fba742f58..85104696f0 100644 --- a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs +++ b/src/Umbraco.Core/Resolving/MultipleResolverBase.cs @@ -3,6 +3,9 @@ using System.Threading; namespace Umbraco.Core.Resolving { + //NOTE: This class should also support creating instances of the object and managing their lifespan, + // for example, some resolvers would want to return new object each time, per request or per application lifetime. + /// /// A Resolver to return and set a Multiply registered object. /// diff --git a/src/Umbraco.Tests/DataTypeFactoryTests.cs b/src/Umbraco.Tests/DataTypeFactoryTests.cs new file mode 100644 index 0000000000..46f0cedd7a --- /dev/null +++ b/src/Umbraco.Tests/DataTypeFactoryTests.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using SqlCE4Umbraco; +using Umbraco.Core; +using Umbraco.Web; +using umbraco.DataLayer; +using umbraco.MacroEngines; +using umbraco.MacroEngines.Iron; +using umbraco.businesslogic; +using umbraco.cms.businesslogic; +using umbraco.editorControls; +using umbraco.uicontrols; +using System.Linq; + +namespace Umbraco.Tests +{ + [TestFixture] + public class DataTypeFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly, + typeof(ApplicationStartupHandler).Assembly, + typeof(SqlCEHelper).Assembly, + typeof(CMSNode).Assembly, + typeof(System.Guid).Assembly, + typeof(NUnit.Framework.Assert).Assembly, + typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly, + typeof(System.Xml.NameTable).Assembly, + typeof(System.Configuration.GenericEnumConverter).Assembly, + typeof(System.Web.SiteMap).Assembly, + typeof(TabPage).Assembly, + typeof(System.Web.Mvc.ActionResult).Assembly, + typeof(TypeFinder2).Assembly, + typeof(ISqlHelper).Assembly, + typeof(DLRScriptingEngine).Assembly, + typeof(ICultureDictionary).Assembly, + typeof(UmbracoContext).Assembly, + typeof(BaseDataType).Assembly, + + }; + } + + [Test] + public void Find_All_DataTypes() + { + umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); + Assert.AreEqual(33, umbraco.cms.businesslogic.datatype.controls.Factory._controls.Count); + } + + [Test] + public void Get_All_Instances() + { + umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); + var factory = new umbraco.cms.businesslogic.datatype.controls.Factory(); + Assert.AreEqual(33, factory.GetAll().Count()); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/EnumerableExtensionsTests.cs b/src/Umbraco.Tests/EnumerableExtensionsTests.cs index 9bacfeb39a..c13cee8ea9 100644 --- a/src/Umbraco.Tests/EnumerableExtensionsTests.cs +++ b/src/Umbraco.Tests/EnumerableExtensionsTests.cs @@ -8,7 +8,6 @@ using Umbraco.Core.Resolving; namespace Umbraco.Tests { - [TestFixture] public class EnumerableExtensionsTests { diff --git a/src/Umbraco.Tests/PluginTypeResolverTests.cs b/src/Umbraco.Tests/PluginTypeResolverTests.cs index 96e0ede87a..058c71cb31 100644 --- a/src/Umbraco.Tests/PluginTypeResolverTests.cs +++ b/src/Umbraco.Tests/PluginTypeResolverTests.cs @@ -8,6 +8,7 @@ using umbraco.MacroEngines; using umbraco.MacroEngines.Iron; using umbraco.businesslogic; using umbraco.cms.businesslogic; +using umbraco.editorControls; using umbraco.uicontrols; using umbraco.cms; @@ -40,8 +41,8 @@ namespace Umbraco.Tests typeof(ISqlHelper).Assembly, typeof(DLRScriptingEngine).Assembly, typeof(ICultureDictionary).Assembly, - typeof(UmbracoContext).Assembly - + typeof(UmbracoContext).Assembly, + typeof(BaseDataType).Assembly }; } @@ -81,6 +82,13 @@ namespace Umbraco.Tests Assert.AreEqual(1, types.Count()); } + [Test] + public void Resolves_DataTypes() + { + var types = PluginTypeResolver.Current.ResolveDataTypes(); + Assert.AreEqual(33, types.Count()); + } + public interface IFindMe { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ae089775fd..d00037beb8 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -49,6 +49,7 @@ + @@ -92,6 +93,10 @@ {C7CB79F0-1C97-4B33-BFA7-00731B579AE2} umbraco.datalayer + + {255F5DF1-4E43-4758-AC05-7A0B68EB021B} + umbraco.editorControls + {511F6D8D-7717-440A-9A57-A507E9A8B27F} umbraco.interfaces diff --git a/src/umbraco.cms/PluginTypeResolverExtensions.cs b/src/umbraco.cms/PluginTypeResolverExtensions.cs index 1ccda0c54d..6b5c56f1f5 100644 --- a/src/umbraco.cms/PluginTypeResolverExtensions.cs +++ b/src/umbraco.cms/PluginTypeResolverExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Umbraco.Core; using umbraco.BusinessLogic.Actions; using umbraco.businesslogic; +using umbraco.cms.businesslogic.macro; using umbraco.interfaces; namespace umbraco.cms @@ -23,5 +24,25 @@ namespace umbraco.cms return resolver.ResolveTypes(); } + /// + /// Returns all available IDataType in application + /// + /// + /// + internal static IEnumerable ResolveDataTypes(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + + /// + /// Returns all available IDataType in application + /// + /// + /// + internal static IEnumerable ResolveMacroEngines(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/datatype/factory.cs b/src/umbraco.cms/businesslogic/datatype/factory.cs index 617c6bf093..240742cddd 100644 --- a/src/umbraco.cms/businesslogic/datatype/factory.cs +++ b/src/umbraco.cms/businesslogic/datatype/factory.cs @@ -1,7 +1,9 @@ using System; using System.Collections; +using System.Collections.Concurrent; +using System.Linq; using System.Web; - +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.interfaces; using System.Collections.Generic; @@ -17,7 +19,7 @@ namespace umbraco.cms.businesslogic.datatype.controls { #region Declarations - private static readonly Dictionary _controls = new Dictionary(); + internal static readonly ConcurrentDictionary _controls = new ConcurrentDictionary(); #endregion @@ -53,7 +55,7 @@ namespace umbraco.cms.businesslogic.datatype.controls } if (_controls.ContainsKey(DataEditorId)) { - IDataType newObject = Activator.CreateInstance(_controls[DataEditorId]) as IDataType; + var newObject = PluginTypeResolver.Current.CreateInstance(_controls[DataEditorId]); return newObject; } else @@ -68,10 +70,10 @@ namespace umbraco.cms.businesslogic.datatype.controls /// A list of IDataType's public IDataType[] GetAll() { - IDataType[] retVal = new IDataType[_controls.Count]; - int c = 0; + var retVal = new IDataType[_controls.Count]; + var c = 0; - foreach (Guid id in _controls.Keys) + foreach (var id in _controls.Keys) { retVal[c] = GetNewObject(id); c++; @@ -80,39 +82,19 @@ namespace umbraco.cms.businesslogic.datatype.controls return retVal; } - private static void Initialize() + internal static void Initialize() { // Get all datatypes from interface - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - GetDataTypes(types); - } - - private static void GetDataTypes(IEnumerable types) - { - foreach (var t in types) - { - IDataType typeInstance = null; - try - { - if (t.IsVisible) - { - typeInstance = Activator.CreateInstance(t) as IDataType; - } - } - catch { } - if (typeInstance != null) - { - try - { - _controls.Add(typeInstance.Id, t); - } - catch (Exception ee) - { - BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, -1, "Can't import datatype '" + t.FullName + "': " + ee.ToString()); - } - } - } + var types = PluginTypeResolver.Current.ResolveDataTypes().ToArray(); + foreach (var t in types) + { + var instance = PluginTypeResolver.Current.CreateInstance(t); + if (instance != null) + { + _controls.TryAdd(instance.Id, t); + } + } } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs b/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs index 506c1a2d51..fe5406c76c 100644 --- a/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs +++ b/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs @@ -1,59 +1,50 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.interfaces; namespace umbraco.cms.businesslogic.macro { + + //TODO: This class needs to be changed to use the new MultipleResolverBase + public class MacroEngineFactory { - private static readonly Dictionary m_engines = new Dictionary(); - private static readonly List m_allEngines = new List(); - private static object locker = new object(); + private static readonly List AllEngines = new List(); + private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + private static volatile bool _isInitialized = false; public MacroEngineFactory() { - Initialize(); + EnsureInitialize(); } + internal static void EnsureInitialize() + { + if (_isInitialized) + return; + + using (new WriteLock(Lock)) + { + AllEngines.Clear(); + + AllEngines.AddRange( + PluginTypeResolver.Current.CreateInstances( + PluginTypeResolver.Current.ResolveMacroEngines())); + + _isInitialized = true; + } + } + + [Obsolete("Use EnsureInitialize method instead")] protected static void Initialize() { - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - GetEngines(types); - } - - private static void GetEngines(IEnumerable types) - { - foreach (Type t in types) - { - IMacroEngine typeInstance = null; - try - { - if (t.IsVisible) - { - typeInstance = Activator.CreateInstance(t) as IMacroEngine; - } - } - catch { } - if (typeInstance != null) - { - try - { - lock (locker) - { - if (!m_engines.ContainsKey(typeInstance.Name)) - m_engines.Add(typeInstance.Name, t); - } - } - catch (Exception ee) - { - BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, -1, "Can't import MacroEngine '" + t.FullName + "': " + ee); - } - } - } + EnsureInitialize(); } public static IEnumerable GetSupportedLanguages() { @@ -78,27 +69,15 @@ namespace umbraco.cms.businesslogic.macro public static List GetAll() { - - if (m_allEngines.Count == 0) - { - Initialize(); - foreach (string name in m_engines.Keys) { - m_allEngines.Add(GetEngine(name)); - } - } - - return m_allEngines; + EnsureInitialize(); + return AllEngines; } public static IMacroEngine GetEngine(string name) { - if (m_engines.ContainsKey(name)) - { - var newObject = Activator.CreateInstance(m_engines[name]) as IMacroEngine; - return newObject; - } - - return null; + EnsureInitialize(); + var engine = AllEngines.FirstOrDefault(x => x.Name == name); + return engine; } public static IMacroEngine GetByFilename(string filename) @@ -114,8 +93,7 @@ namespace umbraco.cms.businesslogic.macro public static IMacroEngine GetByExtension(string extension) { - IMacroEngine engine = - GetAll().Find(t => t.SupportedExtensions.Contains(extension)); + var engine = GetAll().Find(t => t.SupportedExtensions.Contains(extension)); if (engine != null) { return engine;