Couple of fixed with dynamic extension loader

Perf: Use the cache manager to keep the list of dependency descriptors in
      memory, with invalidation when "dependencies.xml" changes on disk

When loading an assembly from the list of references assemblies, notify
the dependencies folder component, so it removes the entry.

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-06-09 18:55:35 -07:00
parent 9c3f6bfc74
commit 4becc488c8
5 changed files with 80 additions and 39 deletions

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -23,5 +24,9 @@ namespace Orchard.Tests.Stubs {
public IVolatileToken WhenPathChanges(string path) {
return new WebSiteFolder.Token(path);
}
public void WhenPathChanges(string path, Action action) {
throw new NotImplementedException();
}
}
}

View File

@@ -3,14 +3,20 @@ using System.Reflection;
using System.Web.Compilation;
using System.Web.Hosting;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
namespace Orchard.Environment.Extensions.Loaders {
/// <summary>
/// Load an extension by looking through the BuildManager referenced assemblies
/// </summary>
public class ReferencedExtensionLoader : IExtensionLoader {
private readonly IDependenciesFolder _dependenciesFolder;
public int Order { get { return 20; } }
public ReferencedExtensionLoader(IDependenciesFolder dependenciesFolder) {
_dependenciesFolder = dependenciesFolder;
}
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
if (HostingEnvironment.IsHosted == false)
return null;
@@ -22,6 +28,8 @@ namespace Orchard.Environment.Extensions.Loaders {
if (assembly == null)
return null;
_dependenciesFolder.StoreReferencedAssembly(descriptor.Name);
return new ExtensionEntry {
Descriptor = descriptor,
Assembly = assembly,

View File

@@ -3,13 +3,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Caching;
using System.Web.Hosting;
using System.Xml.Linq;
using Orchard.Caching;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Environment.Topology;
using Orchard.FileSystems.WebSite;
namespace Orchard.FileSystems.Dependencies {
public class DependencyDescriptor {
@@ -20,21 +18,27 @@ namespace Orchard.FileSystems.Dependencies {
}
public interface IDependenciesFolder : IVolatileProvider {
void StoreBuildProviderAssembly(string moduleName, string virtualPath, Assembly assembly);
void StoreReferencedAssembly(string moduleName);
void StorePrecompiledAssembly(string moduleName, string virtualPath);
void StoreBuildProviderAssembly(string moduleName, string virtualPath, Assembly assembly);
DependencyDescriptor GetDescriptor(string moduleName);
Assembly LoadAssembly(string assemblyName);
}
public class DefaultDependenciesFolder : IDependenciesFolder {
private readonly string _prefix = Guid.NewGuid().ToString("n");
private const string _basePath = "~/App_Data/Dependencies";
private readonly string _basePath = "~/App_Data/Dependencies";
private readonly ICacheManager _cacheManager;
private readonly IWebSiteFolder _webSiteFolder;
private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IExtensionManagerEvents _events;
private readonly InvalidationToken _token;
public DefaultDependenciesFolder(IVirtualPathProvider virtualPathProvider, IExtensionManagerEvents events) {
public DefaultDependenciesFolder(ICacheManager cacheManager, IWebSiteFolder webSiteFolder, IVirtualPathProvider virtualPathProvider, IExtensionManagerEvents events) {
_cacheManager = cacheManager;
_webSiteFolder = webSiteFolder;
_virtualPathProvider = virtualPathProvider;
_events = events;
_token = new InvalidationToken();
}
private string BasePath {
@@ -49,9 +53,33 @@ namespace Orchard.FileSystems.Dependencies {
}
}
public void StoreBuildProviderAssembly(string moduleName, string virtualPath, Assembly assembly) {
_virtualPathProvider.CreateDirectory(BasePath);
private IList<DependencyDescriptor> Descriptors {
get {
return _cacheManager.Get(PersistencePath,
ctx => {
ctx.Monitor(_webSiteFolder.WhenPathChanges(ctx.Key));
ctx.Monitor(_token);
_virtualPathProvider.CreateDirectory(BasePath);
return ReadDependencies(ctx.Key);
});
}
}
public class InvalidationToken : IVolatileToken {
public bool IsCurrent { get; set; }
}
public void StoreReferencedAssembly(string moduleName) {
if (Descriptors.Any(d => d.ModuleName == moduleName)) {
// Remove the moduleName from the list of assemblies in the dependency folder
var newDescriptors = Descriptors.Where(d => d.ModuleName != moduleName);
WriteDependencies(PersistencePath, newDescriptors);
}
}
public void StoreBuildProviderAssembly(string moduleName, string virtualPath, Assembly assembly) {
var descriptor = new DependencyDescriptor {
ModuleName = moduleName,
IsFromBuildProvider = true,
@@ -61,21 +89,7 @@ namespace Orchard.FileSystems.Dependencies {
StoreDepencyInformation(descriptor);
#if true
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath,
new[] { virtualPath },
DateTime.UtcNow);
HostingEnvironment.Cache.Add(
_prefix + virtualPath,
moduleName,
cacheDependency,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
(key, value, reason) => _events.ModuleChanged((string) value));
#endif
_webSiteFolder.WhenPathChanges(virtualPath, () => _events.ModuleChanged(moduleName));
}
public void StorePrecompiledAssembly(string moduleName, string virtualPath) {
@@ -98,11 +112,11 @@ namespace Orchard.FileSystems.Dependencies {
}
public DependencyDescriptor GetDescriptor(string moduleName) {
return ReadDependencies().SingleOrDefault(d => d.ModuleName == moduleName);
return Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
}
private bool IsNewerAssembly(string moduleName, string assemblyFileName) {
var dependency = ReadDependencies().SingleOrDefault(d => d.ModuleName == moduleName);
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
if (dependency == null) {
return true;
}
@@ -116,7 +130,7 @@ namespace Orchard.FileSystems.Dependencies {
}
private void StoreDepencyInformation(DependencyDescriptor descriptor) {
var dependencies = ReadDependencies().ToList();
var dependencies = Descriptors.ToList();
int index = dependencies.FindIndex(d => d.ModuleName == descriptor.ModuleName);
if (index < 0) {
dependencies.Add(descriptor);
@@ -125,13 +139,13 @@ namespace Orchard.FileSystems.Dependencies {
dependencies[index] = descriptor;
}
WriteDependencies(dependencies);
WriteDependencies(PersistencePath, dependencies);
}
public Assembly LoadAssembly(string assemblyName) {
_virtualPathProvider.CreateDirectory(BasePath);
var dependency = ReadDependencies().SingleOrDefault(d => d.ModuleName == assemblyName);
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == assemblyName);
if (dependency == null)
return null;
@@ -141,11 +155,13 @@ namespace Orchard.FileSystems.Dependencies {
return Assembly.Load(Path.GetFileNameWithoutExtension(dependency.FileName));
}
private IEnumerable<DependencyDescriptor> ReadDependencies() {
if (!_virtualPathProvider.FileExists(PersistencePath))
private IEnumerable<DependencyDescriptor> ReadDependencies(string persistancePath) {
Func<string, XName> ns = (name => XName.Get(name));
if (!_virtualPathProvider.FileExists(persistancePath))
return Enumerable.Empty<DependencyDescriptor>();
using (var stream = _virtualPathProvider.OpenFile(PersistencePath)) {
using (var stream = _virtualPathProvider.OpenFile(persistancePath)) {
XDocument document = XDocument.Load(stream);
return document
.Elements(ns("Dependencies"))
@@ -160,7 +176,9 @@ namespace Orchard.FileSystems.Dependencies {
}
}
private void WriteDependencies(IEnumerable<DependencyDescriptor> dependencies) {
private void WriteDependencies(string persistancePath, IEnumerable<DependencyDescriptor> dependencies) {
Func<string, XName> ns = (name => XName.Get(name));
var document = new XDocument();
document.Add(new XElement(ns("Dependencies")));
var elements = dependencies.Select(d => new XElement("Dependency",
@@ -170,13 +188,12 @@ namespace Orchard.FileSystems.Dependencies {
new XElement(ns("FileName"), d.FileName)));
document.Root.Add(elements);
using (var stream = _virtualPathProvider.CreateText(PersistencePath)) {
using (var stream = _virtualPathProvider.CreateText(persistancePath)) {
document.Save(stream, SaveOptions.None);
}
}
private static XName ns(string name) {
return XName.Get(name/*, "http://schemas.microsoft.com/developer/msbuild/2003"*/);
// Ensure cache is invalidated right away, not waiting for file change notification to happen
_token.IsCurrent = false;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Orchard.Caching;
namespace Orchard.FileSystems.WebSite {
@@ -7,5 +8,6 @@ namespace Orchard.FileSystems.WebSite {
string ReadFile(string path);
IVolatileToken WhenPathChanges(string path);
void WhenPathChanges(string path, Action action);
}
}

View File

@@ -49,6 +49,10 @@ namespace Orchard.FileSystems.WebSite {
return token;
}
public void WhenPathChanges(string virtualPath, Action action) {
BindSignal(virtualPath, (key, value, reason) => action());
}
private Token BindToken(string virtualPath) {
lock (_tokens) {
Weak<Token> weak;
@@ -80,6 +84,11 @@ namespace Orchard.FileSystems.WebSite {
}
private void BindSignal(string virtualPath) {
BindSignal(virtualPath, _thunk.Signal);
}
private void BindSignal(string virtualPath, CacheItemRemovedCallback callback) {
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath,
new[] { virtualPath },
@@ -92,7 +101,7 @@ namespace Orchard.FileSystems.WebSite {
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
_thunk.Signal);
callback);
}
public void Signal(string key, object value, CacheItemRemovedReason reason) {