2 // System.Web.HttpApplicationFactory
5 // bin_watcher must work.
9 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
11 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
12 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
36 using System.Reflection;
38 using System.Web.SessionState;
39 using System.Web.Configuration;
42 using System.Web.Compilation;
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 bool needs_init = true;
73 bool app_start_needed = true;
75 HttpApplicationState app_state;
77 FileSystemWatcher app_file_watcher;
78 FileSystemWatcher bin_watcher;
80 Stack available = new Stack ();
82 // Watch this thing out when getting an instance
83 IHttpHandler custom_application;
85 bool IsEventHandler (MethodInfo m)
87 if (m.ReturnType != typeof (void))
90 ParameterInfo [] pi = m.GetParameters ();
91 int length = pi.Length;
98 if (pi [0].ParameterType != typeof (object) ||
99 pi [1].ParameterType != typeof (EventArgs))
105 void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
107 string name = method.Name.Replace ("_On", "_");
108 if (appTypeEventHandlers [name] == null) {
109 appTypeEventHandlers [name] = method;
113 MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
115 if (old_method != null){
116 list = new ArrayList (4);
117 list.Add (old_method);
118 appTypeEventHandlers [name] = list;
120 list = appTypeEventHandlers [name] as ArrayList;
125 Hashtable GetApplicationTypeEvents (HttpApplication app)
127 Type appType = app.GetType ();
128 Hashtable appTypeEventHandlers = new Hashtable ();
129 BindingFlags flags = BindingFlags.Public |
130 BindingFlags.NonPublic |
131 BindingFlags.Instance |
134 MethodInfo [] methods = appType.GetMethods (flags);
135 foreach (MethodInfo m in methods) {
136 if (IsEventHandler (m))
137 AddEvent (m, appTypeEventHandlers);
140 return appTypeEventHandlers;
143 bool FireEvent (string method_name, object target, object [] args)
145 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
146 MethodInfo method = possibleEvents [method_name] as MethodInfo;
150 if (method.GetParameters ().Length == 0)
153 method.Invoke (target, args);
158 void FireOnAppStart (HttpContext context)
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);
169 if (app_type == null)
170 return; // we didn't even get an application
172 HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
173 FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
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.
181 public static void Dispose ()
183 theFactory.FireOnAppEnd ();
187 FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd)
189 FileSystemWatcher watcher = new FileSystemWatcher ();
191 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
192 watcher.Filter = Path.GetFileName (file);
194 watcher.Changed += hnd;
195 watcher.Created += hnd;
196 watcher.Deleted += hnd;
198 watcher.EnableRaisingEvents = true;
203 void OnAppFileChanged (object sender, FileSystemEventArgs args)
205 bin_watcher.EnableRaisingEvents = false;
206 app_file_watcher.EnableRaisingEvents = false;
207 HttpRuntime.UnloadAppDomain ();
211 internal static void AttachEvents (HttpApplication app)
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)
220 string moduleName = key.Substring (0, pos);
222 if (moduleName == "Application") {
225 target = app.Modules [moduleName];
230 string eventName = key.Substring (pos + 1);
231 EventInfo evt = target.GetType ().GetEvent (eventName);
235 string usualName = moduleName + "_" + eventName;
236 object methodData = possibleEvents [usualName];
237 if (methodData == null)
240 if (methodData is MethodInfo) {
241 factory.AddHandler (evt, target, app, (MethodInfo) methodData);
245 ArrayList list = (ArrayList) methodData;
246 foreach (MethodInfo method in list)
247 factory.AddHandler (evt, target, app, method);
251 void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
253 int length = method.GetParameters ().Length;
256 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
257 evt.AddEventHandler (target, npi.FakeDelegate);
259 evt.AddEventHandler (target, Delegate.CreateDelegate (
260 typeof (EventHandler), app, method.Name));
264 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
266 if (list == null || list.Count == 0)
269 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
270 foreach (ObjectTagBuilder tag in list) {
277 internal static HttpApplicationState ApplicationState {
280 HttpApplicationFactory factory = theFactory;
281 if (factory.app_state == null)
282 factory.app_state = new HttpApplicationState (null, null);
283 return factory.app_state;
287 if (theFactory.app_state == null) {
288 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
289 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
291 theFactory.app_state = new HttpApplicationState (app, ses);
293 return theFactory.app_state;
298 public static void SetCustomApplication (IHttpHandler customApplication)
300 theFactory.custom_application = customApplication;
303 internal static Type AppType {
305 return theFactory.app_type;
309 void InitType (HttpContext context)
315 string physical_app_path = context.Request.PhysicalApplicationPath;
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");
322 WebConfigurationSettings.Init (context);
324 if (File.Exists (app_file)) {
326 app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
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);
334 app_file_watcher = CreateWatcher (app_file, new FileSystemEventHandler (OnAppFileChanged));
337 app_type = typeof (System.Web.HttpApplication);
338 app_state = new HttpApplicationState ();
343 // Now init the settings
350 // Multiple-threads might hit this one on startup, and we have
351 // to delay-initialize until we have the HttpContext
353 internal static HttpApplication GetApplication (HttpContext context)
355 HttpApplicationFactory factory = theFactory;
356 if (factory.needs_init){
360 factory.InitType (context);
362 if (factory.app_start_needed) {
363 factory.FireOnAppStart (context);
364 factory.app_start_needed = false;
370 if (factory.available.Count > 0)
371 return (HttpApplication) factory.available.Pop ();
374 HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
379 internal static void Recycle (HttpApplication app)
381 HttpApplicationFactory factory = theFactory;
383 if (factory.available.Count < 32)
384 factory.available.Push (app);