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