From f4dc89de16dfb06315f216a10e6c4a874f67164b Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Sat, 24 Sep 2011 20:47:53 -0700 Subject: [PATCH] Fixing shell context activation as per module enabling/disabling --HG-- branch : 1.x --- .../Environment/DefaultOrchardHostTests.cs | 2 +- .../Descriptor/ShellDescriptorManager.cs | 11 +- .../Orchard.Setup/Services/SetupService.cs | 11 +- src/Orchard/Environment/DefaultOrchardHost.cs | 129 ++++++++++++------ .../Descriptor/IShellDescriptorManager.cs | 2 +- src/Orchard/Environment/IHostLocalRestart.cs | 2 +- src/Orchard/Environment/RunningShellTable.cs | 2 +- .../State/ShellStateCoordinator.cs | 2 +- 8 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs b/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs index 4b2a87495..26754dc0b 100644 --- a/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs +++ b/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs @@ -139,7 +139,7 @@ namespace Orchard.Tests.Environment { public void NormalDependenciesShouldBeUniquePerRequestContainer() { var host = _lifetime.Resolve(); var container1 = host.CreateShellContainer_Obsolete(); - ((IShellDescriptorManagerEventHandler)host).Changed(null); + ((IShellDescriptorManagerEventHandler)host).Changed(null, String.Empty); var container2 = host.CreateShellContainer_Obsolete(); var requestContainer1a = container1.BeginLifetimeScope(); var requestContainer1b = container1.BeginLifetimeScope(); diff --git a/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs b/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs index 13e2a8604..290c87043 100644 --- a/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs +++ b/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs @@ -2,20 +2,25 @@ using System.Collections.Generic; using Orchard.Core.Settings.Descriptor.Records; using Orchard.Data; +using Orchard.Environment.Configuration; using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions.Models; using Orchard.Localization; namespace Orchard.Core.Settings.Descriptor { public class ShellDescriptorManager : IShellDescriptorManager { private readonly IRepository _shellDescriptorRepository; private readonly IShellDescriptorManagerEventHandler _events; + private readonly ShellSettings _shellSettings; public ShellDescriptorManager( IRepository shellDescriptorRepository, - IShellDescriptorManagerEventHandler events) { + IShellDescriptorManagerEventHandler events, + ShellSettings shellSettings) { _shellDescriptorRepository = shellDescriptorRepository; _events = events; + _shellSettings = shellSettings; T = NullLocalizer.Instance; } @@ -82,9 +87,7 @@ namespace Orchard.Core.Settings.Descriptor { }); } - _events.Changed(GetShellDescriptorFromRecord(shellDescriptorRecord)); + _events.Changed(GetShellDescriptorFromRecord(shellDescriptorRecord), _shellSettings.Name); } - - } } diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 49aa208c2..b2120e144 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -150,6 +150,9 @@ namespace Orchard.Setup.Services { } } + _shellSettingsManager.SaveSettings(shellSettings); + + // in effect "pump messages" see PostMessage circa 1980 while ( _processingEngine.AreTasksPending() ) _processingEngine.ExecuteNextTask(); @@ -170,11 +173,11 @@ namespace Orchard.Setup.Services { } } - while (_processingEngine.AreTasksPending()) - _processingEngine.ExecuteNextTask(); - - _shellSettingsManager.SaveSettings(shellSettings); + //// execute pending recipe steps + //while (_processingEngine.AreTasksPending()) + // _processingEngine.ExecuteNextTask(); + return executionId; } diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index b3b964bb4..3e783adbc 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Collections.Generic; +using System.Transactions; using Orchard.Caching; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -23,7 +24,8 @@ namespace Orchard.Environment { private readonly ICacheManager _cacheManager; private readonly object _syncLock = new object(); - private IEnumerable _current; + private IEnumerable _shellContexts; + private IEnumerable _tenantsToRestart; public DefaultOrchardHost( IShellSettingsManager shellSettingsManager, @@ -42,6 +44,7 @@ namespace Orchard.Environment { _extensionMonitoringCoordinator = extensionMonitoringCoordinator; _cacheManager = cacheManager; _hostLocalRestart = hostLocalRestart; + _tenantsToRestart = Enumerable.Empty(); T = NullLocalizer.Instance; Logger = NullLogger.Instance; @@ -88,44 +91,65 @@ namespace Orchard.Environment { return shellContext.LifetimeScope.CreateWorkContextScope(); } + /// + /// Ensures shells are activated, or re-activated if extensions have changed + /// IEnumerable BuildCurrent() { - if (_current == null) { + if (_shellContexts == null) { lock (_syncLock) { - if (_current == null) { + if (_shellContexts == null) { SetupExtensions(); MonitorExtensions(); - _current = CreateAndActivate().ToArray(); + CreateAndActivateShells(); } } } - return _current; + + return _shellContexts; } - IEnumerable CreateAndActivate() { + void StartUpdatedShells() { + lock (_syncLock) { + if (_tenantsToRestart.Any()) { + foreach (var settings in _tenantsToRestart.Distinct().ToList()) { + ActivateShell(settings); + } + + _tenantsToRestart = Enumerable.Empty(); + } + } + } + + void CreateAndActivateShells() { Logger.Information("Start creation of shells"); - IEnumerable result; + // is there any tenant right now ? var allSettings = _shellSettingsManager.LoadSettings(); + + // load all tenants, and activate their shell if (allSettings.Any()) { - result = allSettings.Select( - settings => { - var context = CreateShellContext(settings); - ActivateShell(context); - return context; - }); + foreach (var settings in allSettings) { + var context = CreateShellContext(settings); + ActivateShell(context); + } } + // no settings, run the Setup else { var setupContext = CreateSetupContext(); ActivateShell(setupContext); - result = new[] {setupContext}; } Logger.Information("Done creating shells"); - return result; } + /// + /// Start a Shell and register its settings in RunningShellTable + /// private void ActivateShell(ShellContext context) { + Logger.Debug("Activating context for tenant {0}", context.Settings.Name); context.Shell.Activate(); + + _shellContexts = (_shellContexts ?? Enumerable.Empty()).Union(new [] {context}); _runningShellTable.Add(context.Settings); } @@ -161,27 +185,34 @@ namespace Orchard.Environment { }); } + /// + /// Terminates all active shell contexts, and dispose their scope, forcing + /// them to be reloaded if necessary. + /// private void DisposeShellContext() { Logger.Information("Disposing active shell contexts"); - if (_current != null) { - foreach (var shellContext in _current) { + if (_shellContexts != null) { + foreach (var shellContext in _shellContexts) { shellContext.Shell.Terminate(); shellContext.LifetimeScope.Dispose(); } - _current = null; + _shellContexts = null; } } protected virtual void BeginRequest() { + // Ensure all shell contexts are loaded, or need to be reloaded if + // extensions have changed MonitorExtensions(); BuildCurrent(); + StartUpdatedShells(); } protected virtual void EndRequest() { // Synchronously process all pending tasks. It's safe to do this at this point // of the pipeline, as the request transaction has been closed, so creating a new - // environment and transaction for these tasks will behave as expected. + // environment and transaction for these tasks will behave as expected.) while (_processingEngine.AreTasksPending()) { _processingEngine.ExecuteNextTask(); } @@ -190,33 +221,37 @@ namespace Orchard.Environment { /// /// Register and activate a new Shell when a tenant is created /// - void IShellSettingsManagerEventHandler.Saved(ShellSettings settings) - { - // is it a new tenant ? - var shellContext = _current.FirstOrDefault(c => c.Settings.Name == settings.Name); - - ShellContext context; - - // if null, this is a new tenant, register and activate a new context - if(shellContext == null) - { - context = CreateShellContext(settings); - ActivateShell(context); - _current = _current.Union(new[] { context }); - _runningShellTable.Add(settings); + void IShellSettingsManagerEventHandler.Saved(ShellSettings settings) { + lock (_syncLock) { + _tenantsToRestart = _tenantsToRestart.Where(x => x.Name != settings.Name).Union(new[] { settings }); } - else - { - context = _shellContextFactory.CreateShellContext(settings); + } + void ActivateShell(ShellSettings settings) { + // look for the associated shell context + var shellContext = _shellContexts.FirstOrDefault(c => c.Settings.Name == settings.Name); + + // is this is a new tenant ? + if (shellContext == null) { + // create the Shell + var context = CreateShellContext(settings); + + // activate the Shell + ActivateShell(context); + } + // reload the shell as its settings have changed + else { // dispose previous context shellContext.Shell.Terminate(); shellContext.LifetimeScope.Dispose(); + var context = _shellContextFactory.CreateShellContext(settings); + // activate and register modified context - _current = _current.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { shellContext }); - + _shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { context }); + context.Shell.Activate(); + _runningShellTable.Update(settings); } } @@ -224,7 +259,23 @@ namespace Orchard.Environment { /// /// A feature is enabled/disabled /// - void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) { + void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { + lock (_syncLock) { + var context = _shellContexts.Where(x => x.Settings.Name == tenant).FirstOrDefault(); + + // some shells might need to be started, e.g. created by command line + if(context == null) { + StartUpdatedShells(); + context = _shellContexts.Where(x => x.Settings.Name == tenant).First(); + } + + // don't update the settings themselves here + if(_tenantsToRestart.Any(x => x.Name == tenant)) { + return; + } + + _tenantsToRestart = _tenantsToRestart.Union(new[] { context.Settings }); + } } } } diff --git a/src/Orchard/Environment/Descriptor/IShellDescriptorManager.cs b/src/Orchard/Environment/Descriptor/IShellDescriptorManager.cs index aa6a03e1e..68437f97f 100644 --- a/src/Orchard/Environment/Descriptor/IShellDescriptorManager.cs +++ b/src/Orchard/Environment/Descriptor/IShellDescriptorManager.cs @@ -28,6 +28,6 @@ namespace Orchard.Environment.Descriptor { } public interface IShellDescriptorManagerEventHandler : IEventHandler { - void Changed(ShellDescriptor descriptor); + void Changed(ShellDescriptor descriptor, string tenant); } } diff --git a/src/Orchard/Environment/IHostLocalRestart.cs b/src/Orchard/Environment/IHostLocalRestart.cs index 054f10c8a..8d33d551d 100644 --- a/src/Orchard/Environment/IHostLocalRestart.cs +++ b/src/Orchard/Environment/IHostLocalRestart.cs @@ -36,7 +36,7 @@ namespace Orchard.Environment { TouchFile(); } - void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) { + void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { TouchFile(); } diff --git a/src/Orchard/Environment/RunningShellTable.cs b/src/Orchard/Environment/RunningShellTable.cs index c213a0c1b..8fc3309bc 100644 --- a/src/Orchard/Environment/RunningShellTable.cs +++ b/src/Orchard/Environment/RunningShellTable.cs @@ -89,7 +89,7 @@ namespace Orchard.Environment { .Where(group => host.EndsWith(group.Key, StringComparison.OrdinalIgnoreCase)) .SelectMany(group => group .OrderByDescending(settings => (settings.RequestUrlPrefix ?? "").Length)) - .Where(settings => settings.State.CurrentState == TenantState.State.Running && appRelativePath.StartsWith(settings.RequestUrlPrefix ?? "", StringComparison.OrdinalIgnoreCase)) + .Where(settings => settings.State.CurrentState != TenantState.State.Disabled && appRelativePath.StartsWith(settings.RequestUrlPrefix ?? "", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); return mostQualifiedMatch ?? _fallback; diff --git a/src/Orchard/Environment/State/ShellStateCoordinator.cs b/src/Orchard/Environment/State/ShellStateCoordinator.cs index de48f7727..7a6a27e97 100644 --- a/src/Orchard/Environment/State/ShellStateCoordinator.cs +++ b/src/Orchard/Environment/State/ShellStateCoordinator.cs @@ -33,7 +33,7 @@ namespace Orchard.Environment.State { public ILogger Logger { get; set; } - void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) { + void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { // deduce and apply state changes involved var shellState = _stateManager.GetShellState(); foreach (var feature in descriptor.Features) {