WORK IN PROGRESS, GET THE STABLE SOURCE FROM THE DOWNLOADS TAB

Merging into main 4.1.0 branch:

Resolves a number of open issues, see associated WorkItems
- Fix WP crash on invalid connection string due to generation of XML cache on a separate thread
- Added option to generate xml cache file on local filesystem (in ASP.NET Temporary Files / CodeGenDir location) by adding <add key="umbracoContentXMLUseLocalTemp" value="true" /> to web.config file. This allows you to run from a SAN on a load-balanced environment whilst allowing each web node to have isolated copies of the xml cache file to avoid WP crashes due to multiple file locks
- Others in WorkItems :)

[TFS Changeset #64002]
This commit is contained in:
boxbinary
2010-02-16 04:12:10 +00:00
parent 8d18a04e62
commit bbf1a05681
10 changed files with 221 additions and 55 deletions

View File

@@ -181,26 +181,40 @@ namespace umbraco.BasePages {
/// </summary>
/// <value>The umbraco user context ID.</value>
public static string umbracoUserContextID {
get {
if (System.Web.HttpContext.Current.Request.Cookies.Get("UserContext") != null)
return System.Web.HttpContext.Current.Request.Cookies.Get("UserContext").Value;
else
return "";
get
{
if (HttpContext.Current != null)
if (HttpContext.Current.Request != null)
if (HttpContext.Current.Request.Cookies != null)
{
HttpCookie userContext = HttpContext.Current.Request.Cookies.Get("UserContext");
if (userContext != null)
return userContext.Value;
}
return "";
}
set {
// Clearing all old cookies before setting a new one.
try {
if (System.Web.HttpContext.Current.Request.Cookies["UserContext"] != null) {
System.Web.HttpContext.Current.Response.Cookies.Clear();
if (HttpContext.Current != null)
{
// Clearing all old cookies before setting a new one.
try
{
if (HttpContext.Current.Request != null)
if (HttpContext.Current.Request.Cookies["UserContext"] != null)
{
HttpContext.Current.Response.Cookies.Clear();
}
}
} catch {
catch
{
}
// Create new cookie.
var c = new HttpCookie("UserContext");
c.Name = "UserContext";
c.Value = value;
c.Expires = DateTime.Now.AddDays(1);
HttpContext.Current.Response.Cookies.Add(c);
}
// Create new cookie.
System.Web.HttpCookie c = new System.Web.HttpCookie("UserContext");
c.Name = "UserContext";
c.Value = value;
c.Expires = DateTime.Now.AddDays(1);
System.Web.HttpContext.Current.Response.Cookies.Add(c);
}
}

View File

@@ -46,6 +46,10 @@ namespace umbraco.IO
public static string MapPath(string path, bool useHttpContext)
{
// Check if the path is already mapped
if (path.Length >= 2 && path[1] == Path.VolumeSeparatorChar)
return path;
if (useHttpContext)
{
//string retval;

View File

@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
namespace umbraco.IO
{
@@ -76,8 +79,27 @@ namespace umbraco.IO
{
get
{
if (ContentCacheXmlIsEphemeral)
{
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
}
return IOHelper.returnPath("umbracoContentXML", "~/data/umbraco.config");
}
}
public static bool ContentCacheXmlIsEphemeral
{
get
{
bool returnValue = false;
string configSetting = ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"];
if (!string.IsNullOrEmpty(configSetting))
if(bool.TryParse(configSetting, out returnValue))
return returnValue;
return false;
}
}
}
}

View File

@@ -320,7 +320,10 @@ namespace umbraco.cms.businesslogic.web
public static string GenerateDtd()
{
StringBuilder dtd = new StringBuilder();
dtd.AppendLine("<!DOCTYPE umbraco [ ");
// Renamed 'umbraco' to 'root' since the top level of the DOCTYPE should specify the name of the root node for it to be valid;
// there's no mention of 'umbraco' anywhere in the schema that this DOCTYPE governs
// (Alex N 20100212)
dtd.AppendLine("<!DOCTYPE root [ ");
if (UmbracoSettings.UseLegacyXmlSchema)
{
@@ -328,13 +331,33 @@ namespace umbraco.cms.businesslogic.web
}
else
{
List<DocumentType> dts = GetAllAsList();
foreach (DocumentType dt in dts)
// TEMPORARY: Added Try-Catch to this call since trying to generate a DTD against a corrupt db
// or a broken connection string is not handled yet
// (Alex N 20100212)
try
{
string safeAlias = helpers.Casing.SafeAlias(dt.Alias);
dtd.AppendLine(String.Format("<!ELEMENT {0} ANY>", safeAlias));
dtd.AppendLine(String.Format("<!ATTLIST {0} id ID #REQUIRED>", safeAlias));
StringBuilder strictSchemaBuilder = new StringBuilder();
List<DocumentType> dts = GetAllAsList();
foreach (DocumentType dt in dts)
{
string safeAlias = helpers.Casing.SafeAlias(dt.Alias);
if (safeAlias != null)
{
strictSchemaBuilder.AppendLine(String.Format("<!ELEMENT {0} ANY>", safeAlias));
strictSchemaBuilder.AppendLine(String.Format("<!ATTLIST {0} id ID #REQUIRED>", safeAlias));
}
}
// Only commit the strong schema to the container if we didn't generate an error building it
dtd.Append(strictSchemaBuilder);
}
catch (Exception exception)
{
// Note, Log.Add quietly swallows the exception if it can't write to the database
Log.Add(LogTypes.System, -1, string.Format("{0} while trying to build DTD for Xml schema; is Umbraco installed correctly and the connection string configured?", exception.Message));
}
}
dtd.AppendLine("]>");

View File

@@ -29,7 +29,25 @@ namespace umbraco
{
#region Declarations
private readonly string UmbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml, false);
private string _umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml, false);
/// <summary>
/// Gets the path of the umbraco XML disk cache file.
/// </summary>
/// <value>The name of the umbraco XML disk cache file.</value>
public string UmbracoXmlDiskCacheFileName
{
get
{
return _umbracoXmlDiskCacheFileName;
}
set
{
_umbracoXmlDiskCacheFileName = value;
}
}
/*
HttpRuntime.AppDomainAppPath + '\\' +
SystemFiles.ContentCacheXml.Replace('/', '\\').TrimStart('\\');
@@ -60,13 +78,16 @@ namespace umbraco
static content()
{
Trace.Write("Initializing content");
ThreadPool.QueueUserWorkItem(
delegate
{
XmlDocument xmlDoc = Instance.XmlContentInternal;
Trace.WriteLine("Content initialized");
});
//Trace.Write("Initializing content");
//ThreadPool.QueueUserWorkItem(
// delegate
// {
// XmlDocument xmlDoc = Instance.XmlContentInternal;
// Trace.WriteLine("Content initialized");
// });
Trace.WriteLine("Checking for xml content initialisation...");
Instance.CheckXmlContentPopulation();
}
#endregion
@@ -124,18 +145,7 @@ namespace umbraco
{
get
{
if (isInitializing)
{
lock (_xmlContentInternalSyncLock)
{
if (isInitializing)
{
_xmlContent = LoadContent();
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
SaveContentToDiskAsync(_xmlContent);
}
}
}
CheckXmlContentPopulation();
return _xmlContent;
}
@@ -157,6 +167,36 @@ namespace umbraco
}
}
/// <summary>
/// Triggers the XML content population if necessary.
/// </summary>
/// <returns></returns>
private bool CheckXmlContentPopulation()
{
if (isInitializing)
{
lock (_xmlContentInternalSyncLock)
{
if (isInitializing)
{
Trace.WriteLine(string.Format("Initializing content on thread '{0}' (Threadpool? {1})", Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread.ToString()));
_xmlContent = LoadContent();
Trace.WriteLine("Content initialized (loaded)");
// Only save new XML cache to disk if we just repopulated it
// 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);
}
return true;
}
}
}
Trace.WriteLine("Content initialized (was already in context)");
return false;
}
#endregion
protected static ISqlHelper SqlHelper
@@ -164,6 +204,8 @@ namespace umbraco
get { return Application.SqlHelper; }
}
#region Public Methods
/// <summary>
@@ -760,15 +802,30 @@ namespace umbraco
private XmlDocument LoadContentFromDatabase()
{
XmlDocument xmlDoc = new XmlDocument();
InitContentDocumentBase(xmlDoc);
// 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 */
}
Log.Add(LogTypes.System, User.GetUser(0), -1, "Loading content from database...");
// Try to log to the DB
Log.Add(LogTypes.System, staticUser, -1, "Loading content from database...");
// Moved this to after the logging since the 2010 schema accesses the DB just to generate the DTD
InitContentDocumentBase(xmlDoc);
Hashtable nodes = new Hashtable();
Hashtable parents = new Hashtable();
try
{
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Republishing starting");
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 =
@@ -800,8 +857,9 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
}
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Xml Pages loaded");
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);
@@ -811,23 +869,23 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, User.GetUser(0), -1,
Log.Add(LogTypes.Error, staticUser, -1,
string.Format("Error while generating XmlDocument from database: {0}", ee));
}
}
catch (OutOfMemoryException)
{
Log.Add(LogTypes.Error, User.GetUser(0), -1,
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, User.GetUser(0), -1, string.Format("Error Republishing: {0}", ee));
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Error Republishing: {0}", ee));
}
finally
{
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Done republishing Xml Index");
Log.Add(LogTypes.Debug, staticUser, -1, "Done republishing Xml Index");
}
return xmlDoc;
@@ -874,20 +932,42 @@ order by umbracoNode.level, umbracoNode.sortOrder";
{
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
{
staticUser = User.GetCurrent();
}
catch
{}
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)))
{
// 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);
Log.Add(LogTypes.Debug, User.GetUser(0), -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
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();
Log.Add(LogTypes.Error, User.GetUser(0), -1, string.Format("Xml wasn't saved: {0}", ee));
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));
}
}
}

