2 // System.Web.HttpApplicationFactory
5 // Patrik Torstensson (ptorsten@hotmail.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
9 // (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
35 using System.Reflection;
37 using System.Web.Compilation;
38 using System.Web.SessionState;
40 namespace System.Web {
41 class HttpApplicationFactory {
42 private string _appFilename;
43 private Type _appType;
45 private bool _appInitialized;
46 private bool _appFiredEnd;
48 private Stack _appFreePublicList;
49 private int _appFreePublicInstances;
51 FileSystemWatcher appFileWatcher;
52 FileSystemWatcher binWatcher;
54 static private int _appMaxFreePublicInstances = 32;
56 private HttpApplicationState _state;
\r
58 static IHttpHandler custApplication;
\r
60 static private HttpApplicationFactory s_Factory = new HttpApplicationFactory();
\r
62 public HttpApplicationFactory() {
\r
63 _appInitialized = false;
\r
64 _appFiredEnd = false;
\r
66 _appFreePublicList = new Stack();
\r
67 _appFreePublicInstances = 0;
\r
70 static private string GetAppFilename (HttpContext context)
\r
72 string physicalAppPath = context.Request.PhysicalApplicationPath;
\r
73 string appFilePath = Path.Combine (physicalAppPath, "Global.asax");
\r
74 if (File.Exists (appFilePath))
\r
77 return Path.Combine (physicalAppPath, "global.asax");
\r
80 void CompileApp (HttpContext context)
82 if (File.Exists (_appFilename)) {
83 _appType = ApplicationFileParser.GetCompiledApplicationType (_appFilename, context);
84 if (_appType == null) {
85 string msg = String.Format ("Error compiling application file ({0}).", _appFilename);
86 throw new ApplicationException (msg);
89 appFileWatcher = CreateWatcher (_appFilename, new FileSystemEventHandler (OnAppFileChanged));
91 _appType = typeof (System.Web.HttpApplication);
92 _state = new HttpApplicationState ();
96 static bool IsEventHandler (MethodInfo m)
\r
98 if (m.ReturnType != typeof (void))
\r
101 ParameterInfo [] pi = m.GetParameters ();
\r
102 int length = pi.Length;
\r
109 if (pi [0].ParameterType != typeof (object) ||
\r
110 pi [1].ParameterType != typeof (EventArgs))
\r
116 static void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
\r
118 string name = method.Name.Replace ("_On", "_");
\r
119 if (appTypeEventHandlers [name] == null) {
\r
120 appTypeEventHandlers [name] = method;
\r
125 if (appTypeEventHandlers [name] is MethodInfo)
\r
126 list = new ArrayList ();
\r
128 list = appTypeEventHandlers [name] as ArrayList;
\r
133 static Hashtable GetApplicationTypeEvents (HttpApplication app)
\r
135 Type appType = app.GetType ();
\r
136 Hashtable appTypeEventHandlers = new Hashtable ();
\r
137 BindingFlags flags = BindingFlags.Public |
\r
138 BindingFlags.NonPublic |
\r
139 BindingFlags.Instance |
\r
140 BindingFlags.Static;
\r
142 MethodInfo [] methods = appType.GetMethods (flags);
\r
143 foreach (MethodInfo m in methods) {
\r
144 if (IsEventHandler (m))
\r
145 AddEvent (m, appTypeEventHandlers);
\r
148 return appTypeEventHandlers;
\r
151 static bool FireEvent (string method_name, object target, object [] args)
153 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
154 MethodInfo method = possibleEvents [method_name] as MethodInfo;
158 if (method.GetParameters ().Length == 0)
159 method.Invoke (target, null);
161 method.Invoke (target, args);
166 internal static void FireOnAppStart (HttpApplication app)
\r
168 object [] args = new object [] {app, EventArgs.Empty};
\r
169 FireEvent ("Application_Start", app, args);
\r
174 if (_appType == null)
175 return; // we didn't even get an application
177 HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);
179 FireEvent ("Application_End", app, new object [] {this, EventArgs.Empty});
183 FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd)
185 FileSystemWatcher watcher = new FileSystemWatcher ();
187 watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
188 watcher.Filter = Path.GetFileName (file);
190 watcher.Changed += hnd;
191 watcher.Created += hnd;
192 watcher.Deleted += hnd;
194 watcher.EnableRaisingEvents = true;
199 void OnAppFileChanged (object sender, FileSystemEventArgs args)
201 binWatcher.EnableRaisingEvents = false;
202 appFileWatcher.EnableRaisingEvents = false;
203 HttpRuntime.UnloadAppDomain ();
206 private void InitializeFactory (HttpContext context)
\r
208 _appFilename = GetAppFilename (context);
\r
210 CompileApp (context);
\r
212 // Create a application object
\r
213 HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);
\r
216 app.Startup(context, HttpApplicationFactory.ApplicationState);
\r
218 // Shutdown the application if bin directory changes.
219 string binFiles = HttpRuntime.BinDirectory;
220 if (Directory.Exists (binFiles))
221 binFiles = Path.Combine (binFiles, "*.*");
223 binWatcher = CreateWatcher (binFiles, new FileSystemEventHandler (OnAppFileChanged));
226 HttpApplicationFactory.FireOnAppStart (app);
\r
228 // Recycle our application instance
\r
229 RecyclePublicInstance(app);
\r
232 private void Dispose() {
\r
233 ArrayList torelease = new ArrayList();
\r
234 lock (_appFreePublicList) {
\r
235 while (_appFreePublicList.Count > 0) {
\r
236 torelease.Add(_appFreePublicList.Pop());
\r
237 _appFreePublicInstances--;
\r
241 if (torelease.Count > 0) {
\r
242 foreach (Object obj in torelease) {
\r
243 ((HttpApplication) obj).Cleanup();
\r
247 if (!_appFiredEnd) {
\r
249 if (!_appFiredEnd) {
\r
251 _appFiredEnd = true;
\r
257 internal static IHttpHandler GetInstance(HttpContext context)
\r
259 if (custApplication != null)
\r
260 return custApplication;
\r
262 if (!s_Factory._appInitialized) {
\r
264 if (!s_Factory._appInitialized) {
\r
265 s_Factory.InitializeFactory(context);
\r
266 s_Factory._appInitialized = true;
\r
271 return s_Factory.GetPublicInstance(context);
\r
274 internal static void RecycleInstance(HttpApplication app) {
\r
275 if (!s_Factory._appInitialized)
\r
276 throw new InvalidOperationException("Factory not intialized");
\r
278 s_Factory.RecyclePublicInstance(app);
\r
281 internal static void AttachEvents (HttpApplication app)
\r
283 Hashtable possibleEvents = GetApplicationTypeEvents (app);
\r
284 foreach (string key in possibleEvents.Keys) {
\r
285 int pos = key.IndexOf ('_');
\r
286 if (pos == -1 || key.Length <= pos + 1)
\r
289 string moduleName = key.Substring (0, pos);
\r
291 if (moduleName == "Application") {
\r
294 target = app.Modules [moduleName];
\r
295 if (target == null)
\r
299 string eventName = key.Substring (pos + 1);
\r
300 EventInfo evt = target.GetType ().GetEvent (eventName);
\r
304 string usualName = moduleName + "_" + eventName;
\r
305 object methodData = possibleEvents [usualName];
\r
306 if (methodData == null)
\r
309 if (methodData is MethodInfo) {
\r
310 AddHandler (evt, target, app, (MethodInfo) methodData);
\r
314 ArrayList list = (ArrayList) methodData;
\r
315 foreach (MethodInfo method in list)
\r
316 AddHandler (evt, target, app, method);
\r
320 static void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
\r
322 int length = method.GetParameters ().Length;
\r
325 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
\r
326 evt.AddEventHandler (target, npi.FakeDelegate);
\r
328 evt.AddEventHandler (target, Delegate.CreateDelegate (
\r
329 typeof (EventHandler), app, method.Name));
\r
333 private IHttpHandler GetPublicInstance(HttpContext context) {
\r
334 HttpApplication app = null;
\r
336 lock (_appFreePublicList) {
\r
337 if (_appFreePublicInstances > 0) {
\r
338 app = (HttpApplication) _appFreePublicList.Pop();
\r
339 _appFreePublicInstances--;
\r
344 // Create non-public object
\r
345 app = (HttpApplication) HttpRuntime.CreateInternalObject(_appType);
\r
347 app.Startup(context, HttpApplicationFactory.ApplicationState);
\r
350 return (IHttpHandler) app;
\r
353 internal void RecyclePublicInstance(HttpApplication app) {
\r
354 lock (_appFreePublicList) {
\r
355 if (_appFreePublicInstances < _appMaxFreePublicInstances) {
\r
356 _appFreePublicList.Push(app);
\r
357 _appFreePublicInstances++;
\r
368 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
\r
370 if (list == null || list.Count == 0)
\r
373 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
\r
374 foreach (ObjectTagBuilder tag in list) {
\r
381 static internal HttpApplicationState ApplicationState {
\r
383 if (null == s_Factory._state) {
\r
384 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
\r
385 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
\r
386 s_Factory._state = new HttpApplicationState (app, ses);
\r
389 return s_Factory._state;
\r
393 internal static void EndApplication() {
\r
394 s_Factory.Dispose();
\r
397 public static void SetCustomApplication (IHttpHandler customApplication)
\r
399 custApplication = customApplication;
\r
402 internal Type AppType {
\r
403 get { return _appType; }
\r
406 internal static void SignalError(Exception exc) {
\r
407 // TODO: Raise an error (we probably don't have a HttpContext)
\r