* attribute.cs (GetMarshal): Work even if "DefineCustom" is
[mono.git] / mcs / class / System.Web / System.Web / HttpApplicationFactory.cs
1 //
2 // System.Web.HttpApplicationFactory
3 //
4 // TODO:
5 //   bin_watcher must work.
6 //   
7 //
8 // Author:
9 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
10 //
11 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
12 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 using System;
34 using System.Collections;
35 using System.IO;
36 using System.Reflection;
37 using System.Web.UI;
38 using System.Web.SessionState;
39 using System.Web.Configuration;
40
41 #if !TARGET_J2EE
42 using System.Web.Compilation;
43 #else
44 using vmw.common;
45 #endif
46
47 namespace System.Web {
48         class HttpApplicationFactory {
49                 // Initialized in InitType
50 #if TARGET_J2EE
51                 static HttpApplicationFactory theFactory {
52                         get
53                         {
54                                 HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
55                                 if (factory == null) {
56                                         lock(typeof(HttpApplicationFactory)) {
57                                                 factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
58                                                 if (factory == null) {
59                                                         factory = new HttpApplicationFactory();
60                                                         System.Threading.Thread.Sleep(1);
61                                                         AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
62                                                 }
63                                         }
64                                 }
65                                 return factory;
66                         }
67                 }
68 #else
69                 static HttpApplicationFactory theFactory = new HttpApplicationFactory();
70 #endif
71
72                 bool needs_init = true;
73                 bool app_start_needed = true;
74                 Type app_type;
75                 HttpApplicationState app_state;
76 #if !TARGET_JVM
77                 FileSystemWatcher app_file_watcher;
78                 FileSystemWatcher bin_watcher;
79 #endif
80                 Stack available = new Stack ();
81                 
82                 // Watch this thing out when getting an instance
83                 IHttpHandler custom_application;
84
85                 bool IsEventHandler (MethodInfo m)
86                 {
87                         if (m.ReturnType != typeof (void))
88                                 return false;
89
90                         ParameterInfo [] pi = m.GetParameters ();
91                         int length = pi.Length;
92                         if (length == 0)
93                                 return true;
94
95                         if (length != 2)
96                                 return false;
97
98                         if (pi [0].ParameterType != typeof (object) ||
99                             pi [1].ParameterType != typeof (EventArgs))
100                                 return false;
101                         
102                         return true;
103                 }
104
105                 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
106                 {
107                         string name = method.Name.Replace ("_On", "_");
108                         if (appTypeEventHandlers [name] == null) {
109                                 appTypeEventHandlers [name] = method;
110                                 return;
111                         }
112
113                         MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
114                         ArrayList list;
115                         if (old_method != null){
116                                 list = new ArrayList (4);
117                                 list.Add (old_method);
118                                 appTypeEventHandlers [name] = list;
119                         } else 
120                                 list = appTypeEventHandlers [name] as ArrayList;
121
122                         list.Add (method);
123                 }
124                 
125                 Hashtable GetApplicationTypeEvents (HttpApplication app)
126                 {
127                         Type appType = app.GetType ();
128                         Hashtable appTypeEventHandlers = new Hashtable ();
129                         BindingFlags flags = BindingFlags.Public    |
130                                              BindingFlags.NonPublic | 
131                                              BindingFlags.Instance |
132                                              BindingFlags.Static;
133
134                         MethodInfo [] methods = appType.GetMethods (flags);
135                         foreach (MethodInfo m in methods) {
136                                 if (IsEventHandler (m))
137                                         AddEvent (m, appTypeEventHandlers);
138                         }
139
140                         return appTypeEventHandlers;
141                 }
142
143                 bool FireEvent (string method_name, object target, object [] args)
144                 {
145                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
146                         MethodInfo method = possibleEvents [method_name] as MethodInfo;
147                         if (method == null)
148                                 return false;
149
150                         if (method.GetParameters ().Length == 0)
151                                 args = null;
152
153                         method.Invoke (target, args);
154
155                         return true;
156                 }
157
158                 void FireOnAppStart (HttpContext context)
159                 {
160                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
161                         app.SetContext (context);
162                         object [] args = new object [] {app, EventArgs.Empty};
163                         FireEvent ("Application_Start", app, args);
164                         Recycle (app);
165                 }
166
167                 void FireOnAppEnd ()
168                 {
169                         if (app_type == null)
170                                 return; // we didn't even get an application
171
172                         HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
173                         FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
174                         app.Dispose ();
175                 }
176
177                 //
178                 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
179                 // To reproduce this in action, touch "global.asax" while XSP is running.
180                 //
181                 public static void Dispose ()
182                 {
183                         theFactory.FireOnAppEnd ();
184                 }
185
186 #if !TARGET_JVM
187                 FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd)
188                 {
189                         FileSystemWatcher watcher = new FileSystemWatcher ();
190
191                         watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
192                         watcher.Filter = Path.GetFileName (file);
193
194                         watcher.Changed += hnd;
195                         watcher.Created += hnd;
196                         watcher.Deleted += hnd;
197
198                         watcher.EnableRaisingEvents = true;
199
200                         return watcher;
201                 }
202
203                 void OnAppFileChanged (object sender, FileSystemEventArgs args)
204                 {
205                         bin_watcher.EnableRaisingEvents = false;
206                         app_file_watcher.EnableRaisingEvents = false;
207                         HttpRuntime.UnloadAppDomain ();
208                 }
209 #endif
210
211                 internal static void AttachEvents (HttpApplication app)
212                 {
213                         HttpApplicationFactory factory = theFactory;
214                         Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
215                         foreach (string key in possibleEvents.Keys) {
216                                 int pos = key.IndexOf ('_');
217                                 if (pos == -1 || key.Length <= pos + 1)
218                                         continue;
219
220                                 string moduleName = key.Substring (0, pos);
221                                 object target;
222                                 if (moduleName == "Application") {
223                                         target = app;
224                                 } else {
225                                         target = app.Modules [moduleName];
226                                         if (target == null)
227                                                 continue;
228                                 }
229
230                                 string eventName = key.Substring (pos + 1);
231                                 EventInfo evt = target.GetType ().GetEvent (eventName);
232                                 if (evt == null)
233                                         continue;
234                         
235                                 string usualName = moduleName + "_" + eventName;
236                                 object methodData = possibleEvents [usualName];
237                                 if (methodData == null)
238                                         continue;
239
240                                 if (methodData is MethodInfo) {
241                                         factory.AddHandler (evt, target, app, (MethodInfo) methodData);
242                                         continue;
243                                 }
244
245                                 ArrayList list = (ArrayList) methodData;
246                                 foreach (MethodInfo method in list)
247                                         factory.AddHandler (evt, target, app, method);
248                         }
249                 }
250
251                 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
252                 {
253                         int length = method.GetParameters ().Length;
254
255                         if (length == 0) {
256                                 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
257                                 evt.AddEventHandler (target, npi.FakeDelegate);
258                         } else {
259                                 evt.AddEventHandler (target, Delegate.CreateDelegate (
260                                                         typeof (EventHandler), app, method.Name));
261                         }
262                 }
263
264                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
265                 {
266                         if (list == null || list.Count == 0)
267                                 return null;
268
269                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
270                         foreach (ObjectTagBuilder tag in list) {
271                                 coll.Add (tag);
272                         }
273
274                         return coll;
275                 }
276                 
277                 internal static HttpApplicationState ApplicationState {
278 #if TARGET_J2EE
279                         get {
280                                 HttpApplicationFactory factory = theFactory;
281                                 if (factory.app_state == null)
282                                         factory.app_state = new HttpApplicationState (null, null);
283                                 return factory.app_state;
284                         }
285 #else
286                         get {
287                                 if (theFactory.app_state == null) {
288                                         HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
289                                         HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
290
291                                         theFactory.app_state = new HttpApplicationState (app, ses);
292                                 }
293                                 return theFactory.app_state;
294                         }
295 #endif
296                 }
297
298                 public static void SetCustomApplication (IHttpHandler customApplication)
299                 {
300                         theFactory.custom_application = customApplication;
301                 }
302
303                 internal static Type AppType {
304                         get {
305                                 return theFactory.app_type;
306                         }
307                 }
308
309                 void InitType (HttpContext context)
310                 {
311                         lock (this) {
312                                 if (!needs_init)
313                                         return;
314                                 
315                                 string physical_app_path = context.Request.PhysicalApplicationPath;
316                                 string app_file;
317                                 
318                                 app_file = Path.Combine (physical_app_path, "Global.asax");
319                                 if (!File.Exists (app_file))
320                                         app_file = Path.Combine (physical_app_path, "global.asax");
321                                 
322                                 WebConfigurationSettings.Init (context);
323                                 
324                                 if (File.Exists (app_file)) {
325 #if TARGET_J2EE
326                                         app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
327 #else
328                                         app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
329                                         if (app_type == null) {
330                                                 string msg = String.Format ("Error compiling application file ({0}).", app_file);
331                                                 throw new ApplicationException (msg);
332                                         }
333                                         
334                                         app_file_watcher = CreateWatcher (app_file, new FileSystemEventHandler (OnAppFileChanged));
335 #endif
336                                 } else {
337                                         app_type = typeof (System.Web.HttpApplication);
338                                         app_state = new HttpApplicationState ();
339                                 }
340                                 needs_init = false;
341
342                                 //
343                                 // Now init the settings
344                                 //
345
346                         }
347                 }
348                 
349                 //
350                 // Multiple-threads might hit this one on startup, and we have
351                 // to delay-initialize until we have the HttpContext
352                 //
353                 internal static HttpApplication GetApplication (HttpContext context)
354                 {
355                         HttpApplicationFactory factory = theFactory;
356                         if (factory.needs_init){
357                                 if (context == null)
358                                         return null;
359
360                                 factory.InitType (context);
361                                 lock (factory) {
362                                         if (factory.app_start_needed) {
363                                                 factory.FireOnAppStart (context);
364                                                 factory.app_start_needed = false;
365                                         }
366                                 }
367                         }
368
369                         lock (factory) {
370                                 if (factory.available.Count > 0)
371                                         return (HttpApplication) factory.available.Pop ();
372                         }
373                         
374                         HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
375
376                         return app;
377                 }
378
379                 internal static void Recycle (HttpApplication app)
380                 {
381                         HttpApplicationFactory factory = theFactory;
382                         lock (factory) {
383                                 if (factory.available.Count < 32)
384                                         factory.available.Push (app);
385                                 else
386                                         app.Dispose ();
387                         }
388                 }
389         }
390 }