Fixes to a few CP bugs, removal of more ThreadPool invocations, and addition of a unit test for IOHelper

[TFS Changeset #66602]
This commit is contained in:
boxbinary
2010-06-04 03:01:33 +00:00
parent 2733e8e504
commit 2eb05808c9
15 changed files with 353 additions and 149 deletions

38
INDIGO64.testrunconfig Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="INDIGO64 Tests" id="338122db-6049-471e-b696-cbaf25469edc" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>This is a default test run configuration for a local test run.</Description>
<Execution>
<Hosts type="ASP.NET" skipUnhostableTests="false">
<AspNet name="ASP.NET" executionType="Iis" urlToTest="http://sandbox1.umbraco.local">
<DevelopmentServer pathToWebSite="C:\Development\CodePlex\Umbraco\branches\4.1.0\umbraco\presentation" webApplicationRoot="/" />
</AspNet>
</Hosts>
<TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution>
<TestDirectory useLoadContext="true" />
</AssemblyResolution>
</UnitTestRunConfig>
<WebTestRunConfiguration testTypeId="4e7599fa-5ecb-43e9-a887-cd63cf72d207">
<Browser name="Internet Explorer 7.0">
<Headers>
<Header name="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" />
<Header name="Accept" value="*/*" />
<Header name="Accept-Language" value="{{$IEAcceptLanguage}}" />
<Header name="Accept-Encoding" value="GZIP" />
</Headers>
</Browser>
</WebTestRunConfiguration>
</TestTypeSpecific>
<AgentRule name="LocalMachineDefaultRole">
<DataCollectors>
<DataCollector uri="datacollector://microsoft/TraceDebugger/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.TraceDebuggerDataCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="IntelliTrace">
</DataCollector>
<DataCollector uri="datacollector://microsoft/HttpProxy/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.HttpProxyCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="ASP.NET Client Proxy for IntelliTrace and Test Impact">
</DataCollector>
<DataCollector uri="datacollector://microsoft/AspNetProfiler/1.0" assemblyQualifiedName="Microsoft.VisualStudio.PerformanceTools.DataCollection.AspNetProfileDataCollector, Microsoft.VisualStudio.PerformanceTools.DataCollection, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="ASP.NET Profiler">
</DataCollector>
</DataCollectors>
</AgentRule>
</Execution>
</TestSettings>

View File

@@ -0,0 +1,94 @@
using umbraco.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace umbraco.Test
{
/// <summary>
///This is a test class for IOHelperTest and is intended
///to contain all IOHelperTest Unit Tests
///</summary>
[TestClass()]
public class IOHelperTest
{
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
//[ClassInitialize()]
//public static void MyClassInitialize(TestContext testContext)
//{
//}
//
//Use ClassCleanup to run code after all tests in a class have run
//[ClassCleanup()]
//public static void MyClassCleanup()
//{
//}
//
//Use TestInitialize to run code before running each test
//[TestInitialize()]
//public void MyTestInitialize()
//{
//}
//
//Use TestCleanup to run code after each test has run
//[TestCleanup()]
//public void MyTestCleanup()
//{
//}
//
#endregion
/// <summary>
///A test for MapPath verifying that HttpContext method (which includes vdirs) matches non-HttpContext method
///</summary>
[TestMethod()]
public void IOHelper_MapPathTestVDirTraversal()
{
//System.Diagnostics.Debugger.Break();
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Bin, true), IOHelper.MapPath(SystemDirectories.Bin, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Config, true), IOHelper.MapPath(SystemDirectories.Config, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Css, true), IOHelper.MapPath(SystemDirectories.Css, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Data, true), IOHelper.MapPath(SystemDirectories.Data, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Install, true), IOHelper.MapPath(SystemDirectories.Install, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Masterpages, true), IOHelper.MapPath(SystemDirectories.Masterpages, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Media, true), IOHelper.MapPath(SystemDirectories.Media, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Packages, true), IOHelper.MapPath(SystemDirectories.Packages, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Preview, true), IOHelper.MapPath(SystemDirectories.Preview, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Python, true), IOHelper.MapPath(SystemDirectories.Python, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Root, true), IOHelper.MapPath(SystemDirectories.Root, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Scripts, true), IOHelper.MapPath(SystemDirectories.Scripts, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Umbraco, true), IOHelper.MapPath(SystemDirectories.Umbraco, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Umbraco_client, true), IOHelper.MapPath(SystemDirectories.Umbraco_client, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Usercontrols, true), IOHelper.MapPath(SystemDirectories.Usercontrols, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Webservices, true), IOHelper.MapPath(SystemDirectories.Webservices, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Xslt, true), IOHelper.MapPath(SystemDirectories.Xslt, false));
}
}
}

