2 // System.Web.HttpApplicationFactory
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
8 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections;
32 using System.Reflection;
34 using System.Web.SessionState;
35 using System.Web.Configuration;
38 using System.Web.Compilation;
43 namespace System.Web {
44 class HttpApplicationFactory {
45 // Initialized in InitType
47 static HttpApplicationFactory theFactory {
50 HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
51 if (factory == null) {
52 lock(typeof(HttpApplicationFactory)) {
53 factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
54 if (factory == null) {
55 factory = new HttpApplicationFactory();
56 System.Threading.Thread.Sleep(1);
57 AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
65 static HttpApplicationFactory theFactory = new HttpApplicationFactory();
68 static MethodInfo session_end;
69 bool needs_init = true;
70 bool app_start_needed = true;
72 HttpApplicationState app_state;
73 Hashtable app_event_handlers;
75 FileSystemWatcher app_file_watcher;
76 FileSystemWatcher bin_watcher;
78 Stack available = new Stack ();
79 Stack available_for_end = new Stack ();
81 // Watch this thing out when getting an instance
82 IHttpHandler custom_application;
84 bool IsEventHandler (MethodInfo m)
86 int pos = m.Name.IndexOf ('_');
87 if (pos == -1 || (m.Name.Length - 1) <= pos)
90 if (m.ReturnType != typeof (void))
93 ParameterInfo [] pi = m.GetParameters ();
94 int length = pi.Length;
101 if (pi [0].ParameterType != typeof (object) ||
102 pi [1].ParameterType != typeof (EventArgs))
108 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
110 string name = method.Name.Replace ("_On", "_");
111 if (appTypeEventHandlers [name] == null) {
112 appTypeEventHandlers [name] = method;
116 MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
118 if (old_method != null){
119 list = new ArrayList (4);
120 list.Add (old_method);
121 appTypeEventHandlers [name] = list;
123 list = appTypeEventHandlers [name] as ArrayList;
128 Hashtable GetApplicationTypeEvents (Type type)
131 if (app_event_handlers != null)
132 return app_event_handlers;
134 app_event_handlers = new Hashtable ();
135 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
136 BindingFlags.Instance | BindingFlags.Static;
138 MethodInfo [] methods = type.GetMethods (flags);
139 foreach (MethodInfo m in methods) {
140 if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m))
141 AddEvent (m, app_event_handlers);
145 return app_event_handlers;
148 Hashtable GetApplicationTypeEvents (HttpApplication app)
151 if (app_event_handlers != null)
152 return app_event_handlers;
154 return GetApplicationTypeEvents (app.GetType ());
158 bool FireEvent (string method_name, object target, object [] args)
160 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
161 MethodInfo method = possibleEvents [method_name] as MethodInfo;
165 if (method.GetParameters ().Length == 0)
168 method.Invoke (target, args);
173 HttpApplication FireOnAppStart (HttpContext context)
175 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
176 context.ApplicationInstance = app;
177 app.SetContext (context);
178 object [] args = new object [] {app, EventArgs.Empty};
179 FireEvent ("Application_Start", app, args);
185 if (app_type == null)
186 return; // we didn't even get an application
188 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
189 FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
194 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
195 // To reproduce this in action, touch "global.asax" while XSP is running.
197 public static void Dispose ()
199 theFactory.FireOnAppEnd ();
203 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd)
205 FileSystemWatcher watcher = new FileSystemWatcher ();
207 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
208 watcher.Filter = Path.GetFileName (file);
210 watcher.Changed += hnd;
211 watcher.Created += hnd;
212 watcher.Deleted += hnd;
214 watcher.EnableRaisingEvents = true;
219 void OnAppFileChanged (object sender, FileSystemEventArgs args)
221 if (bin_watcher != null)
222 bin_watcher.EnableRaisingEvents = false;
223 if (app_file_watcher != null)
224 app_file_watcher.EnableRaisingEvents = false;
225 HttpRuntime.UnloadAppDomain ();
229 internal static void AttachEvents (HttpApplication app)
231 HttpApplicationFactory factory = theFactory;
232 Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
233 foreach (string key in possibleEvents.Keys) {
234 int pos = key.IndexOf ('_');
235 string moduleName = key.Substring (0, pos);
237 if (moduleName == "Application") {
240 target = app.Modules [moduleName];
245 string eventName = key.Substring (pos + 1);
246 EventInfo evt = target.GetType ().GetEvent (eventName);
250 string usualName = moduleName + "_" + eventName;
251 object methodData = possibleEvents [usualName];
252 if (methodData != null && eventName == "End" && moduleName == "Session") {
254 if (session_end == null)
255 session_end = (MethodInfo) methodData;
260 if (methodData == null)
263 if (methodData is MethodInfo) {
264 factory.AddHandler (evt, target, app, (MethodInfo) methodData);
268 ArrayList list = (ArrayList) methodData;
269 foreach (MethodInfo method in list)
270 factory.AddHandler (evt, target, app, method);
274 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
276 int length = method.GetParameters ().Length;
279 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
280 evt.AddEventHandler (target, npi.FakeDelegate);
282 evt.AddEventHandler (target, Delegate.CreateDelegate (
283 typeof (EventHandler), app, method.Name));
287 internal static void InvokeSessionEnd (object state)
289 HttpApplicationFactory factory = theFactory;
290 MethodInfo method = null;
291 HttpApplication app = null;
292 lock (factory.available_for_end) {
293 method = session_end;
297 app = GetApplicationForSessionEnd ();
300 app.SetSession ((HttpSessionState) state);
302 method.Invoke (app, new object [] {app, EventArgs.Empty});
303 } catch (Exception e) {
306 RecycleForSessionEnd (app);
309 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
311 if (list == null || list.Count == 0)
314 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
315 foreach (ObjectTagBuilder tag in list) {
322 internal static HttpApplicationState ApplicationState {
325 HttpApplicationFactory factory = theFactory;
326 if (factory.app_state == null)
327 factory.app_state = new HttpApplicationState (null, null);
328 return factory.app_state;
332 if (theFactory.app_state == null) {
333 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
334 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
336 theFactory.app_state = new HttpApplicationState (app, ses);
338 return theFactory.app_state;
343 public static void SetCustomApplication (IHttpHandler customApplication)
345 theFactory.custom_application = customApplication;
348 internal static Type AppType {
350 return theFactory.app_type;
354 void InitType (HttpContext context)
360 string physical_app_path = context.Request.PhysicalApplicationPath;
363 app_file = Path.Combine (physical_app_path, "Global.asax");
364 if (!File.Exists (app_file))
365 app_file = Path.Combine (physical_app_path, "global.asax");
367 #if !CONFIGURATION_2_0
368 WebConfigurationSettings.Init (context);
371 if (File.Exists (app_file)) {
373 app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
375 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
376 if (app_type == null) {
377 string msg = String.Format ("Error compiling application file ({0}).", app_file);
378 throw new ApplicationException (msg);
381 app_file_watcher = CreateWatcher (app_file, new FileSystemEventHandler (OnAppFileChanged));
384 app_type = typeof (System.Web.HttpApplication);
385 app_state = new HttpApplicationState ();
390 // Now init the settings
397 // Multiple-threads might hit this one on startup, and we have
398 // to delay-initialize until we have the HttpContext
400 internal static HttpApplication GetApplication (HttpContext context)
402 HttpApplicationFactory factory = theFactory;
403 HttpApplication app = null;
404 if (factory.needs_init){
408 factory.InitType (context);
410 if (factory.app_start_needed) {
412 string bin = HttpRuntime.BinDirectory;
413 if (Directory.Exists (bin))
414 bin = Path.Combine (bin, "*.dll");
416 // We watch bin or bin/*.dll if the directory exists
417 factory.bin_watcher = CreateWatcher (bin, new FileSystemEventHandler (factory.OnAppFileChanged));
419 app = factory.FireOnAppStart (context);
420 factory.app_start_needed = false;
426 lock (factory.available) {
427 if (factory.available.Count > 0) {
428 app = (HttpApplication) factory.available.Pop ();
429 app.RequestCompleted = false;
434 return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
437 // The lock is in InvokeSessionEnd
438 static HttpApplication GetApplicationForSessionEnd ()
440 HttpApplicationFactory factory = theFactory;
441 if (factory.available_for_end.Count > 0)
442 return (HttpApplication) factory.available_for_end.Pop ();
444 HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
445 app.InitOnce (false);
450 internal static void RecycleForSessionEnd (HttpApplication app)
452 HttpApplicationFactory factory = theFactory;
453 lock (factory.available_for_end) {
454 if (factory.available_for_end.Count < 32)
455 factory.available_for_end.Push (app);
461 internal static void Recycle (HttpApplication app)
463 HttpApplicationFactory factory = theFactory;
464 lock (factory.available) {
465 if (factory.available.Count < 32)
466 factory.available.Push (app);
472 internal static bool ContextAvailable {
473 get { return theFactory != null && theFactory.app_start_needed && theFactory.needs_init; }