diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index b6a72f74cf..384478e3cc 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -41,7 +41,8 @@ namespace Umbraco.Core private readonly IServiceProvider _serviceProvider; private readonly IRuntimeCacheProvider _runtimeCache; private readonly ProfilingLogger _logger; - private readonly string _tempFolder; + private readonly Lazy _pluginListFilePath = new Lazy(GetPluginListFilePath); + private readonly Lazy _pluginHashFilePath = new Lazy(GetPluginHashFilePath); private readonly object _typesLock = new object(); private readonly Dictionary _types = new Dictionary(); @@ -66,15 +67,8 @@ namespace Umbraco.Core _serviceProvider = serviceProvider; _runtimeCache = runtimeCache; - _logger = logger; - - // the temp folder where the cache file lives - _tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); - if (Directory.Exists(_tempFolder) == false) - Directory.CreateDirectory(_tempFolder); - - var pluginListFile = GetPluginListFilePath(); - + _logger = logger; + if (detectChanges) { //first check if the cached hash is string.Empty, if it is then we need @@ -86,7 +80,8 @@ namespace Umbraco.Core // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - File.Delete(pluginListFile); + if (File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); WriteCachePluginsHash(); } @@ -96,7 +91,8 @@ namespace Umbraco.Core // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - File.Delete(pluginListFile); + if (File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); // always set to true if we're not detecting (generally only for testing) RequiresRescanning = true; @@ -188,12 +184,11 @@ namespace Umbraco.Core get { if (_cachedAssembliesHash != null) - return _cachedAssembliesHash; + return _cachedAssembliesHash; + + if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty; - var filePath = GetPluginHashFilePath(); - if (File.Exists(filePath) == false) return string.Empty; - - var hash = File.ReadAllText(filePath, Encoding.UTF8); + var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8); _cachedAssembliesHash = hash; return _cachedAssembliesHash; @@ -211,8 +206,8 @@ namespace Umbraco.Core if (_currentAssembliesHash != null) return _currentAssembliesHash; - _currentAssembliesHash = GetFileHash(new List> - { + _currentAssembliesHash = GetFileHash(new List> + { // the bin folder and everything in it new Tuple(new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), false), // the app code folder and everything in it @@ -220,8 +215,8 @@ namespace Umbraco.Core // global.asax (the app domain also monitors this, if it changes will do a full restart) new Tuple(new FileInfo(IOHelper.MapPath("~/global.asax")), false), // trees.config - use the contents to create the hash since this gets resaved on every app startup! - new Tuple(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true) - }, _logger); + new Tuple(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true) + }, _logger); return _currentAssembliesHash; } @@ -231,9 +226,8 @@ namespace Umbraco.Core /// Writes the assembly hash file. /// private void WriteCachePluginsHash() - { - var filePath = GetPluginHashFilePath(); - File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8); + { + File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8); } /// @@ -276,7 +270,7 @@ namespace Umbraco.Core } } } - return generator.GenerateHash(); + return generator.GenerateHash(); } } } @@ -316,7 +310,7 @@ namespace Umbraco.Core uniqInfos.Add(fileOrFolder.FullName); generator.AddFileSystemItem(fileOrFolder); } - return generator.GenerateHash(); + return generator.GenerateHash(); } } } @@ -349,12 +343,11 @@ namespace Umbraco.Core { return ReadCache(); } - catch + catch (Exception ex) { try { - var filePath = GetPluginListFilePath(); - File.Delete(filePath); + File.Delete(_pluginListFilePath.Value); } catch { @@ -367,13 +360,12 @@ namespace Umbraco.Core internal Dictionary, IEnumerable> ReadCache() { - var cache = new Dictionary, IEnumerable>(); - - var filePath = GetPluginListFilePath(); - if (File.Exists(filePath) == false) + var cache = new Dictionary, IEnumerable>(); + + if (File.Exists(_pluginListFilePath.Value) == false) return cache; - using (var stream = GetFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) + using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) using (var reader = new StreamReader(stream)) { while (true) @@ -416,52 +408,86 @@ namespace Umbraco.Core /// Generally only used for resetting cache, for example during the install process. public void ClearPluginCache() { - var path = GetPluginListFilePath(); - if (File.Exists(path)) - File.Delete(path); - - path = GetPluginHashFilePath(); - if (File.Exists(path)) - File.Delete(path); + if (File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); + + if (File.Exists(_pluginHashFilePath.Value)) + File.Delete(_pluginHashFilePath.Value); _runtimeCache.ClearCacheItem(CacheKey); } - private string GetPluginListFilePath() - { + private static string GetPluginListFilePath() + { + string pluginListFilePath; switch (GlobalSettings.LocalTempStorageLocation) - { + { case LocalTempStorage.AspNetTemp: - return Path.Combine(HttpRuntime.CodegenDir, "umbraco-plugins.list"); + pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list"); + break; case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); - var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoPlugins", - //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back - // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not - // utilizing an old path + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path appDomainHash); - return Path.Combine(cachePath, "umbraco-plugins.list"); - case LocalTempStorage.Default: + pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list"); + break; + case LocalTempStorage.Default: default: - return Path.Combine(_tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); + break; } + + //ensure the folder exists + var folder = Path.GetDirectoryName(pluginListFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + return pluginListFilePath; } - private string GetPluginHashFilePath() + private static string GetPluginHashFilePath() { - var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"; - return Path.Combine(_tempFolder, filename); + string pluginHashFilePath; + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash"); + break; + case LocalTempStorage.EnvironmentTemp: + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path + appDomainHash); + pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash"); + break; + case LocalTempStorage.Default: + default: + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); + break; + } + + //ensure the folder exists + var folder = Path.GetDirectoryName(pluginHashFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + return pluginHashFilePath; } internal void WriteCache() { - // be absolutely sure - if (Directory.Exists(_tempFolder) == false) - Directory.CreateDirectory(_tempFolder); - - var filePath = GetPluginListFilePath(); - - using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) + using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) { foreach (var typeList in _types.Values) @@ -477,8 +503,7 @@ namespace Umbraco.Core internal void UpdateCache() { - // note - // at the moment we write the cache to disk every time we update it. ideally we defer the writing + // TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing // since all the updates are going to happen in a row when Umbraco starts. that being said, the // file is small enough, so it is not a priority. WriteCache(); @@ -491,13 +516,13 @@ namespace Umbraco.Core while (true) { try - { + { return new FileStream(path, fileMode, fileAccess, fileShare); } - catch + catch (Exception ex) { if (--attempts == 0) - throw; + throw; LogHelper.Debug(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds)); Thread.Sleep(pauseMilliseconds); @@ -576,7 +601,7 @@ namespace Umbraco.Core if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false) { return ResolveTypesInternal( - typeof (T), null, + typeof(T), null, () => TypeFinder.FindClassesOfType(specificAssemblies ?? AssembliesToScan), cache); } @@ -585,14 +610,14 @@ namespace Umbraco.Core // filter the cached discovered types (and cache the result) var discovered = ResolveTypesInternal( - typeof (IDiscoverable), null, + typeof(IDiscoverable), null, () => TypeFinder.FindClassesOfType(AssembliesToScan), true); return ResolveTypesInternal( - typeof (T), null, + typeof(T), null, () => discovered - .Where(x => typeof (T).IsAssignableFrom(x)), + .Where(x => typeof(T).IsAssignableFrom(x)), true); } @@ -615,7 +640,7 @@ namespace Umbraco.Core if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false) { return ResolveTypesInternal( - typeof (T), typeof (TAttribute), + typeof(T), typeof(TAttribute), () => TypeFinder.FindClassesOfTypeWithAttribute(specificAssemblies ?? AssembliesToScan), cache); } @@ -624,12 +649,12 @@ namespace Umbraco.Core // filter the cached discovered types (and cache the result) var discovered = ResolveTypesInternal( - typeof (IDiscoverable), null, + typeof(IDiscoverable), null, () => TypeFinder.FindClassesOfType(AssembliesToScan), true); return ResolveTypesInternal( - typeof (T), typeof (TAttribute), + typeof(T), typeof(TAttribute), () => discovered .Where(x => typeof(T).IsAssignableFrom(x)) .Where(x => x.GetCustomAttributes(false).Any()), @@ -651,7 +676,7 @@ namespace Umbraco.Core cache &= specificAssemblies == null; return ResolveTypesInternal( - typeof (object), typeof (TAttribute), + typeof(object), typeof(TAttribute), () => TypeFinder.FindClassesWithAttribute(specificAssemblies ?? AssembliesToScan), cache); } @@ -668,14 +693,14 @@ namespace Umbraco.Core var name = ResolvedName(baseType, attributeType); - lock (_typesLock) - using (_logger.TraceDuration( - "Resolving " + name, - "Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find - { - // resolve within a lock & timer - return ResolveTypesInternalLocked(baseType, attributeType, finder, cache); - } + lock (_typesLock) + using (_logger.TraceDuration( + "Resolving " + name, + "Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find + { + // resolve within a lock & timer + return ResolveTypesInternalLocked(baseType, attributeType, finder, cache); + } } private static string ResolvedName(Type baseType, Type attributeType) @@ -707,7 +732,7 @@ namespace Umbraco.Core // else proceed, typeList = new TypeList(baseType, attributeType); - var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false; + var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false; if (scan) { @@ -811,7 +836,7 @@ namespace Umbraco.Core public TypeListKey(Type baseType, Type attributeType) { - BaseType = baseType ?? typeof (object); + BaseType = baseType ?? typeof(object); AttributeType = attributeType; } @@ -829,7 +854,7 @@ namespace Umbraco.Core var hash = 5381; hash = ((hash << 5) + hash) ^ BaseType.GetHashCode(); - hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof (TypeListKey)).GetHashCode(); + hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof(TypeListKey)).GetHashCode(); return hash; } } @@ -892,7 +917,7 @@ namespace Umbraco.Core { // look for IParameterEditor (fast, IDiscoverable) then filter - var propertyEditor = typeof (PropertyEditor); + var propertyEditor = typeof(PropertyEditor); return mgr.ResolveTypes() .Where(x => propertyEditor.IsAssignableFrom(x) && x != propertyEditor); @@ -907,8 +932,8 @@ namespace Umbraco.Core /// public static IEnumerable ResolveParameterEditors(this PluginManager mgr) { - var propertyEditor = typeof (PropertyEditor); - var parameterEditor = typeof (ParameterEditor); + var propertyEditor = typeof(PropertyEditor); + var parameterEditor = typeof(ParameterEditor); return mgr.ResolveTypes() .Where(x => x != propertyEditor && x != parameterEditor); @@ -990,4 +1015,4 @@ namespace Umbraco.Core return mgr.ResolveTypesWithAttribute(); } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 12cb465b50..6f1fc03281 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using umbraco.interfaces; +using Umbraco.Core.Configuration; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Sync @@ -39,6 +40,7 @@ namespace Umbraco.Core.Sync private bool _syncing; private bool _released; private readonly ProfilingLogger _profilingLogger; + private readonly Lazy _distCacheFilePath = new Lazy(GetDistCacheFilePath); protected DatabaseServerMessengerOptions Options { get; private set; } protected ApplicationContext ApplicationContext { get { return _appContext; } } @@ -460,10 +462,9 @@ namespace Umbraco.Core.Sync /// private void ReadLastSynced() { - var path = SyncFilePath; - if (File.Exists(path) == false) return; + if (File.Exists(_distCacheFilePath.Value) == false) return; - var content = File.ReadAllText(path); + var content = File.ReadAllText(_distCacheFilePath.Value); int last; if (int.TryParse(content, out last)) _lastId = last; @@ -478,7 +479,7 @@ namespace Umbraco.Core.Sync /// private void SaveLastSynced(int id) { - File.WriteAllText(SyncFilePath, id.ToString(CultureInfo.InvariantCulture)); + File.WriteAllText(_distCacheFilePath.Value, id.ToString(CultureInfo.InvariantCulture)); _lastId = id; } @@ -498,20 +499,40 @@ namespace Umbraco.Core.Sync + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - /// - /// Gets the sync file path for the local server. - /// - /// The sync file path for the local server. - private static string SyncFilePath + private static string GetDistCacheFilePath() { - get - { - var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache/" + NetworkHelper.FileSafeMachineName); - if (Directory.Exists(tempFolder) == false) - Directory.CreateDirectory(tempFolder); + var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; - return Path.Combine(tempFolder, HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"); + string distCacheFilePath; + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + distCacheFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData", fileName); + break; + case LocalTempStorage.EnvironmentTemp: + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path + appDomainHash); + distCacheFilePath = Path.Combine(cachePath, fileName); + break; + case LocalTempStorage.Default: + default: + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache"); + distCacheFilePath = Path.Combine(tempFolder, fileName); + break; } + + //ensure the folder exists + var folder = Path.GetDirectoryName(distCacheFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + return distCacheFilePath; } #endregion diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 4244188697..aa9fc35524 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -20,7 +20,7 @@ using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Models { [TestFixture] - public class ContentTests : BaseUmbracoConfigurationTest + public class ContentTests : BaseUmbracoApplicationTest { [SetUp] public void Init() diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 87af8f3234..6e2517a4d7 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -118,27 +118,14 @@ namespace Umbraco.Tests.TestHelpers } } - /// - /// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan - /// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs - /// to be set to true if the SetupPluginManager has been overridden. - /// - protected virtual bool PluginManagerResetRequired - { - get { return false; } - } - /// /// Inheritors can resset the plugin manager if they choose to on teardown /// protected virtual void ResetPluginManager() - { - if (PluginManagerResetRequired) - { - PluginManager.Current = null; - } - } - + { + PluginManager.Current = null; + } + protected virtual CacheHelper CreateCacheHelper() { return CacheHelper.CreateDisabledCacheHelper(); @@ -166,10 +153,10 @@ namespace Umbraco.Tests.TestHelpers var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Logger); var scopeProvider = new ScopeProvider(dbFactory); var evtMsgs = new TransientMessagesFactory(); - var applicationContext = new ApplicationContext( - //assign the db context - new DatabaseContext(scopeProvider, Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), - //assign the service context + var applicationContext = new ApplicationContext( + //assign the db context + new DatabaseContext(scopeProvider, Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), + //assign the service context new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), CacheHelper, Logger, evtMsgs), CacheHelper, ProfilingLogger) @@ -185,26 +172,23 @@ namespace Umbraco.Tests.TestHelpers /// protected virtual void SetupPluginManager() { - if (PluginManager.Current == null || PluginManagerResetRequired) + PluginManager.Current = new PluginManager( + new ActivatorServiceProvider(), + CacheHelper.RuntimeCache, ProfilingLogger, false) { - PluginManager.Current = new PluginManager( - new ActivatorServiceProvider(), - CacheHelper.RuntimeCache, ProfilingLogger, false) + AssembliesToScan = new[] { - AssembliesToScan = new[] - { - Assembly.Load("Umbraco.Core"), - Assembly.Load("umbraco"), - Assembly.Load("Umbraco.Tests"), - Assembly.Load("businesslogic"), - Assembly.Load("cms"), - Assembly.Load("controls"), - Assembly.Load("umbraco.editorControls"), - Assembly.Load("umbraco.MacroEngines"), - Assembly.Load("umbraco.providers"), - } - }; - } + Assembly.Load("Umbraco.Core"), + Assembly.Load("umbraco"), + Assembly.Load("Umbraco.Tests"), + Assembly.Load("businesslogic"), + Assembly.Load("cms"), + Assembly.Load("controls"), + Assembly.Load("umbraco.editorControls"), + Assembly.Load("umbraco.MacroEngines"), + Assembly.Load("umbraco.providers"), + } + }; } ///