HttpApplicationFactory.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. //
  2. // System.Web.HttpApplicationFactory
  3. //
  4. // Author:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. //
  7. // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
  8. // (c) Copyright 2004 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.IO;
  32. using System.Reflection;
  33. using System.Web.UI;
  34. using System.Web.SessionState;
  35. using System.Web.Configuration;
  36. using System.Threading;
  37. using System.Web.Util;
  38. using System.Web.Compilation;
  39. #if TARGET_J2EE
  40. using vmw.common;
  41. #endif
  42. #if NET_2_0 && !TARGET_J2EE
  43. using System.CodeDom.Compiler;
  44. #endif
  45. namespace System.Web {
  46. class HttpApplicationFactory {
  47. object this_lock = new object ();
  48. // Initialized in InitType
  49. #if TARGET_J2EE
  50. static HttpApplicationFactory theFactory {
  51. get
  52. {
  53. HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
  54. if (factory == null) {
  55. lock(typeof(HttpApplicationFactory)) {
  56. factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
  57. if (factory == null) {
  58. factory = new HttpApplicationFactory();
  59. System.Threading.Thread.Sleep(1);
  60. AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
  61. }
  62. }
  63. }
  64. return factory;
  65. }
  66. }
  67. #else
  68. static HttpApplicationFactory theFactory = new HttpApplicationFactory();
  69. #endif
  70. object session_end; // This is a MethodInfo
  71. bool needs_init = true;
  72. bool app_start_needed = true;
  73. bool have_app_events;
  74. Type app_type;
  75. HttpApplicationState app_state;
  76. Hashtable app_event_handlers;
  77. static ArrayList watchers = new ArrayList();
  78. static object watchers_lock = new object();
  79. static bool app_shutdown = false;
  80. #if NET_2_0
  81. static bool app_disabled = false;
  82. static string[] app_browsers_files = new string[0];
  83. static string[] default_machine_browsers_files = new string[0];
  84. static string[] app_mono_machine_browsers_files = new string[0];
  85. #endif
  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. pi [1].ParameterType != typeof (EventArgs))
  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. #if NET_2_0
  275. method
  276. #else
  277. method.Name
  278. #endif
  279. ));
  280. }
  281. }
  282. }
  283. internal static void InvokeSessionEnd (object state)
  284. {
  285. InvokeSessionEnd (state, null, EventArgs.Empty);
  286. }
  287. internal static void InvokeSessionEnd (object state, object source, EventArgs e)
  288. {
  289. HttpApplicationFactory factory = theFactory;
  290. MethodInfo method = null;
  291. HttpApplication app = null;
  292. lock (factory.available_for_end) {
  293. method = (MethodInfo) factory.session_end;
  294. if (method == null)
  295. return;
  296. app = GetApplicationForSessionEnd ();
  297. }
  298. app.SetSession ((HttpSessionState) state);
  299. try {
  300. method.Invoke (app, new object [] {(source == null ? app : source), e});
  301. } catch (Exception) {
  302. // Ignore
  303. }
  304. RecycleForSessionEnd (app);
  305. }
  306. static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
  307. {
  308. if (list == null || list.Count == 0)
  309. return null;
  310. HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
  311. foreach (ObjectTagBuilder tag in list) {
  312. coll.Add (tag);
  313. }
  314. return coll;
  315. }
  316. internal static HttpApplicationState ApplicationState {
  317. #if TARGET_J2EE
  318. get {
  319. HttpApplicationFactory factory = theFactory;
  320. if (factory.app_state == null)
  321. factory.app_state = new HttpApplicationState (null, null);
  322. return factory.app_state;
  323. }
  324. #else
  325. get {
  326. if (theFactory.app_state == null) {
  327. HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
  328. HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
  329. theFactory.app_state = new HttpApplicationState (app, ses);
  330. }
  331. return theFactory.app_state;
  332. }
  333. #endif
  334. }
  335. internal static Type AppType {
  336. get {
  337. return theFactory.app_type;
  338. }
  339. }
  340. void InitType (HttpContext context)
  341. {
  342. lock (this_lock) {
  343. if (!needs_init)
  344. return;
  345. #if NET_2_0
  346. try {
  347. #endif
  348. string physical_app_path = HttpRuntime.AppDomainAppPath;
  349. string app_file = null;
  350. app_file = Path.Combine (physical_app_path, "Global.asax");
  351. if (!File.Exists (app_file)) {
  352. app_file = Path.Combine (physical_app_path, "global.asax");
  353. if (!File.Exists (app_file))
  354. app_file = null;
  355. }
  356. #if !NET_2_0
  357. WebConfigurationSettings.Init (context);
  358. #endif
  359. #if NET_2_0 && !TARGET_J2EE
  360. AppResourcesCompiler ac = new AppResourcesCompiler (context);
  361. ac.Compile ();
  362. #if WEBSERVICES_DEP
  363. AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
  364. awrc.Compile ();
  365. #endif
  366. // Todo: Generate profile properties assembly from Web.config here
  367. AppCodeCompiler acc = new AppCodeCompiler ();
  368. acc.Compile ();
  369. // Get the default machine *.browser files.
  370. string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
  371. default_machine_browsers_files = new string[0];
  372. if (Directory.Exists (default_machine_browsers_path)) {
  373. default_machine_browsers_files
  374. = Directory.GetFiles (default_machine_browsers_path, "*.browser");
  375. }
  376. // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files. If there
  377. // are we will be using them instead of the default machine *.browser files.
  378. string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
  379. app_mono_machine_browsers_files = new string[0];
  380. if (Directory.Exists (app_mono_machine_browsers_path)) {
  381. app_mono_machine_browsers_files
  382. = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
  383. }
  384. // Note whether there are any App_Browsers/*.browser files. If there
  385. // are we will be using *.browser files for sniffing in addition to browscap.ini
  386. string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
  387. app_browsers_files = new string[0];
  388. if (Directory.Exists (app_browsers_path)) {
  389. app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
  390. }
  391. #endif
  392. #if NET_2_0
  393. app_type = BuildManager.GetPrecompiledApplicationType ();
  394. #endif
  395. if (app_type == null && app_file != null) {
  396. #if TARGET_J2EE
  397. app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
  398. app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
  399. #else
  400. #if NET_2_0
  401. app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
  402. #else
  403. app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
  404. #endif
  405. #endif
  406. if (app_type == null) {
  407. string msg = String.Format ("Error compiling application file ({0}).", app_file);
  408. throw new ApplicationException (msg);
  409. }
  410. } else if (app_type == null) {
  411. app_type = typeof (System.Web.HttpApplication);
  412. app_state = new HttpApplicationState ();
  413. }
  414. WatchLocationForRestart ("?lobal.asax");
  415. #if CODE_DISABLED_UNTIL_SYSTEM_CONFIGURATION_IS_FIXED
  416. // This is the correct behavior, but until
  417. // System.Configuration is fixed to properly reload
  418. // configuration when it is modified on disk, we need to use
  419. // the recursive watchers below.
  420. WatchLocationForRestart ("?eb.?onfig");
  421. #else
  422. // This is to avoid startup delays. Inotify/FAM code looks
  423. // recursively for all subdirectories and adds them to the
  424. // watch set. This can take a lot of time for deep directory
  425. // trees (see bug #490497)
  426. ThreadPool.QueueUserWorkItem (new WaitCallback (SetUpWebConfigWatchers), null);
  427. #endif
  428. needs_init = false;
  429. #if NET_2_0
  430. } catch (Exception) {
  431. if (BuildManager.CodeAssemblies != null)
  432. BuildManager.CodeAssemblies.Clear ();
  433. if (BuildManager.TopLevelAssemblies != null)
  434. BuildManager.TopLevelAssemblies.Clear ();
  435. if (WebConfigurationManager.ExtraAssemblies != null)
  436. WebConfigurationManager.ExtraAssemblies.Clear ();
  437. throw;
  438. }
  439. #endif
  440. //
  441. // Now init the settings
  442. //
  443. }
  444. }
  445. static void SetUpWebConfigWatchers (object state)
  446. {
  447. WatchLocationForRestart (String.Empty, "?eb.?onfig", true);
  448. }
  449. //
  450. // Multiple-threads might hit this one on startup, and we have
  451. // to delay-initialize until we have the HttpContext
  452. //
  453. internal static HttpApplication GetApplication (HttpContext context)
  454. {
  455. #if TARGET_J2EE
  456. if (context.ApplicationInstance!=null)
  457. return context.ApplicationInstance;
  458. #endif
  459. HttpApplicationFactory factory = theFactory;
  460. HttpApplication app = null;
  461. if (factory.app_start_needed){
  462. if (context == null)
  463. return null;
  464. factory.InitType (context);
  465. lock (factory) {
  466. if (factory.app_start_needed) {
  467. foreach (string dir in HttpApplication.BinDirs)
  468. WatchLocationForRestart (dir, "*.dll");
  469. #if NET_2_0
  470. // Restart if the App_* directories are created...
  471. WatchLocationForRestart (".", "App_Code");
  472. WatchLocationForRestart (".", "App_Browsers");
  473. WatchLocationForRestart (".", "App_GlobalResources");
  474. // ...or their contents is changed.
  475. WatchLocationForRestart ("App_Code", "*", true);
  476. WatchLocationForRestart ("App_Browsers", "*");
  477. WatchLocationForRestart ("App_GlobalResources", "*");
  478. #endif
  479. app = factory.FireOnAppStart (context);
  480. factory.app_start_needed = false;
  481. return app;
  482. }
  483. }
  484. }
  485. app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
  486. if (app != null) {
  487. app.RequestCompleted = false;
  488. return app;
  489. }
  490. lock (factory.available) {
  491. if (factory.available.Count > 0) {
  492. app = (HttpApplication) factory.available.Pop ();
  493. app.RequestCompleted = false;
  494. return app;
  495. }
  496. }
  497. return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
  498. }
  499. // The lock is in InvokeSessionEnd
  500. static HttpApplication GetApplicationForSessionEnd ()
  501. {
  502. HttpApplicationFactory factory = theFactory;
  503. if (factory.available_for_end.Count > 0)
  504. return (HttpApplication) factory.available_for_end.Pop ();
  505. HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
  506. app.InitOnce (false);
  507. return app;
  508. }
  509. internal static void RecycleForSessionEnd (HttpApplication app)
  510. {
  511. bool dispose = false;
  512. HttpApplicationFactory factory = theFactory;
  513. lock (factory.available_for_end) {
  514. if (factory.available_for_end.Count < 64)
  515. factory.available_for_end.Push (app);
  516. else
  517. dispose = true;
  518. }
  519. if (dispose)
  520. app.Dispose ();
  521. }
  522. internal static void Recycle (HttpApplication app)
  523. {
  524. bool dispose = false;
  525. HttpApplicationFactory factory = theFactory;
  526. if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
  527. return;
  528. lock (factory.available) {
  529. if (factory.available.Count < 64)
  530. factory.available.Push (app);
  531. else
  532. dispose = true;
  533. }
  534. if (dispose)
  535. app.Dispose ();
  536. }
  537. internal static bool ContextAvailable {
  538. get { return theFactory != null && !theFactory.app_start_needed; }
  539. }
  540. internal static bool WatchLocationForRestart (string filter)
  541. {
  542. return WatchLocationForRestart (String.Empty, filter, false);
  543. }
  544. internal static bool WatchLocationForRestart (string virtualPath, string filter)
  545. {
  546. return WatchLocationForRestart (virtualPath, filter, false);
  547. }
  548. internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
  549. {
  550. // map the path to the physical one
  551. string physicalPath = HttpRuntime.AppDomainAppPath;
  552. physicalPath = Path.Combine(physicalPath, virtualPath);
  553. bool isDir = Directory.Exists(physicalPath);
  554. bool isFile = isDir ? false : File.Exists(physicalPath);
  555. if (isDir || isFile) {
  556. // create the watcher
  557. FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
  558. RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
  559. FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
  560. if (isDir)
  561. watcher.IncludeSubdirectories = watchSubdirs;
  562. lock (watchers_lock) {
  563. watchers.Add(watcher);
  564. }
  565. return true;
  566. } else {
  567. return false;
  568. }
  569. }
  570. #if NET_2_0
  571. internal static bool ApplicationDisabled {
  572. get { return app_disabled; }
  573. set { app_disabled = value; }
  574. }
  575. internal static string[] AppBrowsersFiles {
  576. get { return app_browsers_files; }
  577. }
  578. static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
  579. static object capabilities_processor_lock = new object();
  580. internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
  581. get {
  582. lock (capabilities_processor_lock) {
  583. if (capabilities_processor == null) {
  584. capabilities_processor = new System.Web.Configuration.nBrowser.Build();
  585. string[] machine_browsers_files = app_mono_machine_browsers_files;
  586. if (machine_browsers_files.Length == 0) {
  587. machine_browsers_files = default_machine_browsers_files;
  588. }
  589. foreach (string f in machine_browsers_files) {
  590. capabilities_processor.AddBrowserFile(f);
  591. }
  592. foreach (string f in app_browsers_files) {
  593. capabilities_processor.AddBrowserFile(f);
  594. }
  595. }
  596. }
  597. return capabilities_processor;
  598. }
  599. }
  600. #endif
  601. internal static void DisableWatchers ()
  602. {
  603. lock (watchers_lock) {
  604. foreach (FileSystemWatcher watcher in watchers)
  605. watcher.EnableRaisingEvents = false;
  606. }
  607. }
  608. internal static void DisableWatcher (string virtualPath, string filter)
  609. {
  610. EnableWatcherEvents (virtualPath, filter, false);
  611. }
  612. internal static void EnableWatcher (string virtualPath, string filter)
  613. {
  614. EnableWatcherEvents (virtualPath, filter, true);
  615. }
  616. static void EnableWatcherEvents (string virtualPath, string filter, bool enable)
  617. {
  618. lock (watchers_lock) {
  619. foreach (FileSystemWatcher watcher in watchers) {
  620. if (
  621. #if NET_2_0
  622. String.Compare (watcher.Path, virtualPath, StringComparison.Ordinal) != 0 || String.Compare (watcher.Filter, filter, StringComparison.Ordinal) != 0
  623. #else
  624. String.Compare (watcher.Path, virtualPath) != 0 || String.Compare (watcher.Filter, filter) != 0
  625. #endif
  626. )
  627. continue;
  628. watcher.EnableRaisingEvents = enable;
  629. }
  630. }
  631. }
  632. internal static void EnableWatchers ()
  633. {
  634. lock (watchers_lock) {
  635. foreach (FileSystemWatcher watcher in watchers)
  636. watcher.EnableRaisingEvents = true;
  637. }
  638. }
  639. static void OnFileRenamed(object sender, RenamedEventArgs args)
  640. {
  641. OnFileChanged(sender, args);
  642. }
  643. static void OnFileChanged(object sender, FileSystemEventArgs args)
  644. {
  645. string name = args.Name;
  646. bool isConfig = false;
  647. if (StrUtils.EndsWith (name, "onfig", true)) {
  648. if (String.Compare (Path.GetFileName (name), "web.config", true) != 0)
  649. return;
  650. isConfig = true;
  651. } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true) != 0)
  652. return;
  653. // {Inotify,FAM}Watcher will notify about events for a directory regardless
  654. // of the filter pattern. This might be a bug in the watchers code, but
  655. // since I couldn't find any rationale for the code in there I'd opted for
  656. // not removing it and instead working around the issue here. Fix for bug
  657. // #495011
  658. FileSystemWatcher watcher = sender as FileSystemWatcher;
  659. if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true) == 0 && Directory.Exists (name))
  660. return;
  661. #if NET_2_0
  662. // We re-enable suppression here since WebConfigurationManager will disable
  663. // it after save is done. WebConfigurationManager is called twice by
  664. // Configuration - just after opening the target file and just after closing
  665. // it. For that reason we will receive two change notifications and if we
  666. // disabled suppression here, it would reload the application on the second
  667. // change notification.
  668. if (isConfig && WebConfigurationManager.SuppressAppReload (true))
  669. return;
  670. #endif
  671. lock (watchers_lock) {
  672. if(app_shutdown)
  673. return;
  674. app_shutdown = true;
  675. // Disable event raising to avoid concurrent restarts
  676. DisableWatchers ();
  677. // Restart application
  678. HttpRuntime.UnloadAppDomain();
  679. }
  680. }
  681. }
  682. }