using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using Umbraco.Core.Logging; using umbraco.interfaces; namespace Umbraco.Core { /// /// Used to resolve all plugin types and cache them and is also used to instantiate plugin types /// /// /// /// This class should be used to resolve all plugin types, the TypeFinder should not be used directly! /// /// This class can expose extension methods to resolve custom plugins /// /// internal class PluginManager { internal PluginManager() { } static PluginManager _resolver; static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); /// /// We will ensure that no matter what, only one of these is created, this is to ensure that caching always takes place /// /// /// The setter is generally only used for unit tests /// internal static PluginManager Current { get { using (var l = new UpgradeableReadLock(Lock)) { if (_resolver == null) { l.UpgradeToWriteLock(); _resolver = new PluginManager(); } return _resolver; } } set { _resolver = value; } } private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private readonly HashSet _types = new HashSet(); private IEnumerable _assemblies; /// /// Returns all classes attributed with XsltExtensionAttribute attribute /// /// internal IEnumerable ResolveCacheRefreshers() { return ResolveTypes(); } /// /// Returns all available IDataType in application /// /// internal IEnumerable ResolveDataTypes() { return ResolveTypes(); } /// /// Returns all available IMacroGuiRendering in application /// /// internal IEnumerable ResolveMacroRenderings() { return ResolveTypes(); } /// /// Returns all available IPackageAction in application /// /// internal IEnumerable ResolvePackageActions() { return ResolveTypes(); } /// /// Returns all available IAction in application /// /// internal IEnumerable ResolveActions() { return ResolveTypes(); } /// /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set /// this will search all assemblies known to have plugins and exclude ones known to not have them. /// internal IEnumerable AssembliesToScan { get { return _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); } set { _assemblies = value; } } /// /// Used to resolve and create instances of the specified type based on the resolved/cached plugin types /// /// /// set to true if an exception is to be thrown if there is an error during instantiation /// internal IEnumerable FindAndCreateInstances(bool throwException = false) { var types = ResolveTypes(); return CreateInstances(types, throwException); } /// /// Used to create instances of the specified type based on the resolved/cached plugin types /// /// /// /// set to true if an exception is to be thrown if there is an error during instantiation /// internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false) { var typesAsArray = types.ToArray(); using (DisposableTimer.DebugDuration( String.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName), String.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName))) { var instances = new List(); foreach (var t in typesAsArray) { try { var typeInstance = (T)Activator.CreateInstance(t); instances.Add(typeInstance); } catch (Exception ex) { LogHelper.Error(String.Format("Error creating type {0}", t.FullName), ex); if (throwException) { throw ex; } } } 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, bool typeIsAttribute = false) { using (var readLock = new UpgradeableReadLock(_lock)) { using (DisposableTimer.TraceDuration( String.Format("Starting resolution types of {0}", typeof(T).FullName), String.Format("Completed resolution of types of {0}", typeof(T).FullName))) { //check if the TypeList already exists, if so return it, if not we'll create it var typeList = _types.SingleOrDefault(x => x.GetListType().IsType()); if (typeList == null) { //upgrade to a write lock since we're adding to the collection readLock.UpgradeToWriteLock(); typeList = new TypeList(typeIsAttribute); foreach (var t in finder()) { typeList.AddType(t); } //add the type list to the collection _types.Add(typeList); } return typeList.GetTypes(); } } } /// /// Generic method to find the specified type and cache the result /// /// /// internal IEnumerable ResolveTypes() { return ResolveTypes(() => TypeFinder.FindClassesOfType(AssembliesToScan)); } /// /// Generic method to find the specified type that has an attribute and cache the result /// /// /// /// internal IEnumerable ResolveTypesWithAttribute() where TAttribute : Attribute { return ResolveTypes(() => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan)); } /// /// Generic method to find any type that has the specified attribute /// /// /// internal IEnumerable ResolveAttributedTypes() where TAttribute : Attribute { return ResolveTypes( () => TypeFinder.FindClassesWithAttribute(AssembliesToScan), true); } /// /// Used for unit tests /// /// internal HashSet GetTypeLists() { return _types; } #region Private classes internal abstract class TypeList { public abstract void AddType(Type t); public abstract Type GetListType(); public abstract IEnumerable GetTypes(); } internal class TypeList : TypeList { private readonly bool _typeIsAttribute; public TypeList(bool typeIsAttribute = false) { _typeIsAttribute = typeIsAttribute; } private readonly List _types = new List(); public override void AddType(Type t) { //if the type is an attribute type we won't do the type check if (_typeIsAttribute || t.IsType()) { _types.Add(t); } } public override Type GetListType() { return typeof(T); } public override IEnumerable GetTypes() { return _types; } } #endregion } }