Building out caching

Passing along signal of VPP file monitoring
Expiring results as appropriate in AvailableExtensions

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-05-16 12:59:04 -07:00
parent c138cc996d
commit d46d94f1ee
30 changed files with 410 additions and 146 deletions

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Orchard.Caching;
using Orchard.Environment;
using Autofac;
using Orchard.Environment.FileSystems;
using Orchard.Services;
namespace Orchard.Tests.Caching {
[TestFixture]
public class CacheScopeTests {
private IContainer _hostContainer;
[SetUp]
public void Init() {
_hostContainer = OrchardStarter.CreateHostContainer(builder => {
builder.RegisterType<Alpha>().InstancePerDependency();
});
}
public class Alpha {
public ICacheManager CacheManager { get; set; }
public Alpha(ICacheManager cacheManager) {
CacheManager = cacheManager;
}
}
[Test]
public void ComponentsAtHostLevelHaveAccessToCache() {
var alpha = _hostContainer.Resolve<Alpha>();
Assert.That(alpha.CacheManager, Is.Not.Null);
}
[Test]
public void HostLevelHasAccessToGlobalVolatileProviders() {
Assert.That(_hostContainer.Resolve<IVirtualPathProvider>(), Is.Not.Null);
Assert.That(_hostContainer.Resolve<IAppDataFolder>(), Is.Not.Null);
Assert.That(_hostContainer.Resolve<IClock>(), Is.Not.Null);
}
}
}

View File

@@ -1,4 +1,5 @@
using Autofac; using System;
using Autofac;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Caching; using Orchard.Caching;
@@ -11,9 +12,12 @@ namespace Orchard.Tests.Caching {
[SetUp] [SetUp]
public void Init() { public void Init() {
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
builder.RegisterModule(new CacheModule());
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>(); builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
_container = builder.Build(); _container = builder.Build();
_cacheManager = _container.Resolve<ICacheManager>(); _cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType()));
} }
[Test] [Test]
@@ -28,5 +32,66 @@ namespace Orchard.Tests.Caching {
var result = _cacheManager.Get("testItem", ctx => ""); var result = _cacheManager.Get("testItem", ctx => "");
Assert.That(result, Is.EqualTo("testResult")); Assert.That(result, Is.EqualTo("testResult"));
} }
[Test]
public void CacheModuleProvidesTypeSpecificManager() {
var scope = _container.BeginLifetimeScope(builder => {
builder.RegisterModule(new CacheModule());
builder.RegisterType<ComponentOne>();
builder.RegisterType<ComponentTwo>();
});
var c1 = scope.Resolve<ComponentOne>();
var c2 = scope.Resolve<ComponentTwo>();
var w1a = c1.CacheManager.Get("hello", ctx => "world1");
var w1b = c1.CacheManager.Get("hello", ctx => "world2");
var w2a = c2.CacheManager.Get("hello", ctx => "world3");
var w2b = c2.CacheManager.Get("hello", ctx => "world4");
Assert.That(w1a, Is.EqualTo("world1"));
Assert.That(w1b, Is.EqualTo("world1"));
Assert.That(w2a, Is.EqualTo("world3"));
Assert.That(w2b, Is.EqualTo("world3"));
var c3 = scope.Resolve<ComponentOne>();
var c4 = scope.Resolve<ComponentTwo>();
var w3a = c3.CacheManager.Get("hello", ctx => "world5");
var w3b = c3.CacheManager.Get("hello", ctx => "world6");
var w4a = c4.CacheManager.Get("hello", ctx => "world7");
var w4b = c4.CacheManager.Get("hello", ctx => "world8");
Assert.That(w3a, Is.EqualTo("world1"));
Assert.That(w3b, Is.EqualTo("world1"));
Assert.That(w4a, Is.EqualTo("world3"));
Assert.That(w4b, Is.EqualTo("world3"));
Assert.That(c1.CacheManager,
Is.Not.SameAs(c3.CacheManager));
Assert.That(c1.CacheManager.GetCache<string, string>(),
Is.SameAs(c3.CacheManager.GetCache<string, string>()));
Assert.That(c1.CacheManager,
Is.Not.SameAs(c2.CacheManager));
Assert.That(c1.CacheManager.GetCache<string, string>(),
Is.Not.SameAs(c2.CacheManager.GetCache<string, string>()));
}
class ComponentOne {
public ICacheManager CacheManager { get; set; }
public ComponentOne(ICacheManager cacheManager) {
CacheManager = cacheManager;
}
}
class ComponentTwo {
public ICacheManager CacheManager { get; set; }
public ComponentTwo(ICacheManager cacheManager) {
CacheManager = cacheManager;
}
}
} }
} }

