Want to follow this site? Here's the RSS feed.

Themelia Framework 2.0 Beta 3 Released

Tuesday, September 23, 2008

This weekend I was finally able to release beta 3 of my Themelia Framework 2.0.  This foundation, primarily for the web, provides a new paradigm in web development through its advanced HTTP routing subsystem.  That's not all Themelia is or does, but it will probably be the primary aspect of it that people think of.  It's also not the type of HTTP routing you are used to with simple URL rewriting systems or .NET 3.5 SP1's built in routing system.  It's, in fact, a new type of web development.

In order to help people understand and use Themelia effectively, I have written (and rewritten) extensive documentation for various of its features.  There are still a few more documents to be posted, but all of the HTTP routing specific docs are already posted here on my NetFXHarmonics.com web site.  Below is a list, in order, of the various Themelia docs:

At beta 3, Themelia is feature complete.  There shouldn't be any more major interface changes.  Therefore, at this time I need people to start using it.  The excuse of "I can't use beta software" doesn't work in the real world as using beta software is how software goes gets out of beta.  Also, the exceedingly naive and unprofessional excuse of "I can't use open source" software doesn't work at all as the lines between open-source (e.g. Prototype/JQuery/Firefox/Chrome), shared-source (e.g. .NET Framework), and commercial software (e.g. Windows) blurs more by the day.  Adapt or be assimilated.

Of course, you can always look over the code of Themelia to increase your own awareness of various areas of technology.  Perhaps you want to learn more about dependency injection or want to see how to deal with HTTP handler mappings using IIS7's integrated runtime.  Whatever.  The code is up there for your reading pleasure.  Also, if you have some sort of optimization technique you would like to suggest or even an architectural recommendation, send it my way.

Fundamentals of Themelia - Redirects

Tuesday, September 9, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Sometimes you want to handle a route with an HTTP handler, sometimes you want to do an alias, but other times you may want to just a simple redirect.  Maybe something moved or you just want a "shrink" URL on your web site so others can find a link faster.  Fortunately, Themelia provides the ability to setup redirects in a variety of different ways.  Further, to make things simple, redirects are configured very similarly to aliases.

The first way to setup a redirect is the two-part model of setting up a redirect handler and writing a redirect entry:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="pathStartsWith" name="Redirect" text="jd8" />
      </handlers>
      <redirects>
        <add key="jd8" destination="http://www.somedudeswebsite.com/this/is/a/really/long/url/no/one/will/remember" />
      </redirects>
    </add>
  </webDomains>
</themelia.web>

This configuration should be nearly self explanatory: your handler routes all paths that start with "jd8" to the "Redirect" handler.  When the "Redirect" handler loads, it knows to look in it's area (<redirects>) for the appropriate information.

As is explained in Like with basic HTTP routing, handlers are loaded based on rules of specificity.  Also, as with page aliases, once the correct handler is matches, then the appropriate redirect is loaded based upon the key of the redirect which must match the text of the handler.

Initialization Parameters

Another option you have is to use Themelia initialization parameters.  Various features in Themelia support this and while the mechanics of them are beyond the scope of the current discussion, using them for redirects is very simple.  The basic idea is that instead of doing a two-part registration of a redirect, you tell the handler itself some information that will be passed to the handler.  As you will see form the following example, the initialization parameter syntax for redirects makes Themelia redirects even simpler to work with:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="pathStartsWith" name="Redirect" text="jd8">
          <parameters>
            <add name="Destination" value="http://www.somedudeswebsite.com/this/is/a/really/long/url/no/one/will/remember" />
          </parameters>
        </add>
      </handlers>
    </add>
  </webDomains>
</themelia.web>

All you need to do is set the destination parameter to the appropriate URL and you're done.

Redirect Fall Through Processor

Say you had an entire section of your web site that you would like to use for nothing but redirects.  Perhaps you would like to setup a /shrink/ path on your web site to allow easy to remember URLs for your company or family.  If you understand the fundamentals of Themelia, your mind should lean towards using a web domain with a fall through processor for redirects.  Well, guess what... Themelia ships with a fall through processor called the RedirectFallThroughProcessor.  This scenario is easily created by the following sample syntax:

