merge 100015:100420
[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 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
37 using System.Web.Compilation;
38 #if TARGET_J2EE
39 using vmw.common;
40 #endif
41
42 #if NET_2_0 && !TARGET_J2EE
43 using System.CodeDom.Compiler;
44 #endif
45
46 namespace System.Web {
47         class HttpApplicationFactory {
48                 object this_lock = new object ();
49                 
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                 MethodInfo session_end;
73                 bool needs_init = true;
74                 bool app_start_needed = true;
75                 Type app_type;
76                 HttpApplicationState app_state;
77                 Hashtable app_event_handlers;
78                 static ArrayList watchers = new ArrayList();
79                 static object watchers_lock = new object();
80                 static bool app_shutdown = false;
81 #if NET_2_0
82                 static bool app_disabled = false;
83                 static string[] app_browsers_files = new string[0];
84 #endif
85                 Stack available = new Stack ();
86                 Stack available_for_end = new Stack ();
87                 
88                 bool IsEventHandler (MethodInfo m)
89                 {
90                         int pos = m.Name.IndexOf ('_');
91                         if (pos == -1 || (m.Name.Length - 1) <= pos)
92                                 return false;
93
94                         if (m.ReturnType != typeof (void))
95                                 return false;
96
97                         ParameterInfo [] pi = m.GetParameters ();
98                         int length = pi.Length;
99                         if (length == 0)
100                                 return true;
101
102                         if (length != 2)
103                                 return false;
104
105                         if (pi [0].ParameterType != typeof (object) ||
106                             pi [1].ParameterType != typeof (EventArgs))
107                                 return false;
108                         
109                         return true;
110                 }
111
112                 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
113                 {
114                         string name = method.Name.Replace ("_On", "_");
115                         if (appTypeEventHandlers [name] == null) {
116                                 appTypeEventHandlers [name] = method;
117                                 return;
118                         }
119
120                         MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
121                         ArrayList list;
122                         if (old_method != null){
123                                 list = new ArrayList (4);
124                                 list.Add (old_method);
125                                 appTypeEventHandlers [name] = list;
126                         } else 
127                                 list = appTypeEventHandlers [name] as ArrayList;
128
129                         list.Add (method);
130                 }
131                 
132                 Hashtable GetApplicationTypeEvents (Type type)
133                 {
134                         lock (this_lock) {
135                                 if (app_event_handlers != null)
136                                         return app_event_handlers;
137
138                                 app_event_handlers = new Hashtable ();
139                                 BindingFlags flags = BindingFlags.Public    | BindingFlags.NonPublic | 
140                                         BindingFlags.Instance  | BindingFlags.Static;
141
142                                 MethodInfo [] methods = type.GetMethods (flags);
143                                 foreach (MethodInfo m in methods) {
144                                         if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m))
145                                                 AddEvent (m, app_event_handlers);
146                                 }
147                         }
148
149                         return app_event_handlers;
150                 }
151
152                 Hashtable GetApplicationTypeEvents (HttpApplication app)
153                 {
154                         lock (this_lock) {
155                                 if (app_event_handlers != null)
156                                         return app_event_handlers;
157
158                                 return GetApplicationTypeEvents (app.GetType ());
159                         }
160                 }
161
162                 bool FireEvent (string method_name, object target, object [] args)
163                 {
164                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
165                         MethodInfo method = possibleEvents [method_name] as MethodInfo;
166                         if (method == null)
167                                 return false;
168
169                         if (method.GetParameters ().Length == 0)
170                                 args = null;
171
172                         method.Invoke (target, args);
173
174                         return true;
175                 }
176
177                 HttpApplication FireOnAppStart (HttpContext context)
178                 {
179                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
180                         context.ApplicationInstance = app;
181                         app.SetContext (context);
182                         object [] args = new object [] {app, EventArgs.Empty};
183                         FireEvent ("Application_Start", app, args);
184                         return app;
185                 }
186
187                 void FireOnAppEnd ()
188                 {
189                         if (app_type == null)
190                                 return; // we didn't even get an application
191
192                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
193                         FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
194                         app.Dispose ();
195                         app_type = null;
196                 }
197
198                 //
199                 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
200                 // To reproduce this in action, touch "global.asax" while XSP is running.
201                 //
202                 public static void Dispose ()
203                 {
204                         theFactory.FireOnAppEnd ();
205                 }
206
207                 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
208                 {
209                         FileSystemWatcher watcher = new FileSystemWatcher ();
210
211                         watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
212                         watcher.Filter = Path.GetFileName (file);
213                         
214                         // This will enable the Modify flag for Linux/inotify
215                         watcher.NotifyFilter |= NotifyFilters.Size;
216                         
217                         watcher.Changed += hnd;
218                         watcher.Created += hnd;
219                         watcher.Deleted += hnd;
220                         watcher.Renamed += reh;
221
222                         watcher.EnableRaisingEvents = true;
223
224                         return watcher;
225                 }
226
227                 internal static void AttachEvents (HttpApplication app)
228                 {
229                         HttpApplicationFactory factory = theFactory;
230                         Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
231                         foreach (string key in possibleEvents.Keys) {
232                                 int pos = key.IndexOf ('_');
233                                 string moduleName = key.Substring (0, pos);
234                                 object target;
235                                 if (moduleName == "Application") {
236                                         target = app;
237                                 } else {
238                                         target = app.Modules [moduleName];
239                                         if (target == null)
240                                                 continue;
241                                 }
242
243                                 string eventName = key.Substring (pos + 1);
244                                 EventInfo evt = target.GetType ().GetEvent (eventName);
245                                 if (evt == null)
246                                         continue;
247
248                                 string usualName = moduleName + "_" + eventName;
249                                 object methodData = possibleEvents [usualName];
250                                 if (methodData != null && eventName == "End" && moduleName == "Session") {
251                                         lock (factory) {
252                                                 if (factory.session_end == null)
253                                                         factory.session_end = (MethodInfo) methodData;
254                                         }
255                                         continue;
256                                 }
257
258                                 if (methodData == null)
259                                         continue;
260
261                                 if (methodData is MethodInfo) {
262                                         factory.AddHandler (evt, target, app, (MethodInfo) methodData);
263                                         continue;
264                                 }
265
266                                 ArrayList list = (ArrayList) methodData;
267                                 foreach (MethodInfo method in list)
268                                         factory.AddHandler (evt, target, app, method);
269                         }
270                 }
271
272                 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
273                 {
274                         int length = method.GetParameters ().Length;
275
276                         if (length == 0) {
277                                 NoParamsInvoker npi = new NoParamsInvoker (app, method);
278                                 evt.AddEventHandler (target, npi.FakeDelegate);
279                         } else {
280                                 evt.AddEventHandler (target, Delegate.CreateDelegate (
281                                                              evt.EventHandlerType, app,
282 #if NET_2_0
283                                                              method
284 #else
285                                                              method.Name
286 #endif
287                                                      ));
288                         }
289                         
290                 }
291
292                 internal static void InvokeSessionEnd (object state)
293                 {
294                         InvokeSessionEnd (state, null, EventArgs.Empty);
295                 }
296                 
297                 internal static void InvokeSessionEnd (object state, object source, EventArgs e)
298                 {
299                         HttpApplicationFactory factory = theFactory;
300                         MethodInfo method = null;
301                         HttpApplication app = null;
302                         lock (factory.available_for_end) {
303                                 method = factory.session_end;
304                                 if (method == null)
305                                         return;
306
307                                 app = GetApplicationForSessionEnd ();
308                         }
309
310                         app.SetSession ((HttpSessionState) state);
311                         try {
312                                 method.Invoke (app, new object [] {(source == null ? app : source), e});
313                         } catch (Exception) {
314                                 // Ignore
315                         }
316                         RecycleForSessionEnd (app);
317                 }
318
319                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
320                 {
321                         if (list == null || list.Count == 0)
322                                 return null;
323
324                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
325                         foreach (ObjectTagBuilder tag in list) {
326                                 coll.Add (tag);
327                         }
328
329                         return coll;
330                 }
331                 
332                 internal static HttpApplicationState ApplicationState {
333 #if TARGET_J2EE
334                         get {
335                                 HttpApplicationFactory factory = theFactory;
336                                 if (factory.app_state == null)
337                                         factory.app_state = new HttpApplicationState (null, null);
338                                 return factory.app_state;
339                         }
340 #else
341                         get {
342                                 if (theFactory.app_state == null) {
343                                         HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
344                                         HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
345
346                                         theFactory.app_state = new HttpApplicationState (app, ses);
347                                 }
348                                 return theFactory.app_state;
349                         }
350 #endif
351                 }
352
353                 internal static Type AppType {
354                         get {
355                                 return theFactory.app_type;
356                         }
357                 }
358                 
359                 void InitType (HttpContext context)
360                 {
361                         lock (this_lock) {
362                                 if (!needs_init)
363                                         return;
364
365 #if NET_2_0
366                                 try {
367 #endif
368                                         string physical_app_path = HttpRuntime.AppDomainAppPath;
369                                         string app_file = null;
370                                         
371                                         app_file = Path.Combine (physical_app_path, "Global.asax");
372                                         if (!File.Exists (app_file)) {
373                                                 app_file = Path.Combine (physical_app_path, "global.asax");
374                                                 if (!File.Exists (app_file))
375                                                         app_file = null;
376                                         }
377                         
378 #if !NET_2_0
379                                         WebConfigurationSettings.Init (context);
380 #endif
381                 
382 #if NET_2_0 && !TARGET_J2EE
383                                         AppResourcesCompiler ac = new AppResourcesCompiler (context);
384                                         ac.Compile ();
385
386 #if WEBSERVICES_DEP
387                                         AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
388                                         awrc.Compile ();
389 #endif
390                                         
391                                         // Todo: Generate profile properties assembly from Web.config here
392                                 
393                                         AppCodeCompiler acc = new AppCodeCompiler ();
394                                         acc.Compile ();
395
396                                         // Note whether there are any App_Browsers/*.browser files.  If there
397                                         // are we will be using *.browser files for sniffing in addition to browscap.ini
398                                         string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
399                                         app_browsers_files = new string[0];
400                                         if (Directory.Exists (app_browsers_path)) {
401                                                 app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
402                                         }
403 #endif
404
405                                         if (app_file != null) {
406 #if TARGET_J2EE
407                                                 app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
408                                                 app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
409 #else
410 #if NET_2_0
411                                                 app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
412 #else
413                                                 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
414 #endif
415 #endif
416                                                 if (app_type == null) {
417                                                         string msg = String.Format ("Error compiling application file ({0}).", app_file);
418                                                         throw new ApplicationException (msg);
419                                                 }
420                                         } else {
421                                                 app_type = typeof (System.Web.HttpApplication);
422                                                 app_state = new HttpApplicationState ();
423                                         }
424
425                                         WatchLocationForRestart("Global.asax");
426                                         WatchLocationForRestart("global.asax");
427                                         WatchLocationForRestart("Web.config");
428                                         WatchLocationForRestart("web.config");
429                                         WatchLocationForRestart("Web.Config");
430                                         needs_init = false;
431 #if NET_2_0
432                                 } catch (Exception) {
433                                         if (BuildManager.CodeAssemblies != null)
434                                                 BuildManager.CodeAssemblies.Clear ();
435                                         if (BuildManager.TopLevelAssemblies != null)
436                                                 BuildManager.TopLevelAssemblies.Clear ();
437                                         if (WebConfigurationManager.ExtraAssemblies != null)
438                                                 WebConfigurationManager.ExtraAssemblies.Clear ();
439                                         throw;
440                                 }
441 #endif
442                                 
443                                 //
444                                 // Now init the settings
445                                 //
446
447                         }
448                 }
449                 
450                 //
451                 // Multiple-threads might hit this one on startup, and we have
452                 // to delay-initialize until we have the HttpContext
453                 //
454                 internal static HttpApplication GetApplication (HttpContext context)
455                 {
456 #if TARGET_J2EE
457                         if (context.ApplicationInstance!=null)
458                                 return context.ApplicationInstance;
459 #endif
460                         HttpApplicationFactory factory = theFactory;
461                         HttpApplication app = null;
462                         if (factory.app_start_needed){
463                                 if (context == null)
464                                         return null;
465
466                                 factory.InitType (context);
467                                 lock (factory) {
468                                         if (factory.app_start_needed) {
469                                                 foreach (string dir in HttpApplication.BinDirs)
470                                                         WatchLocationForRestart (dir, "*.dll");
471 #if NET_2_0
472                                                                         // Restart if the App_* directories are created...
473                                                 WatchLocationForRestart (".", "App_Code");
474                                                 WatchLocationForRestart (".", "App_Browsers");
475                                                 WatchLocationForRestart (".", "App_GlobalResources");
476                                                 // ...or their contents is changed.
477                                                 WatchLocationForRestart ("App_Code", "*", true);
478                                                 WatchLocationForRestart ("App_Browsers", "*");
479                                                 WatchLocationForRestart ("App_GlobalResources", "*");
480 #endif
481                                                 app = factory.FireOnAppStart (context);
482                                                 factory.app_start_needed = false;
483                                                 return app;
484                                         }
485                                 }
486                         }
487
488                         lock (factory.available) {
489                                 if (factory.available.Count > 0) {
490                                         app = (HttpApplication) factory.available.Pop ();
491                                         app.RequestCompleted = false;
492                                         return app;
493                                 }
494                         }
495                         
496                         return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
497                 }
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
506                         HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
507                         app.InitOnce (false);
508
509                         return app;
510                 }
511
512                 internal static void RecycleForSessionEnd (HttpApplication app)
513                 {
514                         HttpApplicationFactory factory = theFactory;
515                         lock (factory.available_for_end) {
516                                 if (factory.available_for_end.Count < 32)
517                                         factory.available_for_end.Push (app);
518                                 else
519                                         app.Dispose ();
520                         }
521                 }
522
523                 internal static void Recycle (HttpApplication app)
524                 {
525                         HttpApplicationFactory factory = theFactory;
526                         lock (factory.available) {
527                                 if (factory.available.Count < 32)
528                                         factory.available.Push (app);
529                                 else
530                                         app.Dispose ();
531                         }
532                 }
533
534                 internal static bool ContextAvailable {
535                         get { return theFactory != null && !theFactory.app_start_needed; }
536                 }
537
538
539                 internal static bool WatchLocationForRestart (string filter)
540                 {
541                         return WatchLocationForRestart ("", filter, false);
542                 }
543
544                 internal static bool WatchLocationForRestart (string virtualPath, string filter)
545                 {
546                         return WatchLocationForRestart (virtualPath, filter, false);
547                 }
548                 
549                 internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
550                 {
551                         // map the path to the physical one
552                         string physicalPath = HttpRuntime.AppDomainAppPath;
553                         physicalPath = Path.Combine(physicalPath, virtualPath);
554                         bool isDir = Directory.Exists(physicalPath);
555                         bool isFile = isDir ? false : File.Exists(physicalPath);
556                         
557                         if (isDir || isFile) {
558                                 // create the watcher
559                                 FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
560                                 RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
561                                 FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
562                                 if (isDir)
563                                         watcher.IncludeSubdirectories = watchSubdirs;
564                                 
565                                 lock (watchers_lock) {
566                                         watchers.Add(watcher);
567                                 }
568                                 return true;
569                         } else {
570                                 return false;
571                         }
572                 }
573
574 #if NET_2_0
575                 internal static bool ApplicationDisabled {
576                         get { return app_disabled; }
577                         set { app_disabled = value; }
578                 }
579
580                 internal static string[] AppBrowsersFiles {
581                         get { return app_browsers_files; }
582                 }
583                 
584                 static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
585                 static object capabilities_processor_lock = new object();
586                 internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
587                         get {
588                                 lock (capabilities_processor_lock) {
589                                         if (capabilities_processor == null) {
590                                                 capabilities_processor = new System.Web.Configuration.nBrowser.Build();
591                                                 string machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
592                                                 if (Directory.Exists (machine_browsers_path)) {
593                                                         string[] machine_browsers_files 
594                                                                 = Directory.GetFiles (machine_browsers_path, "*.browser");
595                                                         foreach (string f in machine_browsers_files) {
596                                                                 capabilities_processor.AddBrowserFile(f);
597                                                         }
598                                                 }
599                                                 foreach (string f in app_browsers_files) {
600                                                         capabilities_processor.AddBrowserFile(f);
601                                                 }
602                                         }
603                                 }
604                                 return capabilities_processor;
605                         }
606                 }
607 #endif
608                 
609                 internal static void DisableWatchers ()
610                 {
611                         lock (watchers_lock) {
612                                 foreach (FileSystemWatcher watcher in watchers)
613                                         watcher.EnableRaisingEvents = false;
614                         }
615                 }
616
617                 internal static void EnableWatchers ()
618                 {
619                         lock (watchers_lock) {
620                                 foreach (FileSystemWatcher watcher in watchers)
621                                         watcher.EnableRaisingEvents = true;
622                         }
623                 }
624                 
625                 static void OnFileRenamed(object sender, RenamedEventArgs args)
626                 {
627                         OnFileChanged(sender, args);
628                 }
629
630                 static void OnFileChanged(object sender, FileSystemEventArgs args)
631                 {
632                         lock (watchers_lock) {
633                                 if(app_shutdown)
634                                         return;
635                                 app_shutdown = true;
636
637                                 // Disable event raising to avoid concurrent restarts
638                                 DisableWatchers ();
639                                 
640                                 // Restart application
641                                 HttpRuntime.UnloadAppDomain();
642                         }
643                 }
644         }
645 }
646