2 // System.Web.HttpApplicationFactory
\r
5 // Patrik Torstensson (ptorsten@hotmail.com)
\r
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
\r
8 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
\r
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
\r
34 using System.Reflection;
\r
35 using System.Web.UI;
\r
36 using System.Web.Compilation;
\r
37 using System.Web.SessionState;
\r
39 namespace System.Web {
\r
40 class HttpApplicationFactory {
\r
41 private string _appFilename;
\r
42 private Type _appType;
\r
44 private bool _appInitialized;
\r
45 private bool _appFiredEnd;
\r
47 private Stack _appFreePublicList;
\r
48 private int _appFreePublicInstances;
\r
49 static private int _appMaxFreePublicInstances = 32;
\r
51 private HttpApplicationState _state;
\r
53 static IHttpHandler custApplication;
\r
55 static private HttpApplicationFactory s_Factory = new HttpApplicationFactory();
\r
57 public HttpApplicationFactory() {
\r
58 _appInitialized = false;
\r
59 _appFiredEnd = false;
\r
61 _appFreePublicList = new Stack();
\r
62 _appFreePublicInstances = 0;
\r
65 static private string GetAppFilename (HttpContext context)
\r
67 string physicalAppPath = context.Request.PhysicalApplicationPath;
\r
68 string appFilePath = Path.Combine (physicalAppPath, "Global.asax");
\r
69 if (File.Exists (appFilePath))
\r
72 return Path.Combine (physicalAppPath, "global.asax");
\r
75 private void CompileApp(HttpContext context) {
\r
76 if (File.Exists(_appFilename)) {
\r
77 // Setup filemonitor for all filedepend also. CacheDependency?
\r
79 _appType = ApplicationFileParser.GetCompiledApplicationType (_appFilename, context);
\r
80 if (_appType == null) {
\r
81 string msg = String.Format ("Error compiling application file ({0}).", _appFilename);
\r
82 throw new ApplicationException (msg);
\r
85 _appType = typeof (System.Web.HttpApplication);
\r
86 _state = new HttpApplicationState ();
\r
90 static bool IsEventHandler (MethodInfo m)
\r
92 if (m.ReturnType != typeof (void))
\r
95 ParameterInfo [] pi = m.GetParameters ();
\r
96 int length = pi.Length;
\r
103 if (pi [0].ParameterType != typeof (object) ||
\r
104 pi [1].ParameterType != typeof (EventArgs))
\r
110 static void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
\r
112 string name = method.Name.Replace ("_On", "_");
\r
113 if (appTypeEventHandlers [name] == null) {
\r
114 appTypeEventHandlers [name] = method;
\r
119 if (appTypeEventHandlers [name] is MethodInfo)
\r
120 list = new ArrayList ();
\r
122 list = appTypeEventHandlers [name] as ArrayList;
\r
127 static Hashtable GetApplicationTypeEvents (HttpApplication app)
\r
129 Type appType = app.GetType ();
\r
130 Hashtable appTypeEventHandlers = new Hashtable ();
\r
131 ArrayList evtMethods = new ArrayList ();
\r
132 BindingFlags flags = BindingFlags.Public |
\r
133 BindingFlags.NonPublic |
\r
134 BindingFlags.Instance |
\r
135 BindingFlags.Static;
\r
137 MethodInfo [] methods = appType.GetMethods (flags);
\r
138 foreach (MethodInfo m in methods) {
\r
139 if (IsEventHandler (m))
\r
140 AddEvent (m, appTypeEventHandlers);
\r
143 return appTypeEventHandlers;
\r
146 static bool FireEvent (string method_name, object target, object [] args)
148 Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
149 MethodInfo method = possibleEvents [method_name] as MethodInfo;
153 if (method.GetParameters ().Length == 0)
154 method.Invoke (target, null);
156 method.Invoke (target, args);
161 internal static void FireOnAppStart (HttpApplication app)
\r
163 object [] args = new object [] {app, EventArgs.Empty};
\r
164 FireEvent ("Application_Start", app, args);
\r
169 if (_appType == null)
170 return; // we didn't even get an application
172 HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);
174 FireEvent ("Application_End", app, new object [] {this, EventArgs.Empty});
178 private void InitializeFactory (HttpContext context)
\r
180 _appFilename = GetAppFilename (context);
\r
182 CompileApp (context);
\r
184 // Create a application object
\r
185 HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);
\r
188 app.Startup(context, HttpApplicationFactory.ApplicationState);
\r
191 HttpApplicationFactory.FireOnAppStart (app);
\r
193 // Recycle our application instance
\r
194 RecyclePublicInstance(app);
\r
197 private void Dispose() {
\r
198 ArrayList torelease = new ArrayList();
\r
199 lock (_appFreePublicList) {
\r
200 while (_appFreePublicList.Count > 0) {
\r
201 torelease.Add(_appFreePublicList.Pop());
\r
202 _appFreePublicInstances--;
\r
206 if (torelease.Count > 0) {
\r
207 foreach (Object obj in torelease) {
\r
208 ((HttpApplication) obj).Cleanup();
\r
212 if (!_appFiredEnd) {
\r
214 if (!_appFiredEnd) {
\r
216 _appFiredEnd = true;
\r
222 internal static IHttpHandler GetInstance(HttpContext context)
\r
224 if (custApplication != null)
\r
225 return custApplication;
\r
227 if (!s_Factory._appInitialized) {
\r
229 if (!s_Factory._appInitialized) {
\r
230 s_Factory.InitializeFactory(context);
\r
231 s_Factory._appInitialized = true;
\r
236 return s_Factory.GetPublicInstance(context);
\r
239 internal static void RecycleInstance(HttpApplication app) {
\r
240 if (!s_Factory._appInitialized)
\r
241 throw new InvalidOperationException("Factory not intialized");
\r
243 s_Factory.RecyclePublicInstance(app);
\r
246 internal static void AttachEvents (HttpApplication app)
\r
248 Hashtable possibleEvents = GetApplicationTypeEvents (app);
\r
249 foreach (string key in possibleEvents.Keys) {
\r
250 int pos = key.IndexOf ('_');
\r
251 if (pos == -1 || key.Length <= pos + 1)
\r
254 string moduleName = key.Substring (0, pos);
\r
256 if (moduleName == "Application") {
\r
259 target = app.Modules [moduleName];
\r
260 if (target == null)
\r
264 string eventName = key.Substring (pos + 1);
\r
265 EventInfo evt = target.GetType ().GetEvent (eventName);
\r
269 string usualName = moduleName + "_" + eventName;
\r
270 object methodData = possibleEvents [usualName];
\r
271 if (methodData == null)
\r
274 if (methodData is MethodInfo) {
\r
275 AddHandler (evt, target, app, (MethodInfo) methodData);
\r
279 ArrayList list = (ArrayList) methodData;
\r
280 foreach (MethodInfo method in list)
\r
281 AddHandler (evt, target, app, method);
\r
285 static void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
\r
287 int length = method.GetParameters ().Length;
\r
290 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
\r
291 evt.AddEventHandler (target, npi.FakeDelegate);
\r
293 evt.AddEventHandler (target, Delegate.CreateDelegate (
\r
294 typeof (EventHandler), app, method.Name));
\r
298 private IHttpHandler GetPublicInstance(HttpContext context) {
\r
299 HttpApplication app = null;
\r
301 lock (_appFreePublicList) {
\r
302 if (_appFreePublicInstances > 0) {
\r
303 app = (HttpApplication) _appFreePublicList.Pop();
\r
304 _appFreePublicInstances--;
\r
309 // Create non-public object
\r
310 app = (HttpApplication) HttpRuntime.CreateInternalObject(_appType);
\r
312 app.Startup(context, HttpApplicationFactory.ApplicationState);
\r
315 return (IHttpHandler) app;
\r
318 internal void RecyclePublicInstance(HttpApplication app) {
\r
319 lock (_appFreePublicList) {
\r
320 if (_appFreePublicInstances < _appMaxFreePublicInstances) {
\r
321 _appFreePublicList.Push(app);
\r
322 _appFreePublicInstances++;
\r
333 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
\r
335 if (list == null || list.Count == 0)
\r
338 HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
\r
339 foreach (ObjectTagBuilder tag in list) {
\r
346 static internal HttpApplicationState ApplicationState {
\r
348 if (null == s_Factory._state) {
\r
349 HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
\r
350 HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
\r
351 s_Factory._state = new HttpApplicationState (app, ses);
\r
354 return s_Factory._state;
\r
358 internal static void EndApplication() {
\r
359 s_Factory.Dispose();
\r
362 public static void SetCustomApplication (IHttpHandler customApplication)
\r
364 custApplication = customApplication;
\r
367 internal Type AppType {
\r
368 get { return _appType; }
\r
371 internal static void SignalError(Exception exc) {
\r
372 // TODO: Raise an error (we probably don't have a HttpContext)
\r