using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Umbraco.Core.Logging; using umbraco.interfaces; namespace Umbraco.Core.ObjectResolution { /// /// A resolver to return all IApplicationEvents objects /// /// /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances. /// public sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable { private readonly LegacyStartupHandlerResolver _legacyResolver; /// /// Constructor /// /// /// /// internal ApplicationEventsResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable applicationEventHandlers) : base(serviceProvider, logger, applicationEventHandlers) { //create the legacy resolver and only include the legacy types _legacyResolver = new LegacyStartupHandlerResolver( serviceProvider, logger, applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom(x) == false)); } /// /// Override in order to only return types of IApplicationEventHandler and above, /// do not include the legacy types of IApplicationStartupHandler /// protected override IEnumerable InstanceTypes { get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom); } } private List _orderedAndFiltered; /// /// Gets the implementations. /// public IEnumerable ApplicationEventHandlers { get { if (_orderedAndFiltered == null) { _resolved = true; _orderedAndFiltered = GetSortedValues().ToList(); OnCollectionResolved(_orderedAndFiltered); } return _orderedAndFiltered; } } /// /// A delegate that can be set in the pre-boot phase in order to filter or re-order the event handler collection /// /// /// This can be set on startup in the pre-boot process in either a custom boot manager or global.asax (UmbracoApplication) /// public Action> FilterCollection { get { return _filterCollection; } set { if (_resolved) throw new InvalidOperationException("Cannot set the FilterCollection delegate once the ApplicationEventHandlers are resolved"); _filterCollection = value; } } /// /// Allow any filters to be applied to the event handler list /// /// /// /// This allows custom logic to execute in order to filter or re-order the event handlers prior to executing, /// however this also ensures that any core handlers are executed first to ensure the stabiliy of Umbraco. /// private void OnCollectionResolved(List handlers) { if (FilterCollection != null) { FilterCollection(handlers); } //find all of the core handlers and their weight, remove them from the main list var coreItems = new List>(); foreach (var handler in handlers.ToArray()) { //Yuck, but not sure what else we can do if ( handler.GetType().Assembly.FullName.StartsWith("Umbraco.", StringComparison.OrdinalIgnoreCase) || handler.GetType().Assembly.FullName.StartsWith("Concorde.")) { coreItems.Add(new Tuple(handler, GetObjectWeight(handler))); handlers.Remove(handler); } } //re-add the core handlers to the beginning of the list ordered by their weight foreach (var coreHandler in coreItems.OrderBy(x => x.Item2)) { handlers.Insert(0, coreHandler.Item1); } } /// /// Create instances of all of the legacy startup handlers /// public void InstantiateLegacyStartupHandlers() { //this will instantiate them all var handlers = _legacyResolver.LegacyStartupHandlers; } protected override bool SupportsClear { get { return false; } } protected override bool SupportsInsert { get { return false; } } private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable { internal LegacyStartupHandlerResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable legacyStartupHandlers) : base(serviceProvider, logger, legacyStartupHandlers) { } public IEnumerable LegacyStartupHandlers { get { return Values; } } public void Dispose() { ResetCollections(); } } private bool _disposed; private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); private Action> _filterCollection; private bool _resolved = false; /// /// Gets a value indicating whether this instance is disposed. /// /// /// true if this instance is disposed; otherwise, false. /// public bool IsDisposed { get { return _disposed; } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass of this type implements a finalizer. GC.SuppressFinalize(this); } ~ApplicationEventsResolver() { // Run dispose but let the class know it was due to the finalizer running. Dispose(false); } private void Dispose(bool disposing) { // Only operate if we haven't already disposed if (IsDisposed || disposing == false) return; using (new WriteLock(_disposalLocker)) { // Check again now we're inside the lock if (IsDisposed) return; // Call to actually release resources. This method is only // kept separate so that the entire disposal logic can be used as a VS snippet DisposeResources(); // Indicate that the instance has been disposed. _disposed = true; } } /// /// Clear out all of the instances, we don't want them hanging around and cluttering up memory /// private void DisposeResources() { _legacyResolver.Dispose(); ResetCollections(); _orderedAndFiltered.Clear(); _orderedAndFiltered = null; } } }