Mark tests as not working under TARGET_JVM
[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                 // 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         
71
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                 Stack available = new Stack ();
81                 Stack available_for_end = new Stack ();
82                 
83                 bool IsEventHandler (MethodInfo m)
84                 {
85                         int pos = m.Name.IndexOf ('_');
86                         if (pos == -1 || (m.Name.Length - 1) <= pos)
87                                 return false;
88
89                         if (m.ReturnType != typeof (void))
90                                 return false;
91
92                         ParameterInfo [] pi = m.GetParameters ();
93                         int length = pi.Length;
94                         if (length == 0)
95                                 return true;
96
97                         if (length != 2)
98                                 return false;
99
100                         if (pi [0].ParameterType != typeof (object) ||
101                             pi [1].ParameterType != typeof (EventArgs))
102                                 return false;
103                         
104                         return true;
105                 }
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
115                         MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
116                         ArrayList list;
117                         if (old_method != null){
118                                 list = new ArrayList (4);
119                                 list.Add (old_method);
120                                 appTypeEventHandlers [name] = list;
121                         } else 
122                                 list = appTypeEventHandlers [name] as ArrayList;
123
124                         list.Add (method);
125                 }
126                 
127                 Hashtable GetApplicationTypeEvents (Type type)
128                 {
129                         lock (this) {
130                                 if (app_event_handlers != null)
131                                         return app_event_handlers;
132
133                                 app_event_handlers = new Hashtable ();
134                                 BindingFlags flags = BindingFlags.Public    | BindingFlags.NonPublic | 
135                                         BindingFlags.Instance  | BindingFlags.Static;
136
137                                 MethodInfo [] methods = type.GetMethods (flags);
138                                 foreach (MethodInfo m in methods) {
139                                         if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m))
140                                                 AddEvent (m, app_event_handlers);
141                                 }
142                         }
143
144                         return app_event_handlers;
145                 }
146
147                 Hashtable GetApplicationTypeEvents (HttpApplication app)
148                 {
149                         lock (this) {
150                                 if (app_event_handlers != null)
151                                         return app_event_handlers;
152
153                                 return GetApplicationTypeEvents (app.GetType ());
154                         }
155                 }
156
157                 bool FireEvent (string method_name, object target, object [] args)
158                 {
159                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
160                         MethodInfo method = possibleEvents [method_name] as MethodInfo;
161                         if (method == null)
162                                 return false;
163
164                         if (method.GetParameters ().Length == 0)
165                                 args = null;
166
167                         method.Invoke (target, args);
168
169                         return true;
170                 }
171
172                 HttpApplication FireOnAppStart (HttpContext context)
173                 {
174                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
175                         context.ApplicationInstance = app;
176                         app.SetContext (context);
177                         object [] args = new object [] {app, EventArgs.Empty};
178                         FireEvent ("Application_Start", app, args);
179                         return app;
180                 }
181
182                 void FireOnAppEnd ()
183                 {
184                         if (app_type == null)
185                                 return; // we didn't even get an application
186
187                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
188                         FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
189                         app.Dispose ();
190                 }
191
192                 //
193                 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
194                 // To reproduce this in action, touch "global.asax" while XSP is running.
195                 //
196                 public static void Dispose ()
197                 {
198                         theFactory.FireOnAppEnd ();
199                 }
200
201                 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
202                 {
203                         FileSystemWatcher watcher = new FileSystemWatcher ();
204
205                         watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
206                         watcher.Filter = Path.GetFileName (file);
207
208                         watcher.Changed += hnd;
209                         watcher.Created += hnd;
210                         watcher.Deleted += hnd;
211                         watcher.Renamed += reh;
212
213                         watcher.EnableRaisingEvents = true;
214
215                         return watcher;
216                 }
217
218                 internal static void AttachEvents (HttpApplication app)
219                 {
220                         HttpApplicationFactory factory = theFactory;
221                         Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
222                         foreach (string key in possibleEvents.Keys) {
223                                 int pos = key.IndexOf ('_');
224                                 string moduleName = key.Substring (0, pos);
225                                 object target;
226                                 if (moduleName == "Application") {
227                                         target = app;
228                                 } else {
229                                         target = app.Modules [moduleName];
230                                         if (target == null)
231                                                 continue;
232                                 }
233
234                                 string eventName = key.Substring (pos + 1);
235                                 EventInfo evt = target.GetType ().GetEvent (eventName);
236                                 if (evt == null)
237                                         continue;
238
239                                 string usualName = moduleName + "_" + eventName;
240                                 object methodData = possibleEvents [usualName];
241                                 if (methodData != null && eventName == "End" && moduleName == "Session") {
242                                         lock (factory) {
243                                                 if (factory.session_end == null)
244                                                         factory.session_end = (MethodInfo) methodData;
245                                         }
246                                         continue;
247                                 }
248
249                                 if (methodData == null)
250                                         continue;
251
252                                 if (methodData is MethodInfo) {
253                                         factory.AddHandler (evt, target, app, (MethodInfo) methodData);
254                                         continue;
255                                 }
256
257                                 ArrayList list = (ArrayList) methodData;
258                                 foreach (MethodInfo method in list)
259                                         factory.AddHandler (evt, target, app, method);
260                         }
261                 }
262
263                 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
264                 {
265                         int length = method.GetParameters ().Length;
266
267                         if (length == 0) {
268                                 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
269                                 evt.AddEventHandler (target, npi.FakeDelegate);
270                         } else {
271                                 evt.AddEventHandler (target, Delegate.CreateDelegate (
272                                                              evt.EventHandlerType, app, method.Name));
273                         }
274                 }
275
276                 internal static void InvokeSessionEnd (object state)
277                 {
278                         InvokeSessionEnd (state, null, EventArgs.Empty);
279                 }
280                 
281                 internal static void InvokeSessionEnd (object state, object source, EventArgs e)
282                 {
283                         HttpApplicationFactory factory = theFactory;
284                         MethodInfo method = null;
285                         HttpApplication app = null;
286                         lock (factory.available_for_end) {
287                                 method = factory.session_end;
288                                 if (method == null)
289                                         return;
290
291                                 app = GetApplicationForSessionEnd ();
292                         }
293
294                         app.SetSession ((HttpSessionState) state);
295                         try {
296                                 method.Invoke (app, new object [] {(source == null ? app : source), e});
297                         } catch (Exception) {
298                                 // Ignore
299                         }
300                         RecycleForSessionEnd (app);
301                 }
302
303                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
304                 {
305                         if (list == null || list.Count == 0)
306                                 return null;
307
308                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
309                         foreach (ObjectTagBuilder tag in list) {
310                                 coll.Add (tag);
311                         }
312
313                         return coll;
314                 }
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
330                                         theFactory.app_state = new HttpApplicationState (app, ses);
331                                 }
332                                 return theFactory.app_state;
333                         }
334 #endif
335                 }
336
337                 internal static Type AppType {
338                         get {
339                                 return theFactory.app_type;
340                         }
341                 }
342                 
343                 void InitType (HttpContext context)
344                 {
345                         lock (this) {
346                                 if (!needs_init)
347                                         return;
348
349 #if NET_2_0
350                                 try {
351 #endif
352                                         string physical_app_path = context.Request.PhysicalApplicationPath;
353                                         string app_file = null;
354                                         
355                                         app_file = Path.Combine (physical_app_path, "Global.asax");
356                                         if (!File.Exists (app_file)) {
357                                                 app_file = Path.Combine (physical_app_path, "global.asax");
358                                                 if (!File.Exists (app_file))
359                                                         app_file = null;
360                                         }
361                         
362 #if !NET_2_0
363                                         WebConfigurationSettings.Init (context);
364 #endif
365                 
366 #if NET_2_0 && !TARGET_J2EE
367                                         AppResourcesCompiler ac = new AppResourcesCompiler (context);
368                                         ac.Compile ();
369                                 
370                                         // Todo: Process App_WebResources here
371                                 
372                                         // Todo: Generate profile properties assembly from Web.config here
373                                 
374                                         // Todo: Compile code from App_Code here
375                                         AppCodeCompiler acc = new AppCodeCompiler ();
376                                         acc.Compile ();
377 #endif
378
379                                         if (app_file != null) {
380 #if TARGET_J2EE
381                                                 app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
382 #else
383                                                 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
384                                                 if (app_type == null) {
385                                                         string msg = String.Format ("Error compiling application file ({0}).", app_file);
386                                                         throw new ApplicationException (msg);
387                                                 }
388 #endif
389                                         } else {
390                                                 app_type = typeof (System.Web.HttpApplication);
391                                                 app_state = new HttpApplicationState ();
392                                         }
393
394                                         if (app_file != null)
395                                                 WatchLocationForRestart(app_file);
396                                             
397                                         if (File.Exists(Path.Combine(physical_app_path, "Web.config")))
398                                                 WatchLocationForRestart("Web.config");
399                                         else if (File.Exists(Path.Combine(physical_app_path, "web.config")))
400                                                 WatchLocationForRestart("web.config");
401                                         else if (File.Exists(Path.Combine(physical_app_path, "Web.Config")))
402                                                 WatchLocationForRestart("Web.Config");
403                                         needs_init = false;
404 #if NET_2_0
405                                 } catch (Exception) {
406                                         if (BuildManager.CodeAssemblies != null)
407                                                 BuildManager.CodeAssemblies.Clear ();
408                                         if (BuildManager.TopLevelAssemblies != null)
409                                                 BuildManager.TopLevelAssemblies.Clear ();
410                                         if (WebConfigurationManager.ExtraAssemblies != null)
411                                                 WebConfigurationManager.ExtraAssemblies.Clear ();
412                                         throw;
413                                 }
414 #endif
415                                 
416                                 //
417                                 // Now init the settings
418                                 //
419
420                         }
421                 }
422                 
423                 //
424                 // Multiple-threads might hit this one on startup, and we have
425                 // to delay-initialize until we have the HttpContext
426                 //
427                 internal static HttpApplication GetApplication (HttpContext context)
428                 {
429                         HttpApplicationFactory factory = theFactory;
430                         HttpApplication app = null;
431                         if (factory.app_start_needed){
432                                 if (context == null)
433                                         return null;
434
435                                 factory.InitType (context);
436                                 lock (factory) {
437                                         if (factory.app_start_needed) {
438                                                 WatchLocationForRestart (AppDomain.CurrentDomain.SetupInformation.PrivateBinPath,
439                                                                          "*.dll");
440 #if NET_2_0
441                                                 WatchLocationForRestart ("App_Code", "");
442                                                 WatchLocationForRestart ("App_Browsers", "");
443                                                 WatchLocationForRestart ("App_GlobalResources", "");
444 #endif
445                                                 app = factory.FireOnAppStart (context);
446                                                 factory.app_start_needed = false;
447                                                 return app;
448                                         }
449                                 }
450                         }
451
452                         lock (factory.available) {
453                                 if (factory.available.Count > 0) {
454                                         app = (HttpApplication) factory.available.Pop ();
455                                         app.RequestCompleted = false;
456                                         return app;
457                                 }
458                         }
459                         
460                         return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
461                 }
462
463                 // The lock is in InvokeSessionEnd
464                 static HttpApplication GetApplicationForSessionEnd ()
465                 {
466                         HttpApplicationFactory factory = theFactory;
467                         if (factory.available_for_end.Count > 0)
468                                 return (HttpApplication) factory.available_for_end.Pop ();
469
470                         HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
471                         app.InitOnce (false);
472
473                         return app;
474                 }
475
476                 internal static void RecycleForSessionEnd (HttpApplication app)
477                 {
478                         HttpApplicationFactory factory = theFactory;
479                         lock (factory.available_for_end) {
480                                 if (factory.available_for_end.Count < 32)
481                                         factory.available_for_end.Push (app);
482                                 else
483                                         app.Dispose ();
484                         }
485                 }
486
487                 internal static void Recycle (HttpApplication app)
488                 {
489                         HttpApplicationFactory factory = theFactory;
490                         lock (factory.available) {
491                                 if (factory.available.Count < 32)
492                                         factory.available.Push (app);
493                                 else
494                                         app.Dispose ();
495                         }
496                 }
497
498                 internal static bool ContextAvailable {
499                         get { return theFactory != null && !theFactory.app_start_needed; }
500                 }
501                 
502                 internal static bool WatchLocationForRestart(string filter)
503                 {
504                         return WatchLocationForRestart("", filter);
505                 }
506         
507                 internal static bool WatchLocationForRestart(string virtualPath, string filter)
508                 {
509                         // map the path to the physical one
510                         string physicalPath = HttpRuntime.AppDomainAppPath;
511                         physicalPath = Path.Combine(physicalPath, virtualPath);
512
513                         if (Directory.Exists(physicalPath) || File.Exists(physicalPath)) {
514                                 physicalPath = Path.Combine(physicalPath, filter);
515
516                                 // create the watcher
517                                 FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
518                                 RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
519                                 FileSystemWatcher watcher = CreateWatcher(physicalPath, fseh, reh);
520                         
521                                 lock (watchers_lock) {
522                                         watchers.Add(watcher);
523                                 }
524                                 return true;
525                         } else {
526                                 return false;
527                         }
528                 }
529
530                 static void OnFileRenamed(object sender, RenamedEventArgs args)
531                 {
532                         OnFileChanged(sender, args);
533                 }
534
535                 static void OnFileChanged(object sender, FileSystemEventArgs args)
536                 {
537                         lock (watchers_lock) {
538                                 // Disable event raising to avoid concurrent restarts
539                                 foreach (FileSystemWatcher watcher in watchers) {
540                                         watcher.EnableRaisingEvents = false;
541                                 }
542                                 // Restart application
543                                 HttpRuntime.UnloadAppDomain();
544                         }
545                 }
546         }
547 }
548