<themelia.web>
  <webDomains>
    <add name="shrink" path="shrink">
      <fallThroughProcessors>
        <add type="RedirectFallThroughProcessor" />
      </fallThroughProcessors>
      <redirects>
        <add key="jd8" destination="http://www.somedudeswebsite.com/this/is/a/really/long/url/no/one/will/remember" />
        <add key="f93" destination="http://www.somedudeswebsite.com/this/is/another/really/long/url/no/one/will/remember" />
        <add key="d8l" destination="http://www.somedudeswebsite.com/this/is/yet/another/really/long/url/no/one/will/remember" />
      </redirects>
    </add>
  </webDomains>
</themelia.web>

Now your people can simply go to http://www.yourwebsite.com/shrink/jd8, http://www.yourwebsite.com/shrink/jd8, and http://www.yourwebsite.com/shrink/jd8 to access the long URLs.

Custom Redirecting

If you are rather ambitious, perhaps you would like a more powerful mechanism for dynamically storing shrunk URLs.  Using Themelia this is also incredibly simple.  Though we could store out shrunk URLs anywhere, especially in a database, for our example, lets store them in a plain old XML file that looks like the following redirect.xml file:

<shrink>
  <url matchType="webDomainPathStartsWith" alias="dev" target="http://www.codeplex.com/devserver/" />
  <url matchType="webDomainPathStartsWith" alias="min" target="http://www.codeplex.com/minima/" />
</shrink>

Now what we can do is create a custom fall through processor which catches all requests in a web domain and matches them against our XML file (of course, in reality we would use a database with caching with properly set dependencies, but for the same of our example, XML without locking is easier to demonstrate):

using System;
//+
namespace Sample.Web.Routing
{
    public class CustomRedirectFallthroughProcessor : Themelia.Web.Routing.FallThroughProcessorBase
    {
        //- @GetHandler -//
        public override System.Web.IHttpHandler GetHandler(System.Web.HttpContext context, String requestType, String virtualPath, String path, params Object[] parameterArray)
        {
            System.IO.StreamReader reader = new System.IO.StreamReader(@"redirect.xml");
            System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(reader);
            while (xmlReader.Read())
            {
                if (xmlReader.NodeType == System.Xml.XmlNodeType.Element)
                {
                    String matchType = xmlReader.GetAttribute("matchType");
                    String alias = xmlReader.GetAttribute("alias");
                    String target = xmlReader.GetAttribute("target");
                    //+
                    if (Themelia.Web.Routing.RouteMatcher.Match(matchType, alias))
                    {
                        Themelia.Web.Http.Redirect(target);
                    }
                }
            }
            //+
            return null;
        }
    }
}

Now all we have to do is register our fall through processor in our "shrink" web domain and viola, we have our own URL shrink server:

<themelia.web>
  <webDomains>
    <add name="shrink" path="shrink">
      <fallThroughProcessors>
        <add type="Sample.Web.Routing.CustomRedirectFallthroughProcessor, Sample.Web" />
      </fallThroughProcessors>
    </add>
  </webDomains>
</themelia.web>

With this example www.yourdomain.com/shrink/dev goes to http://www.codeplex.com/devserver  and www.yourdomain.com/shrink/min goes to http://www.codeplex.com/minima.  Given other matchTypes or different web domains, you can match different types of patterns as well.

That's really all there is to it.  No longer do you need to rely on an external service for all your URLs and no more importing and updating URLs.  If your data were in a centralized database, everything could be connected to that centralized location.  Not only that, but you would, of course, split this up into various web domains to allow each web domain to have its own shrunk URLs.

404 Replacement

This isn't always a good idea.  In fact, it's rarely a good idea.  However, for internal applications, it could be rather helpful.  Sometimes you don't want 404 messages on your application at all (on a web site, you normally want to leave them.) In these, situations perhaps you want a redirect to another location when an improper link is accessed.  The Themelia configuration for this is simple:

<themelia.web>
  <webDomains>
    <add>
      <fallThroughProcessors>
        <add type="RedirectFallThroughProcessor" />
      </fallThroughProcessors>
      <redirects>
        <add match="" destination="/" />
      </redirects>
    </add>
  </webDomains>
</themelia.web>

In short, we have the redirect fall through processor with a catch all redirect sending the user to the root.  This will effectively send all access to non-registered areas of your application to the root.

Links

Fundamentals of Themelia - Error Processors

