// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
-// (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
+// (c) Copyright 2004-2009 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
//
using System;
using System.Collections;
+using System.Globalization;
using System.IO;
using System.Reflection;
using System.Web.UI;
using System.Web.SessionState;
using System.Web.Configuration;
+using System.Threading;
+using System.Web.Util;
using System.Web.Compilation;
-#if TARGET_J2EE
-using vmw.common;
-#endif
-
-#if NET_2_0 && !TARGET_J2EE
using System.CodeDom.Compiler;
-#endif
-namespace System.Web {
- class HttpApplicationFactory {
+namespace System.Web
+{
+ sealed class HttpApplicationFactory
+ {
+ object this_lock = new object ();
+
// Initialized in InitType
-#if TARGET_J2EE
- static HttpApplicationFactory theFactory {
- get
- {
- HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
- if (factory == null) {
- lock(typeof(HttpApplicationFactory)) {
- factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
- if (factory == null) {
- factory = new HttpApplicationFactory();
- System.Threading.Thread.Sleep(1);
- AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
- }
- }
- }
- return factory;
- }
- }
-#else
static HttpApplicationFactory theFactory = new HttpApplicationFactory();
-#endif
-
-
- MethodInfo session_end;
+ object session_end; // This is a MethodInfo
bool needs_init = true;
bool app_start_needed = true;
+ bool have_app_events;
Type app_type;
HttpApplicationState app_state;
Hashtable app_event_handlers;
static ArrayList watchers = new ArrayList();
static object watchers_lock = new object();
static bool app_shutdown = false;
+ static bool app_disabled = false;
+ static string[] app_browsers_files = new string[0];
+ static string[] default_machine_browsers_files = new string[0];
+ static string[] app_mono_machine_browsers_files = new string[0];
Stack available = new Stack ();
+ object next_free;
Stack available_for_end = new Stack ();
bool IsEventHandler (MethodInfo m)
return false;
if (pi [0].ParameterType != typeof (object) ||
- pi [1].ParameterType != typeof (EventArgs))
+ !typeof (EventArgs).IsAssignableFrom (pi [1].ParameterType))
return false;
return true;
list.Add (method);
}
+
+ ArrayList GetMethodsDeep (Type type)
+ {
+ ArrayList al = new ArrayList ();
+ MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
+ al.AddRange (methods);
+
+ Type t = type.BaseType;
+ while (t != null) {
+ methods = t.GetMethods (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
+ al.AddRange (methods);
+ t = t.BaseType;
+ }
+
+ return al;
+ }
Hashtable GetApplicationTypeEvents (Type type)
{
- lock (this) {
+ if (have_app_events)
+ return app_event_handlers;
+
+ lock (this_lock) {
if (app_event_handlers != null)
return app_event_handlers;
app_event_handlers = new Hashtable ();
- BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.Static;
-
- MethodInfo [] methods = type.GetMethods (flags);
- foreach (MethodInfo m in methods) {
- if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m))
+ ArrayList methods = GetMethodsDeep (type);
+ Hashtable used = null;
+ MethodInfo m;
+ string mname;
+
+ foreach (object o in methods) {
+ m = o as MethodInfo;
+ if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m)) {
+ mname = m.ToString ();
+ if (used == null)
+ used = new Hashtable ();
+ else if (used.ContainsKey (mname))
+ continue;
+ used.Add (mname, m);
AddEvent (m, app_event_handlers);
+ }
}
+ used = null;
+ have_app_events = true;
}
return app_event_handlers;
Hashtable GetApplicationTypeEvents (HttpApplication app)
{
- lock (this) {
- if (app_event_handlers != null)
- return app_event_handlers;
+ if (have_app_events)
+ return app_event_handlers;
- return GetApplicationTypeEvents (app.GetType ());
- }
+ return GetApplicationTypeEvents (app.GetType ());
}
bool FireEvent (string method_name, object target, object [] args)
context.ApplicationInstance = app;
app.SetContext (context);
object [] args = new object [] {app, EventArgs.Empty};
+ app.InApplicationStart = true;
FireEvent ("Application_Start", app, args);
+ app.InApplicationStart = false;
return app;
}
HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
- app.Dispose ();
+ app.DisposeInternal ();
+ app_type = null;
}
//
watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
watcher.Filter = Path.GetFileName (file);
-
+
+ // This will enable the Modify flag for Linux/inotify
+ watcher.NotifyFilter |= NotifyFilters.Size;
+
watcher.Changed += hnd;
watcher.Created += hnd;
watcher.Deleted += hnd;
string usualName = moduleName + "_" + eventName;
object methodData = possibleEvents [usualName];
- if (methodData != null && eventName == "End" && moduleName == "Session") {
- lock (factory) {
- if (factory.session_end == null)
- factory.session_end = (MethodInfo) methodData;
- }
+ if (methodData == null)
continue;
- }
- if (methodData == null)
+ if (eventName == "End" && moduleName == "Session") {
+ Interlocked.CompareExchange (ref factory.session_end, methodData, null);
continue;
+ }
if (methodData is MethodInfo) {
factory.AddHandler (evt, target, app, (MethodInfo) methodData);
NoParamsInvoker npi = new NoParamsInvoker (app, method);
evt.AddEventHandler (target, npi.FakeDelegate);
} else {
- evt.AddEventHandler (target, Delegate.CreateDelegate (
-#if NET_2_0
- evt.EventHandlerType, app, method));
-#else
- evt.EventHandlerType, app, method.Name));
-#endif
+ if (method.IsStatic) {
+ evt.AddEventHandler (target, Delegate.CreateDelegate (
+ evt.EventHandlerType, method));
+ } else {
+ evt.AddEventHandler (target, Delegate.CreateDelegate (
+ evt.EventHandlerType, app,
+ method));
+ }
}
+
}
internal static void InvokeSessionEnd (object state)
MethodInfo method = null;
HttpApplication app = null;
lock (factory.available_for_end) {
- method = factory.session_end;
+ method = (MethodInfo) factory.session_end;
if (method == null)
return;
}
internal static HttpApplicationState ApplicationState {
-#if TARGET_J2EE
- get {
- HttpApplicationFactory factory = theFactory;
- if (factory.app_state == null)
- factory.app_state = new HttpApplicationState (null, null);
- return factory.app_state;
- }
-#else
get {
if (theFactory.app_state == null) {
HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
}
return theFactory.app_state;
}
-#endif
}
internal static Type AppType {
void InitType (HttpContext context)
{
- lock (this) {
+ lock (this_lock) {
if (!needs_init)
return;
-#if NET_2_0
try {
-#endif
- string physical_app_path = context.Request.PhysicalApplicationPath;
+ string physical_app_path = HttpRuntime.AppDomainAppPath;
string app_file = null;
app_file = Path.Combine (physical_app_path, "Global.asax");
if (!File.Exists (app_file))
app_file = null;
}
-
-#if !NET_2_0
- WebConfigurationSettings.Init (context);
-#endif
-
-#if NET_2_0 && !TARGET_J2EE
+ BuildManager.CallPreStartMethods ();
+ BuildManager.CompilingTopLevelAssemblies = true;
AppResourcesCompiler ac = new AppResourcesCompiler (context);
ac.Compile ();
-
- // Todo: Process App_WebResources here
-
+
+#if WEBSERVICES_DEP
+ AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
+ awrc.Compile ();
+#endif
// Todo: Generate profile properties assembly from Web.config here
- // Todo: Compile code from App_Code here
AppCodeCompiler acc = new AppCodeCompiler ();
acc.Compile ();
-#endif
- if (app_file != null) {
-#if TARGET_J2EE
- app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
- app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
-#else
- app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
-#endif
+ BuildManager.AllowReferencedAssembliesCaching = true;
+
+ // Get the default machine *.browser files.
+ string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
+ default_machine_browsers_files = new string[0];
+ if (Directory.Exists (default_machine_browsers_path)) {
+ default_machine_browsers_files
+ = Directory.GetFiles (default_machine_browsers_path, "*.browser");
+ }
+
+ // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files. If there
+ // are we will be using them instead of the default machine *.browser files.
+ string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
+ app_mono_machine_browsers_files = new string[0];
+ if (Directory.Exists (app_mono_machine_browsers_path)) {
+ app_mono_machine_browsers_files
+ = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
+ }
+
+ // Note whether there are any App_Browsers/*.browser files. If there
+ // are we will be using *.browser files for sniffing in addition to browscap.ini
+ string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
+ app_browsers_files = new string[0];
+ if (Directory.Exists (app_browsers_path)) {
+ app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
+ }
+ BuildManager.CompilingTopLevelAssemblies = false;
+ app_type = BuildManager.GetPrecompiledApplicationType ();
+ if (app_type == null && app_file != null) {
+ app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
if (app_type == null) {
string msg = String.Format ("Error compiling application file ({0}).", app_file);
throw new ApplicationException (msg);
}
- } else {
+ } else if (app_type == null) {
app_type = typeof (System.Web.HttpApplication);
app_state = new HttpApplicationState ();
}
- if (app_file != null)
- WatchLocationForRestart(app_file);
-
- if (File.Exists(Path.Combine(physical_app_path, "Web.config")))
- WatchLocationForRestart("Web.config");
- else if (File.Exists(Path.Combine(physical_app_path, "web.config")))
- WatchLocationForRestart("web.config");
- else if (File.Exists(Path.Combine(physical_app_path, "Web.Config")))
- WatchLocationForRestart("Web.Config");
+ WatchLocationForRestart ("?lobal.asax");
+#if CODE_DISABLED_UNTIL_SYSTEM_CONFIGURATION_IS_FIXED
+ // This is the correct behavior, but until
+ // System.Configuration is fixed to properly reload
+ // configuration when it is modified on disk, we need to use
+ // the recursive watchers below.
+ WatchLocationForRestart ("?eb.?onfig");
+#else
+ // This is to avoid startup delays. Inotify/FAM code looks
+ // recursively for all subdirectories and adds them to the
+ // watch set. This can take a lot of time for deep directory
+ // trees (see bug #490497)
+ ThreadPool.QueueUserWorkItem (delegate {
+ try {
+ WatchLocationForRestart (String.Empty, "?eb.?onfig", true);
+ } catch (Exception e) {
+ Console.Error.WriteLine (e);
+ } }, null);
+#endif
+
needs_init = false;
-#if NET_2_0
} catch (Exception) {
if (BuildManager.CodeAssemblies != null)
BuildManager.CodeAssemblies.Clear ();
WebConfigurationManager.ExtraAssemblies.Clear ();
throw;
}
-#endif
-
- //
- // Now init the settings
- //
-
}
}
factory.InitType (context);
lock (factory) {
if (factory.app_start_needed) {
- WatchLocationForRestart (AppDomain.CurrentDomain.SetupInformation.PrivateBinPath,
- "*.dll");
-#if NET_2_0
- WatchLocationForRestart ("App_Code", "");
- WatchLocationForRestart ("App_Browsers", "");
- WatchLocationForRestart ("App_GlobalResources", "");
-#endif
+ foreach (string dir in HttpApplication.BinDirs)
+ WatchLocationForRestart (dir, "*.dll");
+ // Restart if the App_* directories are created...
+ WatchLocationForRestart (".", "App_Code");
+ WatchLocationForRestart (".", "App_Browsers");
+ WatchLocationForRestart (".", "App_GlobalResources");
+ // ...or their contents is changed.
+ WatchLocationForRestart ("App_Code", "*", true);
+ WatchLocationForRestart ("App_Browsers", "*");
+ WatchLocationForRestart ("App_GlobalResources", "*");
app = factory.FireOnAppStart (context);
factory.app_start_needed = false;
return app;
}
}
+ app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
+ if (app != null) {
+ app.RequestCompleted = false;
+ return app;
+ }
+
lock (factory.available) {
if (factory.available.Count > 0) {
app = (HttpApplication) factory.available.Pop ();
internal static void RecycleForSessionEnd (HttpApplication app)
{
+ bool dispose = false;
HttpApplicationFactory factory = theFactory;
lock (factory.available_for_end) {
- if (factory.available_for_end.Count < 32)
+ if (factory.available_for_end.Count < 64)
factory.available_for_end.Push (app);
else
- app.Dispose ();
+ dispose = true;
}
+ if (dispose)
+ app.Dispose ();
}
internal static void Recycle (HttpApplication app)
{
+ bool dispose = false;
HttpApplicationFactory factory = theFactory;
+ if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
+ return;
+
lock (factory.available) {
- if (factory.available.Count < 32)
+ if (factory.available.Count < 64)
factory.available.Push (app);
else
- app.Dispose ();
+ dispose = true;
}
+ if (dispose)
+ app.Dispose ();
}
internal static bool ContextAvailable {
get { return theFactory != null && !theFactory.app_start_needed; }
}
-
- internal static bool WatchLocationForRestart(string filter)
+
+
+ internal static bool WatchLocationForRestart (string filter)
{
- return WatchLocationForRestart("", filter);
+ return WatchLocationForRestart (String.Empty, filter, false);
}
-
- internal static bool WatchLocationForRestart(string virtualPath, string filter)
+
+ internal static bool WatchLocationForRestart (string virtualPath, string filter)
+ {
+ return WatchLocationForRestart (virtualPath, filter, false);
+ }
+
+ internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
{
// map the path to the physical one
string physicalPath = HttpRuntime.AppDomainAppPath;
physicalPath = Path.Combine(physicalPath, virtualPath);
+ bool isDir = Directory.Exists(physicalPath);
+ bool isFile = isDir ? false : File.Exists(physicalPath);
- if (Directory.Exists(physicalPath) || File.Exists(physicalPath)) {
- physicalPath = Path.Combine(physicalPath, filter);
-
+ if (isDir || isFile) {
// create the watcher
FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
- FileSystemWatcher watcher = CreateWatcher(physicalPath, fseh, reh);
-
+ FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
+ if (isDir)
+ watcher.IncludeSubdirectories = watchSubdirs;
+
lock (watchers_lock) {
watchers.Add(watcher);
}
}
}
+ internal static bool ApplicationDisabled {
+ get { return app_disabled; }
+ set { app_disabled = value; }
+ }
+
+ internal static string[] AppBrowsersFiles {
+ get { return app_browsers_files; }
+ }
+
+ static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
+ static object capabilities_processor_lock = new object();
+ internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
+ get {
+ lock (capabilities_processor_lock) {
+ if (capabilities_processor == null) {
+ capabilities_processor = new System.Web.Configuration.nBrowser.Build();
+ string[] machine_browsers_files = app_mono_machine_browsers_files;
+ if (machine_browsers_files.Length == 0) {
+ machine_browsers_files = default_machine_browsers_files;
+ }
+ foreach (string f in machine_browsers_files) {
+ capabilities_processor.AddBrowserFile(f);
+ }
+ foreach (string f in app_browsers_files) {
+ capabilities_processor.AddBrowserFile(f);
+ }
+ }
+ }
+ return capabilities_processor;
+ }
+ }
+
+ internal static void DisableWatchers ()
+ {
+ lock (watchers_lock) {
+ foreach (FileSystemWatcher watcher in watchers)
+ watcher.EnableRaisingEvents = false;
+ }
+ }
+
+ internal static void DisableWatcher (string virtualPath, string filter)
+ {
+ EnableWatcherEvents (virtualPath, filter, false);
+ }
+
+ internal static void EnableWatcher (string virtualPath, string filter)
+ {
+ EnableWatcherEvents (virtualPath, filter, true);
+ }
+
+ static void EnableWatcherEvents (string virtualPath, string filter, bool enable)
+ {
+ lock (watchers_lock) {
+ foreach (FileSystemWatcher watcher in watchers) {
+ if (String.Compare (watcher.Path, virtualPath, StringComparison.Ordinal) != 0 || String.Compare (watcher.Filter, filter, StringComparison.Ordinal) != 0)
+ continue;
+
+ watcher.EnableRaisingEvents = enable;
+ }
+ }
+ }
+
+ internal static void EnableWatchers ()
+ {
+ lock (watchers_lock) {
+ foreach (FileSystemWatcher watcher in watchers)
+ watcher.EnableRaisingEvents = true;
+ }
+ }
+
static void OnFileRenamed(object sender, RenamedEventArgs args)
{
OnFileChanged(sender, args);
static void OnFileChanged(object sender, FileSystemEventArgs args)
{
+ if (HttpRuntime.DomainUnloading)
+ return;
+ string name = args.Name;
+ bool isConfig = false;
+
+ if (StrUtils.EndsWith (name, "onfig", true)) {
+ if (String.Compare (Path.GetFileName (name), "web.config", true, Helpers.InvariantCulture) != 0)
+ return;
+ isConfig = true;
+ } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true, Helpers.InvariantCulture) != 0)
+ return;
+
+ Console.WriteLine ("Change: " + name);
+
+ // {Inotify,FAM}Watcher will notify about events for a directory regardless
+ // of the filter pattern. This might be a bug in the watchers code, but
+ // since I couldn't find any rationale for the code in there I'd opted for
+ // not removing it and instead working around the issue here. Fix for bug
+ // #495011
+ FileSystemWatcher watcher = sender as FileSystemWatcher;
+ if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true, Helpers.InvariantCulture) == 0 && Directory.Exists (name))
+ return;
+
+ // We re-enable suppression here since WebConfigurationManager will disable
+ // it after save is done. WebConfigurationManager is called twice by
+ // Configuration - just after opening the target file and just after closing
+ // it. For that reason we will receive two change notifications and if we
+ // disabled suppression here, it would reload the application on the second
+ // change notification.
+ if (isConfig && WebConfigurationManager.SuppressAppReload (true))
+ return;
+
lock (watchers_lock) {
if(app_shutdown)
return;
app_shutdown = true;
// Disable event raising to avoid concurrent restarts
- foreach (FileSystemWatcher watcher in watchers) {
- watcher.EnableRaisingEvents = false;
- }
+ DisableWatchers ();
+
// Restart application
HttpRuntime.UnloadAppDomain();
}