-// \r
-// System.Web.HttpApplicationFactory\r
-//\r
-// Author:\r
-// Patrik Torstensson (ptorsten@hotmail.com)\r
-// Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
-//\r
-// (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)\r
-//\r
-using System;\r
-using System.Collections;\r
-using System.IO;\r
-using System.Reflection;\r
-using System.Web.UI;\r
-using System.Web.Compilation;\r
-using System.Web.SessionState;\r
-\r
-namespace System.Web {\r
- class HttpApplicationFactory {\r
- private string _appFilename;\r
- private Type _appType;\r
-\r
- private bool _appInitialized;\r
- private bool _appFiredEnd;\r
-\r
- private Stack _appFreePublicList;\r
- private int _appFreePublicInstances;\r
- static private int _appMaxFreePublicInstances = 32;\r
-\r
- private HttpApplicationState _state;\r
-\r
- static IHttpHandler custApplication;\r
-\r
- static private HttpApplicationFactory s_Factory = new HttpApplicationFactory();\r
-\r
- public HttpApplicationFactory() {\r
- _appInitialized = false;\r
- _appFiredEnd = false;\r
-\r
- _appFreePublicList = new Stack();\r
- _appFreePublicInstances = 0;\r
- }\r
-\r
- static private string GetAppFilename (HttpContext context)\r
- {\r
- string physicalAppPath = context.Request.PhysicalApplicationPath;\r
- string appFilePath = Path.Combine (physicalAppPath, "Global.asax");\r
- if (File.Exists (appFilePath))\r
- return appFilePath;\r
-\r
- return Path.Combine (physicalAppPath, "global.asax");\r
- }\r
-\r
- private void CompileApp(HttpContext context) {\r
- if (File.Exists(_appFilename)) {\r
- // Setup filemonitor for all filedepend also. CacheDependency?\r
-\r
- _appType = ApplicationFileParser.GetCompiledApplicationType (_appFilename, context);\r
- if (_appType == null) {\r
- string msg = String.Format ("Error compiling application file ({0}).", _appFilename);\r
- throw new ApplicationException (msg);\r
- }\r
- } else {\r
- _appType = typeof (System.Web.HttpApplication);\r
- _state = new HttpApplicationState ();\r
- }\r
- }\r
-\r
- static bool IsEventHandler (MethodInfo m)\r
- {\r
- if (m.ReturnType != typeof (void))\r
- return false;\r
-\r
- ParameterInfo [] pi = m.GetParameters ();\r
- if (pi.Length != 2)\r
- return false;\r
-\r
- if (pi [0].ParameterType != typeof (object) ||\r
- pi [1].ParameterType != typeof (EventArgs))\r
- return false;\r
- \r
- return true;\r
- }\r
-\r
- static void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)\r
- {\r
- string name = method.Name.Replace ("_On", "_");\r
- if (appTypeEventHandlers [name] == null) {\r
- appTypeEventHandlers [name] = method;\r
- return;\r
- }\r
- \r
- ArrayList list;\r
- if (appTypeEventHandlers [name] is MethodInfo)\r
- list = new ArrayList ();\r
- else\r
- list = appTypeEventHandlers [name] as ArrayList;\r
-\r
- list.Add (method);\r
- }\r
- \r
- static Hashtable GetApplicationTypeEvents (HttpApplication app)\r
- {\r
- Type appType = app.GetType ();\r
- Hashtable appTypeEventHandlers = new Hashtable ();\r
- ArrayList evtMethods = new ArrayList ();\r
- BindingFlags flags = BindingFlags.Public |\r
- BindingFlags.NonPublic | \r
- BindingFlags.DeclaredOnly |\r
- BindingFlags.Instance |\r
- BindingFlags.Static;\r
-\r
- MethodInfo [] methods = appType.GetMethods (flags);\r
- foreach (MethodInfo m in methods) {\r
- if (IsEventHandler (m))\r
- AddEvent (m, appTypeEventHandlers);\r
- }\r
-\r
- Type baseType = appType.BaseType;\r
- if (baseType == typeof (HttpApplication))\r
- return appTypeEventHandlers;\r
-\r
- flags = BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;\r
-\r
- methods = appType.GetMethods (flags);\r
- foreach (MethodInfo m in methods) {\r
- if (IsEventHandler (m))\r
- AddEvent (m, appTypeEventHandlers);\r
- }\r
-\r
- return appTypeEventHandlers;\r
- }\r
-\r
- static void FireEvents (string method_name, object target, object [] args)\r
- {\r
- Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);\r
- MethodInfo method = possibleEvents [method_name] as MethodInfo;\r
- if (possibleEvents [method_name] == null)\r
- return;\r
-\r
- method.Invoke (target, args);\r
- }\r
- \r
- internal static void FireOnAppStart (HttpApplication app)\r
- {\r
- FireEvents ("Application_Start", app, new object [] {app, EventArgs.Empty});\r
- }\r
-\r
- void FireOnAppEnd ()\r
- {\r
- // FireEvents ("Application_End", this, new object [] {this, EventArgs.Empty});\r
- }\r
-\r
- void FireOnSessionStart (HttpSessionState state, object source, EventArgs args)\r
- {\r
- // FireEvents ("Session_Start", state, new object [] {source, EventArgs.Empty});\r
- }\r
- \r
- void FireOnSessionEnd (HttpSessionState state, object source, EventArgs args)\r
- {\r
- // FireEvents ("Session_End", state, new object [] {source, args});\r
- }\r
- \r
- private void InitializeFactory (HttpContext context)\r
- {\r
- _appFilename = GetAppFilename (context);\r
-\r
- CompileApp (context);\r
-\r
- // Create a application object\r
- HttpApplication app = (HttpApplication) HttpRuntime.CreateInternalObject (_appType);\r
-\r
- // Startup\r
- app.Startup(context, HttpApplicationFactory.ApplicationState);\r
-\r
- // Fire OnAppStart\r
- HttpApplicationFactory.FireOnAppStart (app);\r
-\r
- // Recycle our application instance\r
- RecyclePublicInstance(app);\r
- }\r
-\r
- private void Dispose() {\r
- ArrayList torelease = new ArrayList();\r
- lock (_appFreePublicList) {\r
- while (_appFreePublicList.Count > 0) {\r
- torelease.Add(_appFreePublicList.Pop());\r
- _appFreePublicInstances--;\r
- }\r
- }\r
-\r
- if (torelease.Count > 0) {\r
- foreach (Object obj in torelease) {\r
- ((HttpApplication) obj).Cleanup();\r
- }\r
- }\r
-\r
- if (!_appFiredEnd) {\r
- lock (this) {\r
- if (!_appFiredEnd) {\r
- FireOnAppEnd();\r
- _appFiredEnd = true;\r
- }\r
- }\r
- }\r
- }\r
-\r
- internal static IHttpHandler GetInstance(HttpContext context)\r
- {\r
- if (custApplication != null)\r
- return custApplication;\r
-\r
- if (!s_Factory._appInitialized) {\r
- lock (s_Factory) {\r
- if (!s_Factory._appInitialized) {\r
- s_Factory.InitializeFactory(context);\r
- s_Factory._appInitialized = true;\r
- }\r
- }\r
- }\r
-\r
- return s_Factory.GetPublicInstance(context);\r
- }\r
-\r
- internal static void RecycleInstance(HttpApplication app) {\r
- if (!s_Factory._appInitialized)\r
- throw new InvalidOperationException("Factory not intialized");\r
-\r
- s_Factory.RecyclePublicInstance(app);\r
- }\r
-\r
- internal static void AttachEvents (HttpApplication app)\r
- {\r
- Hashtable possibleEvents = GetApplicationTypeEvents (app);\r
- foreach (string key in possibleEvents.Keys) {\r
- int pos = key.IndexOf ('_');\r
- if (pos == -1 || key.Length <= pos + 1)\r
- continue;\r
-\r
- string moduleName = key.Substring (0, pos);\r
- object target;\r
- if (moduleName == "Application")\r
- target = app;\r
- else\r
- target = app.Modules [moduleName];\r
-\r
- if (target == null)\r
- continue;\r
- \r
- Type targetType = target.GetType ();\r
-\r
- string eventName = key.Substring (pos + 1);\r
- EventInfo evt = targetType.GetEvent (eventName);\r
- if (evt == null)\r
- continue;\r
- \r
- string usualName = moduleName + "_" + eventName;\r
- object methodData = possibleEvents [usualName];\r
- if (methodData == null)\r
- continue;\r
-\r
- if (methodData is MethodInfo) {\r
- MethodInfo method = (MethodInfo) methodData;\r
- evt.AddEventHandler (target, Delegate.CreateDelegate (\r
- typeof (EventHandler), app, method.Name));\r
- continue;\r
- }\r
-\r
- ArrayList list = (ArrayList) methodData;\r
- foreach (MethodInfo method in list)\r
- evt.AddEventHandler (target, Delegate.CreateDelegate (\r
- typeof (EventHandler), app, method.Name));\r
- }\r
- }\r
-\r
- private IHttpHandler GetPublicInstance(HttpContext context) {\r
- HttpApplication app = null;\r
-\r
- lock (_appFreePublicList) {\r
- if (_appFreePublicInstances > 0) {\r
- app = (HttpApplication) _appFreePublicList.Pop();\r
- _appFreePublicInstances--;\r
- }\r
- }\r
-\r
- if (app == null) {\r
- // Create non-public object\r
- app = (HttpApplication) HttpRuntime.CreateInternalObject(_appType);\r
-\r
- app.Startup(context, HttpApplicationFactory.ApplicationState);\r
- }\r
-\r
- return (IHttpHandler) app;\r
- }\r
-\r
- internal void RecyclePublicInstance(HttpApplication app) {\r
- lock (_appFreePublicList) {\r
- if (_appFreePublicInstances < _appMaxFreePublicInstances) {\r
- _appFreePublicList.Push(app);\r
- _appFreePublicInstances++;\r
-\r
- app = null;\r
- }\r
- }\r
- \r
- if (app != null) {\r
- app.Cleanup();\r
- }\r
- }\r
-\r
- static internal HttpApplicationState ApplicationState {\r
- get {\r
- if (null == s_Factory._state) {\r
- s_Factory._state = new HttpApplicationState();\r
- }\r
-\r
- return s_Factory._state;\r
- }\r
- }\r
-\r
- internal static void EndApplication() {\r
- s_Factory.Dispose();\r
- }\r
-\r
- public static void SetCustomApplication (IHttpHandler customApplication)\r
- {\r
- custApplication = customApplication;\r
- }\r
-\r
- internal Type AppType {\r
- get { return _appType; }\r
- }\r
- }\r
-}\r
+//
+// System.Web.HttpApplicationFactory
+//
+// TODO:
+// bin_watcher must work.
+//
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
+// (c) Copyright 2004 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Web.UI;
+using System.Web.SessionState;
+using System.Web.Configuration;
+
+#if !TARGET_J2EE
+using System.Web.Compilation;
+#else
+using vmw.common;
+#endif
+
+namespace System.Web {
+ class HttpApplicationFactory {
+ // Initialized in InitType
+#if TARGET_J2EE
+ static HttpApplicationFactory theFactory {
+ get
+ {
+ HttpApplicationFactory factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
+ if (factory == null) {
+ lock(typeof(HttpApplicationFactory)) {
+ factory = (HttpApplicationFactory)AppDomain.CurrentDomain.GetData("HttpApplicationFactory");
+ if (factory == null) {
+ factory = new HttpApplicationFactory();
+ System.Threading.Thread.Sleep(1);
+ AppDomain.CurrentDomain.SetData("HttpApplicationFactory", factory);
+ }
+ }
+ }
+ return factory;
+ }
+ }
+#else
+ static HttpApplicationFactory theFactory = new HttpApplicationFactory();
+#endif
+
+ bool needs_init = true;
+ Type app_type;
+ HttpApplicationState app_state;
+#if !TARGET_JVM
+ FileSystemWatcher app_file_watcher;
+ FileSystemWatcher bin_watcher;
+#endif
+ Stack available = new Stack ();
+
+ // Watch this thing out when getting an instance
+ IHttpHandler custom_application;
+
+ bool IsEventHandler (MethodInfo m)
+ {
+ if (m.ReturnType != typeof (void))
+ return false;
+
+ ParameterInfo [] pi = m.GetParameters ();
+ int length = pi.Length;
+ if (length == 0)
+ return true;
+
+ if (length != 2)
+ return false;
+
+ if (pi [0].ParameterType != typeof (object) ||
+ pi [1].ParameterType != typeof (EventArgs))
+ return false;
+
+ return true;
+ }
+
+ void AddEvent (MethodInfo method, Hashtable appTypeEventHandlers)
+ {
+ string name = method.Name.Replace ("_On", "_");
+ if (appTypeEventHandlers [name] == null) {
+ appTypeEventHandlers [name] = method;
+ return;
+ }
+
+ MethodInfo old_method = appTypeEventHandlers [name] as MethodInfo;
+ ArrayList list;
+ if (old_method != null){
+ list = new ArrayList (4);
+ list.Add (old_method);
+ appTypeEventHandlers [name] = list;
+ } else
+ list = appTypeEventHandlers [name] as ArrayList;
+
+ list.Add (method);
+ }
+
+ Hashtable GetApplicationTypeEvents (HttpApplication app)
+ {
+ Type appType = app.GetType ();
+ Hashtable appTypeEventHandlers = new Hashtable ();
+ BindingFlags flags = BindingFlags.Public |
+ BindingFlags.NonPublic |
+ BindingFlags.Instance |
+ BindingFlags.Static;
+
+ MethodInfo [] methods = appType.GetMethods (flags);
+ foreach (MethodInfo m in methods) {
+ if (IsEventHandler (m))
+ AddEvent (m, appTypeEventHandlers);
+ }
+
+ return appTypeEventHandlers;
+ }
+
+ bool FireEvent (string method_name, object target, object [] args)
+ {
+ Hashtable possibleEvents = GetApplicationTypeEvents ((HttpApplication) target);
+ MethodInfo method = possibleEvents [method_name] as MethodInfo;
+ if (method == null)
+ return false;
+
+ if (method.GetParameters ().Length == 0)
+ args = null;
+
+ method.Invoke (target, args);
+
+ return true;
+ }
+
+ void FireOnAppStart (HttpContext context)
+ {
+ HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
+ app.SetContext (context);
+ object [] args = new object [] {app, EventArgs.Empty};
+ FireEvent ("Application_Start", app, args);
+ Recycle (app);
+ }
+
+ void FireOnAppEnd ()
+ {
+ if (app_type == null)
+ return; // we didn't even get an application
+
+ HttpApplication app = (HttpApplication) Activator.CreateInstance (app_type, true);
+ FireEvent ("Application_End", app, new object [] {new object (), EventArgs.Empty});
+ app.Dispose ();
+ }
+
+ //
+ // This is invoked by HttpRuntime.Dispose, when we unload an AppDomain
+ // To reproduce this in action, touch "global.asax" while XSP is running.
+ //
+ public static void Dispose ()
+ {
+ theFactory.FireOnAppEnd ();
+ }
+
+#if !TARGET_JVM
+ FileSystemWatcher CreateWatcher (string file, FileSystemEventHandler hnd)
+ {
+ FileSystemWatcher watcher = new FileSystemWatcher ();
+
+ watcher.Path = Path.GetFullPath (Path.GetDirectoryName (file));
+ watcher.Filter = Path.GetFileName (file);
+
+ watcher.Changed += hnd;
+ watcher.Created += hnd;
+ watcher.Deleted += hnd;
+
+ watcher.EnableRaisingEvents = true;
+
+ return watcher;
+ }
+
+ void OnAppFileChanged (object sender, FileSystemEventArgs args)
+ {
+ bin_watcher.EnableRaisingEvents = false;
+ app_file_watcher.EnableRaisingEvents = false;
+ HttpRuntime.UnloadAppDomain ();
+ }
+#endif
+
+ internal static void AttachEvents (HttpApplication app)
+ {
+ HttpApplicationFactory factory = theFactory;
+ Hashtable possibleEvents = factory.GetApplicationTypeEvents (app);
+ foreach (string key in possibleEvents.Keys) {
+ int pos = key.IndexOf ('_');
+ if (pos == -1 || key.Length <= pos + 1)
+ continue;
+
+ string moduleName = key.Substring (0, pos);
+ object target;
+ if (moduleName == "Application") {
+ target = app;
+ } else {
+ target = app.Modules [moduleName];
+ if (target == null)
+ continue;
+ }
+
+ string eventName = key.Substring (pos + 1);
+ EventInfo evt = target.GetType ().GetEvent (eventName);
+ if (evt == null)
+ continue;
+
+ string usualName = moduleName + "_" + eventName;
+ object methodData = possibleEvents [usualName];
+ if (methodData == null)
+ continue;
+
+ if (methodData is MethodInfo) {
+ factory.AddHandler (evt, target, app, (MethodInfo) methodData);
+ continue;
+ }
+
+ ArrayList list = (ArrayList) methodData;
+ foreach (MethodInfo method in list)
+ factory.AddHandler (evt, target, app, method);
+ }
+ }
+
+ void AddHandler (EventInfo evt, object target, HttpApplication app, MethodInfo method)
+ {
+ int length = method.GetParameters ().Length;
+
+ if (length == 0) {
+ NoParamsInvoker npi = new NoParamsInvoker (app, method.Name);
+ evt.AddEventHandler (target, npi.FakeDelegate);
+ } else {
+ evt.AddEventHandler (target, Delegate.CreateDelegate (
+ typeof (EventHandler), app, method.Name));
+ }
+ }
+
+ static HttpStaticObjectsCollection MakeStaticCollection (ArrayList list)
+ {
+ if (list == null || list.Count == 0)
+ return null;
+
+ HttpStaticObjectsCollection coll = new HttpStaticObjectsCollection ();
+ foreach (ObjectTagBuilder tag in list) {
+ coll.Add (tag);
+ }
+
+ return coll;
+ }
+
+ internal static HttpApplicationState ApplicationState {
+#if TARGET_J2EE
+ get {
+ HttpApplicationFactory factory = theFactory;
+ if (factory.app_state == null)
+ factory.app_state = new HttpApplicationState (null, null);
+ return factory.app_state;
+ }
+#else
+ get {
+ if (theFactory.app_state == null) {
+ HttpStaticObjectsCollection app = MakeStaticCollection (GlobalAsaxCompiler.ApplicationObjects);
+ HttpStaticObjectsCollection ses = MakeStaticCollection (GlobalAsaxCompiler.SessionObjects);
+
+ theFactory.app_state = new HttpApplicationState (app, ses);
+ }
+ return theFactory.app_state;
+ }
+#endif
+ }
+
+ public static void SetCustomApplication (IHttpHandler customApplication)
+ {
+ theFactory.custom_application = customApplication;
+ }
+
+ internal static Type AppType {
+ get {
+ return theFactory.app_type;
+ }
+ }
+
+ void InitType (HttpContext context)
+ {
+ lock (this) {
+ if (!needs_init)
+ return;
+
+ string physical_app_path = context.Request.PhysicalApplicationPath;
+ string app_file;
+
+ app_file = Path.Combine (physical_app_path, "Global.asax");
+ if (!File.Exists (app_file))
+ app_file = Path.Combine (physical_app_path, "global.asax");
+
+ WebConfigurationSettings.Init (context);
+
+ if (File.Exists (app_file)) {
+#if TARGET_J2EE
+ app_type = System.Web.J2EE.PageMapper.GetObjectType(app_file);
+#else
+ app_type = ApplicationFileParser.GetCompiledApplicationType (app_file, context);
+ if (app_type == null) {
+ string msg = String.Format ("Error compiling application file ({0}).", app_file);
+ throw new ApplicationException (msg);
+ }
+
+ app_file_watcher = CreateWatcher (app_file, new FileSystemEventHandler (OnAppFileChanged));
+#endif
+ } else {
+ app_type = typeof (System.Web.HttpApplication);
+ app_state = new HttpApplicationState ();
+ }
+ needs_init = false;
+
+ //
+ // Now init the settings
+ //
+
+ }
+ }
+
+ //
+ // Multiple-threads might hit this one on startup, and we have
+ // to delay-initialize until we have the HttpContext
+ //
+ internal static HttpApplication GetApplication (HttpContext context)
+ {
+ HttpApplicationFactory factory = theFactory;
+ if (factory.needs_init){
+ if (context == null)
+ return null;
+
+ factory.InitType (context);
+ factory.FireOnAppStart (context);
+ }
+
+ lock (factory) {
+ if (factory.available.Count > 0)
+ return (HttpApplication) factory.available.Pop ();
+ }
+
+ HttpApplication app = (HttpApplication) Activator.CreateInstance (factory.app_type, true);
+
+ return app;
+ }
+
+ internal static void Recycle (HttpApplication app)
+ {
+ HttpApplicationFactory factory = theFactory;
+ lock (factory) {
+ if (factory.available.Count < 32)
+ factory.available.Push (app);
+ else
+ app.Dispose ();
+ }
+ }
+ }
+}