Refactor IAppDataFolder and default implementation

Refactor IAppDataFolder so that it can be used for implementing
DefaultDependenciesFolder.

--HG--
branch : dev
rename : src/Orchard/Environment/Extensions/Loaders/WebFormsExtensionsVirtualFile.cs => src/Orchard/FileSystems/Dependencies/WebFormsExtensionsVirtualFile.cs
rename : src/Orchard/Environment/Extensions/Loaders/WebFormsExtensionsVirtualPathProvider.cs => src/Orchard/FileSystems/Dependencies/WebFormsExtensionsVirtualPathProvider.cs
rename : src/Orchard/Environment/IVirtualPathProvider.cs => src/Orchard/FileSystems/VirtualPath/DefaultVirtualPathProvider.cs
rename : src/Orchard/Environment/ICustomVirtualPathProvider.cs => src/Orchard/FileSystems/VirtualPath/ICustomVirtualPathProvider.cs
This commit is contained in:
Renaud Paquay
2010-06-11 15:48:40 -07:00
parent 4e9ebd1755
commit 4b9c9da8fd
26 changed files with 460 additions and 335 deletions

View File

@@ -3,10 +3,13 @@ using System.IO;
using System.Linq;
using Autofac;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.VirtualPath;
using Orchard.Indexing;
using Orchard.Core.Indexing.Lucene;
using Orchard.Services;
namespace Orchard.Tests.Indexing {
public class DefaultIndexProviderTests {
@@ -28,7 +31,7 @@ namespace Orchard.Tests.Indexing {
}
Directory.CreateDirectory(_basePath);
_appDataFolder = new AppDataFolder();
_appDataFolder = new AppDataFolder(new DefaultVirtualPathMonitor(new Clock()));
_appDataFolder.SetBasePath(_basePath);
var builder = new ContainerBuilder();

View File

@@ -3,10 +3,13 @@ using System.IO;
using System.Linq;
using Autofac;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.VirtualPath;
using Orchard.Indexing;
using Orchard.Core.Indexing.Lucene;
using Orchard.Services;
namespace Orchard.Tests.Indexing {
public class DefaultSearchBuilderTests {
@@ -29,7 +32,7 @@ namespace Orchard.Tests.Indexing {
Directory.CreateDirectory(_basePath);
_appDataFolder = new AppDataFolder();
_appDataFolder = new AppDataFolder(new DefaultVirtualPathMonitor(new Clock()));
_appDataFolder.SetBasePath(_basePath);
var builder = new ContainerBuilder();

View File

@@ -1,7 +1,10 @@
using System.IO;
using System.Linq;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.VirtualPath;
using Orchard.Services;
namespace Orchard.Tests.Environment.Configuration {
[TestFixture]
@@ -18,7 +21,7 @@ namespace Orchard.Tests.Environment.Configuration {
File.WriteAllText(Path.Combine(_tempFolder, "alpha\\gamma.txt"), "gamma-content");
Directory.CreateDirectory(Path.Combine(_tempFolder, "alpha\\omega"));
_appDataFolder = new AppDataFolder();
_appDataFolder = new AppDataFolder(new DefaultVirtualPathMonitor(new Clock()));
_appDataFolder.SetBasePath(_tempFolder);
}
@@ -31,8 +34,8 @@ namespace Orchard.Tests.Environment.Configuration {
public void ListFilesShouldContainSubPathAndFileName() {
var files = _appDataFolder.ListFiles("alpha");
Assert.That(files.Count(), Is.EqualTo(2));
Assert.That(files, Has.Some.EqualTo("alpha\\beta.txt"));
Assert.That(files, Has.Some.EqualTo("alpha\\gamma.txt"));
Assert.That(files, Has.Some.EqualTo("alpha/beta.txt"));
Assert.That(files, Has.Some.EqualTo("alpha/gamma.txt"));
}
[Test]
@@ -51,7 +54,7 @@ namespace Orchard.Tests.Environment.Configuration {
public void ListSubdirectoriesShouldContainFullSubpath() {
var files = _appDataFolder.ListDirectories("alpha");
Assert.That(files.Count(), Is.EqualTo(1));
Assert.That(files, Has.Some.EqualTo("alpha\\omega"));
Assert.That(files, Has.Some.EqualTo("alpha/omega"));
}
[Test]

View File

@@ -2,8 +2,11 @@
using System.Linq;
using Moq;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.VirtualPath;
using Orchard.Services;
namespace Orchard.Tests.Environment.Configuration {
[TestFixture]
@@ -13,7 +16,7 @@ namespace Orchard.Tests.Environment.Configuration {
[SetUp]
public void Init() {
_appData = new AppDataFolder();
_appData = new AppDataFolder(new DefaultVirtualPathMonitor(new Clock()));
_tempFolder = Path.GetTempFileName();
File.Delete(_tempFolder);
_appData.SetBasePath(_tempFolder);

View File

@@ -3,9 +3,12 @@ using System.Runtime.Serialization;
using System.Xml;
using Autofac;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.VirtualPath;
using Orchard.Services;
namespace Orchard.Tests.Environment.Topology {
[TestFixture]
@@ -19,7 +22,7 @@ namespace Orchard.Tests.Environment.Topology {
_tempFolder = Path.GetTempFileName();
File.Delete(_tempFolder);
Directory.CreateDirectory(_tempFolder);
_appDataFolder = new AppDataFolder();
_appDataFolder = new AppDataFolder(new DefaultVirtualPathMonitor(new Clock()));
_appDataFolder.SetBasePath(_tempFolder);
var builder = new ContainerBuilder();
builder.RegisterInstance(_appDataFolder).As<IAppDataFolder>();

View File

@@ -22,11 +22,15 @@ namespace Orchard.Tests.Stubs {
}
public IVolatileToken WhenPathChanges(string path) {
return new WebSiteFolder.Token(path);
return new Token {IsCurrent = true};
}
public void WhenPathChanges(string path, Action action) {
throw new NotImplementedException();
}
public class Token : IVolatileToken {
public bool IsCurrent { get; set; }
}
}
}

View File

@@ -1,5 +1,5 @@
using System.Diagnostics;
using System.Web.Compilation;
using System.Web.Compilation;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Compilers {
public class CSharpExtensionBuildProvider : BuildProvider {

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Compilers {
/// <summary>

View File

@@ -1,6 +1,7 @@
using System.CodeDom;
using System.IO;
using System.Linq;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Compilers {
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Loaders {
public class DynamicExtensionLoader : IExtensionLoader {

View File

@@ -1,5 +1,6 @@
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Loaders {
/// <summary>

View File

@@ -17,6 +17,7 @@ using Orchard.Environment.Topology;
using Orchard.Events;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.FileSystems.WebSite;
using Orchard.Logging;
using Orchard.Services;
@@ -40,6 +41,7 @@ namespace Orchard.Environment {
RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
RegisterVolatileProvider<Clock, IClock>(builder);
RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
@@ -123,11 +125,12 @@ namespace Orchard.Environment {
//
// Register Virtual Path Providers
//
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
if (HostingEnvironment.IsHosted) {
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
}
}
return container.Resolve<IOrchardHost>();
}
}

View File

@@ -1,39 +1,74 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Hosting;
using Orchard.Caching;
using Orchard.Environment;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.FileSystems.AppData {
public class AppDataFolder : IAppDataFolder {
private readonly IVirtualPathMonitor _virtualPathMonitor;
protected string _basePath;
public AppDataFolder() {
public AppDataFolder(IVirtualPathMonitor virtualPathMonitor) {
_virtualPathMonitor = virtualPathMonitor;
_basePath = HostingEnvironment.MapPath("~/App_Data");
}
/// <summary>
/// Combine a set of virtual paths into a physical path based at "_basePath"
/// </summary>
private string CombineToPhysicalPath(params string[] paths) {
return Path.Combine(_basePath, Path.Combine(paths))
.Replace('/', Path.DirectorySeparatorChar);
}
public IVolatileToken WhenPathChanges(string path) {
var replace = Path.Combine("~/App_Data", path).Replace(Path.DirectorySeparatorChar, '/');
return _virtualPathMonitor.WhenPathChanges(replace);
}
public void CreateFile(string path, string content) {
var filePath = Path.Combine(_basePath, path);
using(var stream = CreateFile(path)) {
using(var tw = new StreamWriter(stream)) {
tw.Write(content);
}
}
}
public Stream CreateFile(string path) {
var filePath = CombineToPhysicalPath(path);
var folderPath = Path.GetDirectoryName(filePath);
if (!Directory.Exists(folderPath))
Directory.CreateDirectory(folderPath);
File.WriteAllText(filePath, content);
return File.Create(filePath);
}
public string ReadFile(string path) {
return File.ReadAllText(Path.Combine(_basePath, path));
return File.ReadAllText(CombineToPhysicalPath(path));
}
public Stream OpenFile(string path) {
return File.OpenRead(CombineToPhysicalPath(path));
}
public void DeleteFile(string path) {
File.Delete(Path.Combine(_basePath, path));
File.Delete(CombineToPhysicalPath(path));
}
public bool FileExists(string path) {
var filePath = Path.Combine(_basePath, path);
var filePath = CombineToPhysicalPath(path);
return File.Exists(filePath);
}
public string Combine(params string[] paths) {
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
}
public IEnumerable<string> ListFiles(string path) {
var directoryPath = Path.Combine(_basePath, path);
var directoryPath = CombineToPhysicalPath(path);
if (!Directory.Exists(directoryPath))
return Enumerable.Empty<string>();
@@ -41,12 +76,12 @@ namespace Orchard.FileSystems.AppData {
return files.Select(file => {
var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName);
return Combine(path, fileName);
});
}
public IEnumerable<string> ListDirectories(string path) {
var directoryPath = Path.Combine(_basePath, path);
var directoryPath = CombineToPhysicalPath(path);
if (!Directory.Exists(directoryPath))
return Enumerable.Empty<string>();
@@ -54,12 +89,12 @@ namespace Orchard.FileSystems.AppData {
return files.Select(file => {
var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName);
return Combine(path, fileName);
});
}
public string CreateDirectory(string path) {
var directory = Path.Combine(_basePath, path);
var directory = CombineToPhysicalPath(path);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
return directory;
@@ -70,8 +105,7 @@ namespace Orchard.FileSystems.AppData {
}
public string MapPath(string path) {
return Path.Combine(_basePath, path);
return CombineToPhysicalPath(path);
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Orchard.Caching;
namespace Orchard.FileSystems.AppData {
@@ -11,13 +13,20 @@ namespace Orchard.FileSystems.AppData {
IEnumerable<string> ListFiles(string path);
IEnumerable<string> ListDirectories(string path);
void CreateFile(string path, string content);
string ReadFile(string path);
void DeleteFile(string path);
bool FileExists(string path);
string Combine(params string[] paths);
void CreateFile(string path, string content);
Stream CreateFile(string path);
string ReadFile(string path);
Stream OpenFile(string path);
void DeleteFile(string path);
string CreateDirectory(string path);
IVolatileToken WhenPathChanges(string path);
/// <summary>
/// May be called to initialize component when not in a hosting environment

View File

@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Orchard.Caching;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.WebSite;
namespace Orchard.FileSystems.Dependencies {
public class DefaultDependenciesFolder : IDependenciesFolder {
private readonly string _basePath = "Dependencies";
private readonly ICacheManager _cacheManager;
private readonly IWebSiteFolder _webSiteFolder;
private readonly IAppDataFolder _appDataFolder;
private readonly IExtensionManagerEvents _events;
private readonly InvalidationToken _writeThroughToken;
public DefaultDependenciesFolder(ICacheManager cacheManager, IWebSiteFolder webSiteFolder, IAppDataFolder appDataFolder, IExtensionManagerEvents events) {
_cacheManager = cacheManager;
_webSiteFolder = webSiteFolder;
_appDataFolder = appDataFolder;
_events = events;
_writeThroughToken = new InvalidationToken();
}
private string BasePath {
get {
return _basePath;
}
}
private string PersistencePath {
get {
return _appDataFolder.Combine(BasePath, "dependencies.xml");
}
}
private IEnumerable<DependencyDescriptor> Descriptors {
get {
return _cacheManager.Get(PersistencePath,
ctx => {
ctx.Monitor(_appDataFolder.WhenPathChanges(ctx.Key));
ctx.Monitor(_writeThroughToken);
_appDataFolder.CreateDirectory(BasePath);
return ReadDependencies(ctx.Key).ToList();
});
}
}
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,
VirtualPath = virtualPath,
FileName = assembly.Location
};
StoreDepencyInformation(descriptor);
_webSiteFolder.WhenPathChanges(virtualPath, () => _events.ModuleChanged(moduleName));
}
public void StorePrecompiledAssembly(string moduleName, string virtualPath) {
_appDataFolder.CreateDirectory(BasePath);
// Only store assembly if it's more recent that what we have stored already (if anything)
var assemblyFileName = _appDataFolder.MapPath(virtualPath);
if (IsNewerAssembly(moduleName, assemblyFileName)) {
var destinationFileName = Path.GetFileName(assemblyFileName);
var destinationPath = _appDataFolder.MapPath(_appDataFolder.Combine(BasePath, destinationFileName));
File.Copy(assemblyFileName, destinationPath, true);
StoreDepencyInformation(new DependencyDescriptor {
ModuleName = moduleName,
IsFromBuildProvider = false,
VirtualPath = virtualPath,
FileName = destinationFileName
});
}
}
public DependencyDescriptor GetDescriptor(string moduleName) {
return Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
}
private bool IsNewerAssembly(string moduleName, string assemblyFileName) {
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
if (dependency == null) {
return true;
}
var existingFileName = _appDataFolder.MapPath(_appDataFolder.Combine(BasePath, dependency.FileName));
if (!File.Exists(existingFileName)) {
return true;
}
return (File.GetLastWriteTimeUtc(existingFileName) < File.GetLastWriteTimeUtc(assemblyFileName));
}
private void StoreDepencyInformation(DependencyDescriptor descriptor) {
var dependencies = Descriptors.ToList();
int index = dependencies.FindIndex(d => d.ModuleName == descriptor.ModuleName);
if (index < 0) {
dependencies.Add(descriptor);
}
else {
dependencies[index] = descriptor;
}
WriteDependencies(PersistencePath, dependencies);
}
public Assembly LoadAssembly(string assemblyName) {
_appDataFolder.CreateDirectory(BasePath);
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == assemblyName);
if (dependency == null)
return null;
if (!_appDataFolder.FileExists(_appDataFolder.Combine(BasePath, dependency.FileName)))
return null;
return Assembly.Load(Path.GetFileNameWithoutExtension(dependency.FileName));
}
private IEnumerable<DependencyDescriptor> ReadDependencies(string persistancePath) {
Func<string, XName> ns = (name => XName.Get(name));
Func<XElement, string, string> elem = (e, name) => e.Element(ns(name)).Value;
if (!_appDataFolder.FileExists(persistancePath))
return Enumerable.Empty<DependencyDescriptor>();
using (var stream = _appDataFolder.OpenFile(persistancePath)) {
XDocument document = XDocument.Load(stream);
return document
.Elements(ns("Dependencies"))
.Elements(ns("Dependency"))
.Select(e => new DependencyDescriptor {
ModuleName = elem(e, "ModuleName"),
VirtualPath = elem(e, "VirtualPath"),
FileName = elem(e, "FileName"),
IsFromBuildProvider = bool.Parse(elem(e, "IsFromBuildProvider"))
})
.ToList();
}
}
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",
new XElement(ns("ModuleName"), d.ModuleName),
new XElement(ns("VirtualPath"), d.VirtualPath),
new XElement(ns("IsFromBuildProvider"), d.IsFromBuildProvider),
new XElement(ns("FileName"), d.FileName)));
document.Root.Add(elements);
using (var stream = _appDataFolder.CreateFile(persistancePath)) {
document.Save(stream, SaveOptions.None);
}
// Ensure cache is invalidated right away, not waiting for file change notification to happen
_writeThroughToken.IsCurrent = false;
}
}
}

View File

@@ -1,13 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.WebSite;
namespace Orchard.FileSystems.Dependencies {
public class DependencyDescriptor {
@@ -24,176 +17,4 @@ namespace Orchard.FileSystems.Dependencies {
DependencyDescriptor GetDescriptor(string moduleName);
Assembly LoadAssembly(string assemblyName);
}
public class DefaultDependenciesFolder : IDependenciesFolder {
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(ICacheManager cacheManager, IWebSiteFolder webSiteFolder, IVirtualPathProvider virtualPathProvider, IExtensionManagerEvents events) {
_cacheManager = cacheManager;
_webSiteFolder = webSiteFolder;
_virtualPathProvider = virtualPathProvider;
_events = events;
_token = new InvalidationToken();
}
private string BasePath {
get {
return _basePath;
}
}
private string PersistencePath {
get {
return _virtualPathProvider.Combine(BasePath, "dependencies.xml");
}
}
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,
VirtualPath = virtualPath,
FileName = assembly.Location
};
StoreDepencyInformation(descriptor);
_webSiteFolder.WhenPathChanges(virtualPath, () => _events.ModuleChanged(moduleName));
}
public void StorePrecompiledAssembly(string moduleName, string virtualPath) {
_virtualPathProvider.CreateDirectory(BasePath);
// Only store assembly if it's more recent that what we have stored already (if anything)
var assemblyFileName = _virtualPathProvider.MapPath(virtualPath);
if (IsNewerAssembly(moduleName, assemblyFileName)) {
var destinationFileName = Path.GetFileName(assemblyFileName);
var destinationPath = _virtualPathProvider.MapPath(_virtualPathProvider.Combine(BasePath, destinationFileName));
File.Copy(assemblyFileName, destinationPath, true);
StoreDepencyInformation(new DependencyDescriptor {
ModuleName = moduleName,
IsFromBuildProvider = false,
VirtualPath = virtualPath,
FileName = destinationFileName
});
}
}
public DependencyDescriptor GetDescriptor(string moduleName) {
return Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
}
private bool IsNewerAssembly(string moduleName, string assemblyFileName) {
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == moduleName);
if (dependency == null) {
return true;
}
var existingFileName = _virtualPathProvider.MapPath(_virtualPathProvider.Combine(BasePath, dependency.FileName));
if (!File.Exists(existingFileName)) {
return true;
}
return (File.GetLastWriteTimeUtc(existingFileName) < File.GetLastWriteTimeUtc(assemblyFileName));
}
private void StoreDepencyInformation(DependencyDescriptor descriptor) {
var dependencies = Descriptors.ToList();
int index = dependencies.FindIndex(d => d.ModuleName == descriptor.ModuleName);
if (index < 0) {
dependencies.Add(descriptor);
}
else {
dependencies[index] = descriptor;
}
WriteDependencies(PersistencePath, dependencies);
}
public Assembly LoadAssembly(string assemblyName) {
_virtualPathProvider.CreateDirectory(BasePath);
var dependency = Descriptors.SingleOrDefault(d => d.ModuleName == assemblyName);
if (dependency == null)
return null;
if (!_virtualPathProvider.FileExists(_virtualPathProvider.Combine(BasePath, dependency.FileName)))
return null;
return Assembly.Load(Path.GetFileNameWithoutExtension(dependency.FileName));
}
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(persistancePath)) {
XDocument document = XDocument.Load(stream);
return document
.Elements(ns("Dependencies"))
.Elements(ns("Dependency"))
.Select(e => new DependencyDescriptor {
ModuleName = e.Element("ModuleName").Value,
VirtualPath = e.Element("VirtualPath").Value,
FileName = e.Element("FileName").Value,
IsFromBuildProvider = bool.Parse(e.Element("IsFromBuildProvider").Value)
})
.ToList();
}
}
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",
new XElement(ns("ModuleName"), d.ModuleName),
new XElement(ns("VirtualPath"), d.VirtualPath),
new XElement(ns("IsFromBuildProvider"), d.IsFromBuildProvider),
new XElement(ns("FileName"), d.FileName)));
document.Root.Add(elements);
using (var stream = _virtualPathProvider.CreateText(persistancePath)) {
document.Save(stream, SaveOptions.None);
}
// Ensure cache is invalidated right away, not waiting for file change notification to happen
_token.IsCurrent = false;
}
}
}

View File

@@ -1,8 +1,7 @@
using System.IO;
using System.Web.Hosting;
using Orchard.FileSystems.Dependencies;
namespace Orchard.Environment.Extensions.Loaders {
namespace Orchard.FileSystems.Dependencies {
public class WebFormsExtensionsVirtualFile : VirtualFile {
private readonly DependencyDescriptor _dependencyDescriptor;
private readonly VirtualFile _actualFile;

View File

@@ -1,9 +1,9 @@
using System;
using System.Linq;
using System.Web.Hosting;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.Environment.Extensions.Loaders {
namespace Orchard.FileSystems.Dependencies {
public class WebFormsExtensionsVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider {
private readonly IDependenciesFolder _dependenciesFolder;
private readonly string[] _prefixes = { "~/Modules/", "/Modules/" };

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Web.Caching;
using System.Web.Hosting;
using Orchard.Caching;
using Orchard.Services;
namespace Orchard.FileSystems.VirtualPath {
public class DefaultVirtualPathMonitor : IVirtualPathMonitor {
private readonly Thunk _thunk;
private readonly string _prefix = Guid.NewGuid().ToString("n");
private readonly IDictionary<string, Weak<Token>> _tokens = new Dictionary<string, Weak<Token>>();
private readonly IClock _clock;
public DefaultVirtualPathMonitor(IClock clock) {
_clock = clock;
_thunk = new Thunk(this);
}
public IVolatileToken WhenPathChanges(string virtualPath) {
// Fix this to monitor first existing parent directory.
var token = BindToken(virtualPath);
BindSignal(virtualPath);
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;
if (!_tokens.TryGetValue(virtualPath, out weak)) {
weak = new Weak<Token>(new Token(virtualPath));
_tokens[virtualPath] = weak;
}
var token = weak.Target;
if (token == null) {
token = new Token(virtualPath);
weak.Target = token;
}
return token;
}
}
private Token DetachToken(string virtualPath) {
lock (_tokens) {
Weak<Token> weak;
if (!_tokens.TryGetValue(virtualPath, out weak)) {
return null;
}
var token = weak.Target;
weak.Target = null;
return token;
}
}
private void BindSignal(string virtualPath) {
BindSignal(virtualPath, _thunk.Signal);
}
private void BindSignal(string virtualPath, CacheItemRemovedCallback callback) {
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath,
new[] { virtualPath },
_clock.UtcNow);
HostingEnvironment.Cache.Add(
_prefix + virtualPath,
virtualPath,
cacheDependency,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
callback);
}
public void Signal(string key, object value, CacheItemRemovedReason reason) {
var virtualPath = Convert.ToString(value);
var token = DetachToken(virtualPath);
if (token != null)
token.IsCurrent = false;
}
public class Token : IVolatileToken {
public Token(string virtualPath) {
IsCurrent = true;
VirtualPath = virtualPath;
}
public bool IsCurrent { get; set; }
public string VirtualPath { get; private set; }
}
class Thunk {
private readonly Weak<DefaultVirtualPathMonitor> _weak;
public Thunk(DefaultVirtualPathMonitor provider) {
_weak = new Weak<DefaultVirtualPathMonitor>(provider);
}
public void Signal(string key, object value, CacheItemRemovedReason reason) {
var provider = _weak.Target;
if (provider != null)
provider.Signal(key, value, reason);
}
}
}
}

View File

@@ -1,26 +1,14 @@
using System.IO;
using System.Web.Hosting;
using Orchard.Caching;
namespace Orchard.Environment {
public interface IVirtualPathProvider : IVolatileProvider {
string GetDirectoryName(string virtualPath);
string Combine(params string[] paths);
Stream OpenFile(string virtualPath);
StreamWriter CreateText(string virtualPath);
string MapPath(string virtualPath);
bool FileExists(string virtualPath);
bool DirectoryExists(string virtualPath);
void CreateDirectory(string virtualPath);
}
namespace Orchard.FileSystems.VirtualPath {
public class DefaultVirtualPathProvider : IVirtualPathProvider {
public string GetDirectoryName(string virtualPath) {
return Path.GetDirectoryName(virtualPath).Replace('\\', '/');
return Path.GetDirectoryName(virtualPath).Replace(Path.DirectorySeparatorChar, '/');
}
public string Combine(params string[] paths) {
return Path.Combine(paths).Replace('\\', '/');
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
}
public Stream OpenFile(string virtualPath) {

View File

@@ -1,6 +1,6 @@
using System.Web.Hosting;
namespace Orchard.Environment {
namespace Orchard.FileSystems.VirtualPath {
public interface ICustomVirtualPathProvider {
VirtualPathProvider Instance { get; }
}

View File

@@ -0,0 +1,15 @@
using System;
using Orchard.Caching;
namespace Orchard.FileSystems.VirtualPath {
/// <summary>
/// Enable monitoring changes over virtual path
/// </summary>
public interface IVirtualPathMonitor : IVolatileProvider {
IVolatileToken WhenPathChanges(string virtualPath);
// Temporary until we have a generic mechanism for components
// to synchronize their dependencies through a Context.Monitor()
// interface
void WhenPathChanges(string virtualPath, Action action);
}
}

View File

@@ -0,0 +1,17 @@
using System.IO;
using Orchard.Caching;
namespace Orchard.FileSystems.VirtualPath {
public interface IVirtualPathProvider : IVolatileProvider {
string Combine(params string[] paths);
string MapPath(string virtualPath);
bool FileExists(string virtualPath);
Stream OpenFile(string virtualPath);
StreamWriter CreateText(string virtualPath);
bool DirectoryExists(string virtualPath);
void CreateDirectory(string virtualPath);
string GetDirectoryName(string virtualPath);
}
}

View File

@@ -3,11 +3,14 @@ using System.Collections.Generic;
using Orchard.Caching;
namespace Orchard.FileSystems.WebSite {
/// <summary>
/// Abstraction over the virtual files/directories of a web site.
/// </summary>
public interface IWebSiteFolder : IVolatileProvider {
IEnumerable<string> ListDirectories(string path);
string ReadFile(string path);
IEnumerable<string> ListDirectories(string virtualPath);
string ReadFile(string virtualPath);
IVolatileToken WhenPathChanges(string path);
void WhenPathChanges(string path, Action action);
IVolatileToken WhenPathChanges(string virtualPath);
void WhenPathChanges(string virtualPath, Action action);
}
}

View File

@@ -2,22 +2,17 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Caching;
using System.Web.Hosting;
using Orchard.Caching;
using Orchard.Services;
using Orchard.Environment;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.FileSystems.WebSite {
public class WebSiteFolder : IWebSiteFolder {
private readonly IClock _clock;
private readonly Thunk _thunk;
private readonly string _prefix = Guid.NewGuid().ToString("n");
private readonly IDictionary<string, Weak<Token>> _tokens = new Dictionary<string, Weak<Token>>();
private readonly IVirtualPathMonitor _virtualPathMonitor;
public WebSiteFolder(IClock clock) {
_clock = clock;
_thunk = new Thunk(this);
public WebSiteFolder(IVirtualPathMonitor virtualPathMonitor) {
_virtualPathMonitor = virtualPathMonitor;
}
public IEnumerable<string> ListDirectories(string virtualPath) {
@@ -43,95 +38,11 @@ namespace Orchard.FileSystems.WebSite {
}
public IVolatileToken WhenPathChanges(string virtualPath) {
// Fix this to monitor first existing parent directory.
var token = BindToken(virtualPath);
BindSignal(virtualPath);
return token;
return _virtualPathMonitor.WhenPathChanges(virtualPath);
}
public void WhenPathChanges(string virtualPath, Action action) {
BindSignal(virtualPath, (key, value, reason) => action());
}
private Token BindToken(string virtualPath) {
lock (_tokens) {
Weak<Token> weak;
if (!_tokens.TryGetValue(virtualPath, out weak)) {
weak = new Weak<Token>(new Token(virtualPath));
_tokens[virtualPath] = weak;
}
var token = weak.Target;
if (token == null) {
token = new Token(virtualPath);
weak.Target = token;
}
return token;
}
}
private Token DetachToken(string virtualPath) {
lock (_tokens) {
Weak<Token> weak;
if (!_tokens.TryGetValue(virtualPath, out weak)) {
return null;
}
var token = weak.Target;
weak.Target = null;
return token;
}
}
private void BindSignal(string virtualPath) {
BindSignal(virtualPath, _thunk.Signal);
}
private void BindSignal(string virtualPath, CacheItemRemovedCallback callback) {
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath,
new[] { virtualPath },
_clock.UtcNow);
HostingEnvironment.Cache.Add(
_prefix + virtualPath,
virtualPath,
cacheDependency,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
callback);
}
public void Signal(string key, object value, CacheItemRemovedReason reason) {
var virtualPath = Convert.ToString(value);
var token = DetachToken(virtualPath);
if (token != null)
token.IsCurrent = false;
}
public class Token : IVolatileToken {
public Token(string virtualPath) {
IsCurrent = true;
VirtualPath = virtualPath;
}
public bool IsCurrent { get; set; }
public string VirtualPath { get; private set; }
}
class Thunk {
private readonly Weak<WebSiteFolder> _weak;
public Thunk(WebSiteFolder provider) {
_weak = new Weak<WebSiteFolder>(provider);
}
public void Signal(string key, object value, CacheItemRemovedReason reason) {
var provider = _weak.Target;
if (provider != null)
provider.Signal(key, value, reason);
}
_virtualPathMonitor.WhenPathChanges(virtualPath, action);
}
}
}

View File

@@ -353,11 +353,15 @@
<Compile Include="Data\Orderable.cs" />
<Compile Include="Environment\DefaultOrchardShell.cs" />
<Compile Include="Environment\Extensions\Loaders\MergedReadOnlyStreams.cs" />
<Compile Include="Environment\ICustomVirtualPathProvider.cs" />
<Compile Include="Environment\Extensions\Loaders\WebFormsExtensionsVirtualPathProvider.cs" />
<Compile Include="Environment\Extensions\Loaders\WebFormsExtensionsVirtualFile.cs" />
<Compile Include="FileSystems\Dependencies\DefaultDependenciesFolder.cs" />
<Compile Include="FileSystems\VirtualPath\DefaultVirtualPathMonitor.cs" />
<Compile Include="FileSystems\VirtualPath\DefaultVirtualPathProvider.cs" />
<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" />
<Compile Include="Environment\Extensions\Loaders\ProbingExtensionLoader.cs" />
<Compile Include="Environment\Extensions\Compilers\CSharpExtensionBuildProvider.cs" />
@@ -367,7 +371,7 @@
<Compile Include="Environment\Extensions\Compilers\CSharpProjectParser.cs" />
<Compile Include="Environment\IAssemblyBuilder.cs" />
<Compile Include="Environment\IBuildManager.cs" />
<Compile Include="Environment\IVirtualPathProvider.cs" />
<Compile Include="FileSystems\VirtualPath\IVirtualPathProvider.cs" />
<Compile Include="Environment\IOrchardShell.cs" />
<Compile Include="Environment\OrchardStarter.cs" />
<Compile Include="Environment\State\DefaultProcessingEngine.cs" />