Merge pull request #2227 from esdrubal/tzparse
[mono.git] / mcs / class / System.Web / System.Web / HttpApplicationFactory.cs
index be39334a72820c9b72db633d186a85d22a334809..597379a9bad1bf9fd94c6a0ead55e9c87673080f 100644 (file)
@@ -5,7 +5,7 @@
 //     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;
-#if NET_2_0
                static bool app_disabled = false;
-#endif
+               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)
@@ -102,7 +84,7 @@ namespace System.Web {
                                return false;
 
                        if (pi [0].ParameterType != typeof (object) ||
-                           pi [1].ParameterType != typeof (EventArgs))
+                           !typeof (EventArgs).IsAssignableFrom (pi [1].ParameterType))
                                return false;
                        
                        return true;
@@ -127,22 +109,52 @@ namespace System.Web {
 
                        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)
                {
+                       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;
@@ -150,12 +162,10 @@ namespace System.Web {
 
                Hashtable GetApplicationTypeEvents (HttpApplication app)
                {
-                       lock (this_lock) {
-                               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)
@@ -179,7 +189,9 @@ namespace System.Web {
                        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;
                }
 
@@ -190,7 +202,7 @@ namespace System.Web {
 
                        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;
                }
 
@@ -246,16 +258,13 @@ namespace System.Web {
 
                                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);
@@ -276,14 +285,14 @@ namespace System.Web {
                                NoParamsInvoker npi = new NoParamsInvoker (app, method);
                                evt.AddEventHandler (target, npi.FakeDelegate);
                        } else {
-                               evt.AddEventHandler (target, Delegate.CreateDelegate (
-                                                            evt.EventHandlerType, app,
-#if NET_2_0
-                                                            method
-#else
-                                                            method.Name
-#endif
-                                                    ));
+                               if (method.IsStatic) {
+                                       evt.AddEventHandler (target, Delegate.CreateDelegate (
+                                               evt.EventHandlerType, method));
+                               } else {
+                                       evt.AddEventHandler (target, Delegate.CreateDelegate (
+                                                                    evt.EventHandlerType, app,
+                                                                    method));
+                               }
                        }
                        
                }
@@ -299,7 +308,7 @@ namespace System.Web {
                        MethodInfo method = null;
                        HttpApplication app = null;
                        lock (factory.available_for_end) {
-                               method = factory.session_end;
+                               method = (MethodInfo) factory.session_end;
                                if (method == null)
                                        return;
 
@@ -329,14 +338,6 @@ namespace System.Web {
                }
                
                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);
@@ -346,7 +347,6 @@ namespace System.Web {
                                }
                                return theFactory.app_state;
                        }
-#endif
                }
 
                internal static Type AppType {
@@ -361,10 +361,8 @@ namespace System.Web {
                                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");
@@ -373,51 +371,80 @@ namespace System.Web {
                                                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
-#if NET_2_0
+                                       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));
-#else
-                                               app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
-#endif
-#endif
                                                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 ();
                                        }
 
-                                       WatchLocationForRestart("Global.asax");
-                                       WatchLocationForRestart("global.asax");
-                                       WatchLocationForRestart("Web.config");
-                                       WatchLocationForRestart("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 ();
@@ -427,12 +454,6 @@ namespace System.Web {
                                                WebConfigurationManager.ExtraAssemblies.Clear ();
                                        throw;
                                }
-#endif
-                               
-                               //
-                               // Now init the settings
-                               //
-
                        }
                }
                
@@ -442,10 +463,6 @@ namespace System.Web {
                //
                internal static HttpApplication GetApplication (HttpContext context)
                {
-#if TARGET_J2EE
-                       if (context.ApplicationInstance!=null)
-                               return context.ApplicationInstance;
-#endif
                        HttpApplicationFactory factory = theFactory;
                        HttpApplication app = null;
                        if (factory.app_start_needed){
@@ -455,13 +472,16 @@ namespace System.Web {
                                factory.InitType (context);
                                lock (factory) {
                                        if (factory.app_start_needed) {
-                                               foreach (string dir in HttpApplication.BinDirectories)
+                                               foreach (string dir in HttpApplication.BinDirs)
                                                        WatchLocationForRestart (dir, "*.dll");
-#if NET_2_0
+                                               // 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", "*");
-#endif
                                                app = factory.FireOnAppStart (context);
                                                factory.app_start_needed = false;
                                                return app;
@@ -469,6 +489,12 @@ namespace System.Web {
                                }
                        }
 
+                       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 ();
@@ -495,24 +521,33 @@ namespace System.Web {
 
                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 {
@@ -522,7 +557,7 @@ namespace System.Web {
 
                 internal static bool WatchLocationForRestart (string filter)
                {
-                       return WatchLocationForRestart ("", filter, false);
+                       return WatchLocationForRestart (String.Empty, filter, false);
                }
 
                internal static bool WatchLocationForRestart (string virtualPath, string filter)
@@ -537,7 +572,7 @@ namespace System.Web {
                        physicalPath = Path.Combine(physicalPath, virtualPath);
                        bool isDir = Directory.Exists(physicalPath);
                        bool isFile = isDir ? false : File.Exists(physicalPath);
-                       
+
                        if (isDir || isFile) {
                                // create the watcher
                                FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
@@ -555,12 +590,37 @@ namespace System.Web {
                        }
                }
 
-#if NET_2_0
                internal static bool ApplicationDisabled {
                        get { return app_disabled; }
                        set { app_disabled = value; }
                }
-#endif
+
+               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 ()
                {
@@ -570,6 +630,28 @@ namespace System.Web {
                        }
                }
 
+               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) {
@@ -585,6 +667,38 @@ namespace System.Web {
 
                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;