[asp.net] Implemented BuildManager.GetObjectFactory
[mono.git] / mcs / class / System.Web / System.Web / HttpApplicationFactory.cs
1 //
2 // System.Web.HttpApplicationFactory
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
8 // (c) Copyright 2004-2009 Novell, Inc. (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 using System;
30 using System.Collections;
31 using System.Globalization;
32 using System.IO;
33 using System.Reflection;
34 using System.Web.UI;
35 using System.Web.SessionState;
36 using System.Web.Configuration;
37 using System.Threading;
38 using System.Web.Util;
39
40 using System.Web.Compilation;
41 #if TARGET_J2EE
42 using vmw.common;
43 #else
44 using System.CodeDom.Compiler;
45 #endif
46
47 namespace System.Web
48 {
49         sealed class HttpApplicationFactory
50         {
51                 object this_lock = new object ();
52                 
53                 // Initialized in InitType
54 #if TARGET_J2EE
55                 static HttpApplicationFactory theFactory {
56                         get
57                         {
58                                 HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
59                                 if (factory == null) {
60                                         lock(typeof(HttpApplicationFactory)) {
61                                                 factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
62                                                 if (factory == null) {
63                                                         factory = new HttpApplicationFactory();
64                                                         System.Threading.Thread.Sleep(1);
65                                                         AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
66                                                 }
67                                         }
68                                 }
69                                 return factory;
70                         }
71                 }
72 #else
73                 static HttpApplicationFactory theFactory = new HttpApplicationFactory();
74 #endif
75                 object session_end; // This is a MethodInfo
76                 bool needs_init = true;
77                 bool app_start_needed = true;
78                 bool have_app_events;
79                 Type app_type;
80                 HttpApplicationState app_state;
81                 Hashtable app_event_handlers;
82                 static ArrayList watchers = new ArrayList();
83                 static object watchers_lock = new object();
84                 static bool app_shutdown = false;
85                 static bool app_disabled = false;
86                 static string[] app_browsers_files = new string[0];
87                 static string[] default_machine_browsers_files = new string[0];
88                 static string[] app_mono_machine_browsers_files = new string[0];
89                 Stack available = new Stack ();
90                 object next_free;
91                 Stack available_for_end = new Stack ();
92                 
93                 bool IsEventHandler (MethodInfo m)
94                 {
95                         int pos = m.Name.IndexOf ('_');
96                         if (pos == -1 || (m.Name.Length - 1) <= pos)
97                                 return false;
98
99                         if (m.ReturnType != typeof (void))
100                                 return false;
101
102                         ParameterInfo [] pi = m.GetParameters ();
103                         int length = pi.Length;
104                         if (length == 0)
105                                 return true;
106
107                         if (length != 2)
108                                 return false;
109
110                         if (pi [0].ParameterType != typeof (object) ||
111                             !typeof (EventArgs).IsAssignableFrom (pi [1].ParameterType))
112                                 return false;
113                         
114                         return true;
115                 }
116
117                 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
118                 {
119                         string name = method.Name.Replace ("_On", "_");
120                         if (appTypeEventHandlers [name] == null) {
121                                 appTypeEventHandlers [name] = method;
122                                 return;
123                         }
124
125                         MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
126                         ArrayList list;
127                         if (old_method != null){
128                                 list = new ArrayList (4);
129                                 list.Add (old_method);
130                                 appTypeEventHandlers [name] = list;
131                         } else 
132                                 list = appTypeEventHandlers [name] as ArrayList;
133
134                         list.Add (method);
135                 }
136
137                 ArrayList GetMethodsDeep (Type type)
138                 {
139                         ArrayList al = new ArrayList ();
140                         MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance  | BindingFlags.Static | BindingFlags.FlattenHierarchy);
141                         al.AddRange (methods);
142
143                         Type t = type.BaseType;
144                         while (t != null) {
145                                 methods = t.GetMethods (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
146                                 al.AddRange (methods);
147                                 t = t.BaseType;
148                         }
149
150                         return al;
151                 }
152                 
153                 Hashtable GetApplicationTypeEvents (Type type)
154                 {
155                         if (have_app_events)
156                                 return app_event_handlers;
157
158                         lock (this_lock) {
159                                 if (app_event_handlers != null)
160                                         return app_event_handlers;
161
162                                 app_event_handlers = new Hashtable ();
163                                 ArrayList methods = GetMethodsDeep (type);
164                                 Hashtable used = null;
165                                 MethodInfo m;
166                                 string mname;
167                                 
168                                 foreach (object o in methods) {
169                                         m = o as MethodInfo;
170                                         if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m)) {
171                                                 mname = m.ToString ();
172                                                 if (used == null)
173                                                         used = new Hashtable ();
174                                                 else if (used.ContainsKey (mname))
175                                                         continue;
176                                                 used.Add (mname, m);
177                                                 AddEvent (m, app_event_handlers);
178                                         }
179                                 }
180                                 used = null;
181                                 have_app_events = true;
182                         }
183
184                         return app_event_handlers;
185                 }
186
187                 Hashtable GetApplicationTypeEvents (HttpApplication app)
188                 {
189                         if (have_app_events)
190                                 return app_event_handlers;
191
192                         return GetApplicationTypeEvents (app.GetType ());
193                 }
194
195                 bool FireEvent (string method_name, object target, object [] args)
196                 {
197                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
198                         MethodInfo method = possibleEvents [method_name] as MethodInfo;
199                         if (method == null)
200                                 return false;
201
202                         if (method.GetParameters ().Length == 0)
203                                 args = null;
204
205                         method.Invoke (target, args);
206
207                         return true;
208                 }
209
210                 HttpApplication FireOnAppStart (HttpContext context)
211                 {
212                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
213                         context.ApplicationInstance = app;
214                         app.SetContext (context);
215                         object [] args = new object [] {app, EventArgs.Empty};
216                         app.InApplicationStart = true;
217                         FireEvent ("Application_Start", app, args);
218                         app.InApplicationStart = false;
219                         return app;
220                 }
221
222                 void FireOnAppEnd ()
223                 {
224                         if (app_type == null)
225                                 return; // we didn't even get an application
226
227                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
228                         FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
229                         app.DisposeInternal ();
230                         app_type = null;
231                 }
232
233                 //
234                 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
235                 // To reproduce this in action, touch "global.asax" while XSP is running.
236                 //
237                 public static void Dispose ()
238                 {
239                         theFactory.FireOnAppEnd ();
240                 }
241
242                 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
243                 {
244                         FileSystemWatcher watcher = new FileSystemWatcher ();
245
246                         watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
247                         watcher.Filter = Path.GetFileName (file);
248                         
249                         // This will enable the Modify flag for Linux/inotify
250                         watcher.NotifyFilter |= NotifyFilters.Size;
251                         
252                         watcher.Changed += hnd;
253                         watcher.Created += hnd;
254                         watcher.Deleted += hnd;
255                         watcher.Renamed += reh;
256
257                         watcher.EnableRaisingEvents = true;
258
259                         return watcher;
260                 }
261
262                 internal static void AttachEvents (HttpApplication app)
263                 {
264                         HttpApplicationFactory factory = theFactory;
265                         Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
266                         foreach (string key in possibleEvents.Keys) {
267                                 int pos = key.IndexOf ('_');
268                                 string moduleName = key.Substring (0, pos);
269                                 object target;
270                                 if (moduleName == "Application") {
271                                         target = app;
272                                 } else {
273                                         target = app.Modules [moduleName];
274                                         if (target == null)
275                                                 continue;
276                                 }
277
278                                 string eventName = key.Substring (pos + 1);
279                                 EventInfo evt = target.GetType ().GetEvent (eventName);
280                                 if (evt == null)
281                                         continue;
282
283                                 string usualName = moduleName + "_" + eventName;
284                                 object methodData = possibleEvents [usualName];
285                                 if (methodData == null)
286                                         continue;
287
288                                 if (eventName == "End" && moduleName == "Session") {
289                                         Interlocked.CompareExchange (ref factory.session_end, methodData, null);
290                                         continue;
291                                 }
292
293                                 if (methodData is MethodInfo) {
294                                         factory.AddHandler (evt, target, app, (MethodInfo) methodData);
295                                         continue;
296                                 }
297
298                                 ArrayList list = (ArrayList) methodData;
299                                 foreach (MethodInfo method in list)
300                                         factory.AddHandler (evt, target, app, method);
301                         }
302                 }
303
304                 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
305                 {
306                         int length = method.GetParameters ().Length;
307
308                         if (length == 0) {
309                                 NoParamsInvoker npi = new NoParamsInvoker (app, method);
310                                 evt.AddEventHandler (target, npi.FakeDelegate);
311                         } else {
312                                 if (method.IsStatic) {
313                                         evt.AddEventHandler (target, Delegate.CreateDelegate (
314                                                 evt.EventHandlerType, method));
315                                 } else {
316                                         evt.AddEventHandler (target, Delegate.CreateDelegate (
317                                                                      evt.EventHandlerType, app,
318                                                                      method));
319                                 }
320                         }
321                         
322                 }
323
324                 internal static void InvokeSessionEnd (object state)
325                 {
326                         InvokeSessionEnd (state, null, EventArgs.Empty);
327                 }
328                 
329                 internal static void InvokeSessionEnd (object state, object source, EventArgs e)
330                 {
331                         HttpApplicationFactory factory = theFactory;
332                         MethodInfo method = null;
333                         HttpApplication app = null;
334                         lock (factory.available_for_end) {
335                                 method = (MethodInfo) factory.session_end;
336                                 if (method == null)
337                                         return;
338
339                                 app = GetApplicationForSessionEnd ();
340                         }
341
342                         app.SetSession ((HttpSessionState) state);
343                         try {
344                                 method.Invoke (app, new object [] {(source == null ? app : source), e});
345                         } catch (Exception) {
346                                 // Ignore
347                         }
348                         RecycleForSessionEnd (app);
349                 }
350
351                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
352                 {
353                         if (list == null || list.Count == 0)
354                                 return null;
355
356                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
357                         foreach (ObjectTagBuilder tag in list) {
358                                 coll.Add (tag);
359                         }
360
361                         return coll;
362                 }
363                 
364                 internal static HttpApplicationState ApplicationState {
365 #if TARGET_J2EE
366                         get {
367                                 HttpApplicationFactory factory = theFactory;
368                                 if (factory.app_state == null)
369                                         factory.app_state = new HttpApplicationState (null, null);
370                                 return factory.app_state;
371                         }
372 #else
373                         get {
374                                 if (theFactory.app_state == null) {
375                                         HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
376                                         HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
377
378                                         theFactory.app_state = new HttpApplicationState (app, ses);
379                                 }
380                                 return theFactory.app_state;
381                         }
382 #endif
383                 }
384
385                 internal static Type AppType {
386                         get {
387                                 return theFactory.app_type;
388                         }
389                 }
390                 
391                 void InitType (HttpContext context)
392                 {
393                         lock (this_lock) {
394                                 if (!needs_init)
395                                         return;
396
397                                 try {
398                                         string physical_app_path = HttpRuntime.AppDomainAppPath;
399                                         string app_file = null;
400                                         
401                                         app_file = Path.Combine (physical_app_path, "Global.asax");
402                                         if (!File.Exists (app_file)) {
403                                                 app_file = Path.Combine (physical_app_path, "global.asax");
404                                                 if (!File.Exists (app_file))
405                                                         app_file = null;
406                                         }
407 #if NET_4_0
408                                         BuildManager.CallPreStartMethods ();
409                                         BuildManager.CompilingTopLevelAssemblies = true;
410 #endif
411 #if !TARGET_J2EE
412                                         AppResourcesCompiler ac = new AppResourcesCompiler (context);
413                                         ac.Compile ();
414
415 #if WEBSERVICES_DEP
416                                         AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
417                                         awrc.Compile ();
418 #endif
419                                         // Todo: Generate profile properties assembly from Web.config here
420                                 
421                                         AppCodeCompiler acc = new AppCodeCompiler ();
422                                         acc.Compile ();
423
424                                         BuildManager.AllowReferencedAssembliesCaching = true;
425
426                                         // Get the default machine *.browser files.
427                                         string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
428                                         default_machine_browsers_files = new string[0];
429                                         if (Directory.Exists (default_machine_browsers_path)) {
430                                                 default_machine_browsers_files 
431                                                         = Directory.GetFiles (default_machine_browsers_path, "*.browser");
432                                         }
433                                         
434                                         // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files.  If there
435                                         // are we will be using them instead of the default machine *.browser files.
436                                         string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
437                                         app_mono_machine_browsers_files = new string[0];
438                                         if (Directory.Exists (app_mono_machine_browsers_path)) {
439                                                 app_mono_machine_browsers_files 
440                                                         = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
441                                         }
442                                                 
443                                         // Note whether there are any App_Browsers/*.browser files.  If there
444                                         // are we will be using *.browser files for sniffing in addition to browscap.ini
445                                         string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
446                                         app_browsers_files = new string[0];
447                                         if (Directory.Exists (app_browsers_path)) {
448                                                 app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
449                                         }
450 #endif
451 #if NET_4_0
452                                         BuildManager.CompilingTopLevelAssemblies = false;
453 #endif
454                                         app_type = BuildManager.GetPrecompiledApplicationType ();
455                                         if (app_type == null && app_file != null) {
456 #if TARGET_J2EE
457                                                 app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
458                                                 app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
459 #else
460                                                 app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
461 #endif
462                                                 if (app_type == null) {
463                                                         string msg = String.Format ("Error compiling application file ({0}).", app_file);
464                                                         throw new ApplicationException (msg);
465                                                 }
466                                         } else if (app_type == null) {
467                                                 app_type = typeof (System.Web.HttpApplication);
468                                                 app_state = new HttpApplicationState ();
469                                         }
470
471                                         WatchLocationForRestart ("?lobal.asax");
472 #if CODE_DISABLED_UNTIL_SYSTEM_CONFIGURATION_IS_FIXED
473                                         // This is the correct behavior, but until
474                                         // System.Configuration is fixed to properly reload
475                                         // configuration when it is modified on disk, we need to use
476                                         // the recursive watchers below.
477                                         WatchLocationForRestart ("?eb.?onfig");
478 #else
479                                         // This is to avoid startup delays. Inotify/FAM code looks
480                                         // recursively for all subdirectories and adds them to the
481                                         // watch set. This can take a lot of time for deep directory
482                                         // trees (see bug #490497)
483                                         ThreadPool.QueueUserWorkItem (delegate {
484                                                 try {
485                                                         WatchLocationForRestart (String.Empty, "?eb.?onfig", true);
486                                                 } catch (Exception e) {
487                                                         Console.Error.WriteLine (e);
488                                                 } }, null);
489 #endif
490                                         
491                                         needs_init = false;
492                                 } catch (Exception) {
493                                         if (BuildManager.CodeAssemblies != null)
494                                                 BuildManager.CodeAssemblies.Clear ();
495                                         if (BuildManager.TopLevelAssemblies != null)
496                                                 BuildManager.TopLevelAssemblies.Clear ();
497                                         if (WebConfigurationManager.ExtraAssemblies != null)
498                                                 WebConfigurationManager.ExtraAssemblies.Clear ();
499                                         throw;
500                                 }
501                         }
502                 }
503                 
504                 //
505                 // Multiple-threads might hit this one on startup, and we have
506                 // to delay-initialize until we have the HttpContext
507                 //
508                 internal static HttpApplication GetApplication (HttpContext context)
509                 {
510 #if TARGET_J2EE
511                         if (context.ApplicationInstance!=null)
512                                 return context.ApplicationInstance;
513 #endif
514                         HttpApplicationFactory factory = theFactory;
515                         HttpApplication app = null;
516                         if (factory.app_start_needed){
517                                 if (context == null)
518                                         return null;
519
520                                 factory.InitType (context);
521                                 lock (factory) {
522                                         if (factory.app_start_needed) {
523                                                 foreach (string dir in HttpApplication.BinDirs)
524                                                         WatchLocationForRestart (dir, "*.dll");
525                                                 // Restart if the App_* directories are created...
526                                                 WatchLocationForRestart (".", "App_Code");
527                                                 WatchLocationForRestart (".", "App_Browsers");
528                                                 WatchLocationForRestart (".", "App_GlobalResources");
529                                                 // ...or their contents is changed.
530                                                 WatchLocationForRestart ("App_Code", "*", true);
531                                                 WatchLocationForRestart ("App_Browsers", "*");
532                                                 WatchLocationForRestart ("App_GlobalResources", "*");
533                                                 app = factory.FireOnAppStart (context);
534                                                 factory.app_start_needed = false;
535                                                 return app;
536                                         }
537                                 }
538                         }
539
540                         app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
541                         if (app != null) {
542                                 app.RequestCompleted = false;
543                                 return app;
544                         }
545
546                         lock (factory.available) {
547                                 if (factory.available.Count > 0) {
548                                         app = (HttpApplication) factory.available.Pop ();
549                                         app.RequestCompleted = false;
550                                         return app;
551                                 }
552                         }
553                         
554                         return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
555                 }
556
557                 // The lock is in InvokeSessionEnd
558                 static HttpApplication GetApplicationForSessionEnd ()
559                 {
560                         HttpApplicationFactory factory = theFactory;
561                         if (factory.available_for_end.Count > 0)
562                                 return (HttpApplication) factory.available_for_end.Pop ();
563
564                         HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
565                         app.InitOnce (false);
566
567                         return app;
568                 }
569
570                 internal static void RecycleForSessionEnd (HttpApplication app)
571                 {
572                         bool dispose = false;
573                         HttpApplicationFactory factory = theFactory;
574                         lock (factory.available_for_end) {
575                                 if (factory.available_for_end.Count < 64)
576                                         factory.available_for_end.Push (app);
577                                 else
578                                         dispose = true;
579                         }
580                         if (dispose)
581                                 app.Dispose ();
582                 }
583
584                 internal static void Recycle (HttpApplication app)
585                 {
586                         bool dispose = false;
587                         HttpApplicationFactory factory = theFactory;
588                         if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
589                                 return;
590
591                         lock (factory.available) {
592                                 if (factory.available.Count < 64)
593                                         factory.available.Push (app);
594                                 else
595                                         dispose = true;
596                         }
597                         if (dispose)
598                                 app.Dispose ();
599                 }
600
601                 internal static bool ContextAvailable {
602                         get { return theFactory != null && !theFactory.app_start_needed; }
603                 }
604
605
606                 internal static bool WatchLocationForRestart (string filter)
607                 {
608                         return WatchLocationForRestart (String.Empty, filter, false);
609                 }
610
611                 internal static bool WatchLocationForRestart (string virtualPath, string filter)
612                 {
613                         return WatchLocationForRestart (virtualPath, filter, false);
614                 }
615                 
616                 internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
617                 {
618                         // map the path to the physical one
619                         string physicalPath = HttpRuntime.AppDomainAppPath;
620                         physicalPath = Path.Combine(physicalPath, virtualPath);
621                         bool isDir = Directory.Exists(physicalPath);
622                         bool isFile = isDir ? false : File.Exists(physicalPath);
623
624                         if (isDir || isFile) {
625                                 // create the watcher
626                                 FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
627                                 RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
628                                 FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
629                                 if (isDir)
630                                         watcher.IncludeSubdirectories = watchSubdirs;
631                                 
632                                 lock (watchers_lock) {
633                                         watchers.Add(watcher);
634                                 }
635                                 return true;
636                         } else {
637                                 return false;
638                         }
639                 }
640
641                 internal static bool ApplicationDisabled {
642                         get { return app_disabled; }
643                         set { app_disabled = value; }
644                 }
645
646                 internal static string[] AppBrowsersFiles {
647                         get { return app_browsers_files; }
648                 }
649                 
650                 static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
651                 static object capabilities_processor_lock = new object();
652                 internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
653                         get {
654                                 lock (capabilities_processor_lock) {
655                                         if (capabilities_processor == null) {
656                                                 capabilities_processor = new System.Web.Configuration.nBrowser.Build();
657                                                 string[] machine_browsers_files = app_mono_machine_browsers_files;
658                                                 if (machine_browsers_files.Length == 0) {
659                                                         machine_browsers_files = default_machine_browsers_files;
660                                                 }
661                                                 foreach (string f in machine_browsers_files) {
662                                                         capabilities_processor.AddBrowserFile(f);
663                                                 }
664                                                 foreach (string f in app_browsers_files) {
665                                                         capabilities_processor.AddBrowserFile(f);
666                                                 }
667                                         }
668                                 }
669                                 return capabilities_processor;
670                         }
671                 }
672                 
673                 internal static void DisableWatchers ()
674                 {
675                         lock (watchers_lock) {
676                                 foreach (FileSystemWatcher watcher in watchers)
677                                         watcher.EnableRaisingEvents = false;
678                         }
679                 }
680
681                 internal static void DisableWatcher (string virtualPath, string filter)
682                 {
683                         EnableWatcherEvents (virtualPath, filter, false);
684                 }
685
686                 internal static void EnableWatcher (string virtualPath, string filter)
687                 {
688                         EnableWatcherEvents (virtualPath, filter, true);
689                 }
690                 
691                 static void EnableWatcherEvents (string virtualPath, string filter, bool enable)
692                 {
693                         lock (watchers_lock) {
694                                 foreach (FileSystemWatcher watcher in watchers) {
695                                         if (String.Compare (watcher.Path, virtualPath, StringComparison.Ordinal) != 0 || String.Compare (watcher.Filter, filter, StringComparison.Ordinal) != 0)
696                                                 continue;
697                                         
698                                         watcher.EnableRaisingEvents = enable;
699                                 }
700                         }
701                 }
702                 
703                 internal static void EnableWatchers ()
704                 {
705                         lock (watchers_lock) {
706                                 foreach (FileSystemWatcher watcher in watchers)
707                                         watcher.EnableRaisingEvents = true;
708                         }
709                 }
710                 
711                 static void OnFileRenamed(object sender, RenamedEventArgs args)
712                 {
713                         OnFileChanged(sender, args);
714                 }
715
716                 static void OnFileChanged(object sender, FileSystemEventArgs args)
717                 {
718                         if (HttpRuntime.DomainUnloading)
719                                 return;
720                         string name = args.Name;
721                         bool isConfig = false;
722
723                         if (StrUtils.EndsWith (name, "onfig", true)) {
724                                 if (String.Compare (Path.GetFileName (name), "web.config", true, Helpers.InvariantCulture) != 0)
725                                         return;
726                                 isConfig = true;
727                         } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true, Helpers.InvariantCulture) != 0)
728                                 return;
729
730                         Console.WriteLine ("Change: " + name);
731
732                         // {Inotify,FAM}Watcher will notify about events for a directory regardless
733                         // of the filter pattern. This might be a bug in the watchers code, but
734                         // since I couldn't find any rationale for the code in there I'd opted for
735                         // not removing it and instead working around the issue here. Fix for bug
736                         // #495011
737                         FileSystemWatcher watcher = sender as FileSystemWatcher;
738                         if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true, Helpers.InvariantCulture) == 0 && Directory.Exists (name))
739                                 return;
740
741                         // We re-enable suppression here since WebConfigurationManager will disable
742                         // it after save is done. WebConfigurationManager is called twice by
743                         // Configuration - just after opening the target file and just after closing
744                         // it. For that reason we will receive two change notifications and if we
745                         // disabled suppression here, it would reload the application on the second
746                         // change notification.
747                         if (isConfig && WebConfigurationManager.SuppressAppReload (true))
748                                 return;
749                         
750                         lock (watchers_lock) {
751                                 if(app_shutdown)
752                                         return;
753                                 app_shutdown = true;
754
755                                 // Disable event raising to avoid concurrent restarts
756                                 DisableWatchers ();
757                                 
758                                 // Restart application
759                                 HttpRuntime.UnloadAppDomain();
760                         }
761                 }
762         }
763 }
764