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
This commit is contained in:
Renaud Paquay
2010-06-12 12:38:49 -07:00
parent 767f19a4e3
commit 695f001f0f
13 changed files with 128 additions and 39 deletions

View File

@@ -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<IVolatileToken> monitor) {
throw new NotImplementedException();
}
#endregion
}

View File

@@ -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<ShellContext> _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;
}
}
}

View File

@@ -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<ExtensionEntry> 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<Feature> LoadFeatures(IEnumerable<FeatureDescriptor> 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<Type>();
@@ -141,11 +154,12 @@ namespace Orchard.Environment.Extensions {
Directory.Delete(targetFolder, true);
}
private IEnumerable<ExtensionEntry> BuildActiveExtensions() {
public void Monitor(Action<IVolatileToken> 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);
}
}
}
}

View File

@@ -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<Feature> LoadFeatures(IEnumerable<FeatureDescriptor> featureDescriptors);
void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle);
void UninstallExtension(string extensionType, string extensionName);
void Monitor(Action<IVolatileToken> monitor);
}
}

View File

@@ -1,7 +0,0 @@
using Orchard.Events;
namespace Orchard.Environment.Extensions {
public interface IExtensionManagerEvents : IEventHandler {
void ModuleChanged(string moduleName);
}
}

View File

@@ -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<IVolatileToken> 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 {

View File

@@ -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<IVolatileToken> 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 {

View File

@@ -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,24 +10,34 @@ 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 ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name,
descriptor.Name + ".csproj");
if (!_virtualPathProvider.FileExists(projectPath)) {
return null;
public void Monitor(ExtensionDescriptor descriptor, Action<IVolatileToken> monitor) {
string projectPath = GetProjectPath(descriptor);
if (projectPath != null)
monitor(_virtualPathMonitor.WhenPathChanges(projectPath));
}
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)
return null;
return new ExtensionProbeEntry {
Descriptor = descriptor,
LastModificationTimeUtc = File.GetLastWriteTimeUtc(_virtualPathProvider.MapPath(projectPath)),
@@ -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);

View File

@@ -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<IVolatileToken> monitor);
}
public class ExtensionProbeEntry {

View File

@@ -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<IVolatileToken> 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
};
}

View File

@@ -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<IVolatileToken> 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;

View File

@@ -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<IVolatileToken> 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;

View File

@@ -360,7 +360,6 @@
<Compile Include="FileSystems\VirtualPath\ICustomVirtualPathProvider.cs" />
<Compile Include="FileSystems\Dependencies\WebFormsExtensionsVirtualPathProvider.cs" />
<Compile Include="FileSystems\Dependencies\WebFormsExtensionsVirtualFile.cs" />
<Compile Include="Environment\Extensions\IExtensionManagerEvents.cs" />
<Compile Include="Environment\IHostEnvironment.cs" />
<Compile Include="FileSystems\VirtualPath\IVirtualPathMonitor.cs" />
<Compile Include="FileSystems\Dependencies\IDependenciesFolder.cs" />