View File

@@ -0,0 +1,2 @@
businesslogic.dll
Desktop

View File

@@ -140,6 +140,7 @@
<Compile Include="DictionaryTest.cs" />
<Compile Include="DocumentTest.cs" />
<Compile Include="DocumentTypeTest.cs" />
<Compile Include="IOHelperTest.cs" />
<Compile Include="LanguageTest.cs" />
<Compile Include="MediaTest.cs" />
<Compile Include="MediaTypeTest.cs" />
@@ -215,6 +216,9 @@
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Shadow Include="Test References\businesslogic.accessor" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -4,6 +4,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
build.xml = build.xml
default.build = default.build
INDIGO64.testrunconfig = INDIGO64.testrunconfig
SHANDEMVAIO.testrunconfig = SHANDEMVAIO.testrunconfig
SHOCKING.testrunconfig = SHOCKING.testrunconfig
umbraco weekly.build = umbraco weekly.build

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
<RunConfiguration id="338122db-6049-471e-b697-cbaf25469edc" name="SHANDEMVAIO Tests" storage="shandemvaio.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<RunConfiguration id="338122db-6049-471e-b696-cbaf25469edc" name="INDIGO64 Tests" storage="indigo64.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</TestList>
</TestLists>

View File

@@ -9,7 +9,7 @@ using umbraco.BusinessLogic;
namespace umbraco.IO
{
public class IOHelper
public static class IOHelper
{
private static string m_rootDir = "";

View File

@@ -146,7 +146,7 @@ namespace umbraco.IO
{
get
{
string appPath = HttpRuntime.AppDomainAppVirtualPath;
string appPath = HttpRuntime.AppDomainAppVirtualPath ?? string.Empty;
if (appPath == "/")
appPath = string.Empty;

View File

@@ -1,11 +1,6 @@
/// <changelog>
/// <item who="Esben" when="18. november 2006">Rewrote</item>
/// </changelog>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Threading;
@@ -29,7 +24,7 @@ namespace umbraco
{
#region Declarations
private string _umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml, false);
private string _umbracoXmlDiskCacheFileName = string.Empty;
/// <summary>
/// Gets the path of the umbraco XML disk cache file.
@@ -39,6 +34,10 @@ namespace umbraco
{
get
{
if (string.IsNullOrEmpty(_umbracoXmlDiskCacheFileName))
{
_umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml);
}
return _umbracoXmlDiskCacheFileName;
}
set
@@ -47,25 +46,19 @@ namespace umbraco
}
}
/*
HttpRuntime.AppDomainAppPath + '\\' +
SystemFiles.ContentCacheXml.Replace('/', '\\').TrimStart('\\');
*/
private readonly string XmlContextContentItemKey = "UmbracoXmlContextContent";
// Current content
private volatile XmlDocument _xmlContent = null;
// Sync access to disk file
private object _readerWriterSyncLock = new object();
private static object _readerWriterSyncLock = new object();
// Sync access to internal cache
private object _xmlContentInternalSyncLock = new object();
private static object _xmlContentInternalSyncLock = new object();
// Sync database access
private object _dbReadSyncLock = new object();
private static object _dbReadSyncLock = new object();
#endregion
@@ -108,8 +101,6 @@ namespace umbraco
/// subsequent calls will be blocked until initialization is done
/// Further we cache(in context) xmlContent for each request to ensure that
/// we always have the same XmlDoc throughout the whole request.
/// Note that context cache does not need to be locked, because all access
/// to it is done from a web request which runs in a single thread
/// </summary>
public virtual XmlDocument XmlContent
{
@@ -159,10 +150,10 @@ namespace umbraco
_xmlContent = value;
if (!UmbracoSettings.isXmlContentCacheDisabled && UmbracoSettings.continouslyUpdateXmlDiskCache)
SaveContentToDiskAsync(_xmlContent);
QueueXmlForPersistence();
else
// Clear cache...
ClearDiskCacheAsync();
DeleteXmlCache();
}
}
}
@@ -187,7 +178,7 @@ namespace umbraco
// TODO: Re-architect this so that a call to this method doesn't invoke a new thread for saving disk cache
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
{
SaveContentToDiskAsync(_xmlContent);
QueueXmlForPersistence();
}
return true;
}
@@ -225,8 +216,12 @@ namespace umbraco
XmlDocument xmlDoc = LoadContentFromDatabase();
XmlContentInternal = xmlDoc;
if (!UmbracoSettings.isXmlContentCacheDisabled)
SaveContentToDisk(xmlDoc);
// It is correct to manually call PersistXmlToFile here event though the setter of XmlContentInternal
// queues this up, because this delegate is executing on a different thread and may complete
// after the request which invoked it (which would normally persist the file on completion)
// So we are responsible for ensuring the content is persisted in this case.
if (!UmbracoSettings.isXmlContentCacheDisabled && UmbracoSettings.continouslyUpdateXmlDiskCache)
PersistXmlToFile(xmlDoc);
});
FireAfterRefreshContent(e);
@@ -448,6 +443,7 @@ namespace umbraco
}
}
[Obsolete("Method obsolete in version 4.1 and later, please use UpdateDocumentCache", true)]
/// <summary>
/// Updates the document cache async.
/// </summary>
@@ -459,6 +455,7 @@ namespace umbraco
[Obsolete("Method obsolete in version 4.1 and later, please use ClearDocumentCache", true)]
/// <summary>
/// Clears the document cache async.
/// </summary>
@@ -536,7 +533,7 @@ namespace umbraco
/// Unpublishes the node.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.ClearDocumentCache")]
[Obsolete("Please use: umbraco.content.ClearDocumentCache", true)]
public virtual void UnPublishNode(int documentId)
{
ClearDocumentCache(documentId);
@@ -546,7 +543,7 @@ namespace umbraco
/// Uns the publish node async.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.ClearDocumentCacheAsync")]
[Obsolete("Please use: umbraco.content.ClearDocumentCacheAsync", true)]
public virtual void UnPublishNodeAsync(int documentId)
{
ThreadPool.QueueUserWorkItem(delegate { ClearDocumentCache(documentId); });
@@ -556,7 +553,7 @@ namespace umbraco
/// Legacy method - you should use the overloaded publishnode(document d) method whenever possible
/// </summary>
/// <param name="documentId"></param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
public virtual void PublishNode(int documentId)
{
// Get the document
@@ -568,7 +565,7 @@ namespace umbraco
/// Publishes the node async.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCacheAsync")]
[Obsolete("Please use: umbraco.content.UpdateDocumentCacheAsync", true)]
public virtual void PublishNodeAsync(int documentId)
{
UpdateDocumentCacheAsync(documentId);
@@ -578,7 +575,7 @@ namespace umbraco
/// Publishes the node.
/// </summary>
/// <param name="Documents">The documents.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
public virtual void PublishNode(List<Document> Documents)
{
UpdateDocumentCache(Documents);
@@ -589,7 +586,7 @@ namespace umbraco
/// Publishes the node.
/// </summary>
/// <param name="d">The document.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
public virtual void PublishNode(Document d)
{
UpdateDocumentCache(d);
@@ -713,15 +710,21 @@ namespace umbraco
/// <summary>
/// Invalidates the disk content cache file. Effectively just deletes it.
/// </summary>
private void ClearDiskCache()
private void DeleteXmlCache()
{
lock (_readerWriterSyncLock)
{
if (File.Exists(UmbracoXmlDiskCacheFileName))
{
// Reset file attributes, to make sure we can delete file
File.SetAttributes(UmbracoXmlDiskCacheFileName, FileAttributes.Normal);
File.Delete(UmbracoXmlDiskCacheFileName);
try
{
File.SetAttributes(UmbracoXmlDiskCacheFileName, FileAttributes.Normal);
}
finally
{
File.Delete(UmbracoXmlDiskCacheFileName);
}
}
}
}
@@ -736,6 +739,7 @@ namespace umbraco
HttpContext.Current.Items.Remove(XmlContextContentItemKey);
}
[Obsolete("This method is obsolete in version 4.1 and above, please use DeleteXmlCache", true)]
/// <summary>
/// Invalidates the disk content cache file. Effectively just deletes it.
/// </summary>
@@ -745,7 +749,7 @@ namespace umbraco
// We queue this function, because there can be a write process running at the same time
// and we don't want this method to block web request
ThreadPool.QueueUserWorkItem(
delegate { ClearDiskCache(); });
delegate { DeleteXmlCache(); });
}
/// <summary>
@@ -764,7 +768,7 @@ namespace umbraco
{
// This is really bad, loading from cache file failed for some reason, now fallback to loading from database
Debug.WriteLine("Content file cache load failed: " + e);
ClearDiskCache();
DeleteXmlCache();
}
}
return LoadContentFromDatabase();
@@ -772,7 +776,14 @@ namespace umbraco
private bool IsValidDiskCachePresent()
{
return File.Exists(UmbracoXmlDiskCacheFileName);
if (File.Exists(UmbracoXmlDiskCacheFileName))
{
// Only return true if we don't have a zero-byte file
var f = new FileInfo(UmbracoXmlDiskCacheFileName);
if (f.Length > 0)
return true;
}
return false;
}
/// <summary>
@@ -803,90 +814,98 @@ namespace umbraco
private XmlDocument LoadContentFromDatabase()
{
XmlDocument xmlDoc = new XmlDocument();
// Moved User to a local variable - why are we causing user 0 to load from the DB though?
// Alex N 20100212
User staticUser = null;
// Alex N - 2010 06 - Very generic try-catch simply because at the moment, unfortunately, this method gets called inside a ThreadPool thread
// and we need to guarantee it won't tear down the app pool by throwing an unhandled exception
try
{
staticUser = User.GetCurrent(); //User.GetUser(0);
}
catch
{
/* We don't care later if the staticUser is null */
}
// Try to log to the DB
Log.Add(LogTypes.System, staticUser, -1, "Loading content from database...");
// Moved User to a local variable - why are we causing user 0 to load from the DB though?
// Alex N 20100212
User staticUser = null;
try
{
staticUser = User.GetCurrent(); //User.GetUser(0);
}
catch
{
/* We don't care later if the staticUser is null */
}
// Moved this to after the logging since the 2010 schema accesses the DB just to generate the DTD
InitContentDocumentBase(xmlDoc);
// Try to log to the DB
Log.Add(LogTypes.System, staticUser, -1, "Loading content from database...");
Hashtable nodes = new Hashtable();
Hashtable parents = new Hashtable();
try
{
Log.Add(LogTypes.Debug, staticUser, -1, "Republishing starting");
// Moved this to after the logging since the 2010 schema accesses the DB just to generate the DTD
InitContentDocumentBase(xmlDoc);
// Esben Carlsen: At some point we really need to put all data access into to a tier of its own.
string sql =
@"select umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, cmsContentXml.xml from umbracoNode
Hashtable nodes = new Hashtable();
Hashtable parents = new Hashtable();
try
{
Log.Add(LogTypes.Debug, staticUser, -1, "Republishing starting");
// Esben Carlsen: At some point we really need to put all data access into to a tier of its own.
string sql =
@"select umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, cmsContentXml.xml from umbracoNode
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
order by umbracoNode.level, umbracoNode.sortOrder";
lock (_dbReadSyncLock)
{
using (IRecordsReader dr = SqlHelper.ExecuteReader(sql, SqlHelper.CreateParameter("@type", new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"))))
lock (_dbReadSyncLock)
{
while (dr.Read())
using (IRecordsReader dr = SqlHelper.ExecuteReader(sql, SqlHelper.CreateParameter("@type", new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"))))
{
int currentId = dr.GetInt("id");
int parentId = dr.GetInt("parentId");
xmlDoc.LoadXml(dr.GetString("xml"));
nodes.Add(currentId, xmlDoc.FirstChild);
if (parents.ContainsKey(parentId))
((ArrayList)parents[parentId]).Add(currentId);
else
while (dr.Read())
{
ArrayList a = new ArrayList();
a.Add(currentId);
parents.Add(parentId, a);
int currentId = dr.GetInt("id");
int parentId = dr.GetInt("parentId");
xmlDoc.LoadXml(dr.GetString("xml"));
nodes.Add(currentId, xmlDoc.FirstChild);
if (parents.ContainsKey(parentId))
((ArrayList)parents[parentId]).Add(currentId);
else
{
ArrayList a = new ArrayList();
a.Add(currentId);
parents.Add(parentId, a);
}
}
}
}
Log.Add(LogTypes.Debug, staticUser, -1, "Xml Pages loaded");
// TODO: Why is the following line here, it should have already been generated? Alex N 20100212
// Reset
InitContentDocumentBase(xmlDoc);
try
{
GenerateXmlDocument(parents, nodes, -1, xmlDoc.DocumentElement);
Log.Add(LogTypes.Debug, staticUser, -1, "Done republishing Xml Index");
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, staticUser, -1,
string.Format("Error while generating XmlDocument from database: {0}", ee));
}
}
Log.Add(LogTypes.Debug, staticUser, -1, "Xml Pages loaded");
// TODO: Why is the following line here, it should have already been generated? Alex N 20100212
// Reset
InitContentDocumentBase(xmlDoc);
try
catch (OutOfMemoryException)
{
GenerateXmlDocument(parents, nodes, -1, xmlDoc.DocumentElement);
Log.Add(LogTypes.Error, staticUser, -1,
string.Format("Error Republishing: Out Of Memory. Parents: {0}, Nodes: {1}",
parents.Count, nodes.Count));
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, staticUser, -1,
string.Format("Error while generating XmlDocument from database: {0}", ee));
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Error Republishing: {0}", ee));
}
}
catch (OutOfMemoryException)
{
Log.Add(LogTypes.Error, staticUser, -1,
string.Format("Error Republishing: Out Of Memory. Parents: {0}, Nodes: {1}",
parents.Count, nodes.Count));
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Error Republishing: {0}", ee));
}
finally
{
Log.Add(LogTypes.Debug, staticUser, -1, "Done republishing Xml Index");
Log.Add(LogTypes.Error, -1, string.Format("Error Republishing: {0}", ee));
}
return xmlDoc;
@@ -925,72 +944,104 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
}
}
public void PersistXmlToFile()
{
PersistXmlToFile(_xmlContent);
}
/// <summary>
/// Persist a XmlDocument to the Disk Cache
/// </summary>
/// <param name="xmlDoc"></param>
internal void SaveContentToDisk(XmlDocument xmlDoc)
internal void PersistXmlToFile(XmlDocument xmlDoc)
{
lock (_readerWriterSyncLock)
{
Trace.Write(string.Format("Saving content to disk on thread '{0}' (Threadpool? {1})", Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread.ToString()));
// Moved the user into a variable and avoided it throwing an error if one can't be loaded (e.g. empty / corrupt db on initial install)
User staticUser = null;
try
if (xmlDoc != null)
{
staticUser = User.GetCurrent();
}
catch
{}
Trace.Write(string.Format("Saving content to disk on thread '{0}' (Threadpool? {1})", Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread.ToString()));
try
{
Stopwatch stopWatch = Stopwatch.StartNew();
ClearDiskCache();
// Try to create directory for cache path if it doesn't yet exist
if (!File.Exists(UmbracoXmlDiskCacheFileName) && !Directory.Exists(Path.GetDirectoryName(UmbracoXmlDiskCacheFileName)))
// Moved the user into a variable and avoided it throwing an error if one can't be loaded (e.g. empty / corrupt db on initial install)
User staticUser = null;
try
{
// We're already in a try-catch and saving will fail if this does, so don't need another
Directory.CreateDirectory(UmbracoXmlDiskCacheFileName);
staticUser = User.GetCurrent();
}
catch
{ }
xmlDoc.Save(UmbracoXmlDiskCacheFileName);
try
{
Stopwatch stopWatch = Stopwatch.StartNew();
Trace.Write(string.Format("Saved content on thread '{0}' in {1} (Threadpool? {2})", Thread.CurrentThread.Name, stopWatch.Elapsed, Thread.CurrentThread.IsThreadPoolThread.ToString()));
Log.Add(LogTypes.Debug, staticUser, -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
}
catch (Exception ee)
{
// If for whatever reason something goes wrong here, invalidate disk cache
ClearDiskCache();
DeleteXmlCache();
Trace.Write(string.Format("Error saving content on thread '{0}' due to '{1}' (Threadpool? {2})", Thread.CurrentThread.Name, ee.Message, Thread.CurrentThread.IsThreadPoolThread.ToString()));
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Xml wasn't saved: {0}", ee));
// Try to create directory for cache path if it doesn't yet exist
if (!File.Exists(UmbracoXmlDiskCacheFileName) && !Directory.Exists(Path.GetDirectoryName(UmbracoXmlDiskCacheFileName)))
{
// We're already in a try-catch and saving will fail if this does, so don't need another
Directory.CreateDirectory(UmbracoXmlDiskCacheFileName);
}
xmlDoc.Save(UmbracoXmlDiskCacheFileName);
Trace.Write(string.Format("Saved content on thread '{0}' in {1} (Threadpool? {2})", Thread.CurrentThread.Name, stopWatch.Elapsed, Thread.CurrentThread.IsThreadPoolThread.ToString()));
Log.Add(LogTypes.Debug, staticUser, -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
}
catch (Exception ee)
{
// If for whatever reason something goes wrong here, invalidate disk cache
DeleteXmlCache();
Trace.Write(string.Format("Error saving content on thread '{0}' due to '{1}' (Threadpool? {2})", Thread.CurrentThread.Name, ee.Message, Thread.CurrentThread.IsThreadPoolThread.ToString()));
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Xml wasn't saved: {0}", ee));
}
}
}
}
internal const string PersistenceFlagContextKey = "vnc38ykjnkjdnk2jt98ygkxjng";
/// <summary>
/// Persist xml document to disk cache in a background thread
/// Marks a flag in the HttpContext so that, upon page execution completion, the Xml cache will
/// get persisted to disk. Ensure this method is only called from a thread executing a page request
/// since umbraco.presentation.requestModule is the only monitor of this flag and is responsible
/// for enacting the persistence at the PostRequestHandlerExecute stage of the page lifecycle.
/// </summary>
/// <param name="xmlDoc"></param>
private void SaveContentToDiskAsync(XmlDocument xmlDoc)
private void QueueXmlForPersistence()
{
// Save copy of content
if (UmbracoSettings.CloneXmlCacheOnPublish)
/* Alex Norcliffe 2010 06 03 - removing all launching of ThreadPool threads, instead we just
* flag on the context that the Xml should be saved and an event in the requestModule
* will check for this and call PersistXmlToFile() if necessary */
HttpContext.Current.Items[PersistenceFlagContextKey] = true;
//// Save copy of content
//if (UmbracoSettings.CloneXmlCacheOnPublish)
//{
// XmlDocument xmlContentCopy = CloneXmlDoc(_xmlContent);
// ThreadPool.QueueUserWorkItem(
// delegate { PersistXmlToFile(xmlContentCopy); });
//}
//else
// ThreadPool.QueueUserWorkItem(
// delegate { PersistXmlToFile(); });
}
internal bool IsXmlQueuedForPersistenceToFile
{
get
{
XmlDocument xmlContentCopy = CloneXmlDoc(xmlDoc);
ThreadPool.QueueUserWorkItem(
delegate { SaveContentToDisk(xmlContentCopy); });
var val = HttpContext.Current.Items.Contains(PersistenceFlagContextKey);
if (val != null)
{
return bool.Parse(val.ToString());
}
return false;
}
else
ThreadPool.QueueUserWorkItem(
delegate { SaveContentToDisk(xmlDoc); });
}
/// <summary>

View File

@@ -72,7 +72,7 @@ namespace umbraco.presentation.install.steps
// Test umbraco.xml file
try
{
content.Instance.SaveContentToDisk(content.Instance.XmlContent);
content.Instance.PersistXmlToFile();
xmlResult.Text = "Success!";
}
catch (Exception ee)

View File

@@ -287,10 +287,22 @@ namespace umbraco.presentation
context.PostResolveRequestCache += new EventHandler(Application_PostResolveRequestCache);
context.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
// Alex Norcliffe - 2010 06 - Added a check at the end of the page lifecycle to see if we should persist Xml cache to disk
// (a replacement for all those parallel Async methods launching ThreadPool threads)
context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
context.Error += new EventHandler(Application_Error);
mApp = context;
}
void context_PostRequestHandlerExecute(object sender, EventArgs e)
{
if (content.Instance.IsXmlQueuedForPersistenceToFile)
{
content.Instance.PersistXmlToFile();
}
}

View File

@@ -2799,7 +2799,6 @@
<None Include="umbraco\schemas\umbraco.xsx">
<DependentUpon>umbraco.xsd</DependentUpon>
</None>
<Content Include="web.config" />
<Content Include="config\UrlRewriting.config" />
</ItemGroup>
<ItemGroup>

View File

@@ -69,7 +69,7 @@ namespace umbraco.presentation.cache
/// </summary>
/// <param name="Id">The id.</param>
public void Refresh(int Id) {
content.Instance.PublishNode(Id);
content.Instance.UpdateDocumentCache(Id);
}
@@ -78,7 +78,7 @@ namespace umbraco.presentation.cache
/// </summary>
/// <param name="Id">The id.</param>
public void Remove(int Id) {
content.Instance.UnPublishNode(Id);
content.Instance.ClearDocumentCache(Id);
}
#endregion

View File

@@ -68,7 +68,7 @@ namespace umbraco.webservices
[WebMethod]
public void SaveXmlCacheToDisk()
{
content.Instance.SaveContentToDisk(content.Instance.XmlContent);
content.Instance.PersistXmlToFile();
}
#region Component Designer generated code

View File

@@ -3,8 +3,11 @@
<configSections >
<section name="urlrewritingnet" restartOnExternalChanges="true" requirePermission="false" type="UrlRewritingNet.Configuration.UrlRewriteSection, UrlRewritingNet.UrlRewriter" />
<section name="microsoft.scripting" type="Microsoft.Scripting.Hosting.Configuration.Section, Microsoft.Scripting, Version=0.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
<!-- ASPNETAJAX -->
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<!--
ASPNETAJAX ** DISABLED BY ALEX NORCLIFFE 2010 06 03 FOR COMPATIBILITY WITH RUNNING UNDER .NET 4
USERS CAN ADD THIS THEMSELVES IF THEY WISH TO USE ASP.NET AJAX
-->
<!--<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
@@ -14,7 +17,7 @@
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>
</sectionGroup>-->
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="umbraco.presentation.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
@@ -30,7 +33,7 @@
<appSettings>
<add key="umbracoDbDSN" value="server=.\SQLEXPRESS;database=umbraco;user id=web;password=farmer" />
<add key="umbracoConfigurationStatus" value="4.1.0.betaII" />
<add key="umbracoReservedUrls" value="~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx" />
<add key="umbracoReservedUrls" value="~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd" />
<add key="umbracoReservedPaths" value="~/umbraco,~/install/" />
<add key="umbracoContentXML" value="~/data/umbraco.config" />
<add key="umbracoStorageDirectory" value="~/data" />