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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,11 +22,15 @@ namespace Orchard.Tests.Stubs {
} }
public IVolatileToken WhenPathChanges(string path) { public IVolatileToken WhenPathChanges(string path) {
return new WebSiteFolder.Token(path); return new Token {IsCurrent = true};
} }
public void WhenPathChanges(string path, Action action) { public void WhenPathChanges(string path, Action action) {
throw new NotImplementedException(); 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 { namespace Orchard.Environment.Extensions.Compilers {
public class CSharpExtensionBuildProvider : BuildProvider { public class CSharpExtensionBuildProvider : BuildProvider {

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,7 @@ using Orchard.Environment.Topology;
using Orchard.Events; using Orchard.Events;
using Orchard.FileSystems.AppData; using Orchard.FileSystems.AppData;
using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.FileSystems.WebSite; using Orchard.FileSystems.WebSite;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Services; using Orchard.Services;
@@ -40,6 +41,7 @@ namespace Orchard.Environment {
RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder); RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
RegisterVolatileProvider<Clock, IClock>(builder); RegisterVolatileProvider<Clock, IClock>(builder);
RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder); RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder); RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance(); builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
@@ -123,11 +125,12 @@ namespace Orchard.Environment {
// //
// Register Virtual Path Providers // Register Virtual Path Providers
// //
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) { if (HostingEnvironment.IsHosted) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance); foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
}
} }
return container.Resolve<IOrchardHost>(); 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.IO;
using System.Linq; using System.Linq;
using System.Web.Hosting; using System.Web.Hosting;
using Orchard.Caching;
using Orchard.Environment;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.FileSystems.AppData { namespace Orchard.FileSystems.AppData {
public class AppDataFolder : IAppDataFolder { public class AppDataFolder : IAppDataFolder {
private readonly IVirtualPathMonitor _virtualPathMonitor;
protected string _basePath; protected string _basePath;
public AppDataFolder() { public AppDataFolder(IVirtualPathMonitor virtualPathMonitor) {
_virtualPathMonitor = virtualPathMonitor;
_basePath = HostingEnvironment.MapPath("~/App_Data"); _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) { 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); var folderPath = Path.GetDirectoryName(filePath);
if (!Directory.Exists(folderPath)) if (!Directory.Exists(folderPath))
Directory.CreateDirectory(folderPath); Directory.CreateDirectory(folderPath);
File.WriteAllText(filePath, content); return File.Create(filePath);
} }
public string ReadFile(string path) { 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) { public void DeleteFile(string path) {
File.Delete(Path.Combine(_basePath, path)); File.Delete(CombineToPhysicalPath(path));
} }
public bool FileExists(string path) { public bool FileExists(string path) {
var filePath = Path.Combine(_basePath, path); var filePath = CombineToPhysicalPath(path);
return File.Exists(filePath); return File.Exists(filePath);
} }
public string Combine(params string[] paths) {
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
}
public IEnumerable<string> ListFiles(string path) { public IEnumerable<string> ListFiles(string path) {
var directoryPath = Path.Combine(_basePath, path); var directoryPath = CombineToPhysicalPath(path);
if (!Directory.Exists(directoryPath)) if (!Directory.Exists(directoryPath))
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
@@ -41,12 +76,12 @@ namespace Orchard.FileSystems.AppData {
return files.Select(file => { return files.Select(file => {
var fileName = Path.GetFileName(file); var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName); return Combine(path, fileName);
}); });
} }
public IEnumerable<string> ListDirectories(string path) { public IEnumerable<string> ListDirectories(string path) {
var directoryPath = Path.Combine(_basePath, path); var directoryPath = CombineToPhysicalPath(path);
if (!Directory.Exists(directoryPath)) if (!Directory.Exists(directoryPath))
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
@@ -54,12 +89,12 @@ namespace Orchard.FileSystems.AppData {
return files.Select(file => { return files.Select(file => {
var fileName = Path.GetFileName(file); var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName); return Combine(path, fileName);
}); });
} }
public string CreateDirectory(string path) { public string CreateDirectory(string path) {
var directory = Path.Combine(_basePath, path); var directory = CombineToPhysicalPath(path);
if (!Directory.Exists(directory)) if (!Directory.Exists(directory))
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
return directory; return directory;
@@ -70,8 +105,7 @@ namespace Orchard.FileSystems.AppData {
} }
public string MapPath(string path) { 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.Collections.Generic;
using System.IO;
using System.Xml;
using Orchard.Caching; using Orchard.Caching;
namespace Orchard.FileSystems.AppData { namespace Orchard.FileSystems.AppData {
@@ -11,13 +13,20 @@ namespace Orchard.FileSystems.AppData {
IEnumerable<string> ListFiles(string path); IEnumerable<string> ListFiles(string path);
IEnumerable<string> ListDirectories(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); 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); string CreateDirectory(string path);
IVolatileToken WhenPathChanges(string path);
/// <summary> /// <summary>
/// May be called to initialize component when not in a hosting environment /// 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.Reflection;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Orchard.Caching; using Orchard.Caching;
using Orchard.Environment; using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.WebSite;
namespace Orchard.FileSystems.Dependencies { namespace Orchard.FileSystems.Dependencies {
public class DependencyDescriptor { public class DependencyDescriptor {
@@ -24,176 +17,4 @@ namespace Orchard.FileSystems.Dependencies {
DependencyDescriptor GetDescriptor(string moduleName); DependencyDescriptor GetDescriptor(string moduleName);
Assembly LoadAssembly(string assemblyName); 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.IO;
using System.Web.Hosting; using System.Web.Hosting;
using Orchard.FileSystems.Dependencies;
namespace Orchard.Environment.Extensions.Loaders { namespace Orchard.FileSystems.Dependencies {
public class WebFormsExtensionsVirtualFile : VirtualFile { public class WebFormsExtensionsVirtualFile : VirtualFile {
private readonly DependencyDescriptor _dependencyDescriptor; private readonly DependencyDescriptor _dependencyDescriptor;
private readonly VirtualFile _actualFile; private readonly VirtualFile _actualFile;

View File

@@ -1,9 +1,9 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Web.Hosting; 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 { public class WebFormsExtensionsVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider {
private readonly IDependenciesFolder _dependenciesFolder; private readonly IDependenciesFolder _dependenciesFolder;
private readonly string[] _prefixes = { "~/Modules/", "/Modules/" }; 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.IO;
using System.Web.Hosting; 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 class DefaultVirtualPathProvider : IVirtualPathProvider {
public string GetDirectoryName(string virtualPath) { public string GetDirectoryName(string virtualPath) {
return Path.GetDirectoryName(virtualPath).Replace('\\', '/'); return Path.GetDirectoryName(virtualPath).Replace(Path.DirectorySeparatorChar, '/');
} }
public string Combine(params string[] paths) { public string Combine(params string[] paths) {
return Path.Combine(paths).Replace('\\', '/'); return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
} }
public Stream OpenFile(string virtualPath) { public Stream OpenFile(string virtualPath) {

View File

@@ -1,6 +1,6 @@
using System.Web.Hosting; using System.Web.Hosting;
namespace Orchard.Environment { namespace Orchard.FileSystems.VirtualPath {
public interface ICustomVirtualPathProvider { public interface ICustomVirtualPathProvider {
VirtualPathProvider Instance { get; } 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; using Orchard.Caching;
namespace Orchard.FileSystems.WebSite { namespace Orchard.FileSystems.WebSite {
/// <summary>
/// Abstraction over the virtual files/directories of a web site.
/// </summary>
public interface IWebSiteFolder : IVolatileProvider { public interface IWebSiteFolder : IVolatileProvider {
IEnumerable<string> ListDirectories(string path); IEnumerable<string> ListDirectories(string virtualPath);
string ReadFile(string path); string ReadFile(string virtualPath);
IVolatileToken WhenPathChanges(string path); IVolatileToken WhenPathChanges(string virtualPath);
void WhenPathChanges(string path, Action action); void WhenPathChanges(string virtualPath, Action action);
} }
} }

View File

@@ -2,22 +2,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Web.Caching;
using System.Web.Hosting; using System.Web.Hosting;
using Orchard.Caching; using Orchard.Caching;
using Orchard.Services; using Orchard.Environment;
using Orchard.FileSystems.VirtualPath;
namespace Orchard.FileSystems.WebSite { namespace Orchard.FileSystems.WebSite {
public class WebSiteFolder : IWebSiteFolder { public class WebSiteFolder : IWebSiteFolder {
private readonly IClock _clock; private readonly IVirtualPathMonitor _virtualPathMonitor;
private readonly Thunk _thunk;
private readonly string _prefix = Guid.NewGuid().ToString("n");
private readonly IDictionary<string, Weak<Token>> _tokens = new Dictionary<string, Weak<Token>>();
public WebSiteFolder(IVirtualPathMonitor virtualPathMonitor) {
public WebSiteFolder(IClock clock) { _virtualPathMonitor = virtualPathMonitor;
_clock = clock;
_thunk = new Thunk(this);
} }
public IEnumerable<string> ListDirectories(string virtualPath) { public IEnumerable<string> ListDirectories(string virtualPath) {
@@ -43,95 +38,11 @@ namespace Orchard.FileSystems.WebSite {
} }
public IVolatileToken WhenPathChanges(string virtualPath) { public IVolatileToken WhenPathChanges(string virtualPath) {
// Fix this to monitor first existing parent directory. return _virtualPathMonitor.WhenPathChanges(virtualPath);
var token = BindToken(virtualPath);
BindSignal(virtualPath);
return token;
} }
public void WhenPathChanges(string virtualPath, Action action) { public void WhenPathChanges(string virtualPath, Action action) {
BindSignal(virtualPath, (key, value, reason) => action()); _virtualPathMonitor.WhenPathChanges(virtualPath, 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);
}
} }
} }
} }

View File

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