HttpApplicationFactory.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / HttpApplicationFactory.cs / 1305376 / HttpApplicationFactory.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 * The HttpApplicationFactory class 
 *
 * Copyright (c) 1999 Microsoft Corporation 
 */

namespace System.Web {
    using System.Text; 
    using System.Threading;
    using System.Runtime.Remoting.Messaging; 
    using System.Web; 
    using System.Web.Caching;
    using System.Web.Util; 
    using System.Web.Hosting;
    using System.Web.UI;
    using System.Web.Compilation;
    using System.Web.SessionState; 
    using System.Collections;
    using System.Reflection; 
    using System.IO; 
    using System.Globalization;
    using System.Web.Management; 

    /*
     * Application Factory only has and public static methods to get / recycle
     * application instances.  The information cached per application 
     * config file is encapsulated by ApplicationData class.
     * Only one static instance of application factory is created. 
     */ 
    internal class HttpApplicationFactory {
 
        internal const string applicationFileName = "global.asax";

        // the only instance of application factory
        private static HttpApplicationFactory _theApplicationFactory = new HttpApplicationFactory(); 

        // flag to indicate that initialization was done 
        private bool _inited; 

        // filename for the global.asax 
        private String _appFilename;
        private ICollection _fileDependencies;

        // call application on_start only once 
        private bool _appOnStartCalled = false;
 
        // call application on_end only once 
        private bool _appOnEndCalled = false;
 
        // dictionary of application state
        private HttpApplicationState _state;

        // class of the application object 
        private Type _theApplicationType;
 
        // free list of app objects 
        private Stack _freeList = new Stack();
        private int _numFreeAppInstances = 0; 
        private int _minFreeAppInstances = 0;

        // free list of special (context-less) app objects
        // to be used for global events (App_OnEnd, Session_OnEnd, etc.) 
        private Stack _specialFreeList = new Stack();
        private int _numFreeSpecialAppInstances = 0; 
        private const int _maxFreeSpecialAppInstances = 20; 

        // results of the reflection on the app class 
        private MethodInfo   _onStartMethod;        // Application_OnStart
        private int          _onStartParamCount;
        private MethodInfo   _onEndMethod;          // Application_OnEnd
        private int          _onEndParamCount; 
        private MethodInfo   _sessionOnEndMethod;   // Session_OnEnd
        private int          _sessionOnEndParamCount; 
        private EventHandler _sessionOnEndEventHandlerAspCompatHelper; // helper for AspCompat 
        // list of methods suspected as event handlers
        private MethodInfo[] _eventHandlerMethods; 

        internal HttpApplicationFactory() {
            _sessionOnEndEventHandlerAspCompatHelper = new EventHandler(this.SessionOnEndEventHandlerAspCompatHelper);
        } 

        // 
        // Initialization on first request 
        //
 
        private void Init() {
            if (_customApplication != null)
                return;
 
            try {
                try { 
                    _appFilename = GetApplicationFile(); 

                    CompileApplication(); 
                }
                finally {
                    // Always set up global.asax file change notification, even if compilation
                    // failed.  This way, if the problem is fixed, the appdomain will be restarted. 
                    SetupChangesMonitor();
                } 
            } 
            catch { // Protect against exception filters
                throw; 
            }
        }

        internal static void SetupFileChangeNotifications() { 
            // Just call EnsureInited() to make sure global.asax FCN are set up.
            // But don't if we never even got to initialize Fusion 
            if (HttpRuntime.CodegenDirInternal != null) 
                _theApplicationFactory.EnsureInited();
        } 

        private void EnsureInited() {
            if (!_inited) {
                lock (this) { 
                    if (!_inited) {
                        Init(); 
                        _inited = true; 
                    }
                } 
            }
        }

