PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System.Web/System.Web/HttpApplicationFactory.cs

https://bitbucket.org/danipen/mono
C# | 764 lines | 590 code | 103 blank | 71 comment | 135 complexity | 7b49fab76407455f7c8bc7a1b440765d MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  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. using System.Web.Compilation;
  40. #if TARGET_J2EE
  41. using vmw.common;
  42. #else
  43. using System.CodeDom.Compiler;
  44. #endif
  45. namespace System.Web
  46. {
  47. sealed class HttpApplicationFactory
  48. {
  49. object this_lock = new object ();
  50. // Initialized in InitType
  51. #if TARGET_J2EE
  52. static HttpApplicationFactory theFactory {
  53. get
  54. {
  55. HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
  56. if (factory == null) {
  57. lock(typeof(HttpApplicationFactory)) {
  58. factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
  59. if (factory == null) {
  60. factory = new HttpApplicationFactory();
  61. System.Threading.Thread.Sleep(1);
  62. AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
  63. }
  64. }
  65. }
  66. return factory;
  67. }
  68. }
  69. #else
  70. static HttpApplicationFactory theFactory = new HttpApplicationFactory();
  71. #endif
  72. object session_end; // This is a MethodInfo
  73. bool needs_init = true;
  74. bool app_start_needed = true;
  75. bool have_app_events;
  76. Type app_type;
  77. HttpApplicationState app_state;
  78. Hashtable app_event_handlers;
  79. static ArrayList watchers = new ArrayList();
  80. static object watchers_lock = new object();
  81. static bool app_shutdown = false;
  82. static bool app_disabled = false;
  83. static string[] app_browsers_files = new string[0];
  84. static string[] default_machine_browsers_files = new string[0];
  85. static string[] app_mono_machine_browsers_files = new string[0];
  86. Stack available = new Stack ();
  87. object next_free;
  88. Stack available_for_end = new Stack ();
  89. bool IsEventHandler (MethodInfo m)
  90. {
  91. int pos = m.Name.IndexOf ('_');
  92. if (pos == -1 || (m.Name.Length - 1) <= pos)
  93. return false;
  94. if (m.ReturnType != typeof (void))
  95. return false;
  96. ParameterInfo [] pi = m.GetParameters ();
  97. int length = pi.Length;
  98. if (length == 0)
  99. return true;
  100. if (length != 2)
  101. return false;
  102. if (pi [0].ParameterType != typeof (object) ||
  103. !typeof (EventArgs).IsAssignableFrom (pi [1].ParameterType))
  104. return false;
  105. return true;
  106. }
  107. void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
  108. {
  109. string name = method.Name.Replace ("_On", "_");
  110. if (appTypeEventHandlers [name] == null) {
  111. appTypeEventHandlers [name] = method;
  112. return;
  113. }
  114. MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
  115. ArrayList list;
  116. if (old_method != null){
  117. list = new ArrayList (4);
  118. list.Add (old_method);
  119. appTypeEventHandlers [name] = list;
  120. } else
  121. list = appTypeEventHandlers [name] as ArrayList;
  122. list.Add (method);
  123. }
  124. ArrayList GetMethodsDeep (Type type)
  125. {
  126. ArrayList al = new ArrayList ();
  127. MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
  128. al.AddRange (methods);
  129. Type t = type.BaseType;
  130. while (t != null) {
  131. methods = t.GetMethods (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
  132. al.AddRange (methods);
  133. t = t.BaseType;
  134. }
  135. return al;
  136. }
  137. Hashtable GetApplicationTypeEvents (Type type)
  138. {
  139. if (have_app_events)
  140. return app_event_handlers;
  141. lock (this_lock) {
  142. if (app_event_handlers != null)
  143. return app_event_handlers;
  144. app_event_handlers = new Hashtable ();
  145. ArrayList methods = GetMethodsDeep (type);
  146. Hashtable used = null;
  147. MethodInfo m;
  148. string mname;
  149. foreach (object o in methods) {
  150. m = o as MethodInfo;
  151. if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m)) {
  152. mname = m.ToString ();
  153. if (used == null)
  154. used = new Hashtable ();
  155. else if (used.ContainsKey (mname))
  156. continue;
  157. used.Add (mname, m);
  158. AddEvent (m, app_event_handlers);
  159. }
  160. }
  161. used = null;
  162. have_app_events = true;
  163. }
  164. return app_event_handlers;
  165. }
  166. Hashtable GetApplicationTypeEvents (HttpApplication app)
  167. {
  168. if (have_app_events)
  169. return app_event_handlers;
  170. return GetApplicationTypeEvents (app.GetType ());
  171. }
  172. bool FireEvent (string method_name, object target, object [] args)
  173. {
  174. Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
  175. MethodInfo method = possibleEvents [method_name] as MethodInfo;
  176. if (method == null)
  177. return false;
  178. if (method.GetParameters ().Length == 0)
  179. args = null;
  180. method.Invoke (target, args);
  181. return true;
  182. }
  183. HttpApplication FireOnAppStart (HttpContext context)
  184. {
  185. HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
  186. context.ApplicationInstance = app;
  187. app.SetContext (context);
  188. object [] args = new object [] {app, EventArgs.Empty};
  189. app.InApplicationStart = true;
  190. FireEvent ("Application_Start", app, args);
  191. app.InApplicationStart = false;
  192. return app;
  193. }
  194. void FireOnAppEnd ()
  195. {
  196. if (app_type == null)
  197. return; // we didn't even get an application
  198. HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
  199. FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
  200. app.DisposeInternal ();
  201. app_type = null;
  202. }
  203. //
  204. // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
  205. // To reproduce this in action, touch "global.asax" while XSP is running.
  206. //
  207. public static void Dispose ()
  208. {
  209. theFactory.FireOnAppEnd ();
  210. }
  211. static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
  212. {
  213. FileSystemWatcher watcher = new FileSystemWatcher ();
  214. watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
  215. watcher.Filter = Path.GetFileName (file);
  216. // This will enable the Modify flag for Linux/inotify
  217. watcher.NotifyFilter |= NotifyFilters.Size;
  218. watcher.Changed += hnd;
  219. watcher.Created += hnd;
  220. watcher.Deleted += hnd;
  221. watcher.Renamed += reh;
  222. watcher.EnableRaisingEvents = true;
  223. return watcher;
  224. }
  225. internal static void AttachEvents (HttpApplication app)
  226. {
  227. HttpApplicationFactory factory = theFactory;
  228. Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
  229. foreach (string key in possibleEvents.Keys) {
  230. int pos = key.IndexOf ('_');
  231. string moduleName = key.Substring (0, pos);
  232. object target;
  233. if (moduleName == "Application") {
  234. target = app;
  235. } else {
  236. target = app.Modules [moduleName];
  237. if (target == null)
  238. continue;
  239. }
  240. string eventName = key.Substring (pos + 1);
  241. EventInfo evt = target.GetType ().GetEvent (eventName);
  242. if (evt == null)
  243. continue;
  244. string usualName = moduleName + "_" + eventName;
  245. object methodData = possibleEvents [usualName];
  246. if (methodData == null)
  247. continue;
  248. if (eventName == "End" && moduleName == "Session") {
  249. Interlocked.CompareExchange (ref factory.session_end, methodData, null);
  250. continue;
  251. }
  252. if (methodData is MethodInfo) {
  253. factory.AddHandler (evt, target, app, (MethodInfo) methodData);
  254. continue;
  255. }
  256. ArrayList list = (ArrayList) methodData;
  257. foreach (MethodInfo method in list)
  258. factory.AddHandler (evt, target, app, method);
  259. }
  260. }
  261. void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
  262. {
  263. int length = method.GetParameters ().Length;
  264. if (length == 0) {
  265. NoParamsInvoker npi = new NoParamsInvoker (app, method);
  266. evt.AddEventHandler (target, npi.FakeDelegate);
  267. } else {
  268. if (method.IsStatic) {
  269. evt.AddEventHandler (target, Delegate.CreateDelegate (
  270. evt.EventHandlerType, method));
  271. } else {
  272. evt.AddEventHandler (target, Delegate.CreateDelegate (
  273. evt.EventHandlerType, app,
  274. method));
  275. }
  276. }
  277. }
  278. internal static void InvokeSessionEnd (object state)
  279. {
  280. InvokeSessionEnd (state, null, EventArgs.Empty);
  281. }
  282. internal static void InvokeSessionEnd (object state, object source, EventArgs e)
  283. {
  284. HttpApplicationFactory factory = theFactory;
  285. MethodInfo method = null;
  286. HttpApplication app = null;
  287. lock (factory.available_for_end) {
  288. method = (MethodInfo) factory.session_end;
  289. if (method == null)
  290. return;
  291. app = GetApplicationForSessionEnd ();
  292. }
  293. app.SetSession ((HttpSessionState) state);
  294. try {
  295. method.Invoke (app, new object [] {(source == null ? app : source), e});
  296. } catch (Exception) {
  297. // Ignore
  298. }
  299. RecycleForSessionEnd (app);
  300. }
  301. static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
  302. {
  303. if (list == null || list.Count == 0)
  304. return null;
  305. HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
  306. foreach (ObjectTagBuilder tag in list) {
  307. coll.Add (tag);
  308. }
  309. return coll;
  310. }
  311. internal static HttpApplicationState ApplicationState {
  312. #if TARGET_J2EE
  313. get {
  314. HttpApplicationFactory factory = theFactory;
  315. if (factory.app_state == null)
  316. factory.app_state = new HttpApplicationState (null, null);
  317. return factory.app_state;
  318. }
  319. #else
  320. get {
  321. if (theFactory.app_state == null) {
  322. HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
  323. HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
  324. theFactory.app_state = new HttpApplicationState (app, ses);
  325. }
  326. return theFactory.app_state;
  327. }
  328. #endif
  329. }
  330. internal static Type AppType {
  331. get {
  332. return theFactory.app_type;
  333. }
  334. }
  335. void InitType (HttpContext context)
  336. {
  337. lock (this_lock) {
  338. if (!needs_init)
  339. return;
  340. try {
  341. string physical_app_path = HttpRuntime.AppDomainAppPath;
  342. string app_file = null;
  343. app_file = Path.Combine (physical_app_path, "Global.asax");
  344. if (!File.Exists (app_file)) {
  345. app_file = Path.Combine (physical_app_path, "global.asax");
  346. if (!File.Exists (app_file))
  347. app_file = null;
  348. }
  349. #if NET_4_0
  350. BuildManager.CallPreStartMethods ();
  351. BuildManager.CompilingTopLevelAssemblies = true;
  352. #endif
  353. #if !TARGET_J2EE
  354. AppResourcesCompiler ac = new AppResourcesCompiler (context);
  355. ac.Compile ();
  356. #if WEBSERVICES_DEP
  357. AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
  358. awrc.Compile ();
  359. #endif
  360. // Todo: Generate profile properties assembly from Web.config here
  361. AppCodeCompiler acc = new AppCodeCompiler ();
  362. acc.Compile ();
  363. BuildManager.AllowReferencedAssembliesCaching = true;
  364. // Get the default machine *.browser files.
  365. string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
  366. default_machine_browsers_files = new string[0];
  367. if (Directory.Exists (default_machine_browsers_path)) {
  368. default_machine_browsers_files
  369. = Directory.GetFiles (default_machine_browsers_path, "*.browser");
  370. }
  371. // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files. If there
  372. // are we will be using them instead of the default machine *.browser files.
  373. string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
  374. app_mono_machine_browsers_files = new string[0];
  375. if (Directory.Exists (app_mono_machine_browsers_path)) {
  376. app_mono_machine_browsers_files
  377. = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
  378. }
  379. // Note whether there are any App_Browsers/*.browser files. If there
  380. // are we will be using *.browser files for sniffing in addition to browscap.ini
  381. string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
  382. app_browsers_files = new string[0];
  383. if (Directory.Exists (app_browsers_path)) {
  384. app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
  385. }
  386. #endif
  387. #if NET_4_0
  388. BuildManager.CompilingTopLevelAssemblies = false;
  389. #endif
  390. app_type = BuildManager.GetPrecompiledApplicationType ();
  391. if (app_type == null && app_file != null) {
  392. #if TARGET_J2EE
  393. app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
  394. app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
  395. #else
  396. app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
  397. #endif
  398. if (app_type == null) {
  399. string msg = String.Format ("Error compiling application file ({0}).", app_file);
  400. throw new ApplicationException (msg);
  401. }
  402. } else if (app_type == null) {
  403. app_type = typeof (System.Web.HttpApplication);
  404. app_state = new HttpApplicationState ();
  405. }
  406. WatchLocationForRestart ("?lobal.asax");
  407. #if CODE_DISABLED_UNTIL_SYSTEM_CONFIGURATION_IS_FIXED
  408. // This is the correct behavior, but until
  409. // System.Configuration is fixed to properly reload
  410. // configuration when it is modified on disk, we need to use
  411. // the recursive watchers below.
  412. WatchLocationForRestart ("?eb.?onfig");
  413. #else
  414. // This is to avoid startup delays. Inotify/FAM code looks
  415. // recursively for all subdirectories and adds them to the
  416. // watch set. This can take a lot of time for deep directory
  417. // trees (see bug #490497)
  418. ThreadPool.QueueUserWorkItem (delegate {
  419. try {
  420. WatchLocationForRestart (String.Empty, "?eb.?onfig", true);
  421. } catch (Exception e) {
  422. Console.Error.WriteLine (e);
  423. } }, null);
  424. #endif
  425. needs_init = false;
  426. } catch (Exception) {
  427. if (BuildManager.CodeAssemblies != null)
  428. BuildManager.CodeAssemblies.Clear ();
  429. if (BuildManager.TopLevelAssemblies != null)
  430. BuildManager.TopLevelAssemblies.Clear ();
  431. if (WebConfigurationManager.ExtraAssemblies != null)
  432. WebConfigurationManager.ExtraAssemblies.Clear ();
  433. throw;
  434. }
  435. }
  436. }
  437. //
  438. // Multiple-threads might hit this one on startup, and we have
  439. // to delay-initialize until we have the HttpContext
  440. //
  441. internal static HttpApplication GetApplication (HttpContext context)
  442. {
  443. #if TARGET_J2EE
  444. if (context.ApplicationInstance!=null)
  445. return context.ApplicationInstance;
  446. #endif
  447. HttpApplicationFactory factory = theFactory;
  448. HttpApplication app = null;
  449. if (factory.app_start_needed){
  450. if (context == null)
  451. return null;
  452. factory.InitType (context);
  453. lock (factory) {
  454. if (factory.app_start_needed) {
  455. foreach (string dir in HttpApplication.BinDirs)
  456. WatchLocationForRestart (dir, "*.dll");
  457. // Restart if the App_* directories are created...
  458. WatchLocationForRestart (".", "App_Code");
  459. WatchLocationForRestart (".", "App_Browsers");
  460. WatchLocationForRestart (".", "App_GlobalResources");
  461. // ...or their contents is changed.
  462. WatchLocationForRestart ("App_Code", "*", true);
  463. WatchLocationForRestart ("App_Browsers", "*");
  464. WatchLocationForRestart ("App_GlobalResources", "*");
  465. app = factory.FireOnAppStart (context);
  466. factory.app_start_needed = false;
  467. return app;
  468. }
  469. }
  470. }
  471. app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
  472. if (app != null) {
  473. app.RequestCompleted = false;
  474. return app;
  475. }
  476. lock (factory.available) {
  477. if (factory.available.Count > 0) {
  478. app = (HttpApplication) factory.available.Pop ();
  479. app.RequestCompleted = false;
  480. return app;
  481. }
  482. }
  483. return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
  484. }
  485. // The lock is in InvokeSessionEnd
  486. static HttpApplication GetApplicationForSessionEnd ()
  487. {
  488. HttpApplicationFactory factory = theFactory;
  489. if (factory.available_for_end.Count > 0)
  490. return (HttpApplication) factory.available_for_end.Pop ();
  491. HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
  492. app.InitOnce (false);
  493. return app;
  494. }
  495. internal static void RecycleForSessionEnd (HttpApplication app)
  496. {
  497. bool dispose = false;
  498. HttpApplicationFactory factory = theFactory;
  499. lock (factory.available_for_end) {
  500. if (factory.available_for_end.Count < 64)
  501. factory.available_for_end.Push (app);
  502. else
  503. dispose = true;
  504. }
  505. if (dispose)
  506. app.Dispose ();
  507. }
  508. internal static void Recycle (HttpApplication app)
  509. {
  510. bool dispose = false;
  511. HttpApplicationFactory factory = theFactory;
  512. if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
  513. return;
  514. lock (factory.available) {
  515. if (factory.available.Count < 64)
  516. factory.available.Push (app);
  517. else
  518. dispose = true;
  519. }
  520. if (dispose)
  521. app.Dispose ();
  522. }
  523. internal static bool ContextAvailable {
  524. get { return theFactory != null && !theFactory.app_start_needed; }
  525. }
  526. internal static bool WatchLocationForRestart (string filter)
  527. {
  528. return WatchLocationForRestart (String.Empty, filter, false);
  529. }
  530. internal static bool WatchLocationForRestart (string virtualPath, string filter)
  531. {
  532. return WatchLocationForRestart (virtualPath, filter, false);
  533. }
  534. internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
  535. {
  536. // map the path to the physical one
  537. string physicalPath = HttpRuntime.AppDomainAppPath;
  538. physicalPath = Path.Combine(physicalPath, virtualPath);
  539. bool isDir = Directory.Exists(physicalPath);
  540. bool isFile = isDir ? false : File.Exists(physicalPath);
  541. if (isDir || isFile) {
  542. // create the watcher
  543. FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
  544. RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
  545. FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
  546. if (isDir)
  547. watcher.IncludeSubdirectories = watchSubdirs;
  548. lock (watchers_lock) {
  549. watchers.Add(watcher);
  550. }
  551. return true;
  552. } else {
  553. return false;
  554. }
  555. }
  556. internal static bool ApplicationDisabled {
  557. get { return app_disabled; }
  558. set { app_disabled = value; }
  559. }
  560. internal static string[] AppBrowsersFiles {
  561. get { return app_browsers_files; }
  562. }
  563. static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
  564. static object capabilities_processor_lock = new object();
  565. internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
  566. get {
  567. lock (capabilities_processor_lock) {
  568. if (capabilities_processor == null) {
  569. capabilities_processor = new System.Web.Configuration.nBrowser.Build();
  570. string[] machine_browsers_files = app_mono_machine_browsers_files;
  571. if (machine_browsers_files.Length == 0) {
  572. machine_browsers_files = default_machine_browsers_files;
  573. }
  574. foreach (string f in machine_browsers_files) {
  575. capabilities_processor.AddBrowserFile(f);
  576. }
  577. foreach (string f in app_browsers_files) {
  578. capabilities_processor.AddBrowserFile(f);
  579. }
  580. }
  581. }
  582. return capabilities_processor;
  583. }
  584. }
  585. internal static void DisableWatchers ()
  586. {
  587. lock (watchers_lock) {
  588. foreach (FileSystemWatcher watcher in watchers)
  589. watcher.EnableRaisingEvents = false;
  590. }
  591. }
  592. internal static void DisableWatcher (string virtualPath, string filter)
  593. {
  594. EnableWatcherEvents (virtualPath, filter, false);
  595. }
  596. internal static void EnableWatcher (string virtualPath, string filter)
  597. {
  598. EnableWatcherEvents (virtualPath, filter, true);
  599. }
  600. static void EnableWatcherEvents (string virtualPath, string filter, bool enable)
  601. {
  602. lock (watchers_lock) {
  603. foreach (FileSystemWatcher watcher in watchers) {
  604. if (String.Compare (watcher.Path, virtualPath, StringComparison.Ordinal) != 0 || String.Compare (watcher.Filter, filter, StringComparison.Ordinal) != 0)
  605. continue;
  606. watcher.EnableRaisingEvents = enable;
  607. }
  608. }
  609. }
  610. internal static void EnableWatchers ()
  611. {
  612. lock (watchers_lock) {
  613. foreach (FileSystemWatcher watcher in watchers)
  614. watcher.EnableRaisingEvents = true;
  615. }
  616. }
  617. static void OnFileRenamed(object sender, RenamedEventArgs args)
  618. {
  619. OnFileChanged(sender, args);
  620. }
  621. static void OnFileChanged(object sender, FileSystemEventArgs args)
  622. {
  623. if (HttpRuntime.DomainUnloading)
  624. return;
  625. string name = args.Name;
  626. bool isConfig = false;
  627. if (StrUtils.EndsWith (name, "onfig", true)) {
  628. if (String.Compare (Path.GetFileName (name), "web.config", true, Helpers.InvariantCulture) != 0)
  629. return;
  630. isConfig = true;
  631. } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true, Helpers.InvariantCulture) != 0)
  632. return;
  633. Console.WriteLine ("Change: " + name);
  634. // {Inotify,FAM}Watcher will notify about events for a directory regardless
  635. // of the filter pattern. This might be a bug in the watchers code, but
  636. // since I couldn't find any rationale for the code in there I'd opted for
  637. // not removing it and instead working around the issue here. Fix for bug
  638. // #495011
  639. FileSystemWatcher watcher = sender as FileSystemWatcher;
  640. if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true, Helpers.InvariantCulture) == 0 && Directory.Exists (name))
  641. return;
  642. // We re-enable suppression here since WebConfigurationManager will disable
  643. // it after save is done. WebConfigurationManager is called twice by
  644. // Configuration - just after opening the target file and just after closing
  645. // it. For that reason we will receive two change notifications and if we
  646. // disabled suppression here, it would reload the application on the second
  647. // change notification.
  648. if (isConfig && WebConfigurationManager.SuppressAppReload (true))
  649. return;
  650. lock (watchers_lock) {
  651. if(app_shutdown)
  652. return;
  653. app_shutdown = true;
  654. // Disable event raising to avoid concurrent restarts
  655. DisableWatchers ();
  656. // Restart application
  657. HttpRuntime.UnloadAppDomain();
  658. }
  659. }
  660. }
  661. }