Thursday, September 4, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Regardless of what type of web site you are building, you absolutely must have some way to have the web site tell you that it's having problems.  If the web site isn't important enough to keep up, I can't see how it's important enough to put up in the first place.  Thus, ASP.NET developers tap into the beauty of the HttpApplication object's Error event.  By handling this event, we get immediate notification whenever an unhandled exception is thrown anywhere on the web site. For personal web sites, this is awesome, but corporate web sites, it could keep you from being fired. 

When I mention the Error event to junior developers (this is one of the things which, to me, defines a junior developer), they immediately think I'm talking about Global.asax.  Yes, it's related to Global.asax , but that's not where we're going to do this.  This isn't ASP.  This is ASP.NET.  Just because the letters of the file name contain "Global.asa", that doesn't mean it's the same thing  This is the powerful and professional beast known as ASP.NET and as such we should apply the best tried and true elegant design pattern and best practices wherever possible.  In this case, this means not putting all kinds of stuff into your Global.asax file.

Aside from the magical Application_Start method and its buddies, which must be in Global.asax, this file is really just a connection to the web site's HttpApplication instance.  Furthermore, since we are in an object-oriented world, we can inherit from that and keep our implementation away from the file entirely.  Thus the average Global.asax file should look like this:

<%@ Application Language="C#" Inherits="MySite.Web.HttpApplication" %>

That's it.  Well, unless you need the Application_Start method (or its buddies), but for the most part that's it.  Don't go handling events in there.  You will have your own HttpApplication class with error handling in another place. Here's an example:

namespace MySite.Web
{
    public class HttpApplication : System.Web.HttpApplication
    {
        //- @Init -//
        public override void Init()
        {
            this.Error += new EventHandler(OnError);
            //+
            base.Init();
        }

        //- $OnError -//
        private void OnError(Object sender, EventArgs e)
        {
            HttpApplication ha = sender as HttpApplication;
            if (ha != null)
            {
                System.Web.HttpContext context = ha.Context;
                Themelia.Tracing.ReportFacade.Send(context.Error);
            }
        }
    }
}

This example traps each unhandled exception and sends it to the default exception notification contact in Themelia.  All exception details including all inner exceptions will be included in the e-mail.

This is primarily the ASP.NET world before Themelia 2.0 (well, the ReportFacade is Themelia 2.0!): you trap an error and send it, save it, or whatever.

Error Processors

In the real world, sometimes you don't need to be notified of every error on every part of your web site.  Sometimes you only need to know about a certain sensitive portion.  The rest, perhaps, can be saved into a database or queued up for sending once a day.  On the other hand, some parts of a web site should not just notify you of an exception, it should also be logged... in triplicate.  Thus Themelia 2.0 provides error processors which handle error within the boundaries of a web domain.

If you recall, a web domain is a Themelia 2.0 concept that bring the idea of an AppDomain to the web.  What happens in a web domain, stays in the web domain.  Thus, if you register an error processor in a web domain, you will only see error about that particular web domain.

Configuration of error processors is incredibly simply: just register one in a web domain.  Period!

Here's the actual configuration, which replaces everything we did with Global.asax and the custom HttpApplication file:

<themelia.web>
  <webDomains>
    <errorProcessors>
      <add type="EmailSendingErrorProcessor" />
    </errorProcessors>
  </webDomains>
</themelia.web>

In this sample, EmailSendingErrorProcessor is an error processor provided by Themelia and is, obviously, registered in Themelia's processor factory to allow it's usage outside of declaring it's full type (Themelia.Web.Routing.EmailSendingErrorProcessor, Themelia.Web).

If you would like to create your own custom error processor, just create a class which inherits from Themelia.Web.Routing.ErrorProcessorBase.  This abstract class requires that you implement one method:

void OnErrorProcessorExecute(System.Web.HttpContext context, params Object[] parameterArray)

In this method you can do whatever you want.  Here's a sample custom error processor:

public class SavingErrorProcessor : Themelia.Web.Routing.ErrorProcessorBase
{
    //- @OnErrorProcessorExecute -//
    public override void OnErrorProcessorExecute(System.Web.HttpContext context, params object[] parameterArray)
    {
        Exception ex = context.Error;
        if (ex != null && !Http.UrlPartArray.Contains("problem"))
        {
            String problemGuid = Web.ExceptionSaver.Save(ex);
            Http.Redirect("/problem/" + problemGuid);
        }
        else
        {
            try
            {
                Themelia.Reporting.ReportFacade.Send("Unknown exception occurred");
            }
            catch
            {
                //+ just in case the sample web.config isn't configured for mail
            }
        }
    }
}

