HttpHandler for Layar Service

Summary

The HttpHandler for Layars is designed to help Layar Content Developers kickstart their application development process. This project provides a C# implementation of the set of business objects used in the GetPointsOfInterest() communications exchange between the Layar Server and the Content Providers API. These business objects serialize and deserialize themselves appropriately, allowing the developer to work natively in C#.

Layar Architecture

Why an HttpHandler

Reference:
Essentailly HttpHandlers are managed versions of ISAPI extensions and filters. While ISAPI extenions and filters need to be registered with the IIS metabase, HTTP handlers are registered in the web.config file, making them easier to work with in managed hosting environments (not to mention easier to work with than {C++}).

ASP.NET comes with a small set of build-in HTTP handlers. There is a handler to serve ASP.NET pages, one for .NET Web Services, and yet another to accomodate .NET Remoting request for remote objects hosted by IIS. Custom HTTP handlers are used to process HTTP requests in a nonstandard way.

By using a custom HttpHandler, you get to maintain to 'goodness' and familiarity of working with .NET Managed Code, but you avoid the infrastructure build up required in the ASP .NET Page Lifecycle.

Below is an overview of the Request Lifecycle.

IIS Request Processing



Notice the Execute Handler Step, forwards the request to the appropriate HTTP Handler via a file mapping. This is configured in the <web.config> <httpHandlers> section:

<httpHandlers>
  <remove verb="*" path="*.asmx" />
  <add verb="*" path="*.layar" type="com.intellitack.layar.service.LayarGetPOIHttpHandler" />
</httpHandlers>
Update for ASP .NET 4.0


This configuration change allows all HTTP Request whose extension is *.layar to be forwarded to the LayarGetPOIHttpHandler.ProcessRequest shown below.

  public void ProcessRequest(HttpContext context)
        {
            //Get Request from HttpContext
            LayarRequest request = getRequestFromContext(context);

            //Process Request
            LayarResponse response = _layarApplication.processLayarRequest(request);

            //Write Response
            context.Response.Write(response.Serialize());
        }



Process request takes three actions:
  1. Converts the HTTP GET JSON request into a the LayarRequest Object.
  2. Calls the _layarApplications.processLayarRequest() passing the request object.
  3. Serializes the response your processLayarRequest() passed and returns it to the user.

Wiring your Layar Application into the Framework

There are 3 parts to the wiring:
  1. com.intellitack.layar.library.ILayarApplication and AbstractLayarApplication
  2. "LayarApplicationClass" key in <appSettings> of your <web.config>
  3. LayarGetPOIHttpHandler() constructor, LayarGetPOIHttpHandler.IsReusable = true and LayarGetPOIHttpHandler._layarApplication

Here's how they work together.

com.intellitack.layar.library.ILayarApplication and AbstractLayarApplication

ILayarApplication

The point of a Layar Application, is to receive a request from the Layar Server. Analyze the parameters on the request. And return a relevant set of POIs.

The com.intellitack.layar.labrary.ILayarApplication codifies the behavior in the aptly named processLayarRequest method.

    public interface ILayerApplication
    {
        LayarResponse processLayarRequest(LayarRequest request);
    }


The AbstractLayarApplication provides a base class that you can extend to implement your own behavior.

  public abstract class AbstractLayerApplication:ILayerApplication
    {
 
        public virtual LayarResponse processLayarRequest(LayarRequest request) 
        {
            throw new NotImplementedException();
        }
    }


While, I encourage you to extend AbstractLayarApplication, rather than implement ILayarApplication, the framework only requires an implementation of ILayarApplication. As utility functions emerge, they will be added to AbstractLayarApplication. If you choose not to extend AbstractLayarApplication, it may be difficult to leverage those functions. Of course, if those functions begin to suck, you can always break inheritance and subclass back to ILayarApplication.

"LayarApplicationClass" key in <appSettings> of your <web.config>

The appropriate ILayarApplication is dynamically loaded from your <web.config> via the "LayarApplicationClass" key.

  <appSettings >
    <add key="LayarApplicationClass" value="com.intellitack.layar.sample.SampleLayar, com.intellitack.layar.sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </appSettings>


Tip: Assembly Qualified Names
You'll notice the value is an Assembly Qualified Name, and since reflection is in the advanced section of most .NET programming books, here is a tip to finding it.

All classes in .NET inherit from System.Object. System.Object implements the GetType() method. The GetType() method returns a System.Type object. Finally, System.Type has a great method Type.AssemblyQualifiedName() which returns a string of the Assembly Qualified Name.