View File

@@ -1,7 +1,6 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Environment.Configuration;
using Orchard.Environment.FileSystems; using Orchard.Environment.FileSystems;
namespace Orchard.Tests.Environment.Configuration { namespace Orchard.Tests.Environment.Configuration {

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.FileSystems;
namespace Orchard.Tests.Environment.Configuration { namespace Orchard.Tests.Environment.Configuration {
[TestFixture] [TestFixture]

View File

@@ -3,7 +3,6 @@ using System.Runtime.Serialization;
using System.Xml; using System.Xml;
using Autofac; using Autofac;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Environment.Configuration;
using Orchard.Environment.FileSystems; using Orchard.Environment.FileSystems;
using Orchard.Environment.Topology; using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models; using Orchard.Environment.Topology.Models;

View File

@@ -125,6 +125,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Caching\CacheScopeTests.cs" />
<Compile Include="Caching\CacheTests.cs" /> <Compile Include="Caching\CacheTests.cs" />
<Compile Include="Commands\CommandHandlerDescriptorBuilderTests.cs" /> <Compile Include="Commands\CommandHandlerDescriptorBuilderTests.cs" />
<Compile Include="Commands\CommandHandlerTests.cs" /> <Compile Include="Commands\CommandHandlerTests.cs" />

View File

@@ -16,5 +16,6 @@ namespace Orchard.Tests.Stubs {
public DateTime FutureMoment(TimeSpan span) { public DateTime FutureMoment(TimeSpan span) {
return UtcNow.Add(span); return UtcNow.Add(span);
} }
} }
} }

View File

@@ -393,7 +393,7 @@
<WebProjectProperties> <WebProjectProperties>
<UseIIS>False</UseIIS> <UseIIS>False</UseIIS>
<AutoAssignPort>False</AutoAssignPort> <AutoAssignPort>False</AutoAssignPort>
<DevelopmentServerPort>80</DevelopmentServerPort> <DevelopmentServerPort>30320</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath> <DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl> <IISUrl>
</IISUrl> </IISUrl>

View File

@@ -1,14 +1,13 @@
using System; using System;
using Orchard.Caching.Providers;
namespace Orchard.Caching { namespace Orchard.Caching {
public class AcquireContext<TKey> { public class AcquireContext<TKey> {
public AcquireContext(TKey key, Action<IVolatileSignal> monitor) { public AcquireContext(TKey key, Action<IVolatileToken> monitor) {
Key = key; Key = key;
IsInvalid = monitor; Monitor = monitor;
} }
public TKey Key { get; private set; } public TKey Key { get; private set; }
public Action<IVolatileSignal> IsInvalid { get; private set; } public Action<IVolatileToken> Monitor { get; private set; }
} }
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.Caching.Providers; using System.Linq;
namespace Orchard.Caching { namespace Orchard.Caching {
public class Cache<TKey, TResult> : ICache<TKey, TResult> { public class Cache<TKey, TResult> : ICache<TKey, TResult> {
@@ -12,19 +12,19 @@ namespace Orchard.Caching {
public TResult Get(TKey key, Func<AcquireContext<TKey>, TResult> acquire) { public TResult Get(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
CacheEntry entry; CacheEntry entry;
if (!_entries.TryGetValue(key, out entry)) { if (!_entries.TryGetValue(key, out entry) || entry.Tokens.Any(t => !t.IsCurrent)) {
entry = new CacheEntry { VolatileItems = new List<IVolatileSignal>() }; entry = new CacheEntry { Tokens = new List<IVolatileToken>() };
var context = new AcquireContext<TKey>(key, volatileItem => entry.VolatileItems.Add(volatileItem)); var context = new AcquireContext<TKey>(key, volatileItem => entry.Tokens.Add(volatileItem));
entry.Result = acquire(context); entry.Result = acquire(context);
_entries.Add(key, entry); _entries[key] = entry;
} }
return entry.Result; return entry.Result;
} }
private class CacheEntry { private class CacheEntry {
public TResult Result { get; set; } public TResult Result { get; set; }
public IList<IVolatileSignal> VolatileItems { get; set; } public IList<IVolatileToken> Tokens { get; set; }
} }
} }

View File

@@ -0,0 +1,29 @@
using System;
using System.Linq;
using Autofac;
namespace Orchard.Caching {
public class CacheModule : Module {
protected override void Load(ContainerBuilder builder) {
builder.RegisterType<DefaultCacheManager>()
.As<ICacheManager>()
.InstancePerDependency();
}
protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) {
var needsCacheManager = registration.Activator.LimitType
.GetConstructors()
.Any(x => x.GetParameters()
.Any(xx => xx.ParameterType == typeof(ICacheManager)));
if (needsCacheManager) {
registration.Preparing += (sender, e) => {
var parameter = new TypedParameter(
typeof(ICacheManager),
e.Context.Resolve<ICacheManager>(new TypedParameter(typeof(Type), registration.Activator.LimitType)));
e.Parameters = e.Parameters.Concat(new[] { parameter });
};
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Castle.Core;
namespace Orchard.Caching {
public class DefaultCacheHolder : ICacheHolder {
private readonly IDictionary<CacheKey, object> _caches = new Dictionary<CacheKey, object>();
class CacheKey : Pair<Type, Pair<Type, Type>> {
public CacheKey(Type component, Type key, Type result)
: base(component, new Pair<Type, Type>(key, result)) {
}
}
public ICache<TKey, TResult> GetCache<TKey, TResult>(Type component) {
var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult));
lock (_caches) {
object value;
if (!_caches.TryGetValue(cacheKey, out value)) {
value = new Cache<TKey, TResult>();
_caches[cacheKey] = value;
}
return (ICache<TKey, TResult>)value;
}
}
}
}

View File

@@ -1,32 +1,21 @@
using System; using System;
using System.Collections.Generic;
using Castle.Core;
namespace Orchard.Caching { namespace Orchard.Caching {
public class DefaultCacheManager : ICacheManager { public class DefaultCacheManager : ICacheManager {
private readonly Dictionary<Pair<Type, Type>, object> _caches; private readonly Type _component;
private readonly ICacheHolder _cacheHolder;
public DefaultCacheManager() { public DefaultCacheManager(Type component, ICacheHolder cacheHolder) {
_caches = new Dictionary<Pair<Type, Type>, object>(); _component = component;
_cacheHolder = cacheHolder;
} }
#region Implementation of ICacheManager public ICache<TKey, TResult> GetCache<TKey, TResult>() {
return _cacheHolder.GetCache<TKey, TResult>(_component);
}
public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) { public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
return GetCache<TKey, TResult>().Get(key, acquire); return GetCache<TKey, TResult>().Get(key, acquire);
} }
public ICache<TKey, TResult> GetCache<TKey, TResult>() {
var cacheKey = new Pair<Type, Type>(typeof(TKey), typeof(TResult));
object value;
if (!_caches.TryGetValue(cacheKey, out value)) {
value = new Cache<TKey, TResult>();
_caches.Add(cacheKey, value);
}
return (ICache<TKey, TResult>)value;
}
#endregion
} }
} }

View File

@@ -0,0 +1,7 @@
using System;
namespace Orchard.Caching {
public interface ICacheHolder : ISingletonDependency {
ICache<TKey, TResult> GetCache<TKey, TResult>(Type component);
}
}

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace Orchard.Caching { namespace Orchard.Caching {
public interface ICacheManager : ISingletonDependency { public interface ICacheManager {
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire); TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire);
ICache<TKey, TResult> GetCache<TKey, TResult>(); ICache<TKey, TResult> GetCache<TKey, TResult>();
} }

View File

@@ -0,0 +1,4 @@
namespace Orchard.Caching {
public interface IVolatileProvider : ISingletonDependency {
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Caching {
public interface IVolatileToken {
bool IsCurrent { get; }
}
}

View File

@@ -1,5 +0,0 @@
namespace Orchard.Caching.Providers {
public interface IVolatileProvider : IDependency {
void Enlist(IVolatileSink sink);
}
}

View File

@@ -1,5 +0,0 @@
namespace Orchard.Caching.Providers {
public interface IVolatileSignal {
IVolatileProvider Provider { get; set; }
}
}

View File

@@ -1,5 +0,0 @@
namespace Orchard.Caching.Providers {
public interface IVolatileSink {
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Runtime.Serialization;
namespace Orchard.Caching {
public class Weak<T> : WeakReference {
public Weak(T target)
: base(target) {
}
public Weak(T target, bool trackResurrection)
: base(target, trackResurrection) {
}
protected Weak(SerializationInfo info, StreamingContext context)
: base(info, context) {
}
public new T Target {
get { return (T)base.Target; }
set { base.Target = value; }
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Orchard.Environment.Extensions.Folders {
private readonly ICacheManager _cacheManager; private readonly ICacheManager _cacheManager;
private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathProvider _virtualPathProvider;
public ExtensionFolders( protected ExtensionFolders(
IEnumerable<string> paths, IEnumerable<string> paths,
string manifestName, string manifestName,
bool manifestIsOptional, bool manifestIsOptional,
@@ -39,28 +39,39 @@ namespace Orchard.Environment.Extensions.Folders {
} }
public IEnumerable<ExtensionDescriptor> AvailableExtensions() { public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
var virtualPaths = _cacheManager.Get("", ctx => {
var x = 5;
return new[] { "hello" };
});
var list = new List<ExtensionDescriptor>(); var list = new List<ExtensionDescriptor>();
foreach (var virtualPath in virtualPaths) { foreach (var locationPath in _paths) {
list.Add(GetExtensionDescriptor(virtualPath)); var subfolderPaths = _cacheManager.Get(locationPath, ctx => {
ctx.Monitor(_virtualPathProvider.WhenPathChanges(ctx.Key));
return _virtualPathProvider.GetSubfolderPaths(ctx.Key);
});
foreach (var subfolderPath in subfolderPaths) {
var extensionName = Path.GetFileName(subfolderPath.TrimEnd('/', '\\'));
var manifestPath = Path.Combine(subfolderPath, _manifestName);
var descriptor = GetExtensionDescriptor(locationPath, extensionName, manifestPath);
if (descriptor != null)
list.Add(descriptor);
}
} }
return list; return list;
} }
ExtensionDescriptor GetExtensionDescriptor(string virtualPath) { ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionName, string manifestPath) {
return _cacheManager.Get(virtualPath, context => { return _cacheManager.Get(manifestPath, context => {
context.IsInvalid(_virtualPathProvider.WhenPathChanges(virtualPath)); context.Monitor(_virtualPathProvider.WhenPathChanges(manifestPath));
var text = _virtualPathProvider.ReadAllText(virtualPath); var manifestText = _virtualPathProvider.ReadAllText(manifestPath);
if (manifestText == null) {
if (_manifestIsOptional) {
manifestText = string.Format("name: {0}", extensionName);
}
else {
return null;
}
}
var parseResult = ParseManifest(text); return GetDescriptorForExtension(locationPath, extensionName, ParseManifest(manifestText));
return GetDescriptorForExtension(parseResult.Name, parseResult);
}); });
} }
@@ -78,52 +89,29 @@ namespace Orchard.Environment.Extensions.Folders {
} }
public ParseResult ParseManifest(string name) { public ParseResult ParseManifest(string name) {
foreach (var path in _paths) { bool success;
if (!Directory.Exists(PathHelpers.GetPhysicalPath(path))) var yamlStream = new YamlParser().ParseYamlStream(new TextInput(name), out success);
continue; if (yamlStream == null || !success) {
return null;
var extensionDirectoryPath = Path.Combine(PathHelpers.GetPhysicalPath(path), name);
if (!Directory.Exists(PathHelpers.GetPhysicalPath(extensionDirectoryPath)))
continue;
var extensionManifestPath = Path.Combine(extensionDirectoryPath, _manifestName);
if (File.Exists(extensionManifestPath)) {
var yamlStream = YamlParser.Load(extensionManifestPath);
return new ParseResult {
Location = path,
Name = name,
YamlDocument = yamlStream.Documents.Single()
};
}
if (_manifestIsOptional) {
var yamlInput = new TextInput(string.Format("name: {0}", name));
var parser = new YamlParser();
bool success;
var yamlStream = parser.ParseYamlStream(yamlInput, out success);
return new ParseResult {
Location = path,
Name = name,
YamlDocument = yamlStream.Documents.Single()
};
}
} }
return null; return new ParseResult {
Name = name,
YamlDocument = yamlStream.Documents.Single()
};
} }
private ExtensionDescriptor GetDescriptorForExtension(string name, ParseResult parseResult) { private ExtensionDescriptor GetDescriptorForExtension(string locationPath, string extensionName, ParseResult parseResult) {
var mapping = (Mapping)parseResult.YamlDocument.Root; var mapping = (Mapping)parseResult.YamlDocument.Root;
var fields = mapping.Entities var fields = mapping.Entities
.Where(x => x.Key is Scalar) .Where(x => x.Key is Scalar)
.ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value);
var extensionDescriptor = new ExtensionDescriptor { var extensionDescriptor = new ExtensionDescriptor {
Location = parseResult.Location, Location = locationPath,
Name = name, Name = extensionName,
ExtensionType = _extensionType, ExtensionType = _extensionType,
DisplayName = GetValue(fields, "name") ?? name, DisplayName = GetValue(fields, "name") ?? extensionName,
Description = GetValue(fields, "description"), Description = GetValue(fields, "description"),
Version = GetValue(fields, "version"), Version = GetValue(fields, "version"),
OrchardVersion = GetValue(fields, "orchardversion"), OrchardVersion = GetValue(fields, "orchardversion"),

View File

@@ -1,11 +1,9 @@
using System; using System.Collections.Generic;
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.Environment.FileSystems;
namespace Orchard.Environment.Configuration { namespace Orchard.Environment.FileSystems {
public class AppDataFolder : IAppDataFolder { public class AppDataFolder : IAppDataFolder {
protected string _basePath; protected string _basePath;
@@ -42,9 +40,9 @@ namespace Orchard.Environment.Configuration {
var files = Directory.GetFiles(directoryPath); var files = Directory.GetFiles(directoryPath);
return files.Select(file => { return files.Select(file => {
var fileName = Path.GetFileName(file); var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName); return Path.Combine(path, fileName);
}); });
} }
public IEnumerable<string> ListDirectories(string path) { public IEnumerable<string> ListDirectories(string path) {
@@ -55,9 +53,9 @@ namespace Orchard.Environment.Configuration {
var files = Directory.GetDirectories(directoryPath); var files = Directory.GetDirectories(directoryPath);
return files.Select(file => { return files.Select(file => {
var fileName = Path.GetFileName(file); var fileName = Path.GetFileName(file);
return Path.Combine(path, fileName); return Path.Combine(path, fileName);
}); });
} }
public string CreateDirectory(string path) { public string CreateDirectory(string path) {
@@ -74,5 +72,6 @@ namespace Orchard.Environment.Configuration {
public string MapPath(string path) { public string MapPath(string path) {
return Path.Combine(_basePath, path); return Path.Combine(_basePath, path);
} }
} }
} }

View File

@@ -1,54 +1,127 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Web.Caching;
using System.Web.Hosting; using System.Web.Hosting;
using Orchard.Caching.Providers; using Orchard.Caching;
using Orchard.Services; using Orchard.Services;
namespace Orchard.Environment.FileSystems { namespace Orchard.Environment.FileSystems {
public class DefaultVirtualPathProvider : IVirtualPathProvider { public class DefaultVirtualPathProvider : IVirtualPathProvider {
private readonly IClock _clock; private readonly IClock _clock;
private readonly IList<IVolatileSink> _sinks = new List<IVolatileSink>(); private readonly Thunk _thunk;
//private string _cachePrefix = Guid.NewGuid().ToString("n"); private readonly string _prefix = Guid.NewGuid().ToString("n");
private readonly IDictionary<string, Weak<Token>> _tokens = new Dictionary<string, Weak<Token>>();
public DefaultVirtualPathProvider(IClock clock) { public DefaultVirtualPathProvider(IClock clock) {
_clock = clock; _clock = clock;
_thunk = new Thunk(this);
}
public IEnumerable<string> GetSubfolderPaths(string virtualPath) {
if (!HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath))
return Enumerable.Empty<string>();
return HostingEnvironment.VirtualPathProvider
.GetDirectory(virtualPath)
.Directories.OfType<VirtualDirectory>()
.Select(d => d.VirtualPath)
.ToArray();
} }
public string ReadAllText(string virtualPath) { public string ReadAllText(string virtualPath) {
if (!HostingEnvironment.VirtualPathProvider.FileExists(virtualPath))
return null;
using (var stream = VirtualPathProvider.OpenFile(virtualPath)) { using (var stream = VirtualPathProvider.OpenFile(virtualPath)) {
using (var reader = new StreamReader(stream)) { using (var reader = new StreamReader(stream)) {
return reader.ReadToEnd(); return reader.ReadToEnd();
} }
} }
//var cd = HostingEnvironment.VirtualPathProvider.. .GetCacheDependency(virtualPath, null, _clock.UtcNow);
//HostingEnvironment.Cache.Add(
// _cachePrefix + virtualPath,
// virtualPath,
// cd,
// NoAbsoluteExpiration,
// );
} }
public IVolatileSignal WhenPathChanges(string virtualPath) { public IVolatileToken WhenPathChanges(string virtualPath) {
var token = BindToken(virtualPath);
return new VirtualPathSignal(this, virtualPath); BindSignal(virtualPath);
return token;
} }
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;
}
class VirtualPathSignal : IVolatileSignal { var token = weak.Target;
private readonly string _virtualPath; if (token == null) {
token = new Token(virtualPath);
weak.Target = token;
}
public VirtualPathSignal(DefaultVirtualPathProvider provider, string virtualPath) { return token;
_virtualPath = virtualPath; }
Provider = provider; }
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) {
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath,
new[] { virtualPath },
_clock.UtcNow);
HostingEnvironment.Cache.Add(
_prefix + virtualPath,
virtualPath,
cacheDependency,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
_thunk.Signal);
}
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<DefaultVirtualPathProvider> _weak;
public Thunk(DefaultVirtualPathProvider provider) {
_weak = new Weak<DefaultVirtualPathProvider>(provider);
} }
public IVolatileProvider Provider { get; set; } public void Signal(string key, object value, CacheItemRemovedReason reason) {
} var provider = _weak.Target;
if (provider != null)
public void Enlist(IVolatileSink sink) { provider.Signal(key, value, reason);
_sinks.Add(sink); }
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.Caching;
namespace Orchard.Environment.FileSystems { namespace Orchard.Environment.FileSystems {
/// <summary> /// <summary>
@@ -6,7 +7,7 @@ namespace Orchard.Environment.FileSystems {
/// Expected to work on physical filesystem, but decouples core /// Expected to work on physical filesystem, but decouples core
/// system from web hosting apis /// system from web hosting apis
/// </summary> /// </summary>
public interface IAppDataFolder { public interface IAppDataFolder : IVolatileProvider {
IEnumerable<string> ListFiles(string path); IEnumerable<string> ListFiles(string path);
IEnumerable<string> ListDirectories(string path); IEnumerable<string> ListDirectories(string path);

View File

@@ -1,9 +1,11 @@
using Orchard.Caching.Providers; using System.Collections.Generic;
using Orchard.Caching;
namespace Orchard.Environment.FileSystems { namespace Orchard.Environment.FileSystems {
public interface IVirtualPathProvider : IVolatileProvider { public interface IVirtualPathProvider : IVolatileProvider {
IVolatileSignal WhenPathChanges(string path); IEnumerable<string> GetSubfolderPaths(string virtualPath);
string ReadAllText(string virtualPath); string ReadAllText(string virtualPath);
IVolatileToken WhenPathChanges(string path);
} }
} }

View File

@@ -5,6 +5,7 @@ using System.Web.Hosting;
using Autofac; using Autofac;
using Autofac.Configuration; using Autofac.Configuration;
using Autofac.Integration.Web; using Autofac.Integration.Web;
using Orchard.Caching;
using Orchard.Environment.AutofacUtil; using Orchard.Environment.AutofacUtil;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
@@ -15,6 +16,7 @@ using Orchard.Environment.ShellBuilders;
using Orchard.Environment.Topology; using Orchard.Environment.Topology;
using Orchard.Events; using Orchard.Events;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Services;
namespace Orchard.Environment { namespace Orchard.Environment {
public static class OrchardStarter { public static class OrchardStarter {
@@ -22,10 +24,15 @@ namespace Orchard.Environment {
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
builder.RegisterModule(new LoggingModule()); builder.RegisterModule(new LoggingModule());
builder.RegisterModule(new EventsModule()); builder.RegisterModule(new EventsModule());
builder.RegisterModule(new CacheModule());
// a single default host implementation is needed for bootstrapping a web app domain // a single default host implementation is needed for bootstrapping a web app domain
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance(); builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
builder.RegisterType<AppDataFolder>().As<IAppDataFolder>().SingleInstance(); builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
RegisterVolatileProvider<Clock, IClock>(builder);
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance(); builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
{ {
@@ -92,6 +99,13 @@ namespace Orchard.Environment {
return builder.Build(); return builder.Build();
} }
private static void RegisterVolatileProvider<TRegister, TService>(ContainerBuilder builder) where TService : IVolatileProvider {
builder.RegisterType<TRegister>()
.As<TService>()
.As<IVolatileProvider>()
.SingleInstance();
}
public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) { public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
var container = CreateHostContainer(registrations); var container = CreateHostContainer(registrations);
return container.Resolve<IOrchardHost>(); return container.Resolve<IOrchardHost>();

View File

@@ -5,6 +5,7 @@
public interface ISingletonDependency : IDependency { public interface ISingletonDependency : IDependency {
} }
public interface ITransientDependency : IDependency { public interface ITransientDependency : IDependency {
} }

View File

@@ -145,15 +145,18 @@
<ItemGroup> <ItemGroup>
<Compile Include="Caching\AcquireContext.cs" /> <Compile Include="Caching\AcquireContext.cs" />
<Compile Include="Caching\Cache.cs" /> <Compile Include="Caching\Cache.cs" />
<Compile Include="Caching\CacheModule.cs" />
<Compile Include="Caching\DefaultCacheHolder.cs" />
<Compile Include="Caching\DefaultCacheManager.cs" /> <Compile Include="Caching\DefaultCacheManager.cs" />
<Compile Include="Caching\Providers\IVolatileSink.cs" /> <Compile Include="Caching\ICacheHolder.cs" />
<Compile Include="Caching\Weak.cs" />
<Compile Include="Environment\FileSystems\DefaultVirtualPathProvider.cs" /> <Compile Include="Environment\FileSystems\DefaultVirtualPathProvider.cs" />
<Compile Include="Environment\FileSystems\IAppDataFolder.cs" /> <Compile Include="Environment\FileSystems\IAppDataFolder.cs" />
<Compile Include="Environment\FileSystems\IVirtualPathProvider.cs" /> <Compile Include="Environment\FileSystems\IVirtualPathProvider.cs" />
<Compile Include="Caching\ICache.cs" /> <Compile Include="Caching\ICache.cs" />
<Compile Include="Caching\ICacheManager.cs" /> <Compile Include="Caching\ICacheManager.cs" />
<Compile Include="Caching\Providers\IVolatileSignal.cs" /> <Compile Include="Caching\IVolatileToken.cs" />
<Compile Include="Caching\Providers\IVolatileProvider.cs" /> <Compile Include="Caching\IVolatileProvider.cs" />
<Compile Include="Commands\CommandParameters.cs" /> <Compile Include="Commands\CommandParameters.cs" />
<Compile Include="Commands\CommandDescriptor.cs" /> <Compile Include="Commands\CommandDescriptor.cs" />
<Compile Include="Commands\CommandHandlerDescriptor.cs" /> <Compile Include="Commands\CommandHandlerDescriptor.cs" />

View File

@@ -1,13 +1,15 @@
using System; using System;
using Orchard.Caching;
namespace Orchard.Services { namespace Orchard.Services {
public interface IClock : IDependency { public interface IClock : IVolatileProvider {
DateTime UtcNow { get; } DateTime UtcNow { get; }
} }
public class Clock : IClock { public class Clock : IClock {
public DateTime UtcNow { public DateTime UtcNow {
get { return DateTime.Now; } get { return DateTime.UtcNow; }
} }
} }
} }