diff --git a/src/Umbraco.Core/Components/AuditEventsComponent.cs b/src/Umbraco.Core/Components/AuditEventsComponent.cs index 134aa18414..f592613e0d 100644 --- a/src/Umbraco.Core/Components/AuditEventsComponent.cs +++ b/src/Umbraco.Core/Components/AuditEventsComponent.cs @@ -12,11 +12,30 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { - public sealed class AuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class AuditEventsComponent : IComponent { - private IAuditService _auditService; - private IUserService _userService; - private IEntityService _entityService; + private readonly IAuditService _auditService; + private readonly IUserService _userService; + private readonly IEntityService _entityService; + + public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService) + { + _auditService = auditService; + _userService = userService; + _entityService = entityService; + + UserService.SavedUserGroup += OnSavedUserGroupWithUsers; + + UserService.SavedUser += OnSavedUser; + UserService.DeletedUser += OnDeletedUser; + UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned; + + MemberService.Saved += OnSavedMember; + MemberService.Deleted += OnDeletedMember; + MemberService.AssignedRoles += OnAssignedRoles; + MemberService.RemovedRoles += OnRemovedRoles; + MemberService.Exported += OnMemberExported; + } private IUser CurrentPerformingUser { @@ -46,25 +65,6 @@ namespace Umbraco.Core.Components } } - public void Initialize(IAuditService auditService, IUserService userService, IEntityService entityService) - { - _auditService = auditService; - _userService = userService; - _entityService = entityService; - - UserService.SavedUserGroup += OnSavedUserGroupWithUsers; - - UserService.SavedUser += OnSavedUser; - UserService.DeletedUser += OnDeletedUser; - UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned; - - MemberService.Saved += OnSavedMember; - MemberService.Deleted += OnDeletedMember; - MemberService.AssignedRoles += OnAssignedRoles; - MemberService.RemovedRoles += OnRemovedRoles; - MemberService.Exported += OnMemberExported; - } - private string FormatEmail(IMember member) { return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>"; diff --git a/src/Umbraco.Core/Components/AuditEventsComposer.cs b/src/Umbraco.Core/Components/AuditEventsComposer.cs new file mode 100644 index 0000000000..2dfacd8208 --- /dev/null +++ b/src/Umbraco.Core/Components/AuditEventsComposer.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Components +{ + public sealed class AuditEventsComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Core/Components/ComponentCollection.cs b/src/Umbraco.Core/Components/ComponentCollection.cs new file mode 100644 index 0000000000..abe493b524 --- /dev/null +++ b/src/Umbraco.Core/Components/ComponentCollection.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Components +{ + /// + /// Represents the collection of implementations. + /// + public class ComponentCollection : BuilderCollectionBase + { + private const int LogThresholdMilliseconds = 100; + + private readonly IProfilingLogger _logger; + + public ComponentCollection(IEnumerable items, IProfilingLogger logger) + : base(items) + { + _logger = logger; + } + + public void Terminate() + { + using (_logger.DebugDuration($"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated.")) + { + foreach (var component in this.Reverse()) // terminate components in reverse order + { + var componentType = component.GetType(); + using (_logger.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + component.DisposeIfDisposable(); + } + } + } + } + } +} diff --git a/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs b/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs new file mode 100644 index 0000000000..584de7a8f2 --- /dev/null +++ b/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Components +{ + /// + /// Builds a . + /// + public class ComponentCollectionBuilder : OrderedCollectionBuilderBase + { + private const int LogThresholdMilliseconds = 100; + + private IProfilingLogger _logger; + + public ComponentCollectionBuilder() + { } + + protected override ComponentCollectionBuilder This => this; + + protected override IEnumerable CreateItems(IFactory factory) + { + _logger = factory.GetInstance(); + + using (_logger.DebugDuration($"Creating components. (log when >{LogThresholdMilliseconds}ms)", "Created.")) + { + return base.CreateItems(factory); + } + } + + protected override IComponent CreateItem(IFactory factory, Type itemType) + { + using (_logger.DebugDuration($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + return base.CreateItem(factory, itemType); + } + } + } +} diff --git a/src/Umbraco.Core/Components/Components.cs b/src/Umbraco.Core/Components/Composers.cs similarity index 54% rename from src/Umbraco.Core/Components/Components.cs rename to src/Umbraco.Core/Components/Composers.cs index c4a23e70b7..cc65e351b5 100644 --- a/src/Umbraco.Core/Components/Components.cs +++ b/src/Umbraco.Core/Components/Composers.cs @@ -13,133 +13,122 @@ namespace Umbraco.Core.Components { // note: this class is NOT thread-safe in any ways - internal class Components + /// + /// Handles the composers. + /// + internal class Composers { private readonly Composition _composition; private readonly IProfilingLogger _logger; - private readonly IEnumerable _componentTypes; - private IUmbracoComponent[] _components; + private readonly IEnumerable _composerTypes; private const int LogThresholdMilliseconds = 100; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The composition. - /// The component types. + /// The composer types. /// A profiling logger. - public Components(Composition composition, IEnumerable componentTypes, IProfilingLogger logger) + public Composers(Composition composition, IEnumerable composerTypes, IProfilingLogger logger) { _composition = composition ?? throw new ArgumentNullException(nameof(composition)); - _componentTypes = componentTypes ?? throw new ArgumentNullException(nameof(componentTypes)); + _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } - internal T Get() => _components.OfType().SingleOrDefault(); - private class EnableInfo { public bool Enabled; public int Weight = -1; } + /// + /// Instantiates and composes the composers. + /// public void Compose() { - var orderedComponentTypes = PrepareComponentTypes(); + // make sure it is there + _composition.WithCollectionBuilder(); - InstantiateComponents(orderedComponentTypes); - ComposeComponents(); - } + IEnumerable orderedComposerTypes; - public void Initialize(IFactory factory) // fixme refactor!!! - { - // use a container scope to ensure that PerScope instances are disposed - // components that require instances that should not survive should register them with PerScope lifetime - - var scopeProvider = factory.GetInstance(); - var initializers = factory.GetInstance(); - - using (var scope = scopeProvider.CreateScope()) - using (_logger.DebugDuration($"Initializing. (log when >{LogThresholdMilliseconds}ms)", "Initialized.")) - using (factory.BeginScope()) + using (_logger.DebugDuration("Preparing composer types.", "Prepared composer types.")) { - foreach (var initializer in initializers) + orderedComposerTypes = PrepareComposerTypes(); + } + + var composers = InstantiateComposers(orderedComposerTypes); + + using (_logger.DebugDuration($"Composing composers. (log when >{LogThresholdMilliseconds}ms)", "Composed composers.")) + { + foreach (var composer in composers) { - var initializerType = initializer.GetType(); - using (_logger.DebugDuration($"Run {initializerType.FullName}.", $"Completed {initializerType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + var componentType = composer.GetType(); + using (_logger.DebugDuration($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) { - initializer.Initialize(); + composer.Compose(_composition); } } - - scope.Complete(); } } - private IEnumerable PrepareComponentTypes() - { - using (_logger.DebugDuration("Preparing component types.", "Prepared component types.")) - { - return PrepareComponentTypes2(); - } - } - - private IEnumerable PrepareComponentTypes2() + private IEnumerable PrepareComposerTypes() { // create a list, remove those that cannot be enabled due to runtime level - var componentTypeList = _componentTypes + var composerTypeList = _composerTypes .Where(x => { // use the min level specified by the attribute if any - // otherwise, user components have Run min level, anything else is Unknown (always run) + // otherwise, user composers have Run min level, anything else is Unknown (always run) var attr = x.GetCustomAttribute(); - var minLevel = attr?.MinLevel ?? (x.Implements() ? RuntimeLevel.Run : RuntimeLevel.Unknown); + var minLevel = attr?.MinLevel ?? (x.Implements() ? RuntimeLevel.Run : RuntimeLevel.Unknown); return _composition.RuntimeState.Level >= minLevel; }) .ToList(); - // enable or disable components - EnableDisableComponents(componentTypeList); + // enable or disable composers + EnableDisableComposers(composerTypeList); - // sort the components according to their dependencies + // sort the composers according to their dependencies var requirements = new Dictionary>(); - foreach (var type in componentTypeList) requirements[type] = null; - foreach (var type in componentTypeList) + foreach (var type in composerTypeList) requirements[type] = null; + foreach (var type in composerTypeList) { - GatherRequirementsFromRequireAttribute(type, componentTypeList, requirements); - GatherRequirementsFromRequiredAttribute(type, componentTypeList, requirements); + GatherRequirementsFromRequireAttribute(type, composerTypeList, requirements); + GatherRequirementsFromRequiredByAttribute(type, composerTypeList, requirements); } // only for debugging, this is verbose - //_logger.Debug(GetComponentsReport(requirements)); + //_logger.Debug(GetComposersReport(requirements)); - // sort components + // sort composers var graph = new TopoGraph>>(kvp => kvp.Key, kvp => kvp.Value); graph.AddItems(requirements); - List sortedComponentTypes; + List sortedComposerTypes; try { - sortedComponentTypes = graph.GetSortedItems().Select(x => x.Key).ToList(); + sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).ToList(); } catch (Exception e) { // in case of an error, force-dump everything to log - _logger.Info("Component Report:\r\n{ComponentReport}", GetComponentsReport(requirements)); - _logger.Error(e, "Failed to sort components."); + _logger.Info("Composer Report:\r\n{ComposerReport}", GetComposersReport(requirements)); + _logger.Error(e, "Failed to sort composers."); throw; } // bit verbose but should help for troubleshooting - var text = "Ordered Components: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComponentTypes) + Environment.NewLine; - _logger.Debug("Ordered Components: {SortedComponentTypes}", sortedComponentTypes); + //var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine; + _logger.Debug("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes); - return sortedComponentTypes; + return sortedComposerTypes; } - private static string GetComponentsReport(Dictionary> requirements) + private static string GetComposersReport(Dictionary> requirements) { var text = new StringBuilder(); - text.AppendLine("Components & Dependencies:"); + text.AppendLine("Composers & Dependencies:"); text.AppendLine(); foreach (var kvp in requirements) @@ -147,20 +136,20 @@ namespace Umbraco.Core.Components var type = kvp.Key; text.AppendLine(type.FullName); - foreach (var attribute in type.GetCustomAttributes()) + foreach (var attribute in type.GetCustomAttributes()) text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) : "")); - foreach (var attribute in type.GetCustomAttributes()) + foreach (var attribute in type.GetCustomAttributes()) text.AppendLine(" -< " + attribute.RequiringType); foreach (var i in type.GetInterfaces()) { text.AppendLine(" : " + i.FullName); - foreach (var attribute in i.GetCustomAttributes()) + foreach (var attribute in i.GetCustomAttributes()) text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) : "")); - foreach (var attribute in i.GetCustomAttributes()) + foreach (var attribute in i.GetCustomAttributes()) text.AppendLine(" -< " + attribute.RequiringType); } if (kvp.Value != null) @@ -173,35 +162,35 @@ namespace Umbraco.Core.Components return text.ToString(); } - private static void EnableDisableComponents(ICollection types) + private static void EnableDisableComposers(ICollection types) { var enabled = new Dictionary(); // process the enable/disable attributes // these two attributes are *not* inherited and apply to *classes* only (not interfaces). - // remote declarations (when a component enables/disables *another* component) - // have priority over local declarations (when a component disables itself) so that - // ppl can enable components that, by default, are disabled. + // remote declarations (when a composer enables/disables *another* composer) + // have priority over local declarations (when a composer disables itself) so that + // ppl can enable composers that, by default, are disabled. // what happens in case of conflicting remote declarations is unspecified. more // precisely, the last declaration to be processed wins, but the order of the // declarations depends on the type finder and is unspecified. - foreach (var componentType in types) + foreach (var composerType in types) { - foreach (var attr in componentType.GetCustomAttributes()) + foreach (var attr in composerType.GetCustomAttributes()) { - var type = attr.EnabledType ?? componentType; + var type = attr.EnabledType ?? composerType; if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); - var weight = type == componentType ? 1 : 2; + var weight = type == composerType ? 1 : 2; if (enableInfo.Weight > weight) continue; enableInfo.Enabled = true; enableInfo.Weight = weight; } - foreach (var attr in componentType.GetCustomAttributes()) + foreach (var attr in composerType.GetCustomAttributes()) { - var type = attr.DisabledType ?? componentType; + var type = attr.DisabledType ?? composerType; if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); - var weight = type == componentType ? 1 : 2; + var weight = type == composerType ? 1 : 2; if (enableInfo.Weight > weight) continue; enableInfo.Enabled = false; @@ -209,7 +198,7 @@ namespace Umbraco.Core.Components } } - // remove components that end up being disabled + // remove composers that end up being disabled foreach (var kvp in enabled.Where(x => x.Value.Enabled == false)) types.Remove(kvp.Key); } @@ -219,16 +208,16 @@ namespace Umbraco.Core.Components // get 'require' attributes // these attributes are *not* inherited because we want to "custom-inherit" for interfaces only var requireAttributes = type - .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces - .Concat(type.GetCustomAttributes()); // those marking the component + .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces + .Concat(type.GetCustomAttributes()); // those marking the composer // what happens in case of conflicting attributes (different strong/weak for same type) is not specified. foreach (var attr in requireAttributes) { if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below) - // requiring an interface = require any enabled component implementing that interface - // unless strong, and then require at least one enabled component implementing that interface + // requiring an interface = require any enabled composer implementing that interface + // unless strong, and then require at least one enabled composer implementing that interface if (attr.RequiredType.IsInterface) { var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList(); @@ -238,9 +227,9 @@ namespace Umbraco.Core.Components requirements[type].AddRange(implems); } else if (attr.Weak == false) // if explicitly set to !weak, is strong, else is weak - throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}."); + throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}."); } - // requiring a class = require that the component is enabled + // requiring a class = require that the composer is enabled // unless weak, and then requires it if it is enabled else { @@ -250,24 +239,24 @@ namespace Umbraco.Core.Components requirements[type].Add(attr.RequiredType); } else if (attr.Weak != true) // if not explicitly set to weak, is strong - throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}."); + throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}."); } } } - private static void GatherRequirementsFromRequiredAttribute(Type type, ICollection types, IDictionary> requirements) + private static void GatherRequirementsFromRequiredByAttribute(Type type, ICollection types, IDictionary> requirements) { // get 'required' attributes // these attributes are *not* inherited because we want to "custom-inherit" for interfaces only var requiredAttributes = type - .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces - .Concat(type.GetCustomAttributes()); // those marking the component + .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces + .Concat(type.GetCustomAttributes()); // those marking the composer foreach (var attr in requiredAttributes) { if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below) - // required by an interface = by any enabled component implementing this that interface + // required by an interface = by any enabled composer implementing this that interface if (attr.RequiringType.IsInterface) { var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList(); @@ -289,50 +278,19 @@ namespace Umbraco.Core.Components } } - private void InstantiateComponents(IEnumerable types) + private IEnumerable InstantiateComposers(IEnumerable types) { - IUmbracoComponent InstantiateComponent(Type type) + IComposer InstantiateComposer(Type type) { var ctor = type.GetConstructor(Array.Empty()); if (ctor == null) - throw new InvalidOperationException($"Component {type.FullName} does not have a parameter-less."); - return (IUmbracoComponent) ctor.Invoke(Array.Empty()); + throw new InvalidOperationException($"Composer {type.FullName} does not have a parameter-less constructor."); + return (IComposer) ctor.Invoke(Array.Empty()); } - using (_logger.DebugDuration("Instantiating components.", "Instantiated components.")) + using (_logger.DebugDuration("Instantiating composers.", "Instantiated composers.")) { - _components = types.Select(InstantiateComponent).ToArray(); - } - } - - private void ComposeComponents() - { - using (_logger.DebugDuration($"Composing components. (log when >{LogThresholdMilliseconds}ms)", "Composed components.")) - { - foreach (var component in _components) - { - var componentType = component.GetType(); - using (_logger.DebugDuration($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) - { - component.Compose(_composition); - } - } - } - } - - public void Terminate() - { - using (_logger.DebugDuration($"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated.")) - { - for (var i = _components.Length - 1; i >= 0; i--) // terminate components in reverse order - { - var component = _components[i]; - var componentType = component.GetType(); - using (_logger.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) - { - component.Terminate(); - } - } + return types.Select(InstantiateComposer).ToArray(); } } } diff --git a/src/Umbraco.Core/Components/CompositionExtensions.cs b/src/Umbraco.Core/Components/CompositionExtensions.cs index eaa25f9176..93d190d17e 100644 --- a/src/Umbraco.Core/Components/CompositionExtensions.cs +++ b/src/Umbraco.Core/Components/CompositionExtensions.cs @@ -116,10 +116,10 @@ namespace Umbraco.Core.Components => composition.WithCollectionBuilder(); /// - /// Gets the initializers collection builder. + /// Gets the components collection builder. /// - public static UmbracoInitializerCollectionBuilder Initializers(this Composition composition) - => composition.WithCollectionBuilder(); + public static ComponentCollectionBuilder Components(this Composition composition) + => composition.WithCollectionBuilder(); #endregion diff --git a/src/Umbraco.Core/Components/DisableAttribute.cs b/src/Umbraco.Core/Components/DisableAttribute.cs new file mode 100644 index 0000000000..f9a7249b89 --- /dev/null +++ b/src/Umbraco.Core/Components/DisableAttribute.cs @@ -0,0 +1,38 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a composer should be disabled. + /// + /// + /// If a type is specified, disables the composer of that type, else disables the composer marked with the attribute. + /// This attribute is *not* inherited. + /// This attribute applies to classes only, it is not possible to enable/disable interfaces. + /// If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority + /// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from + /// another composer. Anything else is unspecified. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class DisableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public DisableAttribute() + { } + + /// + /// Initializes a new instance of the class. + /// + public DisableAttribute(Type disabledType) + { + DisabledType = disabledType; + } + + /// + /// Gets the disabled type, or null if it is the composer marked with the attribute. + /// + public Type DisabledType { get; } + } +} diff --git a/src/Umbraco.Core/Components/DisableComponentAttribute.cs b/src/Umbraco.Core/Components/DisableComponentAttribute.cs deleted file mode 100644 index f7ff71e119..0000000000 --- a/src/Umbraco.Core/Components/DisableComponentAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component should be disabled. - /// - /// - /// If a type is specified, disables the component of that type, else disables the component marked with the attribute. - /// This attribute is *not* inherited. - /// This attribute applies to classes only, it is not possible to enable/disable interfaces. - /// If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority - /// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from - /// another component. Anything else is unspecified. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public class DisableComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public DisableComponentAttribute() - { } - - /// - /// Initializes a new instance of the class. - /// - public DisableComponentAttribute(Type disabledType) - { - DisabledType = disabledType; - } - - /// - /// Gets the disabled type, or null if it is the component marked with the attribute. - /// - public Type DisabledType { get; } - } -} diff --git a/src/Umbraco.Core/Components/EnableAttribute.cs b/src/Umbraco.Core/Components/EnableAttribute.cs new file mode 100644 index 0000000000..edf3cbdc2e --- /dev/null +++ b/src/Umbraco.Core/Components/EnableAttribute.cs @@ -0,0 +1,38 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a composer should be enabled. + /// + /// + /// If a type is specified, enables the composer of that type, else enables the composer marked with the attribute. + /// This attribute is *not* inherited. + /// This attribute applies to classes only, it is not possible to enable/disable interfaces. + /// If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority + /// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from + /// another composer. Anything else is unspecified. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class EnableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public EnableAttribute() + { } + + /// + /// Initializes a new instance of the class. + /// + public EnableAttribute(Type enabledType) + { + EnabledType = enabledType; + } + + /// + /// Gets the enabled type, or null if it is the composer marked with the attribute. + /// + public Type EnabledType { get; } + } +} diff --git a/src/Umbraco.Core/Components/EnableComponentAttribute.cs b/src/Umbraco.Core/Components/EnableComponentAttribute.cs deleted file mode 100644 index fa76dc2404..0000000000 --- a/src/Umbraco.Core/Components/EnableComponentAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component should be enabled. - /// - /// - /// If a type is specified, enables the component of that type, else enables the component marked with the attribute. - /// This attribute is *not* inherited. - /// This attribute applies to classes only, it is not possible to enable/disable interfaces. - /// If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority - /// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from - /// another component. Anything else is unspecified. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public class EnableComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public EnableComponentAttribute() - { } - - /// - /// Initializes a new instance of the class. - /// - public EnableComponentAttribute(Type enabledType) - { - EnabledType = enabledType; - } - - /// - /// Gets the enabled type, or null if it is the component marked with the attribute. - /// - public Type EnabledType { get; } - } -} diff --git a/src/Umbraco.Core/Components/IRuntimeComponent.cs b/src/Umbraco.Core/Components/IRuntimeComponent.cs deleted file mode 100644 index 7f89d21e87..0000000000 --- a/src/Umbraco.Core/Components/IRuntimeComponent.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Umbraco.Core.Components -{ - public interface IRuntimeComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/IUmbracoComponent.cs b/src/Umbraco.Core/Components/IUmbracoComponent.cs index d25b97c270..149f41df83 100644 --- a/src/Umbraco.Core/Components/IUmbracoComponent.cs +++ b/src/Umbraco.Core/Components/IUmbracoComponent.cs @@ -2,20 +2,25 @@ namespace Umbraco.Core.Components { - /// - /// Represents an Umbraco component. - /// - public interface IUmbracoComponent : IDiscoverable - { - /// - /// Composes the component. - /// - /// The composition. - void Compose(Composition composition); + // FIXME BREAK THIS! - /// - /// Terminates the component. - /// - void Terminate(); + public interface IComposer : IDiscoverable + { + void Compose(Composition composition); } + + public interface IRuntimeComposer : IComposer + { } + + [Require(typeof(IRuntimeComposer))] + public interface ICoreComposer : IComposer + { } + + [Require(typeof(ICoreComposer))] + public interface IUserComposer : IComposer + { } + + // will be disposed if disposable, CANT be disposed multiple times, beware! + public interface IComponent + { } } diff --git a/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs b/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs deleted file mode 100644 index 28ff286da3..0000000000 --- a/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Components -{ - [RequireComponent(typeof(IRuntimeComponent))] - public interface IUmbracoCoreComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/IUmbracoUserComponent.cs b/src/Umbraco.Core/Components/IUmbracoUserComponent.cs deleted file mode 100644 index 377528ff26..0000000000 --- a/src/Umbraco.Core/Components/IUmbracoUserComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Components -{ - [RequireComponent(typeof(IUmbracoCoreComponent))] - public interface IUmbracoUserComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/ManifestWatcherComponent.cs b/src/Umbraco.Core/Components/ManifestWatcherComponent.cs index 800ed3bb62..848dec2d49 100644 --- a/src/Umbraco.Core/Components/ManifestWatcherComponent.cs +++ b/src/Umbraco.Core/Components/ManifestWatcherComponent.cs @@ -5,42 +5,15 @@ using Umbraco.Core.Manifest; namespace Umbraco.Core.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class ManifestWatcherComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class ManifestWatcherComponent : IComponent { // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for // package.manifest chances and restarts the application on any change private ManifestWatcher _mw; - public class Initializer : IUmbracoInitializer + public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger) { - private readonly IRuntimeState _runtimeState; - private readonly ILogger _logger; - - public Initializer(IRuntimeState runtimeState, ILogger logger) - { - _runtimeState = runtimeState; - _logger = logger; - } - - public void Initialize() - { - if (_runtimeState.Debug == false) return; - - //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) - // return; - - var appPlugins = IOHelper.MapPath("~/App_Plugins/"); - if (Directory.Exists(appPlugins) == false) return; - - _mw = new ManifestWatcher(_logger); - _mw.Start(Directory.GetDirectories(appPlugins)); - } - } - - public void Initialize(IRuntimeState runtime, ILogger logger) - { - if (runtime.Debug == false) return; + if (runtimeState.Debug == false) return; //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) // return; @@ -52,7 +25,7 @@ namespace Umbraco.Core.Components _mw.Start(Directory.GetDirectories(appPlugins)); } - public override void Terminate() + public void Terminate() { _mw?.Dispose(); _mw = null; diff --git a/src/Umbraco.Core/Components/ManifestWatcherComposer.cs b/src/Umbraco.Core/Components/ManifestWatcherComposer.cs new file mode 100644 index 0000000000..6060a7c895 --- /dev/null +++ b/src/Umbraco.Core/Components/ManifestWatcherComposer.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class ManifestWatcherComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Core/Components/RelateOnCopyComponent.cs b/src/Umbraco.Core/Components/RelateOnCopyComponent.cs index 25fa1c98e2..41ae3c70dc 100644 --- a/src/Umbraco.Core/Components/RelateOnCopyComponent.cs +++ b/src/Umbraco.Core/Components/RelateOnCopyComponent.cs @@ -6,21 +6,11 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { //TODO: This should just exist in the content service/repo! - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class RelateOnCopyComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class RelateOnCopyComponent : IComponent { - public override void Compose(Composition composition) + public RelateOnCopyComponent() { - base.Compose(composition); - composition.Initializers().Append(); - } - - public class Initializer : IUmbracoInitializer - { - public void Initialize() - { - ContentService.Copied += ContentServiceCopied; - } + ContentService.Copied += ContentServiceCopied; } private static void ContentServiceCopied(IContentService sender, Events.CopyEventArgs e) diff --git a/src/Umbraco.Core/Components/RelateOnCopyComposer.cs b/src/Umbraco.Core/Components/RelateOnCopyComposer.cs new file mode 100644 index 0000000000..edea63b5e5 --- /dev/null +++ b/src/Umbraco.Core/Components/RelateOnCopyComposer.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class RelateOnCopyComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/RelateOnTrashComponent.cs b/src/Umbraco.Core/Components/RelateOnTrashComponent.cs index c3eed7f251..93803d4fae 100644 --- a/src/Umbraco.Core/Components/RelateOnTrashComponent.cs +++ b/src/Umbraco.Core/Components/RelateOnTrashComponent.cs @@ -7,24 +7,14 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class RelateOnTrashComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class RelateOnTrashComponent : IComponent { - public override void Compose(Composition composition) + public RelateOnTrashComponent() { - base.Compose(composition); - composition.Initializers().Append(); - } - - public class Initializer : IUmbracoInitializer - { - public void Initialize() - { - ContentService.Moved += ContentService_Moved; - ContentService.Trashed += ContentService_Trashed; - MediaService.Moved += MediaService_Moved; - MediaService.Trashed += MediaService_Trashed; - } + ContentService.Moved += ContentService_Moved; + ContentService.Trashed += ContentService_Trashed; + MediaService.Moved += MediaService_Moved; + MediaService.Trashed += MediaService_Trashed; } private static void ContentService_Moved(IContentService sender, MoveEventArgs e) diff --git a/src/Umbraco.Core/Components/RelateOnTrashComposer.cs b/src/Umbraco.Core/Components/RelateOnTrashComposer.cs new file mode 100644 index 0000000000..d8d7d9e9f4 --- /dev/null +++ b/src/Umbraco.Core/Components/RelateOnTrashComposer.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class RelateOnTrashComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/RequireComponentAttribute.cs b/src/Umbraco.Core/Components/RequireAttribute.cs similarity index 60% rename from src/Umbraco.Core/Components/RequireComponentAttribute.cs rename to src/Umbraco.Core/Components/RequireAttribute.cs index cbf86e48a1..472c3ddf12 100644 --- a/src/Umbraco.Core/Components/RequireComponentAttribute.cs +++ b/src/Umbraco.Core/Components/RequireAttribute.cs @@ -3,40 +3,40 @@ namespace Umbraco.Core.Components { /// - /// Indicates that a component requires another component. + /// Indicates that a composer requires another composer. /// /// - /// This attribute is *not* inherited. This means that a component class inheriting from - /// another component class does *not* inherit its requirements. However, the bootloader checks - /// the *interfaces* of every component for their requirements, so requirements declared on - /// interfaces are inherited by every component class implementing the interface. - /// When targeting a class, indicates a dependency on the component which must be enabled, - /// unless the requirement has explicitly been declared as weak (and then, only if the component + /// This attribute is *not* inherited. This means that a composer class inheriting from + /// another composer class does *not* inherit its requirements. However, the runtime checks + /// the *interfaces* of every composer for their requirements, so requirements declared on + /// interfaces are inherited by every composer class implementing the interface. + /// When targeting a class, indicates a dependency on the composer which must be enabled, + /// unless the requirement has explicitly been declared as weak (and then, only if the composer /// is enabled). - /// When targeting an interface, indicates a dependency on enabled components implementing - /// the interface. It could be no component at all, unless the requirement has explicitly been - /// declared as strong (and at least one component must be enabled). + /// When targeting an interface, indicates a dependency on enabled composers implementing + /// the interface. It could be no composer at all, unless the requirement has explicitly been + /// declared as strong (and at least one composer must be enabled). /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] - public class RequireComponentAttribute : Attribute + public sealed class RequireAttribute : Attribute { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The type of the required component. - public RequireComponentAttribute(Type requiredType) + /// The type of the required composer. + public RequireAttribute(Type requiredType) { - if (typeof(IUmbracoComponent).IsAssignableFrom(requiredType) == false) - throw new ArgumentException($"Type {requiredType.FullName} is invalid here because it does not implement {typeof(IUmbracoComponent).FullName}."); + if (typeof(IComposer).IsAssignableFrom(requiredType) == false) + throw new ArgumentException($"Type {requiredType.FullName} is invalid here because it does not implement {typeof(IComposer).FullName}."); RequiredType = requiredType; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The type of the required component. + /// The type of the required composer. /// A value indicating whether the requirement is weak. - public RequireComponentAttribute(Type requiredType, bool weak) + public RequireAttribute(Type requiredType, bool weak) : this(requiredType) { Weak = weak; @@ -50,8 +50,8 @@ namespace Umbraco.Core.Components /// /// Gets a value indicating whether the requirement is weak. /// - /// Returns true if the requirement is weak (requires the other component if it - /// is enabled), false if the requirement is strong (requires the other component to be + /// Returns true if the requirement is weak (requires the other composer if it + /// is enabled), false if the requirement is strong (requires the other composer to be /// enabled), and null if unspecified, in which case it is strong for classes and weak for /// interfaces. public bool? Weak { get; } diff --git a/src/Umbraco.Core/Components/RequiredByComponentAttribute.cs b/src/Umbraco.Core/Components/RequiredByAttribute.cs similarity index 51% rename from src/Umbraco.Core/Components/RequiredByComponentAttribute.cs rename to src/Umbraco.Core/Components/RequiredByAttribute.cs index 9d206b564b..5da7745892 100644 --- a/src/Umbraco.Core/Components/RequiredByComponentAttribute.cs +++ b/src/Umbraco.Core/Components/RequiredByAttribute.cs @@ -3,32 +3,32 @@ namespace Umbraco.Core.Components { /// - /// Indicates that a component is required by another component. + /// Indicates that a component is required by another composer. /// /// - /// This attribute is *not* inherited. This means that a component class inheriting from - /// another component class does *not* inherit its requirements. However, the bootloader checks - /// the *interfaces* of every component for their requirements, so requirements declared on - /// interfaces are inherited by every component class implementing the interface. - /// When targeting a class, indicates a dependency on the component which must be enabled, - /// unless the requirement has explicitly been declared as weak (and then, only if the component + /// This attribute is *not* inherited. This means that a composer class inheriting from + /// another composer class does *not* inherit its requirements. However, the runtime checks + /// the *interfaces* of every composer for their requirements, so requirements declared on + /// interfaces are inherited by every composer class implementing the interface. + /// When targeting a class, indicates a dependency on the composer which must be enabled, + /// unless the requirement has explicitly been declared as weak (and then, only if the composer /// is enabled). - /// When targeting an interface, indicates a dependency on enabled components implementing - /// the interface. It could be no component at all, unless the requirement has explicitly been - /// declared as strong (and at least one component must be enabled). + /// When targeting an interface, indicates a dependency on enabled composers implementing + /// the interface. It could be no composer at all, unless the requirement has explicitly been + /// declared as strong (and at least one composer must be enabled). /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] - public class RequiredByComponentAttribute : Attribute + public sealed class RequiredByAttribute : Attribute { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The type of the required component. - public RequiredByComponentAttribute(Type requiringType) + /// The type of the required composer. + public RequiredByAttribute(Type requiringType) { - if (typeof(IUmbracoComponent).IsAssignableFrom(requiringType) == false) - throw new ArgumentException($"Type {requiringType.FullName} is invalid here because it does not implement {typeof(IUmbracoComponent).FullName}."); + if (typeof(IComposer).IsAssignableFrom(requiringType) == false) + throw new ArgumentException($"Type {requiringType.FullName} is invalid here because it does not implement {typeof(IComposer).FullName}."); RequiringType = requiringType; } diff --git a/src/Umbraco.Core/Components/UmbracoComponentBase.cs b/src/Umbraco.Core/Components/UmbracoComponentBase.cs deleted file mode 100644 index 476c4c6d59..0000000000 --- a/src/Umbraco.Core/Components/UmbracoComponentBase.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Umbraco.Core.Components -{ - /// - /// Provides a base class for implementations. - /// - public abstract class UmbracoComponentBase : IUmbracoComponent - { - /// - public virtual void Compose(Composition composition) - { } - - /// - public virtual void Terminate() - { } - } -} diff --git a/src/Umbraco.Core/Components/UmbracoCoreComponentBase.cs b/src/Umbraco.Core/Components/UmbracoCoreComponentBase.cs deleted file mode 100644 index 519ef42f85..0000000000 --- a/src/Umbraco.Core/Components/UmbracoCoreComponentBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Core.Components -{ - /// - /// Provides a base class for implementations. - /// - public abstract class UmbracoCoreComponentBase : UmbracoComponentBase, IUmbracoCoreComponent - { } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/UmbracoUserComponentBase.cs b/src/Umbraco.Core/Components/UmbracoUserComponentBase.cs deleted file mode 100644 index 34223a3b0a..0000000000 --- a/src/Umbraco.Core/Components/UmbracoUserComponentBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Core.Components -{ - /// - /// Provides a base class for implementations. - /// - public abstract class UmbracoUserComponentBase : UmbracoComponentBase, IUmbracoUserComponent - { } -} diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index b9ba687316..f370e9bd8c 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Composing Container = container; // register the collection - Container.Register(factory => CreateCollection(factory), CollectionLifetime); + Container.Register(CreateCollection, CollectionLifetime); } /// @@ -103,10 +103,16 @@ namespace Umbraco.Core.Composing RegisterTypes(); // will do it only once return _registeredTypes // respect order - .Select(x => (TItem) factory.GetInstance(x)) + .Select(x => CreateItem(factory, x)) .ToArray(); // safe } + /// + /// Creates a collection item. + /// + protected virtual TItem CreateItem(IFactory factory, Type itemType) + => (TItem) factory.GetInstance(itemType); + /// /// Creates a collection. /// diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 97291c86d0..3a698dd30d 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Threading; using System.Web; @@ -26,7 +27,7 @@ namespace Umbraco.Core.Runtime /// should be possible to use this runtime in console apps. public class CoreRuntime : IRuntime { - private Components.Components _components; + private ComponentCollection _components; private IFactory _factory; private RuntimeState _state; @@ -136,17 +137,17 @@ namespace Umbraco.Core.Runtime AcquireMainDom(mainDom); DetermineRuntimeLevel(databaseFactory, ProfilingLogger); - // get components, and compose them - var componentTypes = ResolveComponentTypes(typeLoader); - _components = new Components.Components(composition, componentTypes, ProfilingLogger); - _components.Compose(); + // get composers, and compose + var composerTypes = ResolveComposerTypes(typeLoader); + composition.WithCollectionBuilder(); + var composers = new Composers(composition, composerTypes, ProfilingLogger); + composers.Compose(); // create the factory _factory = Current.Factory = composition.CreateFactory(); - // run the initializers - for - _components.Initialize(_factory); + // create the components + _components = _factory.GetInstance(); } catch (Exception e) { @@ -257,13 +258,13 @@ namespace Umbraco.Core.Runtime } } - private IEnumerable ResolveComponentTypes(TypeLoader typeLoader) + private IEnumerable ResolveComposerTypes(TypeLoader typeLoader) { - using (var timer = ProfilingLogger.TraceDuration("Resolving component types.", "Resolved.")) + using (var timer = ProfilingLogger.TraceDuration("Resolving composer types.", "Resolved.")) { try { - return GetComponentTypes(typeLoader); + return GetComposerTypes(typeLoader); } catch { @@ -276,10 +277,7 @@ namespace Umbraco.Core.Runtime /// public virtual void Terminate() { - using (ProfilingLogger.DebugDuration("Terminating Umbraco.", "Terminated.")) - { - _components?.Terminate(); - } + _components.Terminate(); } /// @@ -295,10 +293,10 @@ namespace Umbraco.Core.Runtime // getters can be implemented by runtimes inheriting from CoreRuntime /// - /// Gets all component types. + /// Gets all composer types. /// - protected virtual IEnumerable GetComponentTypes(TypeLoader typeLoader) - => typeLoader.GetTypes(); + protected virtual IEnumerable GetComposerTypes(TypeLoader typeLoader) + => typeLoader.GetTypes(); /// /// Gets a logger. diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs index 83405dc13a..fb4c0ec558 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs @@ -1,128 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Configuration; +using System.Collections.Generic; using AutoMapper; -using Umbraco.Core.Cache; using Umbraco.Core.Components; -using Umbraco.Core.Composing; -using Umbraco.Core.Composing.Composers; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Core.IO.MediaPathSchemes; -using Umbraco.Core.Logging; -using Umbraco.Core.Manifest; -using Umbraco.Core.Migrations; -using Umbraco.Core.Migrations.Install; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.Validators; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Core.Sync; -using Umbraco.Core._Legacy.PackageActions; -using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; namespace Umbraco.Core.Runtime { - public class CoreRuntimeComponent : UmbracoComponentBase, IRuntimeComponent + public class CoreRuntimeComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - - // composers - composition - .ComposeConfiguration() - .ComposeRepositories() - .ComposeServices() - .ComposeCoreMappingProfiles() - .ComposeFileSystems(); - - // register persistence mappers - required by database factory so needs to be done here - // means the only place the collection can be modified is in a runtime - afterwards it - // has been frozen and it is too late - composition.WithCollectionBuilder().AddCoreMappers(); - - // register the scope provider - composition.RegisterUnique(); // implements both IScopeProvider and IScopeAccessor - composition.RegisterUnique(f => f.GetInstance()); - composition.RegisterUnique(f => f.GetInstance()); - - // register database builder - // *not* a singleton, don't want to keep it around - composition.Register(); - - // register manifest parser, will be injected in collection builders where needed - composition.RegisterUnique(); - - // register our predefined validators - composition.WithCollectionBuilder() - .Add() - .Add() - .Add() - .Add() - .Add() - .Add(); - - // properties and parameters derive from data editors - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetDataEditors()); - composition.RegisterUnique(); - composition.RegisterUnique(); - - // register a server registrar, by default it's the db registrar - composition.RegisterUnique(f => - { - if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) - return new SingleServerRegistrar(f.GetInstance()); - return new DatabaseServerRegistrar( - new Lazy(f.GetInstance), - new DatabaseServerRegistrarOptions()); - }); - - // by default we'll use the database server messenger with default options (no callbacks), - // this will be overridden by either the legacy thing or the db thing in the corresponding - // components in the web project - fixme - should obsolete the legacy thing - composition.RegisterUnique(factory - => new DatabaseServerMessenger( - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - true, new DatabaseServerMessengerOptions())); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetCacheRefreshers()); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetPackageActions()); - - composition.WithCollectionBuilder() - .Append(composition.TypeLoader.GetTypes()); - - composition.RegisterUnique(); - - composition.RegisterUnique(factory - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); - - composition.WithCollectionBuilder() - .Append(); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.RegisterUnique(factory => new MigrationBuilder(factory)); - - // by default, register a noop factory - composition.RegisterUnique(); - } - - internal void Initialize(IEnumerable mapperProfiles) + internal CoreRuntimeComponent(IEnumerable mapperProfiles) { // mapper profiles have been registered & are created by the container Mapper.Initialize(configuration => diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs new file mode 100644 index 0000000000..de0fae98f9 --- /dev/null +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs @@ -0,0 +1,121 @@ +using System; +using System.Configuration; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Composing.Composers; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Manifest; +using Umbraco.Core.Migrations; +using Umbraco.Core.Migrations.Install; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Core.Sync; +using Umbraco.Core._Legacy.PackageActions; +using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; + +namespace Umbraco.Core.Runtime +{ + public class CoreRuntimeComposer : IRuntimeComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + + // composers + composition + .ComposeConfiguration() + .ComposeRepositories() + .ComposeServices() + .ComposeCoreMappingProfiles() + .ComposeFileSystems(); + + // register persistence mappers - required by database factory so needs to be done here + // means the only place the collection can be modified is in a runtime - afterwards it + // has been frozen and it is too late + composition.WithCollectionBuilder().AddCoreMappers(); + + // register the scope provider + composition.RegisterUnique(); // implements both IScopeProvider and IScopeAccessor + composition.RegisterUnique(f => f.GetInstance()); + composition.RegisterUnique(f => f.GetInstance()); + + // register database builder + // *not* a singleton, don't want to keep it around + composition.Register(); + + // register manifest parser, will be injected in collection builders where needed + composition.RegisterUnique(); + + // register our predefined validators + composition.WithCollectionBuilder() + .Add() + .Add() + .Add() + .Add() + .Add() + .Add(); + + // properties and parameters derive from data editors + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetDataEditors()); + composition.RegisterUnique(); + composition.RegisterUnique(); + + // register a server registrar, by default it's the db registrar + composition.RegisterUnique(f => + { + if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) + return new SingleServerRegistrar(f.GetInstance()); + return new DatabaseServerRegistrar( + new Lazy(f.GetInstance), + new DatabaseServerRegistrarOptions()); + }); + + // by default we'll use the database server messenger with default options (no callbacks), + // this will be overridden by either the legacy thing or the db thing in the corresponding + // components in the web project - fixme - should obsolete the legacy thing + composition.RegisterUnique(factory + => new DatabaseServerMessenger( + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + true, new DatabaseServerMessengerOptions())); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetCacheRefreshers()); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetPackageActions()); + + composition.WithCollectionBuilder() + .Append(composition.TypeLoader.GetTypes()); + + composition.RegisterUnique(); + + composition.RegisterUnique(factory + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); + + composition.WithCollectionBuilder() + .Append(); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.RegisterUnique(factory => new MigrationBuilder(factory)); + + // by default, register a noop factory + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 0022ef26c9..a5a57df80e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -154,24 +154,21 @@ - + + - - - + + - - - - - + + + + + - - - - - + + @@ -1293,6 +1290,7 @@ + diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index f76f6b73b6..73f9656f56 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -67,7 +67,7 @@ - + diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index c2cc0c5038..f50feb1d00 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -63,14 +63,14 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - goes away with RuntimeLevel.Unknown // => reorder components accordingly components.Compose(); - AssertTypeArray(TypeArray(), Composed); + AssertTypeArray(TypeArray(), Composed); } [Test] @@ -79,14 +79,14 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - stays with RuntimeLevel.Run // => reorder components accordingly components.Compose(); - AssertTypeArray(TypeArray(), Composed); + AssertTypeArray(TypeArray(), Composed); } [Test] @@ -95,13 +95,13 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 21 is required by 20 // => reorder components accordingly components.Compose(); - AssertTypeArray(TypeArray(), Composed); + AssertTypeArray(TypeArray(), Composed); } [Test] @@ -110,15 +110,15 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // i23 requires 22 // 24, 25 implement i23 // 25 required by i23 // => reorder components accordingly components.Compose(); - AssertTypeArray(TypeArray(), Composed); + AssertTypeArray(TypeArray(), Composed); } [Test] @@ -127,8 +127,8 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); try { @@ -140,7 +140,7 @@ namespace Umbraco.Tests.Components } catch (Exception e) { - Assert.AreEqual("Broken component dependency: Umbraco.Tests.Components.ComponentTests+Component2 -> Umbraco.Tests.Components.ComponentTests+Component4.", e.Message); + Assert.AreEqual("Broken composer dependency: Umbraco.Tests.Components.ComponentTests+Composer2 -> Umbraco.Tests.Components.ComponentTests+Composer4.", e.Message); } } @@ -150,15 +150,15 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = TypeArray(); - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 13 is required by 1 // 1 is missing // => reorder components accordingly components.Compose(); - AssertTypeArray(TypeArray(), Composed); + AssertTypeArray(TypeArray(), Composed); } [Test] @@ -168,18 +168,26 @@ namespace Umbraco.Tests.Components var factory = MockFactory(m => { m.Setup(x => x.TryGetInstance(It.Is(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource()); + m.Setup(x => x.GetInstance(It.IsAny())).Returns((type) => + { + if (type == typeof(Composer1)) return new Composer1(); + if (type == typeof(Composer5)) return new Composer5(); + if (type == typeof(Component5)) return new Component5(new SomeResource()); + if (type == typeof(IProfilingLogger)) return new ProfilingLogger(Mock.Of(), Mock.Of()); + throw new NotSupportedException(type.FullName); + }); }); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Component1), typeof(Component5) }; - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer1), typeof(Composer5) }; + var composers = new Composers(composition, types, Mock.Of()); Composed.Clear(); Initialized.Clear(); - components.Compose(); - components.Initialize(factory); + composers.Compose(); + var components = composition.WithCollectionBuilder().CreateCollection(factory); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component1), Composed[0]); - Assert.AreEqual(typeof(Component5), Composed[1]); + Assert.AreEqual(typeof(Composer1), Composed[0]); + Assert.AreEqual(typeof(Composer5), Composed[1]); Assert.AreEqual(1, Initialized.Count); Assert.AreEqual("Umbraco.Tests.Components.ComponentTests+SomeResource", Initialized[0]); } @@ -190,13 +198,13 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Component6), typeof(Component7), typeof(Component8) }; - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component6), Composed[0]); - Assert.AreEqual(typeof(Component8), Composed[1]); + Assert.AreEqual(typeof(Composer6), Composed[0]); + Assert.AreEqual(typeof(Composer8), Composed[1]); } [Test] @@ -205,13 +213,13 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Component9), typeof(Component2), typeof(Component4) }; - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component4), Composed[0]); - Assert.AreEqual(typeof(Component2), Composed[1]); + Assert.AreEqual(typeof(Composer4), Composed[0]); + Assert.AreEqual(typeof(Composer2), Composed[1]); //Assert.AreEqual(typeof(Component9), Composed[2]); -- goes away with RuntimeLevel.Unknown } @@ -222,15 +230,15 @@ namespace Umbraco.Tests.Components var factory = MockFactory(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); - var types = new[] { typeof(Component9), typeof(Component2), typeof(Component4) }; - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; + var composers = new Composers(composition, types, Mock.Of()); Composed.Clear(); - components.Compose(); - components.Initialize(factory); + composers.Compose(); + var components = composition.WithCollectionBuilder().CreateCollection(factory); Assert.AreEqual(3, Composed.Count); - Assert.AreEqual(typeof(Component4), Composed[0]); - Assert.AreEqual(typeof(Component2), Composed[1]); - Assert.AreEqual(typeof(Component9), Composed[2]); + Assert.AreEqual(typeof(Composer4), Composed[0]); + Assert.AreEqual(typeof(Composer2), Composed[1]); + Assert.AreEqual(typeof(Composer9), Composed[2]); } [Test] @@ -239,29 +247,29 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Component10) }; - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer10) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); components.Compose(); Assert.AreEqual(1, Composed.Count); - Assert.AreEqual(typeof(Component10), Composed[0]); + Assert.AreEqual(typeof(Composer10), Composed[0]); - types = new[] { typeof(Component11) }; - components = new Core.Components.Components(composition, types, Mock.Of()); + types = new[] { typeof(Composer11) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); Assert.Throws(() => components.Compose()); - types = new[] { typeof(Component2) }; - components = new Core.Components.Components(composition, types, Mock.Of()); + types = new[] { typeof(Composer2) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); Assert.Throws(() => components.Compose()); - types = new[] { typeof(Component12) }; - components = new Core.Components.Components(composition, types, Mock.Of()); + types = new[] { typeof(Composer12) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); components.Compose(); Assert.AreEqual(1, Composed.Count); - Assert.AreEqual(typeof(Component12), Composed[0]); + Assert.AreEqual(typeof(Composer12), Composed[0]); } [Test] @@ -270,106 +278,117 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Component6), typeof(Component8) }; // 8 disables 7 which is not in the list - var components = new Core.Components.Components(composition, types, Mock.Of()); + var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component6), Composed[0]); - Assert.AreEqual(typeof(Component8), Composed[1]); + Assert.AreEqual(typeof(Composer6), Composed[0]); + Assert.AreEqual(typeof(Composer8), Composed[1]); } #region Components - public class TestComponentBase : UmbracoComponentBase + public class TestComposerBase : IComposer { - public override void Compose(Composition composition) + public virtual void Compose(Composition composition) { - base.Compose(composition); Composed.Add(GetType()); } } - public class Component1 : TestComponentBase + public class TestComponentBase : IComponent { } - [RequireComponent(typeof(Component4))] - public class Component2 : TestComponentBase, IUmbracoCoreComponent + public class Composer1 : TestComposerBase { } - public class Component3 : TestComponentBase, IUmbracoUserComponent + [Require(typeof(Composer4))] + public class Composer2 : TestComposerBase, ICoreComposer { } - public class Component4 : TestComponentBase + public class Composer3 : TestComposerBase, IUserComposer { } - public class Component5 : TestComponentBase + public class Composer4 : TestComposerBase + { } + + public class Composer5 : TestComposerBase { - public void Initialize(ISomeResource resource) + public override void Compose(Composition composition) + { + base.Compose(composition); + composition.Components().Append(); + } + } + + public class Component5 : IComponent + { + public Component5(ISomeResource resource) { Initialized.Add(resource.GetType().FullName); } } - [DisableComponent] - public class Component6 : TestComponentBase + [Disable] + public class Composer6 : TestComposerBase { } - public class Component7 : TestComponentBase + public class Composer7 : TestComposerBase { } - [DisableComponent(typeof(Component7))] - [EnableComponent(typeof(Component6))] - public class Component8 : TestComponentBase + [Disable(typeof(Composer7))] + [Enable(typeof(Composer6))] + public class Composer8 : TestComposerBase { } - public interface ITestComponent : IUmbracoUserComponent + public interface ITestComposer : IUserComposer { } - public class Component9 : TestComponentBase, ITestComponent + public class Composer9 : TestComposerBase, ITestComposer { } - [RequireComponent(typeof(ITestComponent))] - public class Component10 : TestComponentBase + [Require(typeof(ITestComposer))] + public class Composer10 : TestComposerBase { } - [RequireComponent(typeof(ITestComponent), false)] - public class Component11 : TestComponentBase + [Require(typeof(ITestComposer), false)] + public class Composer11 : TestComposerBase { } - [RequireComponent(typeof(Component4), true)] - public class Component12 : TestComponentBase, IUmbracoCoreComponent + [Require(typeof(Composer4), true)] + public class Composer12 : TestComposerBase, ICoreComposer { } - [RequiredByComponent(typeof(Component1))] - public class Component13 : TestComponentBase + [RequiredBy(typeof(Composer1))] + public class Composer13 : TestComposerBase { } public interface ISomeResource { } public class SomeResource : ISomeResource { } - public class Component20 : TestComponentBase + public class Composer20 : TestComposerBase { } - [RequiredByComponent(typeof(Component20))] - public class Component21 : TestComponentBase + [RequiredBy(typeof(Composer20))] + public class Composer21 : TestComposerBase { } - public class Component22 : TestComponentBase + public class Composer22 : TestComposerBase { } - [RequireComponent(typeof(Component22))] - public interface IComponent23 : IUmbracoComponent + [Require(typeof(Composer22))] + public interface IComposer23 : IComposer { } - public class Component24 : TestComponentBase, IComponent23 + public class Composer24 : TestComposerBase, IComposer23 { } // should insert itself between 22 and anything i23 - [RequiredByComponent(typeof(IComponent23))] + [RequiredBy(typeof(IComposer23))] //[RequireComponent(typeof(Component22))] - not needed, implement i23 - public class Component25 : TestComponentBase, IComponent23 + public class Composer25 : TestComposerBase, IComposer23 { } #endregion diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index e8f9a61607..ca70ac5b6a 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -63,10 +63,9 @@ namespace Umbraco.Tests.Runtimes } Assert.AreNotEqual(RuntimeLevel.BootFailed, app.Runtime.State.Level, m); + Assert.IsTrue(TestComposer.Ctored); + Assert.IsTrue(TestComposer.Composed); Assert.IsTrue(TestComponent.Ctored); - Assert.IsTrue(TestComponent.Composed); - Assert.IsTrue(TestComponent.Initialized1); - Assert.IsTrue(TestComponent.Initialized2); Assert.IsNotNull(TestComponent.ProfilingLogger); Assert.IsInstanceOf(TestComponent.ProfilingLogger); Assert.IsInstanceOf(((ProfilingLogger) TestComponent.ProfilingLogger).Logger); @@ -155,62 +154,65 @@ namespace Umbraco.Tests.Runtimes // runs with only one single component // UmbracoCoreComponent will be force-added too // and that's it - protected override IEnumerable GetComponentTypes(TypeLoader typeLoader) + protected override IEnumerable GetComposerTypes(TypeLoader typeLoader) { - return new[] { typeof(TestComponent) }; + return new[] { typeof(TestComposer) }; } } - public class TestComponent : UmbracoComponentBase + public class TestComposer : IComposer { // test flags public static bool Ctored; public static bool Composed; + + public static void Reset() + { + Ctored = Composed = false; + } + + public TestComposer() + { + Ctored = true; + } + + public void Compose(Composition composition) + { + composition.Register(factory => SettingsForTests.GetDefaultUmbracoSettings()); + composition.RegisterUnique(); + composition.Components().Append(); + + Composed = true; + } + } + + public class TestComponent : IComponent, IDisposable + { + // test flags + public static bool Ctored; public static bool Initialized1; public static bool Initialized2; public static bool Terminated; public static IProfilingLogger ProfilingLogger; + public bool Disposed; + public static void Reset() { - Ctored = Composed = Initialized1 = Initialized2 = Terminated = false; + Ctored = Initialized1 = Initialized2 = Terminated = false; ProfilingLogger = null; } - public TestComponent() + public TestComponent(IProfilingLogger proflog) { Ctored = true; - } - - public override void Compose(Composition composition) - { - base.Compose(composition); - - composition.Register(factory => SettingsForTests.GetDefaultUmbracoSettings()); - composition.RegisterUnique(); - - Composed = true; - } - - public void Initialize() - { - Initialized1 = true; - } - - public void Initialize(ILogger logger) - { - Initialized2 = true; - } - - public void Initialize(IProfilingLogger proflog) - { ProfilingLogger = proflog; } - public override void Terminate() + public void Dispose() { - base.Terminate(); + Disposed = true; Terminated = true; } } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 6e3b8a2094..c24ca6f351 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Web; +using Examine; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -50,7 +51,7 @@ namespace Umbraco.Tests.Runtimes File.Delete(file); // settings - // reset the current version to 0.0.0 + // reset the current version to 0.0.0, clear connection strings ConfigurationManager.AppSettings["umbracoConfigurationStatus"] = ""; // fixme we need a better management of settings here (and, true config files?) @@ -75,14 +76,15 @@ namespace Umbraco.Tests.Runtimes // determine actual runtime level runtimeState.DetermineRuntimeLevel(databaseFactory, logger); + Console.WriteLine(runtimeState.Level); // going to be Install BUT we want to force components to be there (nucache etc) runtimeState.Level = RuntimeLevel.Run; - var componentTypes = typeLoader.GetTypes() // all of them + var composerTypes = typeLoader.GetTypes() // all of them .Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components - .Where(x => x != typeof(WebRuntimeComponent)); // exclude web runtime - var components = new Core.Components.Components(composition, componentTypes, profilingLogger); - components.Compose(); + .Where(x => x != typeof(WebRuntimeComposer)); // exclude web runtime + var composers = new Composers(composition, composerTypes, profilingLogger); + composers.Compose(); // must registers stuff that WebRuntimeComponent would register otherwise // fixme UmbracoContext creates a snapshot that it does not register with the accessor @@ -98,12 +100,18 @@ namespace Umbraco.Tests.Runtimes composition.RegisterUnique(f => new DistributedCache()); composition.WithCollectionBuilder().Append(); composition.RegisterUnique(); + composition.RegisterUnique(f => ExamineManager.Instance); + + // initialize some components only/individually + composition.WithCollectionBuilder() + .Clear() + .Append(); // create and register the factory Current.Factory = factory = composition.CreateFactory(); - // initialize some components individually - components.Get().Initialize(factory.GetInstance()); + // instantiate and initialize components + var components = factory.GetInstance(); // do stuff Console.WriteLine(runtimeState.Level); @@ -251,16 +259,16 @@ namespace Umbraco.Tests.Runtimes // get the components // all of them? - var componentTypes = typeLoader.GetTypes(); + var composerTypes = typeLoader.GetTypes(); // filtered? //var componentTypes = typeLoader.GetTypes() // .Where(x => !x.FullName.StartsWith("Umbraco.Web")); // single? //var componentTypes = new[] { typeof(CoreRuntimeComponent) }; - var components = new Core.Components.Components(composition, componentTypes, profilingLogger); + var composers = new Composers(composition, composerTypes, profilingLogger); // get components to compose themselves - components.Compose(); + composers.Compose(); // create the factory var factory = composition.CreateFactory(); @@ -276,6 +284,11 @@ namespace Umbraco.Tests.Runtimes // and then, validate var lightInjectContainer = (LightInject.ServiceContainer) factory.Concrete; var results = lightInjectContainer.Validate().ToList(); + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) + { + Console.WriteLine($"{resultGroup.Key}: {resultGroup.Count()}"); + } + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) foreach (var result in resultGroup) { diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 0433603684..e74125adfa 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -42,6 +42,7 @@ namespace Umbraco.Tests.Scoping composition.RegisterUnique(factory => new FileSystems(factory, factory.TryGetInstance())); composition.WithCollectionBuilder(); + Current.Reset(); Current.Factory = composition.CreateFactory(); SettingsForTests.Reset(); // ensure we have configuration diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs index b679ce1bfd..91646e3624 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs @@ -1,22 +1,10 @@ -using Umbraco.Core; -using Umbraco.Core.Components; -using Umbraco.Core.Composing; +using Umbraco.Core.Components; namespace Umbraco.Web.Cache { - /// - /// Installs listeners on service events in order to refresh our caches. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - [RequiredByComponent(typeof(IUmbracoCoreComponent))] // runs before every other IUmbracoCoreComponent! - public class DistributedCacheBinderComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class DistributedCacheBinderComponent : IComponent { - public override void Compose(Composition composition) - { - composition.RegisterUnique(); - } - - public void Initialize(IDistributedCacheBinder distributedCacheBinder) + public DistributedCacheBinderComponent(IDistributedCacheBinder distributedCacheBinder) { distributedCacheBinder.BindEvents(); } diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs b/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs new file mode 100644 index 0000000000..87b181c2d0 --- /dev/null +++ b/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs @@ -0,0 +1,20 @@ +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Cache +{ + /// + /// Installs listeners on service events in order to refresh our caches. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + [RequiredBy(typeof(ICoreComposer))] // runs before every other IUmbracoCoreComponent! + public sealed class DistributedCacheBinderComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.RegisterUnique(); + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs index 79630ef613..81b900fc3a 100644 --- a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs +++ b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs @@ -1,31 +1,28 @@ using System; using Umbraco.Core; using Umbraco.Core.Components; -using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Security; namespace Umbraco.Web.Components { - public sealed class BackOfficeUserAuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class BackOfficeUserAuditEventsComponent : IComponent { - private IAuditService _auditService; - private IUserService _userService; - + private readonly IAuditService _auditService; + private readonly IUserService _userService; + private IUser GetPerformingUser(int userId) { var found = userId >= 0 ? _userService.GetUserById(userId) : null; return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""}; } - - public void Initialize(IAuditService auditService, IUserService userService) + public BackOfficeUserAuditEventsComponent(IAuditService auditService, IUserService userService) { _auditService = auditService; _userService = userService; - + //BackOfficeUserManager.AccountLocked += ; //BackOfficeUserManager.AccountUnlocked += ; BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest; @@ -39,7 +36,7 @@ namespace Umbraco.Web.Components //BackOfficeUserManager.ResetAccessFailedCount += ; } - + private static string FormatEmail(IMembershipUser user) { return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; diff --git a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs new file mode 100644 index 0000000000..021eba799b --- /dev/null +++ b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs @@ -0,0 +1,12 @@ +using Umbraco.Core.Components; + +namespace Umbraco.Web.Components +{ + public sealed class BackOfficeUserAuditEventsComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs index efe44eaacc..4d85a01ccc 100644 --- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs @@ -35,24 +35,14 @@ namespace Umbraco.Web.Components // during Initialize / Startup, we end up checking Examine, which needs to be initialized beforehand // todo - should not be a strong dependency on "examine" but on an "indexing component" - [RequireComponent(typeof(ExamineComponent))] + [Require(typeof(ExamineComposer))] - public sealed class DatabaseServerRegistrarAndMessengerComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class DatabaseServerRegistrarAndMessengerComposer : ICoreComposer { - private object _locker = new object(); - private DatabaseServerRegistrar _registrar; - private BatchedDatabaseServerMessenger _messenger; - private IRuntimeState _runtime; - private ILogger _logger; - private IServerRegistrationService _registrationService; - private BackgroundTaskRunner _touchTaskRunner; - private BackgroundTaskRunner _processTaskRunner; - private bool _started; - private IBackgroundTask[] _tasks; - private IndexRebuilder _indexRebuilder; - - public override void Compose(Composition composition) + public void Compose(Composition composition) { + composition.Components().Append(); + composition.SetServerMessenger(factory => { var runtime = factory.GetInstance(); @@ -61,6 +51,8 @@ namespace Umbraco.Web.Components var proflog = factory.GetInstance(); var scopeProvider = factory.GetInstance(); var sqlContext = factory.GetInstance(); + var logger = factory.GetInstance(); + var indexRebuilder = factory.GetInstance(); return new BatchedDatabaseServerMessenger( runtime, databaseFactory, scopeProvider, sqlContext, proflog, globalSettings, @@ -88,13 +80,31 @@ namespace Umbraco.Web.Components //rebuild indexes if the server is not synced // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific // indexes then they can adjust this logic themselves. - () => ExamineComponent.RebuildIndexes(_indexRebuilder, _logger, false, 5000) + () => + { + ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000); + } } }); }); } + } - public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) + public sealed class DatabaseServerRegistrarAndMessengerComponent : IComponent + { + private object _locker = new object(); + private DatabaseServerRegistrar _registrar; + private BatchedDatabaseServerMessenger _messenger; + private IRuntimeState _runtime; + private ILogger _logger; + private IServerRegistrationService _registrationService; + private BackgroundTaskRunner _touchTaskRunner; + private BackgroundTaskRunner _processTaskRunner; + private bool _started; + private IBackgroundTask[] _tasks; + private IndexRebuilder _indexRebuilder; + + public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) { _registrar = serverRegistrar as DatabaseServerRegistrar; if (_registrar == null) throw new Exception("panic: registar."); diff --git a/src/Umbraco.Web/Components/NotificationsComponent.cs b/src/Umbraco.Web/Components/NotificationsComponent.cs index 685029f038..9314181c15 100644 --- a/src/Umbraco.Web/Components/NotificationsComponent.cs +++ b/src/Umbraco.Web/Components/NotificationsComponent.cs @@ -7,7 +7,6 @@ using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Actions; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using System.Linq; @@ -17,16 +16,9 @@ using System.Globalization; namespace Umbraco.Web.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class NotificationsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class NotificationsComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - composition.RegisterUnique(); - } - - public void Initialize(INotificationService notificationService, Notifier notifier, ActionCollection actions) + public NotificationsComponent(INotificationService notificationService, Notifier notifier, ActionCollection actions) { //Send notifications for the send to publish action ContentService.SentToPublish += (sender, args) => notifier.Notify(actions.GetAction(), args.Entity); diff --git a/src/Umbraco.Web/Components/NotificationsComposer.cs b/src/Umbraco.Web/Components/NotificationsComposer.cs new file mode 100644 index 0000000000..d1830323dd --- /dev/null +++ b/src/Umbraco.Web/Components/NotificationsComposer.cs @@ -0,0 +1,16 @@ +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class NotificationsComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Web/Components/PublicAccessComponent.cs b/src/Umbraco.Web/Components/PublicAccessComponent.cs index 5c18eefc66..09c1108295 100644 --- a/src/Umbraco.Web/Components/PublicAccessComponent.cs +++ b/src/Umbraco.Web/Components/PublicAccessComponent.cs @@ -6,13 +6,9 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.Components { - /// - /// Used to ensure that the public access data file is kept up to date properly - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class PublicAccessComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class PublicAccessComponent : IComponent { - public void Initialize() + public PublicAccessComponent() { MemberGroupService.Saved += MemberGroupService_Saved; } diff --git a/src/Umbraco.Web/Components/PublicAccessComposer.cs b/src/Umbraco.Web/Components/PublicAccessComposer.cs new file mode 100644 index 0000000000..4229df933c --- /dev/null +++ b/src/Umbraco.Web/Components/PublicAccessComposer.cs @@ -0,0 +1,17 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Components +{ + /// + /// Used to ensure that the public access data file is kept up to date properly + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class PublicAccessComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Logging/WebProfilerComponent.cs b/src/Umbraco.Web/Logging/WebProfilerComponent.cs index c6c7b9a545..d37cfdf88b 100755 --- a/src/Umbraco.Web/Logging/WebProfilerComponent.cs +++ b/src/Umbraco.Web/Logging/WebProfilerComponent.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Logging; namespace Umbraco.Web.Logging { - internal class WebProfilerComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal class WebProfilerComponent : IComponent { // the profiler is too important to be composed in a component, // it is composed first thing in WebRuntime.Compose - this component @@ -19,7 +19,7 @@ namespace Umbraco.Web.Logging private WebProfiler _profiler; - public void Initialize(IProfiler profiler, ILogger logger, IRuntimeState runtime) + public WebProfilerComponent(IProfiler profiler, ILogger logger, IRuntimeState runtime) { // although registered in WebRuntime.Compose, ensure that we have not // been replaced by another component, and we are still "the" profiler diff --git a/src/Umbraco.Web/Logging/WebProfilerComposer.cs b/src/Umbraco.Web/Logging/WebProfilerComposer.cs new file mode 100644 index 0000000000..81a099167b --- /dev/null +++ b/src/Umbraco.Web/Logging/WebProfilerComposer.cs @@ -0,0 +1,12 @@ +using Umbraco.Core.Components; + +namespace Umbraco.Web.Logging +{ + internal class WebProfilerComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs index c4f9f6be15..1b133d29c2 100644 --- a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs @@ -1,18 +1,15 @@ using System.Linq; -using Examine; using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services.Implement; -using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - internal class PropertyEditorsComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal class PropertyEditorsComponent : IComponent { - public void Initialize(IRuntimeState runtime, PropertyEditorCollection propertyEditors) + public PropertyEditorsComponent(PropertyEditorCollection propertyEditors) { var fileUpload = propertyEditors.OfType().FirstOrDefault(); if (fileUpload != null) Initialize(fileUpload); diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs new file mode 100644 index 0000000000..cfa8bacd77 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs @@ -0,0 +1,14 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.PropertyEditors +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + internal class PropertyEditorsComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs index 0651ea1aed..cdcae49b76 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs @@ -1,28 +1,9 @@ using Umbraco.Core.Components; -using Umbraco.Core.Composing; -using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Web.PublishedCache.NuCache { - public class NuCacheComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class NuCacheComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - - // register the NuCache database data source - composition.Register(); - - // register the NuCache published snapshot service - // must register default options, required in the service ctor - composition.Register(factory => new PublishedSnapshotService.Options()); - composition.SetPublishedSnapshotService(); - - // add the NuCache health check (hidden from type finder) - // todo - no NuCache health check yet - //composition.HealthChecks().Add(); - } - public void Initialize(IPublishedSnapshotService service) { // nothing - this just ensures that the service is created at boot time diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs new file mode 100644 index 0000000000..0bef9b9fc4 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -0,0 +1,26 @@ +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Web.PublishedCache.NuCache +{ + public class NuCacheComposer : ICoreComposer + { + public void Compose(Composition composition) + { + // register the NuCache database data source + composition.Register(); + + // register the NuCache published snapshot service + // must register default options, required in the service ctor + composition.Register(factory => new PublishedSnapshotService.Options()); + composition.SetPublishedSnapshotService(); + + // add the NuCache health check (hidden from type finder) + // todo - no NuCache health check yet + //composition.HealthChecks().Add(); + + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 2558f18077..fbbdc1129f 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Events; @@ -13,23 +12,13 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.Redirects { - /// - /// Implements an Application Event Handler for managing redirect urls tracking. - /// - /// - /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url - /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably - /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - [DisableComponent] // fixme - re-enable when we fix redirect tracking with variants - public class RedirectTrackingComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class RedirectTrackingComponent : IComponent { private const string ContextKey1 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.1"; private const string ContextKey2 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.2"; private const string ContextKey3 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.3"; - protected void Initialize() + protected RedirectTrackingComponent() { // events are weird // on 'published' we 'could' get the old or the new route depending on event handlers order diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs new file mode 100644 index 0000000000..209da77bf3 --- /dev/null +++ b/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs @@ -0,0 +1,23 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Redirects +{ + /// + /// Implements an Application Event Handler for managing redirect urls tracking. + /// + /// + /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url + /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably + /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + [Disable] // fixme - re-enable when we fix redirect tracking with variants + public class RedirectTrackingComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index b07fb999b9..a35d381d70 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -10,46 +10,23 @@ using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using System.Web.Routing; -using System.Web.Security; using ClientDependency.Core.CompositeFiles.Providers; using ClientDependency.Core.Config; -using Examine; -using Microsoft.AspNet.SignalR; using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Dictionary; -using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Profiling; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.ValueConverters; -using Umbraco.Core.Runtime; using Umbraco.Core.Services; -using Umbraco.Web.Actions; -using Umbraco.Web.Cache; -using Umbraco.Web.Composing.Composers; -using Umbraco.Web.ContentApps; -using Umbraco.Web.Dictionary; -using Umbraco.Web.Editors; -using Umbraco.Web.Features; -using Umbraco.Web.HealthCheck; using Umbraco.Web.Install; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; -using Umbraco.Web.Search; using Umbraco.Web.Security; -using Umbraco.Web.Security.Providers; -using Umbraco.Web.Services; -using Umbraco.Web.SignalR; -using Umbraco.Web.Tour; -using Umbraco.Web.Trees; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; @@ -57,161 +34,9 @@ using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Runtime { - [RequireComponent(typeof(CoreRuntimeComponent))] - public class WebRuntimeComponent : UmbracoComponentBase, IRuntimeComponent + public class WebRuntimeComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - - composition.Register(); - - composition.RegisterUnique(); // required for hybrid accessors - - composition.ComposeWebMappingProfiles(); - - //register the install components - //NOTE: i tried to not have these registered if we weren't installing or upgrading but post install when the site restarts - //it still needs to use the install controller so we can't do that - composition.ComposeInstaller(); - - // register membership stuff - composition.Register(factory => Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider()); - composition.Register(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetInstance())); - composition.Register(); - - // register accessors for cultures - composition.RegisterUnique(); - composition.RegisterUnique(); - - // register the http context and umbraco context accessors - // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when - // we have no http context, eg when booting Umbraco or in background threads, so instead - // let's use an hybrid accessor that can fall back to a ThreadStatic context. - composition.RegisterUnique(); - - // register a per-request HttpContextBase object - // is per-request so only one wrapper is created per request - composition.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), Lifetime.Request); - - // register the published snapshot accessor - the "current" published snapshot is in the umbraco context - composition.RegisterUnique(); - - // we should stop injecting UmbracoContext and always inject IUmbracoContextAccessor, however at the moment - // there are tons of places (controllers...) which require UmbracoContext in their ctor - so let's register - // a way to inject the UmbracoContext - and register it per-request to be more efficient - //TODO: stop doing this - composition.Register(factory => factory.GetInstance().UmbracoContext, Lifetime.Request); - - // register the umbraco helper - composition.RegisterUnique(); - - // register distributed cache - composition.RegisterUnique(f => new DistributedCache()); - - // replace some services - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - - composition.RegisterUnique(factory => ExamineManager.Instance); - - // configure the container for web - composition.ConfigureForWeb(); - - - composition.RegisterUnique(); - - composition - .ComposeUmbracoControllers(GetType().Assembly) - .SetDefaultRenderMvcController(); // default controller for template views - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); // fixme which searchable trees?! - - composition.Register(Lifetime.Request); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.WithCollectionBuilder(); - - composition.RegisterUnique(); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - - var surfaceControllerTypes = new SurfaceControllerTypeCollection(composition.TypeLoader.GetSurfaceControllers()); - composition.RegisterUnique(surfaceControllerTypes); - - var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(composition.TypeLoader.GetUmbracoApiControllers()); - composition.RegisterUnique(umbracoApiControllerTypes); - - // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be - // discovered when CoreBootManager configures the converters. We HAVE to remove one of them - // here because there cannot be two converters for one property editor - and we want the full - // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. - // (the limited one, defined in Core, is there for tests) - same for others - composition.WithCollectionBuilder() - .Remove() - .Remove() - .Remove(); - - // add all known factories, devs can then modify this list on application - // startup either by binding to events or in their own global.asax - composition.WithCollectionBuilder() - .Append(); - - composition.WithCollectionBuilder() - .Append() - .Append() - .Append(); - - composition.RegisterUnique(); - - composition.WithCollectionBuilder() - // all built-in finders in the correct order, - // devs can then modify this list on application startup - .Append() - .Append() - .Append() - //.Append() // disabled, this is an odd finder - .Append() - .Append(); - - composition.RegisterUnique(); - - composition.RegisterUnique(); - - // register *all* checks, except those marked [HideFromTypeFinder] of course - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - - // auto-register views - composition.RegisterAuto(typeof(UmbracoViewPage<>)); - - // register published router - composition.RegisterUnique(); - composition.Register(_ => Current.Config.Umbraco().WebRouting); - - // register preview SignalR hub - composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); - - // register properties fallback - composition.RegisterUnique(); - - // register known content apps - composition.WithCollectionBuilder() - .Append() - .Append() - .Append(); - } - - internal void Initialize( + internal WebRuntimeComponent( IRuntimeState runtime, IUmbracoContextAccessor umbracoContextAccessor, SurfaceControllerTypeCollection surfaceControllerTypes, diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs new file mode 100644 index 0000000000..4cc502e011 --- /dev/null +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -0,0 +1,190 @@ +using System.Web; +using System.Web.Security; +using Examine; +using Microsoft.AspNet.SignalR; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Events; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Runtime; +using Umbraco.Core.Services; +using Umbraco.Web.Actions; +using Umbraco.Web.Cache; +using Umbraco.Web.Composing.Composers; +using Umbraco.Web.ContentApps; +using Umbraco.Web.Dictionary; +using Umbraco.Web.Editors; +using Umbraco.Web.Features; +using Umbraco.Web.HealthCheck; +using Umbraco.Web.Models.PublishedContent; +using Umbraco.Web.Mvc; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Search; +using Umbraco.Web.Security; +using Umbraco.Web.Security.Providers; +using Umbraco.Web.Services; +using Umbraco.Web.SignalR; +using Umbraco.Web.Tour; +using Umbraco.Web.Trees; +using Umbraco.Web.WebApi; +using Current = Umbraco.Web.Composing.Current; + +namespace Umbraco.Web.Runtime +{ + [Require(typeof(CoreRuntimeComposer))] + public sealed class WebRuntimeComposer : IRuntimeComposer + { + public void Compose(Composition composition) + { + composition.Register(); + + composition.RegisterUnique(); // required for hybrid accessors + + composition.ComposeWebMappingProfiles(); + + //register the install components + //NOTE: i tried to not have these registered if we weren't installing or upgrading but post install when the site restarts + //it still needs to use the install controller so we can't do that + composition.ComposeInstaller(); + + // register membership stuff + composition.Register(factory => Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider()); + composition.Register(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetInstance())); + composition.Register(); + + // register accessors for cultures + composition.RegisterUnique(); + composition.RegisterUnique(); + + // register the http context and umbraco context accessors + // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when + // we have no http context, eg when booting Umbraco or in background threads, so instead + // let's use an hybrid accessor that can fall back to a ThreadStatic context. + composition.RegisterUnique(); + + // register a per-request HttpContextBase object + // is per-request so only one wrapper is created per request + composition.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), Lifetime.Request); + + // register the published snapshot accessor - the "current" published snapshot is in the umbraco context + composition.RegisterUnique(); + + // we should stop injecting UmbracoContext and always inject IUmbracoContextAccessor, however at the moment + // there are tons of places (controllers...) which require UmbracoContext in their ctor - so let's register + // a way to inject the UmbracoContext - and register it per-request to be more efficient + //TODO: stop doing this + composition.Register(factory => factory.GetInstance().UmbracoContext, Lifetime.Request); + + // register the umbraco helper + composition.RegisterUnique(); + + // register distributed cache + composition.RegisterUnique(f => new DistributedCache()); + + // replace some services + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + composition.RegisterUnique(factory => ExamineManager.Instance); + + // configure the container for web + composition.ConfigureForWeb(); + + + composition.RegisterUnique(); + + composition + .ComposeUmbracoControllers(GetType().Assembly) + .SetDefaultRenderMvcController(); // default controller for template views + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); // fixme which searchable trees?! + + composition.Register(Lifetime.Request); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.WithCollectionBuilder(); + + composition.RegisterUnique(); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + var surfaceControllerTypes = new SurfaceControllerTypeCollection(composition.TypeLoader.GetSurfaceControllers()); + composition.RegisterUnique(surfaceControllerTypes); + + var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(composition.TypeLoader.GetUmbracoApiControllers()); + composition.RegisterUnique(umbracoApiControllerTypes); + + // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be + // discovered when CoreBootManager configures the converters. We HAVE to remove one of them + // here because there cannot be two converters for one property editor - and we want the full + // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. + // (the limited one, defined in Core, is there for tests) - same for others + composition.WithCollectionBuilder() + .Remove() + .Remove() + .Remove(); + + // add all known factories, devs can then modify this list on application + // startup either by binding to events or in their own global.asax + composition.WithCollectionBuilder() + .Append(); + + composition.WithCollectionBuilder() + .Append() + .Append() + .Append(); + + composition.RegisterUnique(); + + composition.WithCollectionBuilder() + // all built-in finders in the correct order, + // devs can then modify this list on application startup + .Append() + .Append() + .Append() + //.Append() // disabled, this is an odd finder + .Append() + .Append(); + + composition.RegisterUnique(); + + composition.RegisterUnique(); + + // register *all* checks, except those marked [HideFromTypeFinder] of course + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + // auto-register views + composition.RegisterAuto(typeof(UmbracoViewPage<>)); + + // register published router + composition.RegisterUnique(); + composition.Register(_ => Current.Config.Umbraco().WebRouting); + + // register preview SignalR hub + composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); + + // register properties fallback + composition.RegisterUnique(); + + // register known content apps + composition.WithCollectionBuilder() + .Append() + .Append() + .Append(); + } + } +} diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index 37373d0e04..c803abc966 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -15,20 +15,18 @@ namespace Umbraco.Web.Scheduling private readonly IRuntimeState _runtimeState; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; - private readonly ILogger _logger; - private readonly IProfilingLogger _proflog; + private readonly IProfilingLogger _logger; public HealthCheckNotifier(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IRuntimeState runtimeState, - ILogger logger, IProfilingLogger proflog) + IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _healthChecks = healthChecks; _notifications = notifications; _runtimeState = runtimeState; _logger = logger; - _proflog = proflog; } public override async Task PerformRunAsync(CancellationToken token) @@ -53,7 +51,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_proflog.DebugDuration("Health checks executing", "Health checks complete")) + using (_logger.DebugDuration("Health checks executing", "Health checks complete")) { var healthCheckConfig = Current.Config.HealthChecks(); diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index 572e4eb5a1..d5d5741813 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -16,12 +16,11 @@ namespace Umbraco.Web.Scheduling private readonly IProfilingLogger _proflog; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, ILogger logger, IProfilingLogger proflog) + IRuntimeState runtime, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _logger = logger; - _proflog = proflog; if (_httpClient == null) _httpClient = new HttpClient(); } diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index d5cd114977..ae27809987 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -16,12 +16,11 @@ namespace Umbraco.Web.Scheduling private readonly IRuntimeState _runtime; private readonly IAuditService _auditService; private readonly IUmbracoSettingsSection _settings; - private readonly ILogger _logger; - private readonly IProfilingLogger _proflog; + private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; public LogScrubber(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, ILogger logger, IProfilingLogger proflog) + IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; @@ -29,7 +28,6 @@ namespace Umbraco.Web.Scheduling _settings = settings; _scopeProvider = scopeProvider; _logger = logger; - _proflog = proflog; } // maximum age, in minutes @@ -86,7 +84,7 @@ namespace Umbraco.Web.Scheduling // running on a background task, and Log.CleanLogs uses the old SqlHelper, // better wrap in a scope and ensure it's all cleaned up and nothing leaks using (var scope = _scopeProvider.CreateScope()) - using (_proflog.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) + using (_logger.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) { _auditService.CleanLogs(GetLogScrubbingMaximumAge(_settings)); scope.Complete(); diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index 1d0ebb5364..115a28504c 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -20,18 +20,16 @@ namespace Umbraco.Web.Scheduling private static HttpClient _httpClient; private readonly IRuntimeState _runtime; private readonly IUmbracoSettingsSection _settings; - private readonly ILogger _logger; - private readonly IProfilingLogger _proflog; + private readonly IProfilingLogger _logger; private static readonly Hashtable ScheduledTaskTimes = new Hashtable(); public ScheduledTasks(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IUmbracoSettingsSection settings, ILogger logger, IProfilingLogger proflog) + IRuntimeState runtime, IUmbracoSettingsSection settings, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _settings = settings; _logger = logger; - _proflog = proflog; } private async Task ProcessTasksAsync(CancellationToken token) @@ -109,7 +107,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_proflog.DebugDuration("Scheduled tasks executing", "Scheduled tasks complete")) + using (_logger.DebugDuration("Scheduled tasks executing", "Scheduled tasks complete")) { try { diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index 67214fdb54..c7db6c38b3 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -15,46 +15,36 @@ using Umbraco.Web.Routing; namespace Umbraco.Web.Scheduling { - /// - /// Used to do the scheduling for tasks, publishing, etc... - /// - /// - /// All tasks are run in a background task runner which is web aware and will wind down - /// the task correctly instead of killing it completely when the app domain shuts down. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - internal sealed class SchedulerComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal sealed class SchedulerComponent : IComponent { - private IRuntimeState _runtime; - private IContentService _contentService; - private IAuditService _auditService; - private ILogger _logger; - private IProfilingLogger _proflog; - private IScopeProvider _scopeProvider; - private HealthCheckCollection _healthChecks; - private HealthCheckNotificationMethodCollection _notifications; + private readonly IRuntimeState _runtime; + private readonly IContentService _contentService; + private readonly IAuditService _auditService; + private readonly IProfilingLogger _logger; + private readonly IScopeProvider _scopeProvider; + private readonly HealthCheckCollection _healthChecks; + private readonly HealthCheckNotificationMethodCollection _notifications; - private BackgroundTaskRunner _keepAliveRunner; - private BackgroundTaskRunner _publishingRunner; - private BackgroundTaskRunner _tasksRunner; - private BackgroundTaskRunner _scrubberRunner; - private BackgroundTaskRunner _healthCheckRunner; + private readonly BackgroundTaskRunner _keepAliveRunner; + private readonly BackgroundTaskRunner _publishingRunner; + private readonly BackgroundTaskRunner _tasksRunner; + private readonly BackgroundTaskRunner _scrubberRunner; + private readonly BackgroundTaskRunner _healthCheckRunner; private bool _started; private object _locker = new object(); private IBackgroundTask[] _tasks; - public void Initialize(IRuntimeState runtime, + public SchedulerComponent(IRuntimeState runtime, IContentService contentService, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IScopeProvider scopeProvider, ILogger logger, IProfilingLogger proflog) + IScopeProvider scopeProvider, IProfilingLogger logger) { _runtime = runtime; _contentService = contentService; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; - _proflog = proflog; _healthChecks = healthChecks; _notifications = notifications; @@ -98,7 +88,7 @@ namespace Umbraco.Web.Scheduling var healthCheckConfig = Current.Config.HealthChecks(); if (healthCheckConfig.NotificationSettings.Enabled) - tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger, _proflog)); + tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger)); return tasks.ToArray(); }); @@ -108,7 +98,7 @@ namespace Umbraco.Web.Scheduling { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, 60000, 300000, _runtime, _logger, _proflog); + var task = new KeepAlive(_keepAliveRunner, 60000, 300000, _runtime, _logger); _keepAliveRunner.TryAdd(task); return task; } @@ -124,14 +114,14 @@ namespace Umbraco.Web.Scheduling private IBackgroundTask RegisterTaskRunner(IUmbracoSettingsSection settings) { - var task = new ScheduledTasks(_tasksRunner, 60000, 60000, _runtime, settings, _logger, _proflog); + var task = new ScheduledTasks(_tasksRunner, 60000, 60000, _runtime, settings, _logger); _tasksRunner.TryAdd(task); return task; } private IBackgroundTask RegisterHealthCheckNotifier(IHealthChecks healthCheckConfig, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - ILogger logger, IProfilingLogger proflog) + IProfilingLogger logger) { // If first run time not set, start with just small delay after application start int delayInMilliseconds; @@ -150,7 +140,7 @@ namespace Umbraco.Web.Scheduling } var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000; - var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, proflog); + var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger); return task; } @@ -158,7 +148,7 @@ namespace Umbraco.Web.Scheduling { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger, _proflog); + var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComposer.cs b/src/Umbraco.Web/Scheduling/SchedulerComposer.cs new file mode 100644 index 0000000000..5496531709 --- /dev/null +++ b/src/Umbraco.Web/Scheduling/SchedulerComposer.cs @@ -0,0 +1,22 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Scheduling +{ + /// + /// Used to do the scheduling for tasks, publishing, etc... + /// + /// + /// All tasks are run in a background task runner which is web aware and will wind down + /// the task correctly instead of killing it completely when the app domain shuts down. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + internal sealed class SchedulerComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + } + } +} diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index f27d87a4c3..446b3c632a 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -30,11 +30,7 @@ using Umbraco.Web.Trees; namespace Umbraco.Web.Search { - /// - /// Configures and installs Examine. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class ExamineComponent : IComponent { private IExamineManager _examineManager; private IContentValueSetBuilder _contentValueSetBuilder; @@ -54,42 +50,7 @@ namespace Umbraco.Web.Search // but greater that SafeXmlReaderWriter priority which is 60 private const int EnlistPriority = 80; - - public override void Compose(Composition composition) - { - base.Compose(composition); - - // populators are not a collection: once cannot remove ours, and can only add more - // the container can inject IEnumerable and get them all - composition.Register(Lifetime.Singleton); - composition.Register(Lifetime.Singleton); - composition.Register(Lifetime.Singleton); - composition.Register(Lifetime.Singleton); - - composition.Register(Lifetime.Singleton); - composition.RegisterUnique(); - composition.RegisterUnique(factory => - new ContentValueSetBuilder( - factory.GetInstance(), - factory.GetInstance>(), - factory.GetInstance(), - true)); - composition.RegisterUnique(factory => - new ContentValueSetBuilder( - factory.GetInstance(), - factory.GetInstance>(), - factory.GetInstance(), - false)); - composition.RegisterUnique, MediaValueSetBuilder>(); - composition.RegisterUnique, MemberValueSetBuilder>(); - - //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior - //and then we'll use MainDom to control Examine's shutdown - this MUST be done in Compose ie before ExamineManager - //is instantiated, as the value is used during instantiation - ExamineManager.DisableDefaultHostingEnvironmentRegistration(); - } - - internal void Initialize(IRuntimeState runtime, IMainDom mainDom, PropertyEditorCollection propertyEditors, + internal ExamineComponent(IMainDom mainDom, IExamineManager examineManager, IProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, IndexRebuilder indexRebuilder, ServiceContext services, diff --git a/src/Umbraco.Web/Search/ExamineComposer.cs b/src/Umbraco.Web/Search/ExamineComposer.cs new file mode 100644 index 0000000000..e7457c066d --- /dev/null +++ b/src/Umbraco.Web/Search/ExamineComposer.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Examine; +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Examine; + +namespace Umbraco.Web.Search +{ + /// + /// Configures and installs Examine. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class ExamineComposer : ICoreComposer + { + public void Compose(Composition composition) + { + // populators are not a collection: once cannot remove ours, and can only add more + // the container can inject IEnumerable and get them all + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + + composition.Register(Lifetime.Singleton); + composition.RegisterUnique(); + composition.RegisterUnique(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + true)); + composition.RegisterUnique(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + false)); + composition.RegisterUnique, MediaValueSetBuilder>(); + composition.RegisterUnique, MemberValueSetBuilder>(); + + //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior + //and then we'll use MainDom to control Examine's shutdown - this MUST be done in Compose ie before ExamineManager + //is instantiated, as the value is used during instantiation + ExamineManager.DisableDefaultHostingEnvironmentRegistration(); + + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/SignalR/PreviewHubComponent.cs b/src/Umbraco.Web/SignalR/PreviewHubComponent.cs index 984ba85522..1ca8ad0176 100644 --- a/src/Umbraco.Web/SignalR/PreviewHubComponent.cs +++ b/src/Umbraco.Web/SignalR/PreviewHubComponent.cs @@ -1,24 +1,16 @@ using System; using Microsoft.AspNet.SignalR; -using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Sync; using Umbraco.Web.Cache; namespace Umbraco.Web.SignalR { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class PreviewHubComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class PreviewHubComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); - } - // using a lazy arg here means that we won't create the hub until necessary // and therefore we won't have too bad an impact on boot time - public void Initialize(Lazy> hubContext) + public PreviewHubComponent(Lazy> hubContext) { // ContentService.Saved is too soon - the content cache is not ready yet // try using the content cache refresher event, because when it triggers diff --git a/src/Umbraco.Web/SignalR/PreviewHubComposer.cs b/src/Umbraco.Web/SignalR/PreviewHubComposer.cs new file mode 100644 index 0000000000..96988c4ee4 --- /dev/null +++ b/src/Umbraco.Web/SignalR/PreviewHubComposer.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNet.SignalR; +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.SignalR +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class PreviewHubComposer : ICoreComposer + { + public void Compose(Composition composition) + { + composition.Components().Append(); + composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5c1e6ba730..eddb1ee6d3 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -109,11 +109,15 @@ + + + + @@ -123,6 +127,7 @@ + @@ -176,6 +181,12 @@ + + + + + + @@ -202,6 +213,7 @@ +