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;
37 using System.Web.Compilation;
42 #if NET_2_0 && !TARGET_J2EE
43 using System.CodeDom.Compiler;
46 namespace System.Web {
47 class HttpApplicationFactory {
48 object this_lock = new object ();
50 // Initialized in InitType
52 static HttpApplicationFactory theFactory {
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);
70 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;
78 static ArrayList watchers = new ArrayList();
79 static object watchers_lock = new object();
80 static bool app_shutdown = false;
82 static bool app_disabled = false;
83 static string[] app_browsers_files = new string[0];
85 Stack available = new Stack ();
86 Stack available_for_end = new Stack ();
88 bool IsEventHandler (MethodInfo m)
90 int pos = m.Name.IndexOf ('_');
91 if (pos == -1 || (m.Name.Length - 1) <= pos)
94 if (m.ReturnType != typeof (void))
97 ParameterInfo [] pi = m.GetParameters ();
98 int length = pi.Length;
105 if (pi [0].ParameterType != typeof (object) ||
106 pi [1].ParameterType != typeof (EventArgs))
112 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
114 string name = method.Name.Replace ("_On", "_");
115 if (appTypeEventHandlers [name] == null) {
116 appTypeEventHandlers [name] = method;
120 MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
122 if (old_method != null){
123 list = new ArrayList (4);
124 list.Add (old_method);
125 appTypeEventHandlers [name] = list;
127 list = appTypeEventHandlers [name] as ArrayList;
132 ArrayList GetMethodsDeep (Type type)
134 ArrayList al = new ArrayList ();
135 MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
136 al.AddRange (methods);
138 Type t = type.BaseType;
140 methods = t.GetMethods (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
141 al.AddRange (methods);
148 Hashtable GetApplicationTypeEvents (Type type)
151 if (app_event_handlers != null)
152 return app_event_handlers;
154 app_event_handlers = new Hashtable ();
155 ArrayList methods = GetMethodsDeep (type);
156 Hashtable used = null;
160 foreach (object o in methods) {
162 if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m)) {
163 mname = m.ToString ();
165 used = new Hashtable ();
166 else if (used.ContainsKey (mname))
169 AddEvent (m, app_event_handlers);
175 return app_event_handlers;
178 Hashtable GetApplicationTypeEvents (HttpApplication app)
181 if (app_event_handlers != null)
182 return app_event_handlers;
184 return GetApplicationTypeEvents (app.GetType ());
188 bool FireEvent (string method_name, object target, object [] args)
190 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
191 MethodInfo method = possibleEvents [method_name] as MethodInfo;
195 if (method.GetParameters ().Length == 0)
198 method.Invoke (target, args);
203 HttpApplication FireOnAppStart (HttpContext context)
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;
217 if (app_type == null)
218 return; // we didn't even get an application
220 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
221 FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
222 app.DisposeInternal ();
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.
230 public static void Dispose ()
232 theFactory.FireOnAppEnd ();
235 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
237 FileSystemWatcher watcher = new FileSystemWatcher ();
239 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
240 watcher.Filter = Path.GetFileName (file);
242 // This will enable the Modify flag for Linux/inotify
243 watcher.NotifyFilter |= NotifyFilters.Size;
245 watcher.Changed += hnd;
246 watcher.Created += hnd;
247 watcher.Deleted += hnd;
248 watcher.Renamed += reh;
250 watcher.EnableRaisingEvents = true;
255 internal static void AttachEvents (HttpApplication app)
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);
263 if (moduleName == "Application") {
266 target = app.Modules [moduleName];
271 string eventName = key.Substring (pos + 1);
272 EventInfo evt = target.GetType ().GetEvent (eventName);
276 string usualName = moduleName + "_" + eventName;
277 object methodData = possibleEvents [usualName];
278 if (methodData != null && eventName == "End" && moduleName == "Session") {
280 if (factory.session_end == null)
281 factory.session_end = (MethodInfo) methodData;
286 if (methodData == null)
289 if (methodData is MethodInfo) {
290 factory.AddHandler (evt, target, app, (MethodInfo) methodData);
294 ArrayList list = (ArrayList) methodData;
295 foreach (MethodInfo method in list)
296 factory.AddHandler (evt, target, app, method);
300 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
302 int length = method.GetParameters ().Length;
305 NoParamsInvoker npi = new NoParamsInvoker (app, method);
306 evt.AddEventHandler (target, npi.FakeDelegate);
308 if (method.IsStatic) {
309 evt.AddEventHandler (target, Delegate.CreateDelegate (
310 evt.EventHandlerType, method));
312 evt.AddEventHandler (target, Delegate.CreateDelegate (
313 evt.EventHandlerType, app,
325 internal static void InvokeSessionEnd (object state)
327 InvokeSessionEnd (state, null, EventArgs.Empty);
330 internal static void InvokeSessionEnd (object state, object source, EventArgs e)
332 HttpApplicationFactory factory = theFactory;
333 MethodInfo method = null;
334 HttpApplication app = null;
335 lock (factory.available_for_end) {
336 method = factory.session_end;
340 app = GetApplicationForSessionEnd ();
343 app.SetSession ((HttpSessionState) state);
345 method.Invoke (app, new object [] {(source == null ? app : source), e});
346 } catch (Exception) {
349 RecycleForSessionEnd (app);
352 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
354 if (list == null || list.Count == 0)
357 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
358 foreach (ObjectTagBuilder tag in list) {
365 internal static HttpApplicationState ApplicationState {
368 HttpApplicationFactory factory = theFactory;
369 if (factory.app_state == null)
370 factory.app_state = new HttpApplicationState (null, null);
371 return factory.app_state;
375 if (theFactory.app_state == null) {
376 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
377 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
379 theFactory.app_state = new HttpApplicationState (app, ses);
381 return theFactory.app_state;
386 internal static Type AppType {
388 return theFactory.app_type;
392 void InitType (HttpContext context)
401 string physical_app_path = HttpRuntime.AppDomainAppPath;
402 string app_file = null;
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))
412 WebConfigurationSettings.Init (context);
415 #if NET_2_0 && !TARGET_J2EE
416 AppResourcesCompiler ac = new AppResourcesCompiler (context);
420 AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
424 // Todo: Generate profile properties assembly from Web.config here
426 AppCodeCompiler acc = new AppCodeCompiler ();
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");
438 if (app_file != null) {
440 app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
441 app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
444 app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
446 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
449 if (app_type == null) {
450 string msg = String.Format ("Error compiling application file ({0}).", app_file);
451 throw new ApplicationException (msg);
454 app_type = typeof (System.Web.HttpApplication);
455 app_state = new HttpApplicationState ();
458 WatchLocationForRestart("Global.asax");
459 WatchLocationForRestart("global.asax");
460 WatchLocationForRestart("Web.config");
461 WatchLocationForRestart("web.config");
462 WatchLocationForRestart("Web.Config");
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 ();
477 // Now init the settings
484 // Multiple-threads might hit this one on startup, and we have
485 // to delay-initialize until we have the HttpContext
487 internal static HttpApplication GetApplication (HttpContext context)
490 if (context.ApplicationInstance!=null)
491 return context.ApplicationInstance;
493 HttpApplicationFactory factory = theFactory;
494 HttpApplication app = null;
495 if (factory.app_start_needed){
499 factory.InitType (context);
501 if (factory.app_start_needed) {
502 foreach (string dir in HttpApplication.BinDirs)
503 WatchLocationForRestart (dir, "*.dll");
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", "*");
514 app = factory.FireOnAppStart (context);
515 factory.app_start_needed = false;
521 lock (factory.available) {
522 if (factory.available.Count > 0) {
523 app = (HttpApplication) factory.available.Pop ();
524 app.RequestCompleted = false;
529 return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
532 // The lock is in InvokeSessionEnd
533 static HttpApplication GetApplicationForSessionEnd ()
535 HttpApplicationFactory factory = theFactory;
536 if (factory.available_for_end.Count > 0)
537 return (HttpApplication) factory.available_for_end.Pop ();
539 HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
540 app.InitOnce (false);
545 internal static void RecycleForSessionEnd (HttpApplication app)
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);
556 internal static void Recycle (HttpApplication app)
558 HttpApplicationFactory factory = theFactory;
559 lock (factory.available) {
560 if (factory.available.Count < 32)
561 factory.available.Push (app);
567 internal static bool ContextAvailable {
568 get { return theFactory != null && !theFactory.app_start_needed; }
572 internal static bool WatchLocationForRestart (string filter)
574 return WatchLocationForRestart ("", filter, false);
577 internal static bool WatchLocationForRestart (string virtualPath, string filter)
579 return WatchLocationForRestart (virtualPath, filter, false);
582 internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
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);
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);
596 watcher.IncludeSubdirectories = watchSubdirs;
598 lock (watchers_lock) {
599 watchers.Add(watcher);
608 internal static bool ApplicationDisabled {
609 get { return app_disabled; }
610 set { app_disabled = value; }
613 internal static string[] AppBrowsersFiles {
614 get { return app_browsers_files; }
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 {
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);
632 foreach (string f in app_browsers_files) {
633 capabilities_processor.AddBrowserFile(f);
637 return capabilities_processor;
642 internal static void DisableWatchers ()
644 lock (watchers_lock) {
645 foreach (FileSystemWatcher watcher in watchers)
646 watcher.EnableRaisingEvents = false;
650 internal static void EnableWatchers ()
652 lock (watchers_lock) {
653 foreach (FileSystemWatcher watcher in watchers)
654 watcher.EnableRaisingEvents = true;
658 static void OnFileRenamed(object sender, RenamedEventArgs args)
660 OnFileChanged(sender, args);
663 static void OnFileChanged(object sender, FileSystemEventArgs args)
665 lock (watchers_lock) {
670 // Disable event raising to avoid concurrent restarts
673 // Restart application
674 HttpRuntime.UnloadAppDomain();