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;
36 using System.Threading;
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 object this_lock = new object ();
51 // Initialized in InitType
53 static HttpApplicationFactory theFactory {
56 HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
57 if (factory == null) {
58 lock(typeof(HttpApplicationFactory)) {
59 factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
60 if (factory == null) {
61 factory = new HttpApplicationFactory();
62 System.Threading.Thread.Sleep(1);
63 AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
71 static HttpApplicationFactory theFactory = new HttpApplicationFactory();
73 object session_end; // This is a MethodInfo
74 bool needs_init = true;
75 bool app_start_needed = true;
78 HttpApplicationState app_state;
79 Hashtable app_event_handlers;
80 static ArrayList watchers = new ArrayList();
81 static object watchers_lock = new object();
82 static bool app_shutdown = false;
84 static bool app_disabled = false;
85 static string[] app_browsers_files = new string[0];
86 static string[] default_machine_browsers_files = new string[0];
87 static string[] app_mono_machine_browsers_files = new string[0];
89 Stack available = new Stack ();
91 Stack available_for_end = new Stack ();
93 bool IsEventHandler (MethodInfo m)
95 int pos = m.Name.IndexOf ('_');
96 if (pos == -1 || (m.Name.Length - 1) <= pos)
99 if (m.ReturnType != typeof (void))
102 ParameterInfo [] pi = m.GetParameters ();
103 int length = pi.Length;
110 if (pi [0].ParameterType != typeof (object) ||
111 pi [1].ParameterType != typeof (EventArgs))
117 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
119 string name = method.Name.Replace ("_On", "_");
120 if (appTypeEventHandlers [name] == null) {
121 appTypeEventHandlers [name] = method;
125 MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
127 if (old_method != null){
128 list = new ArrayList (4);
129 list.Add (old_method);
130 appTypeEventHandlers [name] = list;
132 list = appTypeEventHandlers [name] as ArrayList;
137 ArrayList GetMethodsDeep (Type type)
139 ArrayList al = new ArrayList ();
140 MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
141 al.AddRange (methods);
143 Type t = type.BaseType;
145 methods = t.GetMethods (BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
146 al.AddRange (methods);
153 Hashtable GetApplicationTypeEvents (Type type)
156 return app_event_handlers;
159 if (app_event_handlers != null)
160 return app_event_handlers;
162 app_event_handlers = new Hashtable ();
163 ArrayList methods = GetMethodsDeep (type);
164 Hashtable used = null;
168 foreach (object o in methods) {
170 if (m.DeclaringType != typeof (HttpApplication) && IsEventHandler (m)) {
171 mname = m.ToString ();
173 used = new Hashtable ();
174 else if (used.ContainsKey (mname))
177 AddEvent (m, app_event_handlers);
181 have_app_events = true;
184 return app_event_handlers;
187 Hashtable GetApplicationTypeEvents (HttpApplication app)
190 return app_event_handlers;
192 return GetApplicationTypeEvents (app.GetType ());
195 bool FireEvent (string method_name, object target, object [] args)
197 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
198 MethodInfo method = possibleEvents [method_name] as MethodInfo;
202 if (method.GetParameters ().Length == 0)
205 method.Invoke (target, args);
210 HttpApplication FireOnAppStart (HttpContext context)
212 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
213 context.ApplicationInstance = app;
214 app.SetContext (context);
215 object [] args = new object [] {app, EventArgs.Empty};
216 app.InApplicationStart = true;
217 FireEvent ("Application_Start", app, args);
218 app.InApplicationStart = false;
224 if (app_type == null)
225 return; // we didn't even get an application
227 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
228 FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
229 app.DisposeInternal ();
234 // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
235 // To reproduce this in action, touch "global.asax" while XSP is running.
237 public static void Dispose ()
239 theFactory.FireOnAppEnd ();
242 static FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd, RenamedEventHandler reh)
244 FileSystemWatcher watcher = new FileSystemWatcher ();
246 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
247 watcher.Filter = Path.GetFileName (file);
249 // This will enable the Modify flag for Linux/inotify
250 watcher.NotifyFilter |= NotifyFilters.Size;
252 watcher.Changed += hnd;
253 watcher.Created += hnd;
254 watcher.Deleted += hnd;
255 watcher.Renamed += reh;
257 watcher.EnableRaisingEvents = true;
262 internal static void AttachEvents (HttpApplication app)
264 HttpApplicationFactory factory = theFactory;
265 Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
266 foreach (string key in possibleEvents.Keys) {
267 int pos = key.IndexOf ('_');
268 string moduleName = key.Substring (0, pos);
270 if (moduleName == "Application") {
273 target = app.Modules [moduleName];
278 string eventName = key.Substring (pos + 1);
279 EventInfo evt = target.GetType ().GetEvent (eventName);
283 string usualName = moduleName + "_" + eventName;
284 object methodData = possibleEvents [usualName];
285 if (methodData == null)
288 if (eventName == "End" && moduleName == "Session") {
289 Interlocked.CompareExchange (ref factory.session_end, methodData, null);
293 if (methodData is MethodInfo) {
294 factory.AddHandler (evt, target, app, (MethodInfo) methodData);
298 ArrayList list = (ArrayList) methodData;
299 foreach (MethodInfo method in list)
300 factory.AddHandler (evt, target, app, method);
304 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
306 int length = method.GetParameters ().Length;
309 NoParamsInvoker npi = new NoParamsInvoker (app, method);
310 evt.AddEventHandler (target, npi.FakeDelegate);
312 if (method.IsStatic) {
313 evt.AddEventHandler (target, Delegate.CreateDelegate (
314 evt.EventHandlerType, method));
316 evt.AddEventHandler (target, Delegate.CreateDelegate (
317 evt.EventHandlerType, app,
329 internal static void InvokeSessionEnd (object state)
331 InvokeSessionEnd (state, null, EventArgs.Empty);
334 internal static void InvokeSessionEnd (object state, object source, EventArgs e)
336 HttpApplicationFactory factory = theFactory;
337 MethodInfo method = null;
338 HttpApplication app = null;
339 lock (factory.available_for_end) {
340 method = (MethodInfo) factory.session_end;
344 app = GetApplicationForSessionEnd ();
347 app.SetSession ((HttpSessionState) state);
349 method.Invoke (app, new object [] {(source == null ? app : source), e});
350 } catch (Exception) {
353 RecycleForSessionEnd (app);
356 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
358 if (list == null || list.Count == 0)
361 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
362 foreach (ObjectTagBuilder tag in list) {
369 internal static HttpApplicationState ApplicationState {
372 HttpApplicationFactory factory = theFactory;
373 if (factory.app_state == null)
374 factory.app_state = new HttpApplicationState (null, null);
375 return factory.app_state;
379 if (theFactory.app_state == null) {
380 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
381 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
383 theFactory.app_state = new HttpApplicationState (app, ses);
385 return theFactory.app_state;
390 internal static Type AppType {
392 return theFactory.app_type;
396 void InitType (HttpContext context)
405 string physical_app_path = HttpRuntime.AppDomainAppPath;
406 string app_file = null;
408 app_file = Path.Combine (physical_app_path, "Global.asax");
409 if (!File.Exists (app_file)) {
410 app_file = Path.Combine (physical_app_path, "global.asax");
411 if (!File.Exists (app_file))
416 WebConfigurationSettings.Init (context);
419 #if NET_2_0 && !TARGET_J2EE
420 AppResourcesCompiler ac = new AppResourcesCompiler (context);
424 AppWebReferencesCompiler awrc = new AppWebReferencesCompiler ();
428 // Todo: Generate profile properties assembly from Web.config here
430 AppCodeCompiler acc = new AppCodeCompiler ();
433 // Get the default machine *.browser files.
434 string default_machine_browsers_path = Path.Combine (HttpRuntime.MachineConfigurationDirectory, "Browsers");
435 default_machine_browsers_files = new string[0];
436 if (Directory.Exists (default_machine_browsers_path)) {
437 default_machine_browsers_files
438 = Directory.GetFiles (default_machine_browsers_path, "*.browser");
441 // Note whether there are any App_Data/Mono_Machine_Browsers/*.browser files. If there
442 // are we will be using them instead of the default machine *.browser files.
443 string app_mono_machine_browsers_path = Path.Combine (Path.Combine (physical_app_path, "App_Data"), "Mono_Machine_Browsers");
444 app_mono_machine_browsers_files = new string[0];
445 if (Directory.Exists (app_mono_machine_browsers_path)) {
446 app_mono_machine_browsers_files
447 = Directory.GetFiles (app_mono_machine_browsers_path, "*.browser");
450 // Note whether there are any App_Browsers/*.browser files. If there
451 // are we will be using *.browser files for sniffing in addition to browscap.ini
452 string app_browsers_path = Path.Combine (physical_app_path, "App_Browsers");
453 app_browsers_files = new string[0];
454 if (Directory.Exists (app_browsers_path)) {
455 app_browsers_files = Directory.GetFiles (app_browsers_path, "*.browser");
460 app_type = BuildManager.GetPrecompiledApplicationType ();
462 if (app_type == null && app_file != null) {
464 app_file = System.Web.Util.UrlUtils.ResolveVirtualPathFromAppAbsolute("~/" + Path.GetFileName(app_file));
465 app_type = System.Web.J2EE.PageMapper.GetObjectType(context, app_file);
468 app_type = BuildManager.GetCompiledType ("~/" + Path.GetFileName (app_file));
470 app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
473 if (app_type == null) {
474 string msg = String.Format ("Error compiling application file ({0}).", app_file);
475 throw new ApplicationException (msg);
477 } else if (app_type == null) {
478 app_type = typeof (System.Web.HttpApplication);
479 app_state = new HttpApplicationState ();
482 WatchLocationForRestart("Global.asax");
483 WatchLocationForRestart("global.asax");
484 WatchLocationForRestart(String.Empty, "Web.config", true);
485 WatchLocationForRestart(String.Empty, "web.config", true);
486 WatchLocationForRestart(String.Empty, "Web.Config", true);
489 } catch (Exception) {
490 if (BuildManager.CodeAssemblies != null)
491 BuildManager.CodeAssemblies.Clear ();
492 if (BuildManager.TopLevelAssemblies != null)
493 BuildManager.TopLevelAssemblies.Clear ();
494 if (WebConfigurationManager.ExtraAssemblies != null)
495 WebConfigurationManager.ExtraAssemblies.Clear ();
501 // Now init the settings
508 // Multiple-threads might hit this one on startup, and we have
509 // to delay-initialize until we have the HttpContext
511 internal static HttpApplication GetApplication (HttpContext context)
514 if (context.ApplicationInstance!=null)
515 return context.ApplicationInstance;
517 HttpApplicationFactory factory = theFactory;
518 HttpApplication app = null;
519 if (factory.app_start_needed){
523 factory.InitType (context);
525 if (factory.app_start_needed) {
526 foreach (string dir in HttpApplication.BinDirs)
527 WatchLocationForRestart (dir, "*.dll");
529 // Restart if the App_* directories are created...
530 WatchLocationForRestart (".", "App_Code");
531 WatchLocationForRestart (".", "App_Browsers");
532 WatchLocationForRestart (".", "App_GlobalResources");
533 // ...or their contents is changed.
534 WatchLocationForRestart ("App_Code", "*", true);
535 WatchLocationForRestart ("App_Browsers", "*");
536 WatchLocationForRestart ("App_GlobalResources", "*");
538 app = factory.FireOnAppStart (context);
539 factory.app_start_needed = false;
545 app = (HttpApplication) Interlocked.Exchange (ref factory.next_free, null);
547 app.RequestCompleted = false;
551 lock (factory.available) {
552 if (factory.available.Count > 0) {
553 app = (HttpApplication) factory.available.Pop ();
554 app.RequestCompleted = false;
559 return (HttpApplication) Activator.CreateInstance (factory.app_type, true);
562 // The lock is in InvokeSessionEnd
563 static HttpApplication GetApplicationForSessionEnd ()
565 HttpApplicationFactory factory = theFactory;
566 if (factory.available_for_end.Count > 0)
567 return (HttpApplication) factory.available_for_end.Pop ();
569 HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
570 app.InitOnce (false);
575 internal static void RecycleForSessionEnd (HttpApplication app)
577 bool dispose = false;
578 HttpApplicationFactory factory = theFactory;
579 lock (factory.available_for_end) {
580 if (factory.available_for_end.Count < 64)
581 factory.available_for_end.Push (app);
589 internal static void Recycle (HttpApplication app)
591 bool dispose = false;
592 HttpApplicationFactory factory = theFactory;
593 if (Interlocked.CompareExchange (ref factory.next_free, app, null) == null)
596 lock (factory.available) {
597 if (factory.available.Count < 64)
598 factory.available.Push (app);
606 internal static bool ContextAvailable {
607 get { return theFactory != null && !theFactory.app_start_needed; }
611 internal static bool WatchLocationForRestart (string filter)
613 return WatchLocationForRestart ("", filter, false);
616 internal static bool WatchLocationForRestart (string virtualPath, string filter)
618 return WatchLocationForRestart (virtualPath, filter, false);
621 internal static bool WatchLocationForRestart(string virtualPath, string filter, bool watchSubdirs)
623 // map the path to the physical one
624 string physicalPath = HttpRuntime.AppDomainAppPath;
625 physicalPath = Path.Combine(physicalPath, virtualPath);
626 bool isDir = Directory.Exists(physicalPath);
627 bool isFile = isDir ? false : File.Exists(physicalPath);
629 if (isDir || isFile) {
630 // create the watcher
631 FileSystemEventHandler fseh = new FileSystemEventHandler(OnFileChanged);
632 RenamedEventHandler reh = new RenamedEventHandler(OnFileRenamed);
633 FileSystemWatcher watcher = CreateWatcher(Path.Combine(physicalPath, filter), fseh, reh);
635 watcher.IncludeSubdirectories = watchSubdirs;
637 lock (watchers_lock) {
638 watchers.Add(watcher);
647 internal static bool ApplicationDisabled {
648 get { return app_disabled; }
649 set { app_disabled = value; }
652 internal static string[] AppBrowsersFiles {
653 get { return app_browsers_files; }
656 static System.Web.Configuration.nBrowser.Build capabilities_processor = null;
657 static object capabilities_processor_lock = new object();
658 internal static System.Web.Configuration.ICapabilitiesProcess CapabilitiesProcessor {
660 lock (capabilities_processor_lock) {
661 if (capabilities_processor == null) {
662 capabilities_processor = new System.Web.Configuration.nBrowser.Build();
663 string[] machine_browsers_files = app_mono_machine_browsers_files;
664 if (machine_browsers_files.Length == 0) {
665 machine_browsers_files = default_machine_browsers_files;
667 foreach (string f in machine_browsers_files) {
668 capabilities_processor.AddBrowserFile(f);
670 foreach (string f in app_browsers_files) {
671 capabilities_processor.AddBrowserFile(f);
675 return capabilities_processor;
680 internal static void DisableWatchers ()
682 lock (watchers_lock) {
683 foreach (FileSystemWatcher watcher in watchers)
684 watcher.EnableRaisingEvents = false;
688 internal static void EnableWatchers ()
690 lock (watchers_lock) {
691 foreach (FileSystemWatcher watcher in watchers)
692 watcher.EnableRaisingEvents = true;
696 static void OnFileRenamed(object sender, RenamedEventArgs args)
698 OnFileChanged(sender, args);
701 static void OnFileChanged(object sender, FileSystemEventArgs args)
703 lock (watchers_lock) {
708 // Disable event raising to avoid concurrent restarts
711 // Restart application
712 HttpRuntime.UnloadAppDomain();