January 3, 2007

ASP.NET 2.0 HTTPModules and EPiServer fun and games

Part of the TMCore EPiServer Module functionality is delivered by hooking events generated by EPiServer. This can be done in a couple of ways, originally we hooked then in the application start phase of the Global.aspx.cs file; however this was a message because it meant that customers had to recompile that file in order for it to work). A far cleaner solution, recommended by the extremely knowledgable Steve Celius was to use an HTTP Module.

HTTP Modules provide exceptionally useful functionality. It published events representing different phases of the application lifecycle that you can hook in to add your own code. Then register your handler via web.config and everything is coolio.

That's when we starting seeing some odd behaviour :)

What we did

Having looked around for various examples of how to implement an HTTP Module we settled on hooking the Init event to register our event handlers; then because we're neat, hook the Dispose event to deregister them. We rapidly found, however, that alone was not enough; simply because init may be called multiple times, and possibly by different threads concurrently.

So we followed the following pattern:

class EPiEventsHttpModule: IHttpModule
{
	private static readonly object mutexLock = new Object();
	private static bool isRegistered = false;

public void Init(HttpApplication app)
{
if (!isRegistered)
{
lock (mutexLock)
{
if (!isRegistered)
{
RegisterEventHandlers();
}
}
}
}

public void Dispose()
{
lock (EPiEventsHttpModule.mutexLock)
{
if (EPiEventsHttpModule.isRegistered)
{
DeregisterEventHandlers();
}
}
}
}

(obviously some code ommitted for clarity here)

This code worked fine under ASP.NET 1.1, however, we started getting reports from our beta customers that under ASP.NET 2.0, they were seeing that events were being fired multiple times. We eventually tracked this down to the fact that multiple event registrations had occurred. For example, we have an event handler that creates a new topic in the topic map when an EPiServer page is created, what we saw that was multiple topics were being created for each page (although because topic maps cannot have 2 topics with the same subject identifier, TMCore was magically merging them for us, resulting in multiple names and occurrences.

The Problem

After some considerable debugging effort we discovered how this seemingly impossible situation could occur. In ASP.NET 1.1 when an unexpected exception propagated up through the ASP.NET layers the exception would be thrown back to the user (if so configured). However, the application would continue on quite happily. However, in ASP.NET 2.0 this behaviour changed, and when uncaught exceptions mad eit up to the top level the application instance would be disposed of, however the Dispose() method of the HTTP Module would not get called. It just quietly gets rid of it. However, what it doesn't do, which is what confused me, is that it doesn't restart the application in a separate heap area. This means that the old HTTP Module handler is still registered.

The EPiServer.Global event handler area is static, and it's persisting even when the application is shut down and restarted [to those Java/J2EE programmers out there: yes, I know, I thought the same thing :-) ]. So, when the next application request comes in, ASP.NET realizes it needs to initialize a new application, and it re-runs the Init method on the HTTP Module. So, bingo, multiple event registrations

The solution

Given that Dispose() is now, to me, considered useless; we can only rely on the Init() method being called. Because .NET event handlers don't provide any kind of feedback (i.e. "was my registration successful") and I can't find a way to query it (i.e. "is this event handler already registered?") it does mean it's a bit of a challenge.

However, fortunately, because there's no feedback on event registration or deregistration; we can move the event deregistration from Dispose() in to the Init() method. One the first load, the CLR won't object to us demanding the deregistration of an non-registered handler, it just silent ignores it; so now we're safe to register. Because deregistration always before any registration, we can be sure that there's only 1 instance of the event handlers registered at any one point, thus solving our problem.

Here's the Init() method from the EPiServer Module so you can see exactly how we solved this:

public void Init(HttpApplication app)
{
	if (!isRegistered)
	{
		lock (regMutexLock) 
		{
			if (!isRegistered) 
			{
					DeregisterEventHandlers();
					RegisterEventHandlers();
			} 
		}	
	}
	else 
	{
		if (log.IsInfoEnabled) 
		{
			log.Info("Event handlers already registered, will not re-register them (sequential request)");
		}
	}
}

October 17, 2006

TMRA 2006 - a quick review

TMRA was a great success! Thanks to Lutz Maicher's organisation and the high quality of the presentations there was a real buzz about the conference. What really struck me about the conference was the growing maturity and diversity of the tools and applications being presented. A few years back we had a poor quality standard and very few tools (let alone good ones). Now it seems there is a growing set of developers and business people who are recognising and exploiting the Topic Maps standard.

For me, TMRA highlighted the diversity and depth of the work being carried out. From the formal reference model work of Robert Barta and Steve Newcomb to the use of subjective and objective measures to evaluate topic map driven search and navigation by Sam Oh, there was a deep held belief that topic maps is the answer to many of the information and knowledge management issues we face. The thing now is, we are proving that hunch through considered and focused works.

October 4, 2006

Off to Leipzig for TMRA and a bit of a jolly!

If only! I'm off to Leipzig on Monday to present at TMRA 2006 and to attend the ISO SC34 WG3 meeting.

The quality of the papers this year seems really good and I'm looking forward to attending and giving a presentation. I'm talking about Topic Map Objects - a really cool framework that brings together Topic Maps and OO developent tools into a thing that is like Hibernate on Topic Map Steriods.

I'm sure there will also be plenty of liquid Topic Map revelry in the local drinking establishments, but nothing will keep us from a productive ISO session ;)