View File

@@ -83,6 +83,8 @@ namespace umbraco.presentation.install.steps
DbConnectionStringBuilder connectionStringBuilder = new DbConnectionStringBuilder();
connectionStringBuilder.ConnectionString = GlobalSettings.DbDSN;
// "Data Source=.\\SQLEXPRESS;Initial Catalog=BB_Umbraco_Sandbox1;integrated security=false;user id=umbraco;pwd=umbraco"
// Prepare the fields
// Prepare data layer type
@@ -97,9 +99,12 @@ namespace umbraco.presentation.install.steps
// Prepare other fields
DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "server");
if (string.IsNullOrEmpty(DatabaseServer.Text)) DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "Data Source");
DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "database");
if (string.IsNullOrEmpty(DatabaseName.Text)) DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "Initial Catalog");
DatabaseUsername.Text = GetConnectionStringValue(connectionStringBuilder, "user id");
DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "password");
if (string.IsNullOrEmpty(DatabasePassword.Text)) DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "pwd");
}
// Make sure ASP.Net displays the password text

View File

@@ -232,7 +232,18 @@ namespace umbraco.presentation
else
error = "No Context available -> "
+ mApp.Context.Server.GetLastError().InnerException;
Log.Add(LogTypes.Error, User.GetUser(0), -1, error);
// Hide error if getting the user throws an error (e.g. corrupt / blank db)
User staticUser = null;
try
{
User.GetCurrent();
}
catch
{
}
Log.Add(LogTypes.Error, staticUser, -1, error);
Trace.TraceError(error);
lock (unhandledErrors)
{

View File

@@ -17,6 +17,10 @@ h2 {
font-size: 14pt;
}
h3 {
font-size: 12pt;
}
p.rel {
padding-left: 2em;
text-indent: -2em;

View File

@@ -47,6 +47,7 @@
<add key="umbracoProfileUrl" value="profiler" />
<add key="umbracoUseSSL" value="false" />
<add key="umbracoUseMediumTrust" value="false" />
<add key="umbracoContentXMLUseLocalTemp" value="false"/> <!-- Set to true to write this to the local CodeGenDir instead, in SAN / NAS situations -->
</appSettings>
<system.net>
<mailSettings>
@@ -226,4 +227,4 @@
</assemblyBinding>
</runtime>
</configuration>
</configuration>

View File

@@ -28,7 +28,7 @@
<UmbracoExamine configSource="config\ExamineSettings.config" />
<ExamineLuceneIndexSets configSource="config\ExamineIndex.config" />
<appSettings>
<add key="umbracoDbDSN" value="server=.\sqlexpress;database=UmbracoTest2;user id=sa;password=test" />
<add key="umbracoDbDSN" value="Data Source=.\SQLEXPRESS;Initial Catalog=BB_Umbraco_Sandbox1;integrated security=false;user id=umbraco;pwd=umbraco" />
<add key="umbracoConfigurationStatus" value="4.1.0.beta" />
<add key="umbracoReservedUrls" value="~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx" />
<add key="umbracoReservedPaths" value="~/umbraco,~/install/" />
@@ -47,6 +47,8 @@
<add key="umbracoProfileUrl" value="profiler" />
<add key="umbracoUseSSL" value="false" />
<add key="umbracoUseMediumTrust" value="false" />
<add key="umbracoContentXMLUseLocalTemp" value="false" />
<!-- Set to true to write this to the local CodeGenDir instead, in SAN / NAS situations -->
</appSettings>
<system.net>
<mailSettings>