2008-11-21 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpApplicationFactory.cs
index ae45df1c4626e0beae7454e8bda817cd52c76544..d9260af0cf209b75aa05515358e92de6dafa8253 100644 (file)
@@ -34,14 +34,19 @@ using System.Web.UI;
 using System.Web.SessionState;
 using System.Web.Configuration;
 
-#if !TARGET_J2EE
 using System.Web.Compilation;
-#else
+#if TARGET_J2EE
 using vmw.common;
 #endif
 
+#if NET_2_0 && !TARGET_J2EE
+using System.CodeDom.Compiler;
+#endif
+
 namespace System.Web {
        class HttpApplicationFactory {
+               object this_lock = new object ();
+               
                // Initialized in InitType
 #if TARGET_J2EE
                static HttpApplicationFactory theFactory {
@@ -64,24 +69,22 @@ namespace System.Web {
 #else
                static HttpApplicationFactory theFactory = new HttpApplicationFactory();
 #endif
-
                MethodInfo session_end;
                bool needs_init = true;
                bool app_start_needed = true;
                Type app_type;
                HttpApplicationState app_state;
                Hashtable app_event_handlers;
-#if !TARGET_JVM
-               FileSystemWatcher app_file_watcher;
-               FileSystemWatcher bin_watcher;
-               FileSystemWatcher config_watcher;
+               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;
+               static string[] app_browsers_files = new string[0];
 #endif
                Stack available = new Stack ();
                Stack available_for_end = new Stack ();
                
-               // Watch this thing out when getting an instance
-               IHttpHandler custom_application;
-
                bool IsEventHandler (MethodInfo m)
                {
                        int pos = m.Name.IndexOf ('_');
@@ -125,22 +128,48 @@ 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)
                {
-                       lock (this) {
+                       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;
                        }
 
                        return app_event_handlers;
@@ -148,7 +177,7 @@ namespace System.Web {
 
                Hashtable GetApplicationTypeEvents (HttpApplication app)
                {
-                       lock (this) {
+                       lock (this_lock) {
                                if (app_event_handlers != null)
                                        return app_event_handlers;
 
@@ -177,7 +206,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;
                }
 
@@ -188,7 +219,8 @@ 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;
                }
 
                //
@@ -200,14 +232,16 @@ namespace System.Web {
                        theFactory.FireOnAppEnd ();
                }
 
-#if !TARGET_JVM
                static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
                {
                        FileSystemWatcher watcher = new FileSystemWatcher ();
 
                        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;
@@ -218,23 +252,6 @@ namespace System.Web {
                        return watcher;
                }
 
-               void OnAppFileRenamed (object sender, RenamedEventArgs args)
-               {
-                       OnAppFileChanged (sender, args);
-               }
-
-               void OnAppFileChanged (object sender, FileSystemEventArgs args)
-               {
-                       if (config_watcher != null)
-                               config_watcher.EnableRaisingEvents = false;
-                       if (bin_watcher != null)
-                               bin_watcher.EnableRaisingEvents = false;
-                       if (app_file_watcher != null)
-                               app_file_watcher.EnableRaisingEvents = false;
-                       HttpRuntime.UnloadAppDomain ();
-               }
-#endif
-
                internal static void AttachEvents (HttpApplication app)
                {
                        HttpApplicationFactory factory = theFactory;
@@ -285,15 +302,27 @@ namespace System.Web {
                        int length = method.GetParameters ().Length;
 
                        if (length == 0) {
-                               NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
+                               NoParamsInvoker npi = new NoParamsInvoker (app, method);
                                evt.AddEventHandler (target, npi.FakeDelegate);
                        } else {
                                evt.AddEventHandler (target, Delegate.CreateDelegate (
-                                                       evt.EventHandlerType, app, method.Name));
+                                                            evt.EventHandlerType, app,
+#if NET_2_0
+                                                            method
+#else
+                                                            method.Name
+#endif
+                                                    ));
                        }
+                       
                }
 
                internal static void InvokeSessionEnd (object state)
+               {
+                       InvokeSessionEnd (state, null, EventArgs.Empty);
+               }
+               
+               internal static void InvokeSessionEnd (object state, object source, EventArgs e)
                {
                        HttpApplicationFactory factory = theFactory;
                        MethodInfo method = null;
@@ -308,7 +337,7 @@ namespace System.Web {
 
                        app.SetSession ((HttpSessionState) state);
                        try {
-                               method.Invoke (app, new object [] {app, EventArgs.Empty});
+                               method.Invoke (app, new object [] {(source == null ? app : source), e});
                        } catch (Exception) {
                                // Ignore
                        }
@@ -349,65 +378,96 @@ namespace System.Web {
 #endif
                }
 
-               public static void SetCustomApplication (IHttpHandler customApplication)
-               {
-                       theFactory.custom_application = customApplication;
-               }
-
                internal static Type AppType {
                        get {
                                return theFactory.app_type;
                        }
                }
-
+               
                void InitType (HttpContext context)
                {
-                       lock (this) {
+                       lock (this_lock) {
                                if (!needs_init)
                                        return;
-                               
-                               string physical_app_path = context.Request.PhysicalApplicationPath;
-                               string app_file;
-                               
-                               app_file = Path.Combine (physical_app_path, "Global.asax");
-                               if (!File.Exists (app_file))
-                                       app_file = Path.Combine (physical_app_path, "global.asax");
 
-                       
 #if NET_2_0
-                               WebConfigurationManager.Init ();
-#else
-                               WebConfigurationSettings.Init (context);
+                               try {
 #endif
-                               
-                               if (File.Exists (app_file)) {
-#if TARGET_J2EE
-                                       app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
-#else
-                                       app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
-                                       if (app_type == null) {
-                                               string msg = String.Format ("Error compiling application file ({0}).", app_file);
-                                               throw new ApplicationException (msg);
+                                       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 = Path.Combine (physical_app_path, "global.asax");
+                                               if (!File.Exists (app_file))
+                                                       app_file = null;
                                        }
+                       
+#if !NET_2_0
+                                       WebConfigurationSettings.Init (context);
 #endif
-                               } else {
-                                       app_type = typeof (System.Web.HttpApplication);
-                                       app_state = new HttpApplicationState ();
-                               }
-
-#if !TARGET_JVM
-                               FileSystemEventHandler fseh = new FileSystemEventHandler (OnAppFileChanged);
-                               RenamedEventHandler reh = new RenamedEventHandler (OnAppFileRenamed);
-                               app_file_watcher = CreateWatcher (app_file, fseh, reh);
+               
+#if NET_2_0 && !TARGET_J2EE
+                                       AppResourcesCompiler ac = new AppResourcesCompiler (context);
+                                       ac.Compile ();
 
-                               string config_file = Path.Combine (physical_app_path, "Web.config");
-                               if (!File.Exists (config_file))
-                                       config_file = Path.Combine (physical_app_path, "web.config");
+#if WEBSERVICES_DEP
+                                       AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
+                                       awrc.Compile ();
+#endif
+                                       
+                                       // Todo: Generate profile properties assembly from Web.config here
+                               
+                                       AppCodeCompiler acc = new AppCodeCompiler ();
+                                       acc.Compile ();
+
+                                       // 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");
+                                       }
+#endif
 
-                               config_watcher = CreateWatcher (config_file, fseh, reh);
+                                       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
+                                               app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
+#else
+                                               app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
 #endif
-                               needs_init = false;
+#endif
+                                               if (app_type == null) {
+                                                       string msg = String.Format ("Error compiling application file ({0}).", app_file);
+                                                       throw new ApplicationException (msg);
+                                               }
+                                       } else {
+                                               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");
+                                       needs_init = false;
+#if NET_2_0
+                               } catch (Exception) {
+                                       if (BuildManager.CodeAssemblies != null)
+                                               BuildManager.CodeAssemblies.Clear ();
+                                       if (BuildManager.TopLevelAssemblies != null)
+                                               BuildManager.TopLevelAssemblies.Clear ();
+                                       if (WebConfigurationManager.ExtraAssemblies != null)
+                                               WebConfigurationManager.ExtraAssemblies.Clear ();
+                                       throw;
+                               }
+#endif
+                               
                                //
                                // Now init the settings
                                //
@@ -421,6 +481,10 @@ 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){
@@ -430,17 +494,19 @@ namespace System.Web {
                                factory.InitType (context);
                                lock (factory) {
                                        if (factory.app_start_needed) {
-#if !TARGET_JVM
-                                               string bin = HttpRuntime.BinDirectory;
-                                               if (Directory.Exists (bin))
-                                                       bin = Path.Combine (bin, "*.dll");
-
-                                               FileSystemEventHandler fseh = new FileSystemEventHandler (factory.OnAppFileChanged);
-                                               RenamedEventHandler reh = new RenamedEventHandler (factory.OnAppFileRenamed);
-                                               // We watch bin or bin/*.dll if the directory exists
-                                               factory.bin_watcher = CreateWatcher (bin, fseh, reh);
+                                               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);
+                                               app = factory.FireOnAppStart (context);
                                                factory.app_start_needed = false;
                                                return app;
                                        }
@@ -496,6 +562,113 @@ namespace System.Web {
                internal static bool ContextAvailable {
                        get { return theFactory != null && !theFactory.app_start_needed; }
                }
+
+
+                internal static bool WatchLocationForRestart (string filter)
+               {
+                       return WatchLocationForRestart ("", filter, false);
+               }
+
+               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 (isDir || isFile) {
+                               // create the watcher
+                               FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
+                               RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
+                               FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
+                               if (isDir)
+                                       watcher.IncludeSubdirectories = watchSubdirs;
+                               
+                               lock (watchers_lock) {
+                                       watchers.Add(watcher);
+                               }
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
+
+#if NET_2_0
+               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_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
+                                               if (Directory.Exists (machine_browsers_path)) {
+                                                       string[] machine_browsers_files 
+                                                               = Directory.GetFiles (machine_browsers_path, "*.browser");
+                                                       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;
+                       }
+               }
+#endif
+               
+               internal static void DisableWatchers ()
+               {
+                       lock (watchers_lock) {
+                               foreach (FileSystemWatcher watcher in watchers)
+                                       watcher.EnableRaisingEvents = false;
+                       }
+               }
+
+               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)
+               {
+                       lock (watchers_lock) {
+                               if(app_shutdown)
+                                       return;
+                               app_shutdown = true;
+
+                               // Disable event raising to avoid concurrent restarts
+                               DisableWatchers ();
+                               
+                               // Restart application
+                               HttpRuntime.UnloadAppDomain();
+                       }
+               }
        }
 }