        internal static void EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) { 
            if (!_theApplicationFactory._appOnStartCalled) {
                Exception error = null; 
                lock (_theApplicationFactory) { 
                    if (!_theApplicationFactory._appOnStartCalled) {
                        using (new DisposableHttpContextWrapper(context)) { 
                            // impersonation could be required (UNC share or app credentials)

                            WebBaseEvent.RaiseSystemEvent(_theApplicationFactory, WebEventCodes.ApplicationStart);
 
                            if (_theApplicationFactory._onStartMethod != null) {
                                app.ProcessSpecialRequest(context, 
                                                          _theApplicationFactory._onStartMethod, 
                                                          _theApplicationFactory._onStartParamCount,
                                                          _theApplicationFactory, 
                                                          EventArgs.Empty,
                                                          null);
                            }
                        } 
                    }
 
                    _theApplicationFactory._appOnStartCalled = true; 
                    error = context.Error;
                } 
                if (error != null) {
                    throw new HttpException(error.Message, error);
                }
            } 
        }
 
        private void EnsureAppStartCalled(HttpContext context) { 
            if (!_appOnStartCalled) {
                lock (this) { 
                    if (!_appOnStartCalled) {
                        using (new DisposableHttpContextWrapper(context)) {
                            // impersonation could be required (UNC share or app credentials)
 
                            WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
 
                            // fire outside of impersonation as HttpApplication logic takes 
                            // care of impersonation by itself
                            FireApplicationOnStart(context); 
                        }

                        _appOnStartCalled = true;
                    } 
                }
            } 
        } 

        internal static String GetApplicationFile() { 
            return Path.Combine(HttpRuntime.AppDomainAppPathInternal, applicationFileName);
        }

        private void CompileApplication() { 
            // Get the Application Type and AppState from the global file
 
            _theApplicationType = BuildManager.GetGlobalAsaxType(); 

            BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult(); 

            if (result != null) {

                // Even if global.asax was already compiled, we need to get the collections 
                // of application and session objects, since they are not persisted when
                // global.asax is compiled.  Ideally, they would be, but since  tags 
                // are only there for ASP compat, it's not worth the trouble. 
                // Note that we only do this is the rare case where we know global.asax contains
                //  tags, to avoid always paying the price (VSWhidbey 453101) 
                if (result.HasAppOrSessionObjects) {
                    GetAppStateByParsingGlobalAsax();
                }
 
                // Remember file dependencies
                _fileDependencies = result.VirtualPathDependencies; 
            } 

            if (_state == null) { 
                _state = new HttpApplicationState();
            }

 
            // Prepare to hookup event handlers via reflection
 
            ReflectOnApplicationType(); 
        }
 
        private void GetAppStateByParsingGlobalAsax() {
            using (new ApplicationImpersonationContext()) {
                // It may not exist if the app is precompiled
                if (FileUtil.FileExists(_appFilename)) { 
                    ApplicationFileParser parser;
 
                    parser = new ApplicationFileParser(); 
                    AssemblySet referencedAssemblies = System.Web.UI.Util.GetReferencedAssemblies(
                        _theApplicationType.Assembly); 
                    referencedAssemblies.Add(typeof(string).Assembly);
                    VirtualPath virtualPath = HttpRuntime.AppDomainAppVirtualPathObject.SimpleCombine(
                        applicationFileName);
                    parser.Parse(referencedAssemblies, virtualPath); 

                    // Create app state 
                    _state = new HttpApplicationState(parser.ApplicationObjects, parser.SessionObjects); 
                }
            } 
        }

        private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
            if (m.ReturnType != typeof(void)) 
                return false;
 
            // has to have either no args or two args (object, eventargs) 
            ParameterInfo[] parameters = m.GetParameters();
 
            switch (parameters.Length) {
                case 0:
                    // ok
                    break; 
                case 2:
                    // param 0 must be object 
                    if (parameters[0].ParameterType != typeof(System.Object)) 
                        return false;
                    // param 1 must be eventargs 
                    if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                        !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                        return false;
                    // ok 
                    break;
 
                default: 
                    return false;
            } 

            // check the name (has to have _ not as first or last char)
            String name = m.Name;
            int j = name.IndexOf('_'); 
            if (j <= 0 || j > name.Length-1)
                return false; 
 
