* Mono.Posix.dll.sources: Rename Mono.Posix to Mono.Unix.
[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
11 //
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:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
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.
30 //
31 using System;\r
32 using System.Collections;\r
33 using System.IO;\r
34 using System.Reflection;\r
35 using System.Web.UI;\r
36 using System.Web.Compilation;\r
37 using System.Web.SessionState;\r
38 \r
39 namespace System.Web {\r
40         class HttpApplicationFactory {\r
41                 private string _appFilename;\r
42                 private Type _appType;\r
43 \r
44                 private bool _appInitialized;\r
45                 private bool _appFiredEnd;\r
46 \r
47                 private Stack                   _appFreePublicList;\r
48                 private int                             _appFreePublicInstances;\r
49                 static private int      _appMaxFreePublicInstances = 32;\r
50 \r
51                 private HttpApplicationState _state;\r
52 \r
53                 static IHttpHandler custApplication;\r
54 \r
55                 static private HttpApplicationFactory s_Factory = new HttpApplicationFactory();\r
56 \r
57                 public HttpApplicationFactory() {\r
58                         _appInitialized = false;\r
59                         _appFiredEnd = false;\r
60 \r
61                         _appFreePublicList = new Stack();\r
62                         _appFreePublicInstances = 0;\r
63                 }\r
64 \r
65                 static private string GetAppFilename (HttpContext context)\r
66                 {\r
67                         string physicalAppPath = context.Request.PhysicalApplicationPath;\r
68                         string appFilePath = Path.Combine (physicalAppPath, "Global.asax");\r
69                         if (File.Exists (appFilePath))\r
70                                 return appFilePath;\r
71 \r
72                         return Path.Combine (physicalAppPath, "global.asax");\r
73                 }\r
74 \r
75                 private void CompileApp(HttpContext context) {\r
76                         if (File.Exists(_appFilename)) {\r
77                                 // Setup filemonitor for all filedepend also. CacheDependency?\r
78 \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
83                                 }\r
84                         } else {\r
85                                 _appType = typeof (System.Web.HttpApplication);\r
86                                 _state = new HttpApplicationState ();\r
87                         }\r
88                 }\r
89 \r
90                 static bool IsEventHandler (MethodInfo m)\r
91                 {\r
92                         if (m.ReturnType != typeof (void))\r
93                                 return false;\r
94 \r
95                         ParameterInfo [] pi = m.GetParameters ();\r
96                         int length = pi.Length;\r
97                         if (length == 0)\r
98                                 return true;\r
99 \r
100                         if (length != 2)\r
101                                 return false;\r
102 \r
103                         if (pi [0].ParameterType != typeof (object) ||\r
104                             pi [1].ParameterType != typeof (EventArgs))\r
105                                 return false;\r
106                         \r
107                         return true;\r
108                 }\r
109 \r
110                 static void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)\r
111                 {\r
112                         string name = method.Name.Replace ("_On", "_");\r
113                         if (appTypeEventHandlers [name] == null) {\r
114                                 appTypeEventHandlers [name] = method;\r
115                                 return;\r
116                         }\r
117                         \r
118                         ArrayList list;\r
119                         if (appTypeEventHandlers [name] is MethodInfo)\r
120                                 list = new ArrayList ();\r
121                         else\r
122                                 list = appTypeEventHandlers [name] as ArrayList;\r
123 \r
124                         list.Add (method);\r
125                 }\r
126                 \r
127                 static Hashtable GetApplicationTypeEvents (HttpApplication app)\r
128                 {\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
136 \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
141                         }\r
142 \r
143                         return appTypeEventHandlers;\r
144                 }\r
145
146                 static bool FireEvent (string method_name, object target, object [] args)
147                 {
148                         Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
149                         MethodInfo method = possibleEvents [method_name] as MethodInfo;
150                         if (method == null)
151                                 return false;
152
153                         if (method.GetParameters ().Length == 0)
154                                 method.Invoke (target, null);
155                         else
156                                 method.Invoke (target, args);
157
158                         return true;
159                 }
160
161                 internal static void FireOnAppStart (HttpApplication app)\r
162                 {
163                         object [] args = new object [] {app, EventArgs.Empty};\r
164                         FireEvent ("Application_Start", app, args);\r
165                 }
166
167                 void FireOnAppEnd ()
168                 {
169                         if (_appType == null)
170                                 return; // we didn't even get an application
171
172                         HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);
173                         AttachEvents (app);
174                         FireEvent ("Application_End", app, new object [] {this, EventArgs.Empty});
175                         app.Dispose ();
176                 }
177
178                 private void InitializeFactory (HttpContext context)\r
179                 {\r
180                         _appFilename = GetAppFilename (context);\r
181 \r
182                         CompileApp (context);\r
183 \r
184                         // Create a application object\r
185                         HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);\r
186 \r
187                         // Startup\r
188                         app.Startup(context, HttpApplicationFactory.ApplicationState);\r
189 \r
190                         // Fire OnAppStart\r
191                         HttpApplicationFactory.FireOnAppStart (app);\r
192 \r
193                         // Recycle our application instance\r
194                         RecyclePublicInstance(app);\r
195                 }\r
196 \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
203                                 }\r
204                         }\r
205 \r
206                         if (torelease.Count > 0) {\r
207                                 foreach (Object obj in torelease) {\r
208                                         ((HttpApplication) obj).Cleanup();\r
209                                 }\r
210                         }\r
211 \r
212                         if (!_appFiredEnd) {\r
213                                 lock (this) {\r
214                                         if (!_appFiredEnd) {\r
215                                                 FireOnAppEnd();\r
216                                                 _appFiredEnd = true;\r
217                                         }\r
218                                 }\r
219                         }\r
220                 }\r
221 \r
222                 internal static IHttpHandler GetInstance(HttpContext context)\r
223                 {\r
224                         if (custApplication != null)\r
225                                 return custApplication;\r
226 \r
227                         if (!s_Factory._appInitialized) {\r
228                                 lock (s_Factory) {\r
229                                         if (!s_Factory._appInitialized) {\r
230                                                 s_Factory.InitializeFactory(context);\r
231                                                 s_Factory._appInitialized = true;\r
232                                         }\r
233                                 }\r
234                         }\r
235 \r
236                         return s_Factory.GetPublicInstance(context);\r
237                 }\r
238 \r
239                 internal static void RecycleInstance(HttpApplication app) {\r
240                         if (!s_Factory._appInitialized)\r
241                                 throw new InvalidOperationException("Factory not intialized");\r
242 \r
243                         s_Factory.RecyclePublicInstance(app);\r
244                 }\r
245 \r
246                 internal static void AttachEvents (HttpApplication app)\r
247                 {\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
252                                         continue;\r
253 \r
254                                 string moduleName = key.Substring (0, pos);\r
255                                 object target;\r
256                                 if (moduleName == "Application") {\r
257                                         target = app;\r
258                                 } else {\r
259                                         target = app.Modules [moduleName];\r
260                                         if (target == null)\r
261                                                 continue;\r
262                                 }\r
263 \r
264                                 string eventName = key.Substring (pos + 1);\r
265                                 EventInfo evt = target.GetType ().GetEvent (eventName);\r
266                                 if (evt == null)\r
267                                         continue;\r
268                         \r
269                                 string usualName = moduleName + "_" + eventName;\r
270                                 object methodData = possibleEvents [usualName];\r
271                                 if (methodData == null)\r
272                                         continue;\r
273 \r
274                                 if (methodData is MethodInfo) {\r
275                                         AddHandler (evt, target, app, (MethodInfo) methodData);\r
276                                         continue;\r
277                                 }\r
278 \r
279                                 ArrayList list = (ArrayList) methodData;\r
280                                 foreach (MethodInfo method in list)\r
281                                         AddHandler (evt, target, app, method);\r
282                         }\r
283                 }\r
284 \r
285                 static void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)\r
286                 {\r
287                         int length = method.GetParameters ().Length;\r
288 \r
289                         if (length == 0) {\r
290                                 NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);\r
291                                 evt.AddEventHandler (target, npi.FakeDelegate);\r
292                         } else {\r
293                                 evt.AddEventHandler (target, Delegate.CreateDelegate (\r
294                                                         typeof (EventHandler), app, method.Name));\r
295                         }\r
296                 }\r
297 \r
298                 private IHttpHandler GetPublicInstance(HttpContext context) {\r
299                         HttpApplication app = null;\r
300 \r
301                         lock (_appFreePublicList) {\r
302                                 if (_appFreePublicInstances > 0) {\r
303                                         app = (HttpApplication) _appFreePublicList.Pop();\r
304                                         _appFreePublicInstances--;\r
305                                 }\r
306                         }\r
307 \r
308                         if (app == null) {\r
309                                 // Create non-public object\r
310                                 app = (HttpApplication) HttpRuntime.CreateInternalObject(_appType);\r
311 \r
312                                 app.Startup(context, HttpApplicationFactory.ApplicationState);\r
313                         }\r
314 \r
315                         return (IHttpHandler) app;\r
316                 }\r
317 \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
323 \r
324                                         app = null;\r
325                                 }\r
326                         }\r
327                         \r
328                         if  (app != null) {\r
329                                 app.Cleanup();\r
330                         }\r
331                 }\r
332 \r
333                 static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)\r
334                 {\r
335                         if (list == null || list.Count == 0)\r
336                                 return null;\r
337 \r
338                         HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();\r
339                         foreach (ObjectTagBuilder tag in list) {\r
340                                 coll.Add (tag);\r
341                         }\r
342 \r
343                         return coll;\r
344                 }\r
345                 \r
346                 static internal HttpApplicationState ApplicationState {\r
347                         get {\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
352                                 }\r
353 \r
354                                 return s_Factory._state;\r
355                         }\r
356                 }\r
357 \r
358                 internal static void EndApplication() {\r
359                         s_Factory.Dispose();\r
360                 }\r
361 \r
362                 public static void SetCustomApplication (IHttpHandler customApplication)\r
363                 {\r
364                         custApplication = customApplication;\r
365                 }\r
366 \r
367                 internal Type AppType {\r
368                         get { return _appType; }\r
369                 }\r
370 \r
371                 internal static void SignalError(Exception exc) {\r
372                         // TODO: Raise an error (we probably don't have a HttpContext)\r
373                 }\r
374         }\r
375 }\r