In this sample (as seen in the Themelia "Basic" sample), when an exception is thrown that is not caught by anyone, this error processor will catch it and check if we are already on the problem page.  If we aren't, then save the exception in the database and send the reference to the appropriate page to allow the user to enter in a description of what he or she was doing when the exception was raised.  If the exception was thrown on that page... oops, then just send us an alert so we know to get on it right away.

Error Processors in WebDomain Inheritance

In the following example, we have three web domains each with its own configuration:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="Sample.Web.Routing.SavingErrorProcessor, Sample.Web" />
      </processors>
    </add>
    <add name="sales" path="sales">
      <processors>
        <add type="EmailSendingErrorProcessor" />
      </processors>
    </add>
    <add name="finance" path="finance" basedOn="sales">
      <processors>
        <add type="Sample.Web.Routing.DatabaseSavingErrorProcessor, Sample.Web" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

When the root web domain has an error, the exception is handled as we previously explained.  If the sales web domain has a problem, send an alert immediately.  Finally, notice that the finance web domain is based on the sales web domain.  Thus, when an error is raised in the finance web domain, an email is sent immediately and the error is saved to the database (assuming thats what the DatabaseSavingErrorProcessor does!) To be clear, this does NOT mean that sales errors will go to the finance web domain.  Web domain inheritance just copies configuration and maintains the protective boundaries.  Thus each web domain will see only its' own exceptions.

Exception Filters

In addition to the flexibility you receive by having error processors run at the web domain level, you may also set filters as to what exception a error processor will handle.  For example, perhaps you only want to save exceptions, email only on a SqlException, but have someone paged on all OutOfMemory exceptions.

Configuring this feature couldn't possibly be simple: you just register exceptions as initialization parameters.  Here's an example configuration:

<themelia.web>
  <webDomains>
    <add name="sales" path="sales">
      <processors>
        <add type="EmailSendingErrorProcessor">
          <parameters>
            <add value="OutOfMemoryException" />
          </parameters>
        </add>
      </processors>
    </add>
  </webDomains>
</themelia.web>

In this example, only exceptions of OutOfMemoryException will be emailed and then only for the sales web domain.

Object Factories

Notice that we aren't specifying the assembly nor are we using the fully qualified type name for the exception.  This is due to Themelia's object factory concept, which work just like handler factories and processor factories.

When using an object factory for exceptions, you create a class which inherits from the Themelia.Web.Routing.ObjectFactoryBase class and override the CreateObject method.  This method accepts a string and returns an object (of type System.Object-- the root of all objects).  Then you want to be sure to return an object of type System.Type.

By way of example, below is a snippet of Themelia's internal object factory for exceptions:

internal class ExceptionTypeObjectFactory : ObjectFactoryBase
{
    //- Type List -//
    #region Type List
    internal static Type _Argument = typeof(System.ArgumentException);
    #endregion

    //- @CreateObject -//
    public override Object CreateObject(String text)
    {
        Type ex = null;
        switch (text)
        {
            case "argumentexception":
                return _Argument;
        }
        //+
        return ex;
    }
}

You may use this example as a template for your own custom exception type object factories.

To register a custom object factory, simply register it like any other Themelia factory:

<themelia.web>
  <webDomains>
    <add>
      <factories>
        <add type="ABCCorp.ExceptionTypeObjectFactory, ABCCorp" />
      </factories>
    </add>
  </webDomains>
</themelia.web>

Lastly, what types are already aliased for use in your own error processors?  Themelia's internal object factory for exceptions actually aliases all types in the mscorlib, System, System.Data, System.Web, and System.Xml assemblies.

{$Footer$}}

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.5 License.

Powered by the Minima 3.1 and Squid Micro-Blogging Library.

Built on Themelia Framework 2.0

Developed using NetFXHarmonics DevServer 1.1.

(all of which are NetFXHarmonics products created by David Betz)

Mini-icons are part of the Silk Icons set of icons at famfamfam.com