From 695f001f0fd230b3057455aea68cc81bf8efddb1 Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Sat, 12 Jun 2010 12:38:49 -0700 Subject: [PATCH] Add ability for OrchardHost to reset its state when Extensions change OrchardHost calls into all extension loaders to all extensions descriptor, allowing each loader to add the set of resource to monitor for change. OrchardHost resets its list of shells at the beginning of a request if any monitored resources has changed. --HG-- branch : dev --- .../Extensions/ExtensionManagerTests.cs | 5 +++ src/Orchard/Environment/DefaultOrchardHost.cs | 23 ++++++++----- .../Extensions/ExtensionManager.cs | 28 ++++++++++++---- .../Extensions/IExtensionManager.cs | 5 ++- .../Extensions/IExtensionManagerEvents.cs | 7 ---- .../Extensions/Loaders/AreaExtensionLoader.cs | 6 ++++ .../Extensions/Loaders/CoreExtensionLoader.cs | 6 ++++ .../Loaders/DynamicExtensionLoader.cs | 33 ++++++++++++++++--- .../Extensions/Loaders/IExtensionLoader.cs | 2 ++ .../Loaders/PrecompiledExtensionLoader.cs | 32 ++++++++++++++---- .../Loaders/ProbingExtensionLoader.cs | 10 +++++- .../Loaders/ReferencedExtensionLoader.cs | 9 ++++- src/Orchard/Orchard.Framework.csproj | 1 - 13 files changed, 128 insertions(+), 39 deletions(-) delete mode 100644 src/Orchard/Environment/Extensions/IExtensionManagerEvents.cs diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index 1d45920e3..8bbbc3806 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Autofac; using NUnit.Framework; +using Orchard.Caching; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Folders; using Orchard.Environment.Extensions.Loaders; @@ -57,6 +58,10 @@ namespace Orchard.Tests.Environment.Extensions { return new ExtensionEntry { Descriptor = entry.Descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } }; } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + throw new NotImplementedException(); + } + #endregion } diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index a94d69477..5880281c0 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -1,10 +1,8 @@ -using System; using System.Linq; using System.Threading; using System.Web.Mvc; -using Autofac; using System.Collections.Generic; -using Autofac.Features.OwnedInstances; +using Orchard.Caching; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.ShellBuilders; @@ -17,13 +15,15 @@ using Orchard.Mvc.ViewEngines; using Orchard.Utility.Extensions; namespace Orchard.Environment { - public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler, IExtensionManagerEvents { + public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler { private readonly ControllerBuilder _controllerBuilder; private readonly IShellSettingsManager _shellSettingsManager; private readonly IShellContextFactory _shellContextFactory; private readonly IRunningShellTable _runningShellTable; private readonly IProcessingEngine _processingEngine; + private readonly IExtensionManager _extensionManager; + private readonly ICacheManager _cacheManager; private IEnumerable _current; @@ -32,12 +32,15 @@ namespace Orchard.Environment { IShellContextFactory shellContextFactory, IRunningShellTable runningShellTable, IProcessingEngine processingEngine, + IExtensionManager extensionManager, + ICacheManager cacheManager, ControllerBuilder controllerBuilder) { - //_containerProvider = containerProvider; _shellSettingsManager = shellSettingsManager; _shellContextFactory = shellContextFactory; _runningShellTable = runningShellTable; _processingEngine = processingEngine; + _extensionManager = extensionManager; + _cacheManager = cacheManager; _controllerBuilder = controllerBuilder; Logger = NullLogger.Instance; } @@ -117,6 +120,12 @@ namespace Orchard.Environment { } protected virtual void BeginRequest() { + _cacheManager.Get("OrchardHost_Extensions", + ctx => { + _extensionManager.Monitor(ctx.Monitor); + _current = null; + return ""; + }); BuildCurrent(); } @@ -147,9 +156,5 @@ namespace Orchard.Environment { void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) { _current = null; } - - void IExtensionManagerEvents.ModuleChanged(string moduleName) { - _current = null; - } } } diff --git a/src/Orchard/Environment/Extensions/ExtensionManager.cs b/src/Orchard/Environment/Extensions/ExtensionManager.cs index 1bd388332..f7e15737f 100644 --- a/src/Orchard/Environment/Extensions/ExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/ExtensionManager.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Web; using ICSharpCode.SharpZipLib.Zip; +using Orchard.Caching; using Orchard.Environment.Extensions.Folders; using Orchard.Environment.Extensions.Helpers; using Orchard.Environment.Extensions.Loaders; @@ -32,6 +33,15 @@ namespace Orchard.Environment.Extensions { return _folders.SelectMany(folder => folder.AvailableExtensions()); } + private IEnumerable LoadedModules() { + foreach (var descriptor in AvailableExtensions()) { + // Extensions that are Themes don't have buildable components. + if (String.Equals(descriptor.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)) { + yield return BuildEntry(descriptor); + } + } + } + public IEnumerable LoadFeatures(IEnumerable featureDescriptors) { return featureDescriptors .Select(featureDescriptor => LoadFeature(featureDescriptor)) @@ -41,9 +51,12 @@ namespace Orchard.Environment.Extensions { private Feature LoadFeature(FeatureDescriptor featureDescriptor) { var featureName = featureDescriptor.Name; string extensionName = GetExtensionForFeature(featureName); - if (extensionName == null) throw new ArgumentException(T("Feature {0} was not found in any of the installed extensions", featureName).ToString()); - var extension = BuildActiveExtensions().Where(x => x.Descriptor.Name == extensionName).FirstOrDefault(); - if (extension == null) throw new InvalidOperationException(T("Extension ") + extensionName + T(" is not active")); + if (extensionName == null) + throw new ArgumentException(T("Feature {0} was not found in any of the installed extensions", featureName).ToString()); + + var extension = LoadedModules().Where(x => x.Descriptor.Name == extensionName).FirstOrDefault(); + if (extension == null) + throw new InvalidOperationException(T("Extension {0} is not active", extensionName).ToString()); var extensionTypes = extension.ExportedTypes.Where(t => t.IsClass && !t.IsAbstract); var featureTypes = new List(); @@ -141,11 +154,12 @@ namespace Orchard.Environment.Extensions { Directory.Delete(targetFolder, true); } - private IEnumerable BuildActiveExtensions() { + public void Monitor(Action monitor) { foreach (var descriptor in AvailableExtensions()) { - // Extensions that are Themes don't have buildable components. - if (String.Equals(descriptor.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)) { - yield return BuildEntry(descriptor); + if (string.Equals(descriptor.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)) { + foreach (var loader in _loaders) { + loader.Monitor(descriptor, monitor); + } } } } diff --git a/src/Orchard/Environment/Extensions/IExtensionManager.cs b/src/Orchard/Environment/Extensions/IExtensionManager.cs index be0950a66..27cda4808 100644 --- a/src/Orchard/Environment/Extensions/IExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/IExtensionManager.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Web; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Extensions { @@ -8,5 +10,6 @@ namespace Orchard.Environment.Extensions { IEnumerable LoadFeatures(IEnumerable featureDescriptors); void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle); void UninstallExtension(string extensionType, string extensionName); + void Monitor(Action monitor); } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/IExtensionManagerEvents.cs b/src/Orchard/Environment/Extensions/IExtensionManagerEvents.cs deleted file mode 100644 index c7be80e9f..000000000 --- a/src/Orchard/Environment/Extensions/IExtensionManagerEvents.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Orchard.Events; - -namespace Orchard.Environment.Extensions { - public interface IExtensionManagerEvents : IEventHandler { - void ModuleChanged(string moduleName); - } -} \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs index e1dbd06b4..d1d227a0c 100644 --- a/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Reflection; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; @@ -17,6 +18,11 @@ namespace Orchard.Environment.Extensions.Loaders { public int Order { get { return 50; } } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + // We don't need to monitor anything since we are loaded + // from the application assembly itself. + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { if (descriptor.Location == "~/Areas") { return new ExtensionProbeEntry { diff --git a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs index ab35c9e9a..e69c1f0e9 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Reflection; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; @@ -20,6 +21,11 @@ namespace Orchard.Environment.Extensions.Loaders { public int Order { get { return 10; } } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + // We don't need to monitor anything since we are loaded + // from the core assembly. + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { if (descriptor.Location == "~/Core") { return new ExtensionProbeEntry { diff --git a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs index 0b5a5580c..89b0aab4e 100644 --- a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; @@ -9,23 +10,33 @@ namespace Orchard.Environment.Extensions.Loaders { private readonly IHostEnvironment _hostEnvironment; private readonly IBuildManager _buildManager; private readonly IVirtualPathProvider _virtualPathProvider; + private readonly IVirtualPathMonitor _virtualPathMonitor; private readonly IDependenciesFolder _dependenciesFolder; - public DynamicExtensionLoader(IHostEnvironment hostEnvironment, IBuildManager buildManager, IVirtualPathProvider virtualPathProvider, IDependenciesFolder dependenciesFolder) { + public DynamicExtensionLoader(IHostEnvironment hostEnvironment, + IBuildManager buildManager, + IVirtualPathProvider virtualPathProvider, + IVirtualPathMonitor virtualPathMonitor, + IDependenciesFolder dependenciesFolder) { _hostEnvironment = hostEnvironment; _buildManager = buildManager; _virtualPathProvider = virtualPathProvider; + _virtualPathMonitor = virtualPathMonitor; _dependenciesFolder = dependenciesFolder; } public int Order { get { return 100; } } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + string projectPath = GetProjectPath(descriptor); + if (projectPath != null) + monitor(_virtualPathMonitor.WhenPathChanges(projectPath)); + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { - string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, - descriptor.Name + ".csproj"); - if (!_virtualPathProvider.FileExists(projectPath)) { + string projectPath = GetProjectPath(descriptor); + if (projectPath == null) return null; - } return new ExtensionProbeEntry { Descriptor = descriptor, @@ -35,6 +46,18 @@ namespace Orchard.Environment.Extensions.Loaders { }; } + private string GetProjectPath(ExtensionDescriptor descriptor) { + string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, + descriptor.Name + ".csproj"); + + if (!_virtualPathProvider.FileExists(projectPath)) { + return null; + } + + return projectPath; + } + + public ExtensionEntry Load(ExtensionProbeEntry entry) { if (entry.Loader == this) { var assembly = _buildManager.GetCompiledAssembly(entry.VirtualPath); diff --git a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs index 15be1f554..ae06342a4 100644 --- a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs @@ -1,4 +1,5 @@ using System; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Extensions.Loaders { @@ -6,6 +7,7 @@ namespace Orchard.Environment.Extensions.Loaders { int Order { get; } ExtensionProbeEntry Probe(ExtensionDescriptor descriptor); ExtensionEntry Load(ExtensionProbeEntry descriptor); + void Monitor(ExtensionDescriptor descriptor, Action monitor); } public class ExtensionProbeEntry { diff --git a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs index 7d51dabc2..2f7ca0655 100644 --- a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs @@ -1,4 +1,6 @@ -using System.IO; +using System; +using System.IO; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; @@ -11,25 +13,41 @@ namespace Orchard.Environment.Extensions.Loaders { public class PrecompiledExtensionLoader : IExtensionLoader { private readonly IDependenciesFolder _dependenciesFolder; private readonly IVirtualPathProvider _virtualPathProvider; + private readonly IVirtualPathMonitor _virtualPathMonitor; - public PrecompiledExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider) { + public PrecompiledExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider, IVirtualPathMonitor virtualPathMonitor) { _dependenciesFolder = dependenciesFolder; _virtualPathProvider = virtualPathProvider; + _virtualPathMonitor = virtualPathMonitor; } public int Order { get { return 30; } } - public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { - var extensionPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, "bin", + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + string assemblyPath = GetAssemblyPath(descriptor); + if (assemblyPath != null) + monitor(_virtualPathMonitor.WhenPathChanges(assemblyPath)); + } + + public string GetAssemblyPath(ExtensionDescriptor descriptor) { + var assemblyPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, "bin", descriptor.Name + ".dll"); - if (!_virtualPathProvider.FileExists(extensionPath)) + if (!_virtualPathProvider.FileExists(assemblyPath)) + return null; + + return assemblyPath; + } + + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { + var assemblyPath = GetAssemblyPath(descriptor); + if (assemblyPath == null) return null; return new ExtensionProbeEntry { Descriptor = descriptor, - LastModificationTimeUtc = File.GetLastWriteTimeUtc(_virtualPathProvider.MapPath(extensionPath)), + LastModificationTimeUtc = File.GetLastWriteTimeUtc(_virtualPathProvider.MapPath(assemblyPath)), Loader = this, - VirtualPath = extensionPath + VirtualPath = assemblyPath }; } diff --git a/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs index a1dde340a..829055f44 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs @@ -1,4 +1,6 @@ -using System.IO; +using System; +using System.IO; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; @@ -17,6 +19,12 @@ namespace Orchard.Environment.Extensions.Loaders { public int Order { get { return 40; } } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + // We don't monitor assemblies loaded from this probing directory, + // because they are just a copy of the assemblies from the module + // bin directory. + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { if (!_dependenciesFolder.HasPrecompiledAssembly(descriptor.Name)) return null; diff --git a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs index 810c935f0..86d4b8e34 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs @@ -1,8 +1,10 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using System.Reflection; using System.Web.Compilation; using System.Web.Hosting; +using Orchard.Caching; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; @@ -19,6 +21,11 @@ namespace Orchard.Environment.Extensions.Loaders { public int Order { get { return 20; } } + public void Monitor(ExtensionDescriptor descriptor, Action monitor) { + // We don't monitor assemblies loaded from the "~/bin" directory, + // because they are monitored by the ASP.NET runtime. + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { if (HostingEnvironment.IsHosted == false) return null; diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index bead55b05..6f77582a6 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -360,7 +360,6 @@ -