2010-05-17 Marek Habersack <mhabersack@novell.com>
[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 #endif
410 #if !TARGET_J2EE
411                                         AppResourcesCompiler ac = new AppResourcesCompiler (context);
412                                         ac.Compile ();
413
414 #if WEBSERVICES_DEP
415                                         AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
416                                         awrc.Compile ();
417 #endif
418                                         // Todo: Generate profile properties assembly from Web.config here
419                                 
420                                         AppCodeCompiler acc = new AppCodeCompiler ();
421                                         acc.Compile ();
422
423                                         BuildManager.AllowReferencedAssembliesCaching = true;
424
425                                         // Get the default machine *.browser files.
426                                         string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
427                                         default_machine_browsers_files = new string[0];
428                                         if (Directory.Exists (default_machine_browsers_path)) {
429                                                 default_machine_browsers_files 
430                                                         = Directory.GetFiles (default_machine_browsers_path, "*.browser");
431                                         }
432                                         
433                                         // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files.  If there
434                                         // are we will be using them instead of the default machine *.browser files.
435                                         string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
436                                         app_mono_machine_browsers_files = new string[0];
437                                         if (Directory.Exists (app_mono_machine_browsers_path)) {
438                                                 app_mono_machine_browsers_files 
439                                                         = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
440                                         }
441                                                 
442                                         // Note whether there are any App_Browsers/*.browser files.  If there
443                                         // are we will be using *.browser files for sniffing in addition to browscap.ini
444                                         string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
445                                         app_browsers_files = new string[0];
446                                         if (Directory.Exists (app_browsers_path)) {
447                                                 app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
448                                         }
449 #endif
450
451                                         app_type = BuildManager.GetPrecompiledApplicationType ();
452                                         if (app_type == null && app_file != null) {
453 #if TARGET_J2EE
454                                                 app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
455                                                 app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
456 #else
457                                                 app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
458 #endif
459                                                 if (app_type == null) {
460                                                         string msg = String.Format ("Error compiling application file ({0}).", app_file);
461                                                         throw new ApplicationException (msg);
462                                                 }
463                                         } else if (app_type == null) {
464                                                 app_type = typeof (System.Web.HttpApplication);
465                                                 app_state = new HttpApplicationState ();
466                                         }
467
468                                         WatchLocationForRestart ("?lobal.asax");
469 #if CODE_DISABLED_UNTIL_SYSTEM_CONFIGURATION_IS_FIXED
470                                         // This is the correct behavior, but until
471                                         // System.Configuration is fixed to properly reload
472                                         // configuration when it is modified on disk, we need to use
473                                         // the recursive watchers below.
474                                         WatchLocationForRestart ("?eb.?onfig");
475 #else
476                                         // This is to avoid startup delays. Inotify/FAM code looks
477                                         // recursively for all subdirectories and adds them to the
478                                         // watch set. This can take a lot of time for deep directory
479                                         // trees (see bug #490497)
480                                         ThreadPool.QueueUserWorkItem (new WaitCallback (SetUpWebConfigWatchers), null);
481 #endif
482                                         
483                                         needs_init = false;
484                                 } catch (Exception) {
485                                         if (BuildManager.CodeAssemblies != null)
486                                                 BuildManager.CodeAssemblies.Clear ();
487                                         if (BuildManager.TopLevelAssemblies != null)
488                                                 BuildManager.TopLevelAssemblies.Clear ();
489                                         if (WebConfigurationManager.ExtraAssemblies != null)
490                                                 WebConfigurationManager.ExtraAssemblies.Clear ();
491                                         throw;
492                                 }
493                         }
494                 }
495
496                 static void SetUpWebConfigWatchers (object state)
497                 {
498                         WatchLocationForRestart (String.Empty, "?eb.?onfig", true);
499                 }
500                 
501                 //
502                 // Multiple-threads might hit this one on startup, and we have
503                 // to delay-initialize until we have the HttpContext
504                 //
505                 internal static HttpApplication GetApplication (HttpContext context)
506                 {
507 #if TARGET_J2EE
508                         if (context.ApplicationInstance!=null)
509                                 return context.ApplicationInstance;
510 #endif
511                         HttpApplicationFactory factory = theFactory;
512                         HttpApplication app = null;
513                         if (factory.app_start_needed){
514                                 if (context == null)
515                                         return null;
516
517                                 factory.InitType (context);
518                                 lock (factory) {
519                                         if (factory.app_start_needed) {
520                                                 foreach (string dir in HttpApplication.BinDirs)
521                                                         WatchLocationForRestart (dir, "*.dll");
522                                                 // Restart if the App_* directories are created...
523                                                 WatchLocationForRestart (".", "App_Code");
524                                                 WatchLocationForRestart (".", "App_Browsers");
525                                                 WatchLocationForRestart (".", "App_GlobalResources");
526                                                 // ...or their contents is changed.
527                                                 WatchLocationForRestart ("App_Code", "*", true);
528                                                 WatchLocationForRestart ("App_Browsers", "*");
529                                                 WatchLocationForRestart ("App_GlobalResources", "*");
530                                                 app = factory.FireOnAppStart (context);
531                                                 factory.app_start_needed = false;
532                                                 return app;
533                                         }
534                                 }
535                         }
536
537                         app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
538                         if (app != null) {
539                                 app.RequestCompleted = false;
540                                 return app;
541                         }
542
543                         lock (factory.available) {
544                                 if (factory.available.Count > 0) {
545                                         app = (HttpApplication) factory.available.Pop ();
546                                         app.RequestCompleted = false;
547                                         return app;
548                                 }
549                         }
550                         
551                         return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
552                 }
553
554                 // The lock is in InvokeSessionEnd
555                 static HttpApplication GetApplicationForSessionEnd ()
556                 {
557                         HttpApplicationFactory factory = theFactory;
558                         if (factory.available_for_end.Count > 0)
559                                 return (HttpApplication) factory.available_for_end.Pop ();
560
561                         HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
562                         app.InitOnce (false);
563
564                         return app;
565                 }
566
567                 internal static void RecycleForSessionEnd (HttpApplication app)
568                 {
569                         bool dispose = false;
570                         HttpApplicationFactory factory = theFactory;
571                         lock (factory.available_for_end) {
572                                 if (factory.available_for_end.Count < 64)
573                                         factory.available_for_end.Push (app);
574                                 else
575                                         dispose = true;
576                         }
577                         if (dispose)
578                                 app.Dispose ();
579                 }
580
581                 internal static void Recycle (HttpApplication app)
582                 {
583                         bool dispose = false;
584                         HttpApplicationFactory factory = theFactory;
585                         if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
586                                 return;
587
588                         lock (factory.available) {
589                                 if (factory.available.Count < 64)
590                                         factory.available.Push (app);
591                                 else
592                                         dispose = true;
593                         }
594                         if (dispose)
595                                 app.Dispose ();
596                 }
597
598                 internal static bool ContextAvailable {
599                         get { return theFactory != null && !theFactory.app_start_needed; }
600                 }
601
602
603                 internal static bool WatchLocationForRestart (string filter)
604                 {
605                         return WatchLocationForRestart (String.Empty, filter, false);
606                 }
607
608                 internal static bool WatchLocationForRestart (string virtualPath, string filter)
609                 {
610                         return WatchLocationForRestart (virtualPath, filter, false);
611                 }
612                 
613                 internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
614                 {
615                         // map the path to the physical one
616                         string physicalPath = HttpRuntime.AppDomainAppPath;
617                         physicalPath = Path.Combine(physicalPath, virtualPath);
618                         bool isDir = Directory.Exists(physicalPath);
619                         bool isFile = isDir ? false : File.Exists(physicalPath);
620
621                         if (isDir || isFile) {
622                                 // create the watcher
623                                 FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
624                                 RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
625                                 FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
626                                 if (isDir)
627                                         watcher.IncludeSubdirectories = watchSubdirs;
628                                 
629                                 lock (watchers_lock) {
630                                         watchers.Add(watcher);
631                                 }
632                                 return true;
633                         } else {
634                                 return false;
635                         }
636                 }
637
638                 internal static bool ApplicationDisabled {
639                         get { return app_disabled; }
640                         set { app_disabled = value; }
641                 }
642
643                 internal static string[] AppBrowsersFiles {
644                         get { return app_browsers_files; }
645                 }
646                 
647                 static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
648                 static object capabilities_processor_lock = new object();
649                 internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
650                         get {
651                                 lock (capabilities_processor_lock) {
652                                         if (capabilities_processor == null) {
653                                                 capabilities_processor = new System.Web.Configuration.nBrowser.Build();
654                                                 string[] machine_browsers_files = app_mono_machine_browsers_files;
655                                                 if (machine_browsers_files.Length == 0) {
656                                                         machine_browsers_files = default_machine_browsers_files;
657                                                 }
658                                                 foreach (string f in machine_browsers_files) {
659                                                         capabilities_processor.AddBrowserFile(f);
660                                                 }
661                                                 foreach (string f in app_browsers_files) {
662                                                         capabilities_processor.AddBrowserFile(f);
663                                                 }
664                                         }
665                                 }
666                                 return capabilities_processor;
667                         }
668                 }
669                 
670                 internal static void DisableWatchers ()
671                 {
672                         lock (watchers_lock) {
673                                 foreach (FileSystemWatcher watcher in watchers)
674                                         watcher.EnableRaisingEvents = false;
675                         }
676                 }
677
678                 internal static void DisableWatcher (string virtualPath, string filter)
679                 {
680                         EnableWatcherEvents (virtualPath, filter, false);
681                 }
682
683                 internal static void EnableWatcher (string virtualPath, string filter)
684                 {
685                         EnableWatcherEvents (virtualPath, filter, true);
686                 }
687                 
688                 static void EnableWatcherEvents (string virtualPath, string filter, bool enable)
689                 {
690                         lock (watchers_lock) {
691                                 foreach (FileSystemWatcher watcher in watchers) {
692                                         if (String.Compare (watcher.Path, virtualPath, StringComparison.Ordinal) != 0 || String.Compare (watcher.Filter, filter, StringComparison.Ordinal) != 0)
693                                                 continue;
694                                         
695                                         watcher.EnableRaisingEvents = enable;
696                                 }
697                         }
698                 }
699                 
700                 internal static void EnableWatchers ()
701                 {
702                         lock (watchers_lock) {
703                                 foreach (FileSystemWatcher watcher in watchers)
704                                         watcher.EnableRaisingEvents = true;
705                         }
706                 }
707                 
708                 static void OnFileRenamed(object sender, RenamedEventArgs args)
709                 {
710                         OnFileChanged(sender, args);
711                 }
712
713                 static void OnFileChanged(object sender, FileSystemEventArgs args)
714                 {
715                         string name = args.Name;
716                         bool isConfig = false;
717                         
718                         if (StrUtils.EndsWith (name, "onfig", true)) {
719                                 if (String.Compare (Path.GetFileName (name), "web.config", true, Helpers.InvariantCulture) != 0)
720                                         return;
721                                 isConfig = true;
722                         } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true, Helpers.InvariantCulture) != 0)
723                                 return;
724
725                         // {Inotify,FAM}Watcher will notify about events for a directory regardless
726                         // of the filter pattern. This might be a bug in the watchers code, but
727                         // since I couldn't find any rationale for the code in there I'd opted for
728                         // not removing it and instead working around the issue here. Fix for bug
729                         // #495011
730                         FileSystemWatcher watcher = sender as FileSystemWatcher;
731                         if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true, Helpers.InvariantCulture) == 0 && Directory.Exists (name))
732                                 return;
733
734                         // We re-enable suppression here since WebConfigurationManager will disable
735                         // it after save is done. WebConfigurationManager is called twice by
736                         // Configuration - just after opening the target file and just after closing
737                         // it. For that reason we will receive two change notifications and if we
738                         // disabled suppression here, it would reload the application on the second
739                         // change notification.
740                         if (isConfig && WebConfigurationManager.SuppressAppReload (true))
741                                 return;
742                         
743                         lock (watchers_lock) {
744                                 if(app_shutdown)
745                                         return;
746                                 app_shutdown = true;
747
748                                 // Disable event raising to avoid concurrent restarts
749                                 DisableWatchers ();
750                                 
751                                 // Restart application
752                                 HttpRuntime.UnloadAppDomain();
753                         }
754                 }
755         }
756 }
757