Saturday, July 26, 2014

How does WebActivator work?

WebActivator allows to execute some code before application starts (or when it's shutting down). Which becomes really useful when you are making your own packages and you want your package to do some actions when application starts/shuts down. And the person who will use your package does not need to do those actions manually.

Let’s look at the features of WebActivator and get inside the source code to see how it works and what does it mean when you meet in code something like:

[assembly: WebActivator.PreApplicationStartMethod(typeof(SomeClass), "SomeMethod", Order=1]
First of all lets look at the WebActivator structure.

As you can see, there are 3 custom attributes,which allow to register some methods invocation at the different time of the application life cycle. We talk about them later, and now lets look at WebActivators life cycle.

First of all, there is a PreApplicationStartMethodAttribute living in System.Web and WebActivator uses this attribute. If you look inside WebActivator’s AssemblyInfo.cs you will find it there:

[assembly: PreApplicationStartMethod(typeof(WebActivatorEx.ActivationManager), "Run")]

This is exactly what's happening: .net guarantees that before application starts the Run() method of WebActivator will be executed. And this is where all the action happens. Now take a closer look at those 3 custom WebActivator attributes.

PreApplicationStartMethodAttribute

This attribute allows to execute code before Application_Start method in Global.asax is called.

Now look at the class diagram. Basically, when the Run() method of ActivationManager is invoked (thanks to the System.Web.PreApplicationStartMethodAttribute) it calls RunPreStartMethods method.

It scans all the assemblies in bin directory (or in directory, where WebActivator dll is, if the application is not being hosted by asp.net) and App_Code directory (if hosted). After that it searches using reflection for all custom PreApplicationStartMethod attributes, orders them by "Order" property and invokes methods, specified in "MethodName" property of these attributes. The same scheme of method invocation is true for PostApplicationStartMethodAttribute and ApplicationShutdownMethodAttribute, the difference is only when they are called.

PostApplicationStartMethodAttribute

(Note: If the application is not being hosted by asp.net, activation method in this attribute will be invoked immediately when ActivationManager Run() method is invoked).

This attribute allows to execute code after Application_Start method in Global.asax is called. But how it determines, that application already started?

If you look back at the class diagram you will notice StartMethodCallingModule class nested in WebActivator. This is custom Http Module implementing IHttpModule interface with two methods - Init() and Dispose().

Inside Init() method RunPostStartMethods() is called. It works in the same way as RunPreStartMethods() described above, just searches for different type of attributes in assemblies. Init() method itself is simple but it gets the work done:

            public void Init(HttpApplication context)
            {
                lock (_lock)
                {
                    if (_initializedModuleCount++ == 0)
                    {
                        RunPostStartMethods();
                    }
                }
            }

And the locked counter inside guarantees, that post start methods will be called only once.

When ActivationManager Run() method is invoked it dynamically registers its custom StartMethodCallingModule http module:

Type startMethodType = typeof(StartMethodCallingModule);
//...
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(startMethodType);

When IIS receives the first request it will do a lot of things, including creating HttpApplication. Note that IIS have application pools, and application pools can have multiple HttpApplication instances. Several HttpApplication instances can be created to process multiple requests incoming at the same time. After that all modules are being instantiated and their Init() method being called for every HttpApplication instance.

And when Init() method is called for WebActivator's custom module - it invokes post start events. For multiple reasons Init() method can be called more than once, that is why there is a counter _initializedModuleCount - it prevents invoking post start methods each time the Init() is called.

And now to the last attribute.

ApplicationShutdownMethodAttribute

This attribute allows to execute the code before application shuts down. The workflow here resembles workflow, described for PostApplicationStartMethodAttribute. The obvious difference is that shutdown methods are invoked when HttpModule disposes. So all the action happens in Dispose() method:

          public void Dispose()
            {
                lock (_lock)
                {
                    if (--_initializedModuleCount == 0)
                    {
                        RunShutdownMethods();
                    }
                }
            }

Almost the same story, as with previous attribute: when the application is shutting down all the modules are being disposed. But shutdown methods will be invoked on dispose of the last module thanks to the counter.

No comments :

Post a Comment