            // special pseudo-events
            if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") || 
                StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
                _onStartMethod = m;
                _onStartParamCount = parameters.Length;
            } 
            else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Application_End")) { 
                _onEndMethod = m; 
                _onEndParamCount = parameters.Length;
            } 
            else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Session_End")) {
                _sessionOnEndMethod = m;
                _sessionOnEndParamCount = parameters.Length; 
            }
 
            return true; 
        }
 
        private void ReflectOnApplicationType() {
            ArrayList handlers = new ArrayList();
            MethodInfo[] methods;
 
            Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
 
            // get this class methods 
            methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            foreach (MethodInfo m in methods) { 
                if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                    handlers.Add(m);
            }
 
            // get base class private methods (GetMethods would not return those)
            Type baseType = _theApplicationType.BaseType; 
            if (baseType != null && baseType != typeof(HttpApplication)) { 
                methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
                foreach (MethodInfo m in methods) { 
                    if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                        handlers.Add(m);
                }
            } 

            // remember as an array 
            _eventHandlerMethods = new MethodInfo[handlers.Count]; 
            for (int i = 0; i < _eventHandlerMethods.Length; i++)
                _eventHandlerMethods[i] = (MethodInfo)handlers[i]; 
        }

        private void SetupChangesMonitor() {
            FileChangeEventHandler handler = new FileChangeEventHandler(this.OnAppFileChange); 

            HttpRuntime.FileChangesMonitor.StartMonitoringFile(_appFilename, handler); 
 
            if (_fileDependencies != null) {
                foreach (string fileName in _fileDependencies) { 
                    HttpRuntime.FileChangesMonitor.StartMonitoringFile(
                        HostingEnvironment.MapPathInternal(fileName), handler);
                }
            } 
        }
 
        private void OnAppFileChange(Object sender, FileChangeEvent e) { 
            // shutdown the app domain if app file changed
            Debug.Trace("AppDomainFactory", "Shutting down appdomain because of application file change"); 
            HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.ChangeInGlobalAsax, "Change in GLOBAL.ASAX");
        }

        // 
        //  Application instance management
        // 
 
        private HttpApplication GetNormalApplicationInstance(HttpContext context) {
            HttpApplication app = null; 

            lock (_freeList) {
                if (_numFreeAppInstances > 0) {
                    app = (HttpApplication)_freeList.Pop(); 
                    _numFreeAppInstances--;
 
                    if (_numFreeAppInstances < _minFreeAppInstances) { 
                        _minFreeAppInstances = _numFreeAppInstances;
                    } 
                }
            }

            if (app == null) { 
                // If ran out of instances, create a new one
                app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType); 
 
                using (new ApplicationImpersonationContext()) {
                    app.InitInternal(context, _state, _eventHandlerMethods); 
                }
            }

            return app; 
        }
 
        private void RecycleNormalApplicationInstance(HttpApplication app) { 
            lock (_freeList) {
                _freeList.Push(app); 
                _numFreeAppInstances++;
            }
        }
 
        private void TrimApplicationInstanceFreeList() {
            // reset last min length 
            int minFreeAppInstances = _minFreeAppInstances; 
            _minFreeAppInstances = _numFreeAppInstances;
 
            // if free list is empty or was empty since last trim, don't trim now
            if (minFreeAppInstances <= 1) {
                return;
            } 

            ArrayList apps = null; 
 
            lock (_freeList) {
                if (_numFreeAppInstances > 1) { 
                    apps = new ArrayList();

                    // trim a percentage at a time or 1 item
                    int trimCount = (_numFreeAppInstances * 3)/100 + 1;  // 3% at the time 

                    while (trimCount > 0) { 
                        apps.Add(_freeList.Pop()); 
                        _numFreeAppInstances--;
                        trimCount--; 
                    }
                    _minFreeAppInstances = _numFreeAppInstances;
                }
            } 

            // dispose the applications that were removed (if any) 
            if (apps != null) { 
                foreach (HttpApplication app in apps) {
                    app.DisposeInternal(); 
                }
            }
        }
 
        internal static HttpApplication GetPipelineApplicationInstance(IntPtr appContext, HttpContext context) {
            _theApplicationFactory.EnsureInited(); 
            return _theApplicationFactory.GetSpecialApplicationInstance(appContext, context); 
        }
 
        internal static void RecyclePipelineApplicationInstance(HttpApplication app) {
            _theApplicationFactory.RecycleSpecialApplicationInstance(app);
        }
 
        private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) {
            HttpApplication app = null; 
 
            lock (_specialFreeList) {
                if (_numFreeSpecialAppInstances > 0) { 
                    app = (HttpApplication)_specialFreeList.Pop();
                    _numFreeSpecialAppInstances--;
                }
            } 

            if (app == null) { 
                // 
                //  Put the context on the thread, to make it available to anyone calling
                //  HttpContext.Current from the HttpApplication constructor or module Init 
                //
                using (new DisposableHttpContextWrapper(context)) {
                    // If ran out of instances, create a new one
                    app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType); 

                    using (new ApplicationImpersonationContext()) { 
                        app.InitSpecial(_state, _eventHandlerMethods, appContext, context); 
                    }
                } 
            }

            return app;
        } 

        private HttpApplication GetSpecialApplicationInstance() { 
            return GetSpecialApplicationInstance(IntPtr.Zero, null); 
        }
 
        private void RecycleSpecialApplicationInstance(HttpApplication app) {
            if (_numFreeSpecialAppInstances < _maxFreeSpecialAppInstances) {
                lock (_specialFreeList) {
                    _specialFreeList.Push(app); 
                    _numFreeSpecialAppInstances++;
                } 
            } 
            // else: don't dispose these
        } 

        //
        //  Application on_start / on_end
        // 

        private void FireApplicationOnStart(HttpContext context) { 
            if (_onStartMethod != null) { 
                HttpApplication app = GetSpecialApplicationInstance();
 
                app.ProcessSpecialRequest(
                                         context,
                                         _onStartMethod,
                                         _onStartParamCount, 
                                         this,
                                         EventArgs.Empty, 
                                         null); 

                RecycleSpecialApplicationInstance(app); 
            }
        }

        private void FireApplicationOnEnd() { 
            if (_onEndMethod != null) {
                HttpApplication app = GetSpecialApplicationInstance(); 
 
                app.ProcessSpecialRequest(
                                         null, 
                                         _onEndMethod,
                                         _onEndParamCount,
                                         this,
                                         EventArgs.Empty, 
                                         null);
 
                RecycleSpecialApplicationInstance(app); 
            }
        } 

        //
        //  Session on_start / on_end
        // 

        class AspCompatSessionOnEndHelper { 
            private HttpApplication _app; 
            private HttpSessionState _session;
            private Object _eventSource; 
            private EventArgs _eventArgs;

            internal AspCompatSessionOnEndHelper(HttpApplication app, HttpSessionState session, Object eventSource, EventArgs eventArgs) {
                _app = app; 
                _session = session;
                _eventSource = eventSource; 
                _eventArgs = eventArgs; 
            }
 
            internal HttpApplication Application { get { return _app; } }
            internal HttpSessionState Session { get { return _session; } }
            internal Object Source { get { return _eventSource; } }
            internal EventArgs Args { get { return _eventArgs; } } 
        }
 
        private void SessionOnEndEventHandlerAspCompatHelper(Object eventSource, EventArgs eventArgs) { 
            AspCompatSessionOnEndHelper helper = (AspCompatSessionOnEndHelper)eventSource;
 
            helper.Application.ProcessSpecialRequest(
                                                     null,
                                                     _sessionOnEndMethod,
                                                     _sessionOnEndParamCount, 
                                                     helper.Source,
                                                     helper.Args, 
                                                     helper.Session); 
        }
 
        private void FireSessionOnEnd(HttpSessionState session, Object eventSource, EventArgs eventArgs) {
            if (_sessionOnEndMethod != null) {
                HttpApplication app = GetSpecialApplicationInstance();
#if !FEATURE_PAL // FEATURE_PAL does not enable COM 
                if (AspCompatApplicationStep.AnyStaObjectsInSessionState(session) || HttpRuntime.ApartmentThreading) {
                    AspCompatSessionOnEndHelper helper = new AspCompatSessionOnEndHelper(app, session, eventSource, eventArgs); 
 
                    AspCompatApplicationStep.RaiseAspCompatEvent(
                                            null, 
                                            app,
                                            session.SessionID,
                                            _sessionOnEndEventHandlerAspCompatHelper,
                                            helper, 
                                            EventArgs.Empty);
                } 
                else { 
#endif // !FEATURE_PAL
                    app.ProcessSpecialRequest( 
                                            null,
                                            _sessionOnEndMethod,
                                            _sessionOnEndParamCount,
                                            eventSource, 
                                            eventArgs,
                                            session); 
#if !FEATURE_PAL // FEATURE_PAL does not enable COM 
                }
#endif // !FEATURE_PAL 

                RecycleSpecialApplicationInstance(app);
            }
        } 

        private void FireApplicationOnError(Exception error) { 
            HttpApplication app = GetSpecialApplicationInstance(); 
            app.RaiseErrorWithoutContext(error);
            RecycleSpecialApplicationInstance(app); 
        }

        //
        //  Dispose resources associated with the app factory 
        //
 
        private void Dispose() { 
            // dispose all app instances
 
            ArrayList apps = new ArrayList();

            lock (_freeList) {
                while (_numFreeAppInstances > 0) { 
                    apps.Add(_freeList.Pop());
                    _numFreeAppInstances--; 
                } 
            }
 
            int n = apps.Count;

            for (int i = 0; i < n; i++)
                ((HttpApplication)(apps[i])).DisposeInternal(); 

            // call application_onEnd (only if application_onStart was called before) 
 
            if (_appOnStartCalled && !_appOnEndCalled) {
                lock (this) { 
                    if (!_appOnEndCalled) {
                        FireApplicationOnEnd();
                        _appOnEndCalled = true;
                    } 
                }
            } 
        } 

        // 
        // Static methods for outside use
        //

        // custom application -- every request goes directly to the same handler 
        private static IHttpHandler _customApplication;
 
        internal static void SetCustomApplication(IHttpHandler customApplication) { 
            // ignore this in app domains where we execute requests (ASURT 128047)
            if (HttpRuntime.AppDomainAppIdInternal == null) // only if 'clean' app domain 
                _customApplication = customApplication;
        }

        internal static IHttpHandler GetApplicationInstance(HttpContext context) { 
            if (_customApplication != null)
                return _customApplication; 
 
            // Check to see if it's a debug auto-attach request
            if (context.Request.IsDebuggingRequest) 
                return new HttpDebugHandler();

            _theApplicationFactory.EnsureInited();
 
            _theApplicationFactory.EnsureAppStartCalled(context);
 
            return _theApplicationFactory.GetNormalApplicationInstance(context); 
        }
 
        internal static void RecycleApplicationInstance(HttpApplication app) {
            _theApplicationFactory.RecycleNormalApplicationInstance(app);
        }
 
        internal static void TrimApplicationInstances() {
            if (_theApplicationFactory != null) { 
                _theApplicationFactory.TrimApplicationInstanceFreeList(); 
            }
        } 

        internal static void EndApplication() {
            _theApplicationFactory.Dispose();
        } 

        internal static void EndSession(HttpSessionState session, Object eventSource, EventArgs eventArgs) { 
            _theApplicationFactory.FireSessionOnEnd(session, eventSource, eventArgs); 
        }
 
        internal static void RaiseError(Exception error) {
            _theApplicationFactory.EnsureInited(); // VSWhidbey 482346
            _theApplicationFactory.FireApplicationOnError(error);
        } 

        internal static HttpApplicationState ApplicationState { 
            get { 
                HttpApplicationState state = _theApplicationFactory._state;
                if (state == null) 
                    state = new HttpApplicationState();
                return state;
            }
        } 
    }
 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK