From d46d94f1eef33c04fec95c96b7a07a2208285a43 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Sun, 16 May 2010 12:59:04 -0700 Subject: [PATCH] Building out caching Passing along signal of VPP file monitoring Expiring results as appropriate in AvailableExtensions --HG-- branch : dev --- src/Orchard.Tests/Caching/CacheScopeTests.cs | 47 +++++++ src/Orchard.Tests/Caching/CacheTests.cs | 71 ++++++++++- .../Configuration/AppDataFolderTests.cs | 1 - .../DefaultTenantManagerTests.cs | 1 + .../DefaultShellDescriptorCacheTests.cs | 1 - .../Orchard.Framework.Tests.csproj | 1 + src/Orchard.Tests/Stubs/StubClock.cs | 1 + src/Orchard.Web/Orchard.Web.csproj | 2 +- src/Orchard/Caching/AcquireContext.cs | 7 +- src/Orchard/Caching/Cache.cs | 12 +- src/Orchard/Caching/CacheModule.cs | 29 +++++ src/Orchard/Caching/DefaultCacheHolder.cs | 27 ++++ src/Orchard/Caching/DefaultCacheManager.cs | 27 ++-- src/Orchard/Caching/ICacheHolder.cs | 7 ++ src/Orchard/Caching/ICacheManager.cs | 2 +- src/Orchard/Caching/IVolatileProvider.cs | 4 + src/Orchard/Caching/IVolatileToken.cs | 5 + .../Caching/Providers/IVolatileProvider.cs | 5 - .../Caching/Providers/IVolatileSignal.cs | 5 - .../Caching/Providers/IVolatileSink.cs | 5 - src/Orchard/Caching/Weak.cs | 23 ++++ .../Extensions/Folders/ExtensionFolders.cs | 88 ++++++------- .../Environment/FileSystems/AppDataFolder.cs | 21 ++-- .../FileSystems/DefaultVirtualPathProvider.cs | 119 ++++++++++++++---- .../Environment/FileSystems/IAppDataFolder.cs | 3 +- .../FileSystems/IVirtualPathProvider.cs | 10 +- src/Orchard/Environment/OrchardStarter.cs | 16 ++- src/Orchard/IDependency.cs | 1 + src/Orchard/Orchard.Framework.csproj | 9 +- src/Orchard/Services/Clock.cs | 6 +- 30 files changed, 410 insertions(+), 146 deletions(-) create mode 100644 src/Orchard.Tests/Caching/CacheScopeTests.cs create mode 100644 src/Orchard/Caching/CacheModule.cs create mode 100644 src/Orchard/Caching/DefaultCacheHolder.cs create mode 100644 src/Orchard/Caching/ICacheHolder.cs create mode 100644 src/Orchard/Caching/IVolatileProvider.cs create mode 100644 src/Orchard/Caching/IVolatileToken.cs delete mode 100644 src/Orchard/Caching/Providers/IVolatileProvider.cs delete mode 100644 src/Orchard/Caching/Providers/IVolatileSignal.cs delete mode 100644 src/Orchard/Caching/Providers/IVolatileSink.cs create mode 100644 src/Orchard/Caching/Weak.cs diff --git a/src/Orchard.Tests/Caching/CacheScopeTests.cs b/src/Orchard.Tests/Caching/CacheScopeTests.cs new file mode 100644 index 000000000..56342e914 --- /dev/null +++ b/src/Orchard.Tests/Caching/CacheScopeTests.cs @@ -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().InstancePerDependency(); + }); + + } + + public class Alpha { + public ICacheManager CacheManager { get; set; } + + public Alpha(ICacheManager cacheManager) { + CacheManager = cacheManager; + } + } + + [Test] + public void ComponentsAtHostLevelHaveAccessToCache() { + var alpha = _hostContainer.Resolve(); + Assert.That(alpha.CacheManager, Is.Not.Null); + } + + [Test] + public void HostLevelHasAccessToGlobalVolatileProviders() { + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + } + + } +} diff --git a/src/Orchard.Tests/Caching/CacheTests.cs b/src/Orchard.Tests/Caching/CacheTests.cs index 12e91490d..09bdf353a 100644 --- a/src/Orchard.Tests/Caching/CacheTests.cs +++ b/src/Orchard.Tests/Caching/CacheTests.cs @@ -1,4 +1,5 @@ -using Autofac; +using System; +using Autofac; using NUnit.Framework; using Orchard.Caching; @@ -11,9 +12,12 @@ namespace Orchard.Tests.Caching { [SetUp] public void Init() { var builder = new ContainerBuilder(); + builder.RegisterModule(new CacheModule()); + builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); _container = builder.Build(); - _cacheManager = _container.Resolve(); + _cacheManager = _container.Resolve(new TypedParameter(typeof(Type), GetType())); } [Test] @@ -28,5 +32,66 @@ namespace Orchard.Tests.Caching { var result = _cacheManager.Get("testItem", ctx => ""); Assert.That(result, Is.EqualTo("testResult")); } + + [Test] + public void CacheModuleProvidesTypeSpecificManager() { + var scope = _container.BeginLifetimeScope(builder => { + builder.RegisterModule(new CacheModule()); + builder.RegisterType(); + builder.RegisterType(); + }); + + var c1 = scope.Resolve(); + var c2 = scope.Resolve(); + 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(); + var c4 = scope.Resolve(); + 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(), + Is.SameAs(c3.CacheManager.GetCache())); + + Assert.That(c1.CacheManager, + Is.Not.SameAs(c2.CacheManager)); + + Assert.That(c1.CacheManager.GetCache(), + Is.Not.SameAs(c2.CacheManager.GetCache())); + } + + 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; + } + } } -} +} \ No newline at end of file diff --git a/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs index 745761ae6..cffe3dcab 100644 --- a/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs @@ -1,7 +1,6 @@ using System.IO; using System.Linq; using NUnit.Framework; -using Orchard.Environment.Configuration; using Orchard.Environment.FileSystems; namespace Orchard.Tests.Environment.Configuration { diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs index 48e77c1fa..39309fce4 100644 --- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs @@ -3,6 +3,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Orchard.Environment.Configuration; +using Orchard.Environment.FileSystems; namespace Orchard.Tests.Environment.Configuration { [TestFixture] diff --git a/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs b/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs index 7f24183fe..4d1d7e4aa 100644 --- a/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs +++ b/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs @@ -3,7 +3,6 @@ using System.Runtime.Serialization; using System.Xml; using Autofac; using NUnit.Framework; -using Orchard.Environment.Configuration; using Orchard.Environment.FileSystems; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index cf8427eeb..f36cac6bf 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -125,6 +125,7 @@ + diff --git a/src/Orchard.Tests/Stubs/StubClock.cs b/src/Orchard.Tests/Stubs/StubClock.cs index e5c60bb5a..55a247d47 100644 --- a/src/Orchard.Tests/Stubs/StubClock.cs +++ b/src/Orchard.Tests/Stubs/StubClock.cs @@ -16,5 +16,6 @@ namespace Orchard.Tests.Stubs { public DateTime FutureMoment(TimeSpan span) { return UtcNow.Add(span); } + } } diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index 30c78ce44..5f74ca391 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -393,7 +393,7 @@ False False - 80 + 30320 / diff --git a/src/Orchard/Caching/AcquireContext.cs b/src/Orchard/Caching/AcquireContext.cs index 5c6cc5404..a2d54e7de 100644 --- a/src/Orchard/Caching/AcquireContext.cs +++ b/src/Orchard/Caching/AcquireContext.cs @@ -1,14 +1,13 @@ using System; -using Orchard.Caching.Providers; namespace Orchard.Caching { public class AcquireContext { - public AcquireContext(TKey key, Action monitor) { + public AcquireContext(TKey key, Action monitor) { Key = key; - IsInvalid = monitor; + Monitor = monitor; } public TKey Key { get; private set; } - public Action IsInvalid { get; private set; } + public Action Monitor { get; private set; } } } diff --git a/src/Orchard/Caching/Cache.cs b/src/Orchard/Caching/Cache.cs index ea0bfcf32..4f0bb402a 100644 --- a/src/Orchard/Caching/Cache.cs +++ b/src/Orchard/Caching/Cache.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Orchard.Caching.Providers; +using System.Linq; namespace Orchard.Caching { public class Cache : ICache { @@ -12,19 +12,19 @@ namespace Orchard.Caching { public TResult Get(TKey key, Func, TResult> acquire) { CacheEntry entry; - if (!_entries.TryGetValue(key, out entry)) { - entry = new CacheEntry { VolatileItems = new List() }; + if (!_entries.TryGetValue(key, out entry) || entry.Tokens.Any(t => !t.IsCurrent)) { + entry = new CacheEntry { Tokens = new List() }; - var context = new AcquireContext(key, volatileItem => entry.VolatileItems.Add(volatileItem)); + var context = new AcquireContext(key, volatileItem => entry.Tokens.Add(volatileItem)); entry.Result = acquire(context); - _entries.Add(key, entry); + _entries[key] = entry; } return entry.Result; } private class CacheEntry { public TResult Result { get; set; } - public IList VolatileItems { get; set; } + public IList Tokens { get; set; } } } diff --git a/src/Orchard/Caching/CacheModule.cs b/src/Orchard/Caching/CacheModule.cs new file mode 100644 index 000000000..bba898374 --- /dev/null +++ b/src/Orchard/Caching/CacheModule.cs @@ -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() + .As() + .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(new TypedParameter(typeof(Type), registration.Activator.LimitType))); + e.Parameters = e.Parameters.Concat(new[] { parameter }); + }; + } + } + } +} diff --git a/src/Orchard/Caching/DefaultCacheHolder.cs b/src/Orchard/Caching/DefaultCacheHolder.cs new file mode 100644 index 000000000..57b0ec140 --- /dev/null +++ b/src/Orchard/Caching/DefaultCacheHolder.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Castle.Core; + +namespace Orchard.Caching { + public class DefaultCacheHolder : ICacheHolder { + private readonly IDictionary _caches = new Dictionary(); + + class CacheKey : Pair> { + public CacheKey(Type component, Type key, Type result) + : base(component, new Pair(key, result)) { + } + } + + public ICache GetCache(Type component) { + var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult)); + lock (_caches) { + object value; + if (!_caches.TryGetValue(cacheKey, out value)) { + value = new Cache(); + _caches[cacheKey] = value; + } + return (ICache)value; + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/DefaultCacheManager.cs b/src/Orchard/Caching/DefaultCacheManager.cs index 8bfc60c89..8fdb2eebc 100644 --- a/src/Orchard/Caching/DefaultCacheManager.cs +++ b/src/Orchard/Caching/DefaultCacheManager.cs @@ -1,32 +1,21 @@ using System; -using System.Collections.Generic; -using Castle.Core; namespace Orchard.Caching { public class DefaultCacheManager : ICacheManager { - private readonly Dictionary, object> _caches; + private readonly Type _component; + private readonly ICacheHolder _cacheHolder; - public DefaultCacheManager() { - _caches = new Dictionary, object>(); + public DefaultCacheManager(Type component, ICacheHolder cacheHolder) { + _component = component; + _cacheHolder = cacheHolder; } - #region Implementation of ICacheManager + public ICache GetCache() { + return _cacheHolder.GetCache(_component); + } public TResult Get(TKey key, Func, TResult> acquire) { return GetCache().Get(key, acquire); } - - public ICache GetCache() { - var cacheKey = new Pair(typeof(TKey), typeof(TResult)); - object value; - if (!_caches.TryGetValue(cacheKey, out value)) { - value = new Cache(); - _caches.Add(cacheKey, value); - } - - return (ICache)value; - } - - #endregion } } diff --git a/src/Orchard/Caching/ICacheHolder.cs b/src/Orchard/Caching/ICacheHolder.cs new file mode 100644 index 000000000..730bd1888 --- /dev/null +++ b/src/Orchard/Caching/ICacheHolder.cs @@ -0,0 +1,7 @@ +using System; + +namespace Orchard.Caching { + public interface ICacheHolder : ISingletonDependency { + ICache GetCache(Type component); + } +} diff --git a/src/Orchard/Caching/ICacheManager.cs b/src/Orchard/Caching/ICacheManager.cs index 5e39a46c3..c54902eb9 100644 --- a/src/Orchard/Caching/ICacheManager.cs +++ b/src/Orchard/Caching/ICacheManager.cs @@ -1,7 +1,7 @@ using System; namespace Orchard.Caching { - public interface ICacheManager : ISingletonDependency { + public interface ICacheManager { TResult Get(TKey key, Func, TResult> acquire); ICache GetCache(); } diff --git a/src/Orchard/Caching/IVolatileProvider.cs b/src/Orchard/Caching/IVolatileProvider.cs new file mode 100644 index 000000000..8f6fffc45 --- /dev/null +++ b/src/Orchard/Caching/IVolatileProvider.cs @@ -0,0 +1,4 @@ +namespace Orchard.Caching { + public interface IVolatileProvider : ISingletonDependency { + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/IVolatileToken.cs b/src/Orchard/Caching/IVolatileToken.cs new file mode 100644 index 000000000..7c0e17c60 --- /dev/null +++ b/src/Orchard/Caching/IVolatileToken.cs @@ -0,0 +1,5 @@ +namespace Orchard.Caching { + public interface IVolatileToken { + bool IsCurrent { get; } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/Providers/IVolatileProvider.cs b/src/Orchard/Caching/Providers/IVolatileProvider.cs deleted file mode 100644 index d2c79deeb..000000000 --- a/src/Orchard/Caching/Providers/IVolatileProvider.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Orchard.Caching.Providers { - public interface IVolatileProvider : IDependency { - void Enlist(IVolatileSink sink); - } -} diff --git a/src/Orchard/Caching/Providers/IVolatileSignal.cs b/src/Orchard/Caching/Providers/IVolatileSignal.cs deleted file mode 100644 index 68ded43ea..000000000 --- a/src/Orchard/Caching/Providers/IVolatileSignal.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Orchard.Caching.Providers { - public interface IVolatileSignal { - IVolatileProvider Provider { get; set; } - } -} \ No newline at end of file diff --git a/src/Orchard/Caching/Providers/IVolatileSink.cs b/src/Orchard/Caching/Providers/IVolatileSink.cs deleted file mode 100644 index 8104fe4d9..000000000 --- a/src/Orchard/Caching/Providers/IVolatileSink.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Orchard.Caching.Providers { - public interface IVolatileSink { - - } -} \ No newline at end of file diff --git a/src/Orchard/Caching/Weak.cs b/src/Orchard/Caching/Weak.cs new file mode 100644 index 000000000..ce8ba0c61 --- /dev/null +++ b/src/Orchard/Caching/Weak.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.Serialization; + +namespace Orchard.Caching { + public class Weak : 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; } + } + } +} diff --git a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs index ea9a4ff15..f59992c25 100644 --- a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs @@ -24,7 +24,7 @@ namespace Orchard.Environment.Extensions.Folders { private readonly ICacheManager _cacheManager; private readonly IVirtualPathProvider _virtualPathProvider; - public ExtensionFolders( + protected ExtensionFolders( IEnumerable paths, string manifestName, bool manifestIsOptional, @@ -39,28 +39,39 @@ namespace Orchard.Environment.Extensions.Folders { } public IEnumerable AvailableExtensions() { - var virtualPaths = _cacheManager.Get("", ctx => { - var x = 5; - return new[] { "hello" }; - }); - - var list = new List(); - foreach (var virtualPath in virtualPaths) { - list.Add(GetExtensionDescriptor(virtualPath)); + foreach (var locationPath in _paths) { + 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; } - ExtensionDescriptor GetExtensionDescriptor(string virtualPath) { - return _cacheManager.Get(virtualPath, context => { + ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionName, string manifestPath) { + 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(parseResult.Name, parseResult); + return GetDescriptorForExtension(locationPath, extensionName, ParseManifest(manifestText)); }); } @@ -78,52 +89,29 @@ namespace Orchard.Environment.Extensions.Folders { } public ParseResult ParseManifest(string name) { - foreach (var path in _paths) { - if (!Directory.Exists(PathHelpers.GetPhysicalPath(path))) - continue; - - 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() - }; - } + bool success; + var yamlStream = new YamlParser().ParseYamlStream(new TextInput(name), out success); + if (yamlStream == null || !success) { + return null; } - 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 fields = mapping.Entities .Where(x => x.Key is Scalar) .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); var extensionDescriptor = new ExtensionDescriptor { - Location = parseResult.Location, - Name = name, + Location = locationPath, + Name = extensionName, ExtensionType = _extensionType, - DisplayName = GetValue(fields, "name") ?? name, + DisplayName = GetValue(fields, "name") ?? extensionName, Description = GetValue(fields, "description"), Version = GetValue(fields, "version"), OrchardVersion = GetValue(fields, "orchardversion"), diff --git a/src/Orchard/Environment/FileSystems/AppDataFolder.cs b/src/Orchard/Environment/FileSystems/AppDataFolder.cs index a28c71906..c7a012bf9 100644 --- a/src/Orchard/Environment/FileSystems/AppDataFolder.cs +++ b/src/Orchard/Environment/FileSystems/AppDataFolder.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Web.Hosting; -using Orchard.Environment.FileSystems; -namespace Orchard.Environment.Configuration { +namespace Orchard.Environment.FileSystems { public class AppDataFolder : IAppDataFolder { protected string _basePath; @@ -42,9 +40,9 @@ namespace Orchard.Environment.Configuration { var files = Directory.GetFiles(directoryPath); return files.Select(file => { - var fileName = Path.GetFileName(file); - return Path.Combine(path, fileName); - }); + var fileName = Path.GetFileName(file); + return Path.Combine(path, fileName); + }); } public IEnumerable ListDirectories(string path) { @@ -55,9 +53,9 @@ namespace Orchard.Environment.Configuration { var files = Directory.GetDirectories(directoryPath); return files.Select(file => { - var fileName = Path.GetFileName(file); - return Path.Combine(path, fileName); - }); + var fileName = Path.GetFileName(file); + return Path.Combine(path, fileName); + }); } public string CreateDirectory(string path) { @@ -74,5 +72,6 @@ namespace Orchard.Environment.Configuration { public string MapPath(string path) { return Path.Combine(_basePath, path); } + } -} +} \ No newline at end of file diff --git a/src/Orchard/Environment/FileSystems/DefaultVirtualPathProvider.cs b/src/Orchard/Environment/FileSystems/DefaultVirtualPathProvider.cs index 733c335e6..8ca8c3f00 100644 --- a/src/Orchard/Environment/FileSystems/DefaultVirtualPathProvider.cs +++ b/src/Orchard/Environment/FileSystems/DefaultVirtualPathProvider.cs @@ -1,54 +1,127 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Web.Caching; using System.Web.Hosting; -using Orchard.Caching.Providers; +using Orchard.Caching; using Orchard.Services; namespace Orchard.Environment.FileSystems { public class DefaultVirtualPathProvider : IVirtualPathProvider { private readonly IClock _clock; - private readonly IList _sinks = new List(); - //private string _cachePrefix = Guid.NewGuid().ToString("n"); + private readonly Thunk _thunk; + private readonly string _prefix = Guid.NewGuid().ToString("n"); + private readonly IDictionary> _tokens = new Dictionary>(); + public DefaultVirtualPathProvider(IClock clock) { _clock = clock; + _thunk = new Thunk(this); + } + + public IEnumerable GetSubfolderPaths(string virtualPath) { + if (!HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath)) + return Enumerable.Empty(); + + return HostingEnvironment.VirtualPathProvider + .GetDirectory(virtualPath) + .Directories.OfType() + .Select(d => d.VirtualPath) + .ToArray(); } public string ReadAllText(string virtualPath) { + if (!HostingEnvironment.VirtualPathProvider.FileExists(virtualPath)) + return null; + using (var stream = VirtualPathProvider.OpenFile(virtualPath)) { using (var reader = new StreamReader(stream)) { 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) { - - return new VirtualPathSignal(this, virtualPath); + public IVolatileToken WhenPathChanges(string virtualPath) { + var token = BindToken(virtualPath); + BindSignal(virtualPath); + return token; } + private Token BindToken(string virtualPath) { + lock (_tokens) { + Weak weak; + if (!_tokens.TryGetValue(virtualPath, out weak)) { + weak = new Weak(new Token(virtualPath)); + _tokens[virtualPath] = weak; + } - class VirtualPathSignal : IVolatileSignal { - private readonly string _virtualPath; + var token = weak.Target; + if (token == null) { + token = new Token(virtualPath); + weak.Target = token; + } - public VirtualPathSignal(DefaultVirtualPathProvider provider, string virtualPath) { - _virtualPath = virtualPath; - Provider = provider; + return token; + } + } + + private Token DetachToken(string virtualPath) { + lock (_tokens) { + Weak 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 _weak; + + public Thunk(DefaultVirtualPathProvider provider) { + _weak = new Weak(provider); } - public IVolatileProvider Provider { get; set; } - } - - public void Enlist(IVolatileSink sink) { - _sinks.Add(sink); + public void Signal(string key, object value, CacheItemRemovedReason reason) { + var provider = _weak.Target; + if (provider != null) + provider.Signal(key, value, reason); + } } } } \ No newline at end of file diff --git a/src/Orchard/Environment/FileSystems/IAppDataFolder.cs b/src/Orchard/Environment/FileSystems/IAppDataFolder.cs index 59b16bc58..369b455c1 100644 --- a/src/Orchard/Environment/FileSystems/IAppDataFolder.cs +++ b/src/Orchard/Environment/FileSystems/IAppDataFolder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Orchard.Caching; namespace Orchard.Environment.FileSystems { /// @@ -6,7 +7,7 @@ namespace Orchard.Environment.FileSystems { /// Expected to work on physical filesystem, but decouples core /// system from web hosting apis /// - public interface IAppDataFolder { + public interface IAppDataFolder : IVolatileProvider { IEnumerable ListFiles(string path); IEnumerable ListDirectories(string path); diff --git a/src/Orchard/Environment/FileSystems/IVirtualPathProvider.cs b/src/Orchard/Environment/FileSystems/IVirtualPathProvider.cs index 7efa1ed44..94278b83c 100644 --- a/src/Orchard/Environment/FileSystems/IVirtualPathProvider.cs +++ b/src/Orchard/Environment/FileSystems/IVirtualPathProvider.cs @@ -1,9 +1,11 @@ -using Orchard.Caching.Providers; +using System.Collections.Generic; +using Orchard.Caching; namespace Orchard.Environment.FileSystems { public interface IVirtualPathProvider : IVolatileProvider { - IVolatileSignal WhenPathChanges(string path); - + IEnumerable GetSubfolderPaths(string virtualPath); string ReadAllText(string virtualPath); + + IVolatileToken WhenPathChanges(string path); } -} \ No newline at end of file +} diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index 0734a66af..b33369422 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -5,6 +5,7 @@ using System.Web.Hosting; using Autofac; using Autofac.Configuration; using Autofac.Integration.Web; +using Orchard.Caching; using Orchard.Environment.AutofacUtil; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -15,6 +16,7 @@ using Orchard.Environment.ShellBuilders; using Orchard.Environment.Topology; using Orchard.Events; using Orchard.Logging; +using Orchard.Services; namespace Orchard.Environment { public static class OrchardStarter { @@ -22,10 +24,15 @@ namespace Orchard.Environment { var builder = new ContainerBuilder(); builder.RegisterModule(new LoggingModule()); builder.RegisterModule(new EventsModule()); + builder.RegisterModule(new CacheModule()); // a single default host implementation is needed for bootstrapping a web app domain builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); builder.RegisterType().As().As().SingleInstance(); { @@ -92,6 +99,13 @@ namespace Orchard.Environment { return builder.Build(); } + private static void RegisterVolatileProvider(ContainerBuilder builder) where TService : IVolatileProvider { + builder.RegisterType() + .As() + .As() + .SingleInstance(); + } + public static IOrchardHost CreateHost(Action registrations) { var container = CreateHostContainer(registrations); return container.Resolve(); diff --git a/src/Orchard/IDependency.cs b/src/Orchard/IDependency.cs index eb81e0a5c..ab8d283dd 100644 --- a/src/Orchard/IDependency.cs +++ b/src/Orchard/IDependency.cs @@ -5,6 +5,7 @@ public interface ISingletonDependency : IDependency { } + public interface ITransientDependency : IDependency { } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index e1d257527..1c36e467c 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -145,15 +145,18 @@ + + - + + - - + + diff --git a/src/Orchard/Services/Clock.cs b/src/Orchard/Services/Clock.cs index 038865060..6e1b5a38b 100644 --- a/src/Orchard/Services/Clock.cs +++ b/src/Orchard/Services/Clock.cs @@ -1,13 +1,15 @@ using System; +using Orchard.Caching; namespace Orchard.Services { - public interface IClock : IDependency { + public interface IClock : IVolatileProvider { DateTime UtcNow { get; } } public class Clock : IClock { public DateTime UtcNow { - get { return DateTime.Now; } + get { return DateTime.UtcNow; } } + } }