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 #if NET_2_0 && !TARGET_J2EE
44 using System.CodeDom.Compiler;
47 namespace System.Web {
48 class HttpApplicationFactory {
49 // Initialized in InitType
51 static HttpApplicationFactory theFactory {
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);
69 static HttpApplicationFactory theFactory = new HttpApplicationFactory();
72 MethodInfo session_end;
73 bool needs_init = true;
74 bool app_start_needed = true;
76 HttpApplicationState app_state;
77 Hashtable app_event_handlers;
79 FileSystemWatcher app_file_watcher;
80 FileSystemWatcher bin_watcher;
81 FileSystemWatcher config_watcher;
83 Stack available = new Stack ();
84 Stack available_for_end = new Stack ();
86 // Watch this thing out when getting an instance
87 IHttpHandler custom_application;
89 bool IsEventHandler (MethodInfo m)
91 int pos = m.Name.IndexOf ('_');
92 if (pos == -1 || (m.Name.Length - 1) <= pos)
95 if (m.ReturnType != typeof (void))
98 ParameterInfo [] pi = m.GetParameters ();
99 int length = pi.Length;
106 if (pi [0].ParameterType != typeof (object) ||
107 pi [1].ParameterType != typeof (EventArgs))
113 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
115 string name = method.Name.Replace ("_On", "_");
116 if (appTypeEventHandlers [name] == null) {
117 appTypeEventHandlers [name] = method;
121 MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
123 if (old_method != null){
124 list = new ArrayList (4);
125 list.Add (old_method);
126 appTypeEventHandlers [name] = list;
128 list = appTypeEventHandlers [name] as ArrayList;
133 Hashtable GetApplicationTypeEvents (Type type)
136 if (app_event_handlers != null)
137 return app_event_handlers;
139 app_event_handlers = new Hashtable ();
140 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
141 BindingFlags.Instance | BindingFlags.Static;
143 MethodInfo [] methods = type.GetMethods (flags);
144 foreach (MethodInfo m in methods) {
145 if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m))
146 AddEvent (m, app_event_handlers);
150 return app_event_handlers;
153 Hashtable GetApplicationTypeEvents (HttpApplication app)
156 if (app_event_handlers != null)
157 return app_event_handlers;
159 return GetApplicationTypeEvents (app.GetType ());
163 bool FireEvent (string method_name, object target, object [] args)
165 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
166 MethodInfo method = possibleEvents [method_name] as MethodInfo;
170 if (method.GetParameters ().Length == 0)
173 method.Invoke (target, args);
178 HttpApplication FireOnAppStart (HttpContext context)
180 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
181 context.ApplicationInstance = app;
182 app.SetContext (context);
183 object [] args = new object [] {app, EventArgs.Empty};
184 FireEvent ("Application_Start", app, args);
190 if (app_type == null)
191 return; // we didn't even get an application
193 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
194 FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
199 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
200 // To reproduce this in action, touch "global.asax" while XSP is running.
202 public static void Dispose ()
204 theFactory.FireOnAppEnd ();
208 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
210 FileSystemWatcher watcher = new FileSystemWatcher ();
212 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
213 watcher.Filter = Path.GetFileName (file);
215 watcher.Changed += hnd;
216 watcher.Created += hnd;
217 watcher.Deleted += hnd;
218 watcher.Renamed += reh;
220 watcher.EnableRaisingEvents = true;
225 void OnAppFileRenamed (object sender, RenamedEventArgs args)
227 OnAppFileChanged (sender, args);
230 void OnAppFileChanged (object sender, FileSystemEventArgs args)
232 if (config_watcher != null)
233 config_watcher.EnableRaisingEvents = false;
234 if (bin_watcher != null)
235 bin_watcher.EnableRaisingEvents = false;
236 if (app_file_watcher != null)
237 app_file_watcher.EnableRaisingEvents = false;
238 HttpRuntime.UnloadAppDomain ();
242 internal static void AttachEvents (HttpApplication app)
244 HttpApplicationFactory factory = theFactory;
245 Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
246 foreach (string key in possibleEvents.Keys) {
247 int pos = key.IndexOf ('_');
248 string moduleName = key.Substring (0, pos);
250 if (moduleName == "Application") {
253 target = app.Modules [moduleName];
258 string eventName = key.Substring (pos + 1);
259 EventInfo evt = target.GetType ().GetEvent (eventName);
263 string usualName = moduleName + "_" + eventName;
264 object methodData = possibleEvents [usualName];
265 if (methodData != null && eventName == "End" && moduleName == "Session") {
267 if (factory.session_end == null)
268 factory.session_end = (MethodInfo) methodData;
273 if (methodData == null)
276 if (methodData is MethodInfo) {
277 factory.AddHandler (evt, target, app, (MethodInfo) methodData);
281 ArrayList list = (ArrayList) methodData;
282 foreach (MethodInfo method in list)
283 factory.AddHandler (evt, target, app, method);
287 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
289 int length = method.GetParameters ().Length;
292 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
293 evt.AddEventHandler (target, npi.FakeDelegate);
295 evt.AddEventHandler (target, Delegate.CreateDelegate (
296 evt.EventHandlerType, app, method.Name));
300 internal static void InvokeSessionEnd (object state)
302 HttpApplicationFactory factory = theFactory;
303 MethodInfo method = null;
304 HttpApplication app = null;
305 lock (factory.available_for_end) {
306 method = factory.session_end;
310 app = GetApplicationForSessionEnd ();
313 app.SetSession ((HttpSessionState) state);
315 method.Invoke (app, new object [] {app, EventArgs.Empty});
316 } catch (Exception) {
319 RecycleForSessionEnd (app);
322 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
324 if (list == null || list.Count == 0)
327 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
328 foreach (ObjectTagBuilder tag in list) {
335 internal static HttpApplicationState ApplicationState {
338 HttpApplicationFactory factory = theFactory;
339 if (factory.app_state == null)
340 factory.app_state = new HttpApplicationState (null, null);
341 return factory.app_state;
345 if (theFactory.app_state == null) {
346 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
347 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
349 theFactory.app_state = new HttpApplicationState (app, ses);
351 return theFactory.app_state;
356 public static void SetCustomApplication (IHttpHandler customApplication)
358 theFactory.custom_application = customApplication;
361 internal static Type AppType {
363 return theFactory.app_type;
367 void InitType (HttpContext context)
373 string physical_app_path = context.Request.PhysicalApplicationPath;
376 app_file = Path.Combine (physical_app_path, "Global.asax");
377 if (!File.Exists (app_file))
378 app_file = Path.Combine (physical_app_path, "global.asax");
382 WebConfigurationManager.Init ();
384 WebConfigurationSettings.Init (context);
387 #if NET_2_0 && !TARGET_J2EE
388 AppGlobalResourcesCompiler agrc = new AppGlobalResourcesCompiler();
391 // Todo: Process App_WebResources here
393 // Todo: Generate profile properties assembly from Web.config here
395 // Todo: Compile code from App_Code here
396 AppCodeCompiler acc = new AppCodeCompiler ();
400 if (File.Exists (app_file)) {
402 app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
404 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
405 if (app_type == null) {
406 string msg = String.Format ("Error compiling application file ({0}).", app_file);
407 throw new ApplicationException (msg);
411 app_type = typeof (System.Web.HttpApplication);
412 app_state = new HttpApplicationState ();
416 FileSystemEventHandler fseh = new FileSystemEventHandler (OnAppFileChanged);
417 RenamedEventHandler reh = new RenamedEventHandler (OnAppFileRenamed);
418 app_file_watcher = CreateWatcher (app_file, fseh, reh);
420 string config_file = Path.Combine (physical_app_path, "Web.config");
421 if (!File.Exists (config_file))
422 config_file = Path.Combine (physical_app_path, "web.config");
423 if (!File.Exists (config_file))
424 config_file = Path.Combine (physical_app_path, "Web.Config");
426 config_watcher = CreateWatcher (config_file, fseh, reh);
431 // Now init the settings
438 // Multiple-threads might hit this one on startup, and we have
439 // to delay-initialize until we have the HttpContext
441 internal static HttpApplication GetApplication (HttpContext context)
443 HttpApplicationFactory factory = theFactory;
444 HttpApplication app = null;
445 if (factory.app_start_needed){
449 factory.InitType (context);
451 if (factory.app_start_needed) {
453 string bin = HttpRuntime.BinDirectory;
454 if (Directory.Exists (bin))
455 bin = Path.Combine (bin, "*.dll");
457 FileSystemEventHandler fseh = new FileSystemEventHandler (factory.OnAppFileChanged);
458 RenamedEventHandler reh = new RenamedEventHandler (factory.OnAppFileRenamed);
459 // We watch bin or bin/*.dll if the directory exists
460 factory.bin_watcher = CreateWatcher (bin, fseh, reh);
462 app = factory.FireOnAppStart (context);
463 factory.app_start_needed = false;
469 lock (factory.available) {
470 if (factory.available.Count > 0) {
471 app = (HttpApplication) factory.available.Pop ();
472 app.RequestCompleted = false;
477 return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
480 // The lock is in InvokeSessionEnd
481 static HttpApplication GetApplicationForSessionEnd ()
483 HttpApplicationFactory factory = theFactory;
484 if (factory.available_for_end.Count > 0)
485 return (HttpApplication) factory.available_for_end.Pop ();
487 HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
488 app.InitOnce (false);
493 internal static void RecycleForSessionEnd (HttpApplication app)
495 HttpApplicationFactory factory = theFactory;
496 lock (factory.available_for_end) {
497 if (factory.available_for_end.Count < 32)
498 factory.available_for_end.Push (app);
504 internal static void Recycle (HttpApplication app)
506 HttpApplicationFactory factory = theFactory;
507 lock (factory.available) {
508 if (factory.available.Count < 32)
509 factory.available.Push (app);
515 internal static bool ContextAvailable {
516 get { return theFactory != null && !theFactory.app_start_needed; }