using System; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; using System.Web; using System.Web.Hosting; using log4net; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core { /// /// The abstract class for the Umbraco HttpApplication /// /// /// This is exposed in the core so that we can have the IApplicationEventHandler in the core project so that /// IApplicationEventHandler's can fire/execute outside of the web contenxt (i.e. in console applications) /// public abstract class UmbracoApplicationBase : System.Web.HttpApplication { public static event EventHandler ApplicationStarting; public static event EventHandler ApplicationStarted; /// /// Called when the HttpApplication.Init() is fired, allows developers to subscribe to the HttpApplication events /// public static event EventHandler ApplicationInit; /// /// Boots up the Umbraco application /// internal void StartApplication(object sender, EventArgs e) { //take care of unhandled exceptions - there is nothing we can do to // prevent the entire w3wp process to go down but at least we can try // and log the exception AppDomain.CurrentDomain.UnhandledException += (_, args) => { var exception = (Exception) args.ExceptionObject; var isTerminating = args.IsTerminating; // always true? var msg = "Unhandled exception in AppDomain"; if (isTerminating) msg += " (terminating)"; LogHelper.Error(msg, exception); }; // this only gets called when an assembly can't be resolved AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; //boot up the application GetBootManager() .Initialize() .Startup(appContext => OnApplicationStarting(sender, e)) .Complete(appContext => OnApplicationStarted(sender, e)); //And now we can dispose of our startup handlers - save some memory ApplicationEventsResolver.Current.Dispose(); } /// /// Called when an assembly can't be resolved. In here we can do magic with the assembly name and try loading another. /// This is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code. /// /// /// /// private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) { // ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again // do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null")) return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005")); return null; } /// /// Initializes the Umbraco application /// /// /// protected void Application_Start(object sender, EventArgs e) { Thread.CurrentThread.SanitizeThreadCulture(); StartApplication(sender, e); } /// /// Override init and raise the event /// /// /// DID YOU KNOW? The Global.asax Init call is the thing that initializes all of the httpmodules, ties up a bunch of stuff with IIS, etc... /// Therefore, since OWIN is an HttpModule when running in IIS/ASP.Net the OWIN startup is not executed until this method fires and by that /// time, Umbraco has performed it's bootup sequence. /// public override void Init() { base.Init(); OnApplicationInit(this, new EventArgs()); } /// /// Developers can override this method to modify objects on startup /// /// /// protected virtual void OnApplicationStarting(object sender, EventArgs e) { if (ApplicationStarting != null) { try { ApplicationStarting(sender, e); } catch (Exception ex) { LogHelper.Error("An error occurred in an ApplicationStarting event handler", ex); throw; } } } /// /// Developers can override this method to do anything they need to do once the application startup routine is completed. /// /// /// protected virtual void OnApplicationStarted(object sender, EventArgs e) { if (ApplicationStarted != null) { try { ApplicationStarted(sender, e); } catch (Exception ex) { LogHelper.Error("An error occurred in an ApplicationStarted event handler", ex); throw; } } } /// /// Called to raise the ApplicationInit event /// /// /// private void OnApplicationInit(object sender, EventArgs e) { if (ApplicationInit != null) { try { ApplicationInit(sender, e); } catch (Exception ex) { LogHelper.Error("An error occurred in an ApplicationInit event handler", ex); throw; } } } /// /// A method that can be overridden to invoke code when the application has an error. /// /// /// protected virtual void OnApplicationError(object sender, EventArgs e) { } protected void Application_Error(object sender, EventArgs e) { // Code that runs when an unhandled error occurs // Get the exception object. var exc = Server.GetLastError(); // Ignore HTTP errors if (exc.GetType() == typeof(HttpException)) { return; } Logger.Error("An unhandled exception occurred", exc); OnApplicationError(sender, e); } /// /// A method that can be overridden to invoke code when the application shuts down. /// /// /// protected virtual void OnApplicationEnd(object sender, EventArgs e) { } protected void Application_End(object sender, EventArgs e) { if (SystemUtilities.GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted) { //Try to log the detailed shutdown message (typical asp.net hack: http://weblogs.asp.net/scottgu/433194) try { var runtime = (HttpRuntime)typeof(HttpRuntime).InvokeMember("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null); if (runtime == null) return; var shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null); var shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null); var shutdownMsg = string.Format("{0}\r\n\r\n_shutDownMessage={1}\r\n\r\n_shutDownStack={2}", HostingEnvironment.ShutdownReason, shutDownMessage, shutDownStack); Logger.Info("Application shutdown. Details: " + shutdownMsg); } catch (Exception) { //if for some reason that fails, then log the normal output Logger.Info("Application shutdown. Reason: " + HostingEnvironment.ShutdownReason); } } OnApplicationEnd(sender, e); //Last thing to do is shutdown log4net LogManager.Shutdown(); } protected abstract IBootManager GetBootManager(); protected ILogger Logger { get { // LoggerResolver can resolve before resolution is frozen if (LoggerResolver.HasCurrent && LoggerResolver.Current.HasValue) { return LoggerResolver.Current.Logger; } return new HttpTraceLogger(); } } private class HttpTraceLogger : ILogger { public void Error(Type callingType, string message, Exception exception) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Warn(callingType.ToString(), message + Environment.NewLine + exception); } public void Warn(Type callingType, string message, params Func[] formatItems) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Warn(callingType.ToString(), string.Format(message, formatItems.Select(x => x()))); } public void WarnWithException(Type callingType, string message, Exception e, params Func[] formatItems) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Warn(callingType.ToString(), string.Format(message + Environment.NewLine + e, formatItems.Select(x => x()))); } public void Info(Type callingType, Func generateMessage) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Write(callingType.ToString(), generateMessage()); } public void Info(Type type, string generateMessageFormat, params Func[] formatItems) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Write(type.ToString(), string.Format(generateMessageFormat, formatItems.Select(x => x()))); } public void Debug(Type callingType, Func generateMessage) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Write(callingType.ToString(), generateMessage()); } public void Debug(Type type, string generateMessageFormat, params Func[] formatItems) { if (HttpContext.Current == null) return; HttpContext.Current.Trace.Write(type.ToString(), string.Format(generateMessageFormat, formatItems.Select(x => x()))); } } } }