So, when you want to get the Assembly Qualified Name to plug into the "LayarApplicationClass" Setting. Stick this utility program into the Main() of a Console App.

 //Replace SampleLayar with your own implementation of AbstractLayarApplication
 SampleLayar mySampleLayarApp = new SampleLayar();
 Type myType = mySampleLayarApp.GetType();
 string assemblyQualifiedName = myType.AssemblyQualifiedName;


LayarGetPOIHttpHandler() constructor, LayarGetPOIHttpHandler.IsReusable = true and LayarGetPOIHttpHandler._layarApplication

And now it all wires together.

Let's look at the LayarGetPOIHttpHandler constructor and the _layarApplication private member variable.

public class LayarGetPOIHttpHandler:IHttpHandler
{
       ILayerApplication _layarApplication;

        #region Construction
        public LayarGetPOIHttpHandler() 
        {
            string targetType = ConfigurationManager.AppSettings.Get("LayarApplicationClass");
            Type layarType = Type.GetType(targetType);
            _layarApplication = Activator.CreateInstance(layarType) as ILayerApplication;
            if (_layarApplication == null) 
            {
                throw new ApplicationException("The type specified in the web.config was not an ILayarApplication");
            }

            
        }
        #endregion
   
        ....
}


You'll see the first thing we do is grab the aforementioned Assembly Qualified Name via the "LayarApplicationClass". We then dynamically create a Type via the Type.GetType() method. Finally, we use the Activator Class to create an instance of the type and cast it as a ILayarApplication.

If layarApplication is null, indicating: "LayarApplicationClass returned null" or the Type was not visible to the running assembly. We throw an application exception (which I'll make a ton more specific, when I'm done writing all this useful documentation)._

So, everything has a cost, and reflection is both complicated (thus the extensive docs) and expensive (in terms of CPU). Thus, we don't want to do this very much.

In order to minimize the number of times we instantiate the HttpHandler, we pool it by setting the LayarGetPOIHttpHandler.IsReusable to true.

public bool IsReusable
        {
            //To enable pooling, return true here.
            //This keeps the handler in memory.
            get { return true; }
        }


Tip: Be very, very, thread safe
So by setting, IsReusable() to true, it means other HttpRequests can use the handler. Which is cool, until you start accessing the member variables of your LayarApplication. The function variables get allocated on each call, so your okay there. Since, I can't say this enough, I'll do it again, and this time with styling. Don't access member variables of your LayarApplication unless you lock{} them. You are going to be wildly successful, and servicing millions of requests. Don't put request state in your LayarApplicaton Class

So, now we have a dynamically instantiated LayarApplication referenced by the _layarApplication private member. Another look at ProcessRequest, should bring it all together for you.

public void ProcessRequest(HttpContext context)
        {
            //Get Request from HttpContext
            LayarRequest request = getRequestFromContext(context);

            //Process Request
            LayarResponse response = _layarApplication.processLayarRequest(request);

            //Write Response
            context.Response.Write(response.Serialize());
        }


The Recap
  • com.intellitack.layar.library.ILayarApplication and AbstractLayarApplication
Is used to give us the polymorphism necessary to channel the LayarRequest down into your application.
  • "LayarApplicationClass" key in <appSettings> of your <web.config>
Is used to indicate the appropriate assembly.
  • LayarGetPOIHttpHandler() constructor, LayarGetPOIHttpHandler.IsReusable = true and LayarGetPOIHttpHandler._layarApplication
Is used to wire everything together. Instantiate the Layar App in the constructor. Set to IsReusable to only do this once (but be aware of threading implications). Leverage the _layarApplication to be a pointer to your code.

Trouble Shooting

Still can't get my LayarApplication to dynamically load
  • Ensure you are using the Assembly Qualified Name.
  • If you assembly is not signed, and not in the GAC, make sure you have a Reference to your project in the GetPOIService Project.

Appendex A: Why not use System.Web.Script.Serialization Namespace JavaScriptSerializer, doesn't it do JSON

Reference:
Well sort of. The biggest problem is the escape characters used in the Layars JSON. Specifically, the \/. DotNET also uses the '\' as an escape character, thus when JSON tries to serialize a string, you get all kinds of wierdness. It was much easier to write my own deserialization / serialization method. Then mess with JavaScriptConverter, JavaScriptSerializer and JavaScriptTypeResolver. I tried it and still couldn't make it work.

Last edited Mar 17, 2010 at 12:52 PM by mykoan, version 12

Comments

No comments yet.