2004-02-17 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpApplicationFactory.cs
1 // \r
2 // System.Web.HttpApplicationFactory\r
3 //\r
4 // Author:\r
5 //      Patrik Torstensson (ptorsten@hotmail.com)\r
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
7 //\r
8 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)\r
9 //\r
10 using System;\r
11 using System.Collections;\r
12 using System.IO;\r
13 using System.Reflection;\r
14 using System.Web.UI;\r
15 using System.Web.Compilation;\r
16 using System.Web.SessionState;\r
17 \r
18 namespace System.Web {\r
19         class HttpApplicationFactory {\r
20                 private string _appFilename;\r
21                 private Type _appType;\r
22 \r
23                 private bool _appInitialized;\r
24                 private bool _appFiredEnd;\r
25 \r
26                 private Stack                   _appFreePublicList;\r
27                 private int                             _appFreePublicInstances;\r
28                 static private int      _appMaxFreePublicInstances = 32;\r
29 \r
30                 private HttpApplicationState _state;\r
31 \r
32                 static IHttpHandler custApplication;\r
33 \r
34                 static private HttpApplicationFactory s_Factory = new HttpApplicationFactory();\r
35 \r
36                 public HttpApplicationFactory() {\r
37                         _appInitialized = false;\r
38                         _appFiredEnd = false;\r
39 \r
40                         _appFreePublicList = new Stack();\r
41                         _appFreePublicInstances = 0;\r
42                 }\r
43 \r
44                 static private string GetAppFilename (HttpContext context)\r
45                 {\r
46                         string physicalAppPath = context.Request.PhysicalApplicationPath;\r
47                         string appFilePath = Path.Combine (physicalAppPath, "Global.asax");\r
48                         if (File.Exists (appFilePath))\r
49                                 return appFilePath;\r
50 \r
51                         return Path.Combine (physicalAppPath, "global.asax");\r
52                 }\r
53 \r
54                 private void CompileApp(HttpContext context) {\r
55                         if (File.Exists(_appFilename)) {\r
56                                 // Setup filemonitor for all filedepend also. CacheDependency?\r
57 \r
58                                 _appType = ApplicationFileParser.GetCompiledApplicationType (_appFilename, context);\r
59                                 if (_appType == null) {\r
60                                         string msg = String.Format ("Error compiling application file ({0}).", _appFilename);\r
61                                         throw new ApplicationException (msg);\r
62                                 }\r
63                         } else {\r
64                                 _appType = typeof (System.Web.HttpApplication);\r
65                                 _state = new HttpApplicationState ();\r
66                         }\r
67                 }\r
68 \r
69                 static bool IsEventHandler (MethodInfo m)\r
70                 {\r
71                         if (m.ReturnType != typeof (void))\r
72                                 return false;\r
73 \r
74                         ParameterInfo [] pi = m.GetParameters ();\r
75                         int length = pi.Length;\r
76                         if (length == 0)\r
77                                 return true;\r
78 \r
79                         if (length != 2)\r
80                                 return false;\r
81 \r
82                         if (pi [0].ParameterType != typeof (object) ||\r
83                             pi [1].ParameterType != typeof (EventArgs))\r
84                                 return false;\r
85                         \r
86                         return true;\r
87                 }\r
88 \r
89                 static void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)\r
90                 {\r
91                         string name = method.Name.Replace ("_On", "_");\r
92                         if (appTypeEventHandlers [name] == null) {\r
93                                 appTypeEventHandlers [name] = method;\r
94                                 return;\r
95                         }\r
96                         \r
97                         ArrayList list;\r
98                         if (appTypeEventHandlers [name] is MethodInfo)\r
99                                 list = new ArrayList ();\r
100                         else\r
101                                 list = appTypeEventHandlers [name] as ArrayList;\r
102 \r
103                         list.Add (method);\r
104                 }\r
105                 \r
106                 static Hashtable GetApplicationTypeEvents (HttpApplication app)\r
107                 {\r
108                         Type appType = app.GetType ();\r
109                         Hashtable appTypeEventHandlers = new Hashtable ();\r
110                         ArrayList evtMethods = new ArrayList ();\r
111                         BindingFlags flags = BindingFlags.Public    |\r
112                                              BindingFlags.NonPublic | \r
113                                              BindingFlags.Instance |\r
114                                              BindingFlags.Static;\r
115 \r
116                         MethodInfo [] methods = appType.GetMethods (flags);\r
117                         foreach (MethodInfo m in methods) {\r
118                                 if (IsEventHandler (m))\r
119                                         AddEvent (m, appTypeEventHandlers);\r
120                         }\r
121 \r
122                         return appTypeEventHandlers;\r
123                 }\r
124 \r
125                 static bool FireEvents (string method_name, object target, object [] args)\r
126                 {\r
127                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);\r
128                         MethodInfo method = possibleEvents [method_name] as MethodInfo;\r
129                         if (method == null)\r
130                                 return false;\r
131 \r
132                         if (method.GetParameters ().Length == 0)\r
133                                 method.Invoke (target, null);\r
134                         else\r
135                                 method.Invoke (target, args);\r
136 \r
137                         return true;\r
138                 }\r
139                 \r
140                 internal static void FireOnAppStart (HttpApplication app)\r
141                 {\r
142                         object [] args = new object [] {app, EventArgs.Empty};\r
143                         FireEvents ("Application_Start", app, args);\r
144                 }\r
145 \r
146                 void FireOnAppEnd ()\r
147                 {\r
148                 //      FireEvents ("Application_End", this, new object [] {this, EventArgs.Empty});\r
149                 }\r
150 \r
151                 void FireOnSessionStart (HttpSessionState state, object source, EventArgs args)\r
152                 {\r
153                 //      FireEvents ("Session_Start", state, new object [] {source, EventArgs.Empty});\r
154                 }\r
155                 \r
156                 void FireOnSessionEnd (HttpSessionState state, object source, EventArgs args)\r
157                 {\r
158                 //      FireEvents ("Session_End", state, new object [] {source, args});\r
159                 }\r
160         \r
161                 private void InitializeFactory (HttpContext context)\r
162                 {\r
163                         _appFilename = GetAppFilename (context);\r
164 \r
165                         CompileApp (context);\r
166 \r
167                         // Create a application object\r
168                         HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);\r
169 \r
170                         // Startup\r
171                         app.Startup(context, HttpApplicationFactory.ApplicationState);\r
172 \r
173                         // Fire OnAppStart\r
174                         HttpApplicationFactory.FireOnAppStart (app);\r
175 \r
176                         // Recycle our application instance\r
177                         RecyclePublicInstance(app);\r
178                 }\r
179 \r
180                 private void Dispose() {\r
181                         ArrayList torelease = new ArrayList();\r
182                         lock (_appFreePublicList) {\r
183                                 while (_appFreePublicList.Count > 0) {\r
184                                         torelease.Add(_appFreePublicList.Pop());\r
185                                         _appFreePublicInstances--;\r
186                                 }\r
187                         }\r
188 \r
189                         if (torelease.Count > 0) {\r
190                                 foreach (Object obj in torelease) {\r
191                                         ((HttpApplication) obj).Cleanup();\r
192                                 }\r
193                         }\r
194 \r
195                         if (!_appFiredEnd) {\r
196                                 lock (this) {\r
197                                         if (!_appFiredEnd) {\r
198                                                 FireOnAppEnd();\r
199                                                 _appFiredEnd = true;\r
200                                         }\r
201                                 }\r
202                         }\r
203                 }\r
204 \r
205                 internal static IHttpHandler GetInstance(HttpContext context)\r
206                 {\r
207                         if (custApplication != null)\r
208                                 return custApplication;\r
209 \r
210                         if (!s_Factory._appInitialized) {\r
211                                 lock (s_Factory) {\r
212                                         if (!s_Factory._appInitialized) {\r
213                                                 s_Factory.InitializeFactory(context);\r
214                                                 s_Factory._appInitialized = true;\r
215                                         }\r
216                                 }\r
217                         }\r
218 \r
219                         return s_Factory.GetPublicInstance(context);\r
220                 }\r
221 \r
222                 internal static void RecycleInstance(HttpApplication app) {\r
223                         if (!s_Factory._appInitialized)\r
224                                 throw new InvalidOperationException("Factory not intialized");\r
225 \r
226                         s_Factory.RecyclePublicInstance(app);\r
227                 }\r
228 \r
229                 internal static void AttachEvents (HttpApplication app)\r
230                 {\r
231                         Hashtable possibleEvents = GetApplicationTypeEvents (app);\r
232                         foreach (string key in possibleEvents.Keys) {\r
233                                 int pos = key.IndexOf ('_');\r
234                                 if (pos == -1 || key.Length <= pos + 1)\r
235                                         continue;\r
236 \r
237                                 string moduleName = key.Substring (0, pos);\r
238                                 object target;\r
239                                 if (moduleName == "Application") {\r
240                                         target = app;\r
241                                 } else {\r
242                                         target = app.Modules [moduleName];\r
243                                 }\r
244 \r
245                                 string eventName = key.Substring (pos + 1);\r
246                                 EventInfo evt = target.GetType ().GetEvent (eventName);\r
247                                 if (evt == null)\r
248                                         continue;\r
249                         \r
250                                 string usualName = moduleName + "_" + eventName;\r
251                                 object methodData = possibleEvents [usualName];\r
252                                 if (methodData == null)\r
253                                         continue;\r
254 \r
255                                 if (methodData is MethodInfo) {\r
256                                         AddHandler (evt, target, app, (MethodInfo) methodData);\r
257                                         continue;\r
258                                 }\r
259 \r
260                                 ArrayList list = (ArrayList) methodData;\r
261                                 foreach (MethodInfo method in list)\r
262                                         AddHandler (evt, target, app, method);\r
263                         }\r
264                 }\r
265 \r
266                 static void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)\r
267                 {\r
268                         int length = method.GetParameters ().Length;\r
269 \r
270                         if (length == 0) {\r
271                                 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);\r
272                                 evt.AddEventHandler (target, npi.FakeDelegate);\r
273                         } else {\r
274                                 evt.AddEventHandler (target, Delegate.CreateDelegate (\r
275                                                         typeof (EventHandler), app, method.Name));\r
276                         }\r
277                 }\r
278 \r
279                 private IHttpHandler GetPublicInstance(HttpContext context) {\r
280                         HttpApplication app = null;\r
281 \r
282                         lock (_appFreePublicList) {\r
283                                 if (_appFreePublicInstances > 0) {\r
284                                         app = (HttpApplication) _appFreePublicList.Pop();\r
285                                         _appFreePublicInstances--;\r
286                                 }\r
287                         }\r
288 \r
289                         if (app == null) {\r
290                                 // Create non-public object\r
291                                 app = (HttpApplication) HttpRuntime.CreateInternalObject(_appType);\r
292 \r
293                                 app.Startup(context, HttpApplicationFactory.ApplicationState);\r
294                         }\r
295 \r
296                         return (IHttpHandler) app;\r
297                 }\r
298 \r
299                 internal void RecyclePublicInstance(HttpApplication app) {\r
300                         lock (_appFreePublicList) {\r
301                                 if (_appFreePublicInstances < _appMaxFreePublicInstances) {\r
302                                         _appFreePublicList.Push(app);\r
303                                         _appFreePublicInstances++;\r
304 \r
305                                         app = null;\r
306                                 }\r
307                         }\r
308                         \r
309                         if  (app != null) {\r
310                                 app.Cleanup();\r
311                         }\r
312                 }\r
313 \r
314                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)\r
315                 {\r
316                         if (list == null || list.Count == 0)\r
317                                 return null;\r
318 \r
319                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();\r
320                         foreach (ObjectTagBuilder tag in list) {\r
321                                 coll.Add (tag);\r
322                         }\r
323 \r
324                         return coll;\r
325                 }\r
326                 \r
327                 static internal HttpApplicationState ApplicationState {\r
328                         get {\r
329                                 if (null == s_Factory._state) {\r
330                                         HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);\r
331                                         HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);\r
332                                         s_Factory._state = new HttpApplicationState (app, ses);\r
333                                 }\r
334 \r
335                                 return s_Factory._state;\r
336                         }\r
337                 }\r
338 \r
339                 internal static void EndApplication() {\r
340                         s_Factory.Dispose();\r
341                 }\r
342 \r
343                 public static void SetCustomApplication (IHttpHandler customApplication)\r
344                 {\r
345                         custApplication = customApplication;\r
346                 }\r
347 \r
348                 internal Type AppType {\r
349                         get { return _appType; }\r
350                 }\r
351         }\r
352 }\r