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. /// Ordering handlers: handlers are ordered by (ascending) weight. By default, handlers from the Umbraco.* or Concorde.* /// assemblies have a -100 weight whereas any other handler has a weight of +100. A custom weight can be assigned to a handler /// by marking the class with the WeightAttribute. For example, the CacheRefresherEventHandler is marked with [Weight(int.MinValue)] /// because its events need to run before anything else. Positive weights are considered "user-space" while negative weights are /// "core". Finally, users can register a filter to process the list (after it has been ordered) and re-order it, or remove handlers. /// BEWARE! handlers order is an important thing, and removing handlers or reordering handlers can have unexpected consequences. /// 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) { _orderedAndFiltered = GetSortedValues().ToList(); if (FilterCollection != null) FilterCollection(_orderedAndFiltered); } return _orderedAndFiltered; } } /// /// Gets or sets a delegate to filter the event handler list (EXPERT!). /// /// /// This can be set on startup in the pre-boot process in either a custom boot manager or global.asax (UmbracoApplication). /// Allows custom logic to execute in order to filter and/or re-order the event handlers prior to executing. /// To be used by custom boot sequences where the boot loader needs to remove some handlers, or raise their priority. /// Filtering the event handler collection can have ugly consequences. Use with care. /// public Action> FilterCollection { get { return _filterCollection; } set { if (_orderedAndFiltered != null) throw new InvalidOperationException("Cannot set the FilterCollection delegate once the ApplicationEventHandlers are resolved"); if (_filterCollection != null) throw new InvalidOperationException("Cannot set the FilterCollection delegate once it's already been specified"); _filterCollection = value; } } protected override int GetObjectWeight(object o) { var type = o.GetType(); var attr = type.GetCustomAttribute(true); if (attr != null) return attr.Weight; var name = type.Assembly.FullName; // we should really attribute all our Core handlers, so this is temp var core = name.InvariantStartsWith("Umbraco.") || name.InvariantStartsWith("Concorde."); return core ? -DefaultPluginWeight : DefaultPluginWeight; } /// /// 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; /// /// 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; } } }