/// <summary>
        /// Process the web request.
        /// </summary>
        /// <param name="context">The context of this single web request.</param>
        public void ProcessRequest(HttpContext context)
        {
            UriTemplateMatch templateMatch = null;
            RestMethodInfo   rmi = null;
            ArrayList        finalParameters = null;
            Object           result = null, p;

            //
            // Initialization phase, register all handlers and then find a match.
            //
            try
            {
                //
                // Register all handlers.
                //
                RegisterHandlers();

                String baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.FilePath;
                rmi = FindHandler(context.Request.HttpMethod.ToUpper(), new Uri(baseUrl), context.Request.Url, ref templateMatch);
                if (rmi == null)
                {
                    throw new MissingMethodException();
                }
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception occurred at init: {0}", e.Message + e.StackTrace));

                return;
            }

            //
            // Parse out any parameters for the method call.
            //
            try
            {
                finalParameters = new ArrayList();

                //
                // Walk each parameter in the method and see if we can convert
                // one of the query variables to the proper type.
                //
                foreach (ParameterInfo pi in rmi.methodInfo.GetParameters())
                {
                    try
                    {
                        p = null;
                        if (typeof(Stream).IsAssignableFrom(pi.ParameterType))
                        {
                            p = context.Request.InputStream;
                        }
                        else if (templateMatch.BoundVariables.AllKeys.Contains(pi.Name.ToUpper()) == true)
                        {
                            p = templateMatch.BoundVariables[pi.Name.ToUpper()];
                            if (p != null)
                            {
                                if (typeof(List <String>).IsAssignableFrom(pi.ParameterType))
                                {
                                    p = p.ToString().Split(new char[1] {
                                        ','
                                    }).ToList <String>();
                                }
                                else
                                {
                                    p = Convert.ChangeType(p, pi.ParameterType);
                                }
                            }
                        }
                    }
                    catch
                    {
                        p = null;
                    }

                    finalParameters.Add(p);
                }
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception occurred at parameter parse: {0} at {1}", e.Message, e.StackTrace));

                return;
            }

            //
            // Force the context to be anonymous, then authenticate if the user
            // is calling a non-anonymous method.
            //
            try
            {
                ArenaContext.Current.SetWebServiceProperties(ArenaContext.Current.CreatePrincipal(""), new Arena.Core.Person());
                String PathAndQuery = String.Empty;

                if (rmi.uriTemplate.ToString() != "/version" &&
                    rmi.uriTemplate.ToString() != "/login" &&
                    rmi.uriTemplate.ToString() != "/help" &&
                    rmi.methodInfo.GetCustomAttributes(typeof(RestApiAnonymous), true).Length == 0 &&
                    rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length == 0)
                {
                    PathAndQuery = context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString());
                    PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower();
                    PathAndQuery = PathAndQuery.Substring(0, PathAndQuery.IndexOf("api_sig") - 1);

                    string apiSession = context.Request.Params["api_session"];

                    // Loop through non-production environments to determine where we are
                    bool checkForExpiration = true;
                    if (ConfigurationSettings.AppSettings["EnableAutomaticSessionExtension"] == "true")
                    {
                        foreach (String url in ConfigurationSettings.AppSettings["Environments"].Split(','))
                        {
                            if (HttpContext.Current.Request.Url.ToString().IndexOf(url) > -1)
                            {
                                checkForExpiration = false;
                            }
                        }
                    }
                    if (!checkForExpiration)
                    {
                        CoreApiSessionService.ExtendExpiration(apiSession);
                    }

                    AuthenticationManager.SetupSessionForRequest(apiSession, false);
                    AuthenticationManager.VerifySignature(context.Request.Url, PathAndQuery, apiSession);
                }
                else if (rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length != 0)
                {
                    PathAndQuery = context.Request.Path;
                    PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower();

                    string secret = String.Empty;

                    foreach (String element in context.Request.Form.AllKeys)
                    {
                        if (element != "api_sig" && element.IndexOf("btn_") == -1)
                        {
                            PathAndQuery += (PathAndQuery.IndexOf("?") > 0 ? "&" : "?");
                            PathAndQuery += element.Trim() + "=" + context.Request.Form[element];
                        }
                        if (element == "api_session")
                        {
                            CoreApiSessionService cass = new CoreApiSessionService();
                            secret = cass.GetSecretBySessionId(context.Request.Form[element]);
                        }
                    }

                    if (!String.IsNullOrEmpty(secret))
                    {
                        string sig = EncryptString(secret + "_" + PathAndQuery.ToLower());
                        if (sig.ToLower() != context.Request.Params["api_sig"].ToLower())
                        {
                            throw new Exception("Invalid signature. " + "Path: " + context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()) + "; Signatured string: " + secret + "_" + PathAndQuery.ToLower());
                        }
                    }
                    else
                    {
                        throw new Exception("Invalid session.");
                    }
                }
            }
            catch (Exception e)
            {
                RESTException restEx = e as RESTException;

                if (restEx != null)
                {
                    // result = new RestErrorMessage(System.Net.HttpStatusCode.Conflict, e.Message + " " + e.StackTrace, string.Empty);
                    result = new RestErrorMessage(restEx);
                }
                else
                {
                    result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty);
                }
            }

            //
            // Perform the actual method call.
            //
            if (result == null)
            {
                // NPM: Capture session and user id's for logging
                string sessionid = System.Web.HttpContext.Current.Request.Params["sessionid"];
                int?   personid  = PersonId();

                try
                {
                    //
                    // Set some default response information.
                    //
                    context.Response.ContentType = "application/xml; charset=utf-8";

                    if (TypeIsServiceContract(rmi.instance.GetType()) == true)
                    {
                        //
                        // Run the request inside of a operation context so response information
                        // can be set. This is a bit of a cheat, but it works.
                        //
                        WebChannelFactory <NoOp> factory = new WebChannelFactory <NoOp>(new Uri("http://localhost/"));
                        NoOp channel = factory.CreateChannel();
                        using (new OperationContextScope((IContextChannel)channel))
                        {
                            result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object)));
                            if (WebOperationContext.Current.OutgoingResponse.ContentType != null)
                            {
                                context.Response.ContentType = WebOperationContext.Current.OutgoingResponse.ContentType;
                            }
                        }
                    }
                    else
                    {
                        //
                        // This is a standard method call, just call it.
                        //
                        result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object)));
                    }

                    // NPM: Log the call as a SUCCESS
                    ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "SUCCESS", "");
                }
                catch (Exception e)
                {
                    // NPM: Log the call as a FAILURE
                    ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "FAILURE", e.InnerException.Message);

                    RESTException restEx;

                    if (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }

                    restEx = e as RESTException;
                    if (restEx != null)
                    {
                        result = new RestErrorMessage(restEx);
                    }
                    else
                    {
                        result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty);
                    }
                }
            }

            //
            // Deal with the response that was generated.
            //
            try
            {
                if (result != null)
                {
                    //
                    // There is probably a better way to do this, but this is the best
                    // I can come up with. Somebody feel free to make this cleaner.
                    //
                    if (typeof(Stream).IsAssignableFrom(result.GetType()) == true)
                    {
                        Stream s = (Stream)result;
                        int    count;

                        //
                        // Response is a data stream, just copy it to the response
                        // stream.
                        //
                        do
                        {
                            byte[] buf = new byte[8192];

                            count = s.Read(buf, 0, 8192);
                            context.Response.BinaryWrite(buf);
                        } while (count > 0);
                    }
                    else if (typeof(Message).IsAssignableFrom(result.GetType()) == true)
                    {
                        Message       msg = (Message)result;
                        StringBuilder sb  = new StringBuilder();
                        StringWriter  sw  = new StringWriter(sb);
                        XmlTextWriter xtw = new XmlTextWriter(sw);

                        //
                        // Response is a Message object. Write it out as an XML
                        // stream.
                        //
                        msg.WriteMessage(xtw);
                        context.Response.Write(sb.ToString());
                    }
                    else
                    {
                        DataContractSerializer serializer = new DataContractSerializer(result.GetType());

                        //
                        // Otherwise, use the DataContractSerializer to convert the object into
                        // an XML stream.
                        //
                        serializer.WriteObject(context.Response.OutputStream, result);
                    }
                }
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception sending response: {0}", e.Message));
                return;
            }
        }
        /// <summary>
        /// Process the web request.
        /// </summary>
        /// <param name="context">The context of this single web request.</param>
        public void ProcessRequest(HttpContext context)
        {
            UriTemplateMatch templateMatch = null;
            RestMethodInfo rmi = null;
            ArrayList finalParameters = null;
            Object result = null, p;

            //
            // Initialization phase, register all handlers and then find a match.
            //
            try
            {
                //
                // Register all handlers.
                //
                RegisterHandlers();

                String baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.FilePath;
                rmi = FindHandler(context.Request.HttpMethod.ToUpper(), new Uri(baseUrl), context.Request.Url, ref templateMatch);
                if (rmi == null)
                    throw new MissingMethodException();
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception occurred at init: {0}", e.Message + e.StackTrace));

                return;
            }

            //
            // Parse out any parameters for the method call.
            //
            try
            {
                finalParameters = new ArrayList();

                //
                // Walk each parameter in the method and see if we can convert
                // one of the query variables to the proper type.
                //
                foreach (ParameterInfo pi in rmi.methodInfo.GetParameters())
                {
                    try
                    {
                        p = null;
                        if (typeof(Stream).IsAssignableFrom(pi.ParameterType))
                        {
                            p = context.Request.InputStream;
                        }
                        else if (templateMatch.BoundVariables.AllKeys.Contains(pi.Name.ToUpper()) == true)
                        {
                            p = templateMatch.BoundVariables[pi.Name.ToUpper()];
                            if (p != null)
                            {
                                if (typeof(List<String>).IsAssignableFrom(pi.ParameterType))
                                {
                                    p = p.ToString().Split(new char[1] { ',' }).ToList<String>();
                                }
                                else
                                    p = Convert.ChangeType(p, pi.ParameterType);
                            }
                        }
                    }
                    catch
                    {
                        p = null;
                    }

                    finalParameters.Add(p);
                }
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception occurred at parameter parse: {0} at {1}", e.Message, e.StackTrace));

                return;
            }

            //
            // Force the context to be anonymous, then authenticate if the user
            // is calling a non-anonymous method.
            //
            try
            {
                ArenaContext.Current.SetWebServiceProperties(ArenaContext.Current.CreatePrincipal(""), new Arena.Core.Person());
                String PathAndQuery = String.Empty;

                if (rmi.uriTemplate.ToString() != "/version" &&
                    rmi.uriTemplate.ToString() != "/login" &&
                    rmi.uriTemplate.ToString() != "/help" &&
                    rmi.methodInfo.GetCustomAttributes(typeof(RestApiAnonymous), true).Length == 0 &&
                    rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length == 0)
                {
                    PathAndQuery = context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString());
                    PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower();
                    PathAndQuery = PathAndQuery.Substring(0, PathAndQuery.IndexOf("api_sig") - 1);

                    string apiSession = context.Request.Params["api_session"];

                    // Loop through non-production environments to determine where we are
                    bool checkForExpiration = true;
                    if (ConfigurationSettings.AppSettings["EnableAutomaticSessionExtension"] == "true")
                    {
                        foreach (String url in ConfigurationSettings.AppSettings["Environments"].Split(','))
                        {
                            if (HttpContext.Current.Request.Url.ToString().IndexOf(url) > -1)
                            {
                                checkForExpiration = false;
                            }
                        }
                    }
                    if (!checkForExpiration)
                    {
                        CoreApiSessionService.ExtendExpiration(apiSession);
                    }

                    AuthenticationManager.SetupSessionForRequest(apiSession, false);
                    AuthenticationManager.VerifySignature(context.Request.Url, PathAndQuery, apiSession);
                }
                else if (rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length != 0)
                {
                    PathAndQuery = context.Request.Path;
                    PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower();

                    string secret = String.Empty;

                    foreach (String element in context.Request.Form.AllKeys)
                    {
                        if (element != "api_sig" && element.IndexOf("btn_") == -1)
                        {
                            PathAndQuery += (PathAndQuery.IndexOf("?") > 0 ? "&" : "?");
                            PathAndQuery += element.Trim() + "=" + context.Request.Form[element];
                        }
                        if (element == "api_session")
                        {
                            CoreApiSessionService cass = new CoreApiSessionService();
                            secret = cass.GetSecretBySessionId(context.Request.Form[element]);
                        }
                    }

                    if (!String.IsNullOrEmpty(secret))
                    {
                        string sig = EncryptString(secret + "_" + PathAndQuery.ToLower());
                        if (sig.ToLower() != context.Request.Params["api_sig"].ToLower())
                        {
                            throw new Exception("Invalid signature. " + "Path: " + context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()) + "; Signatured string: " + secret + "_" + PathAndQuery.ToLower());
                        }
                    }
                    else
                    {
                        throw new Exception("Invalid session.");
                    }
                }
            }
            catch (Exception e)
            {
                RESTException restEx = e as RESTException;

                if (restEx != null)
                {
                    // result = new RestErrorMessage(System.Net.HttpStatusCode.Conflict, e.Message + " " + e.StackTrace, string.Empty);
                    result = new RestErrorMessage(restEx);
                }
                else
                {
                    result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty);
                }
            }

            //
            // Perform the actual method call.
            //
            if (result == null)
            {
                // NPM: Capture session and user id's for logging
                string sessionid = System.Web.HttpContext.Current.Request.Params["sessionid"];
                int? personid = PersonId();

                try
                {
                    //
                    // Set some default response information.
                    //
                    context.Response.ContentType = "application/xml; charset=utf-8";

                    if (TypeIsServiceContract(rmi.instance.GetType()) == true)
                    {
                        //
                        // Run the request inside of a operation context so response information
                        // can be set. This is a bit of a cheat, but it works.
                        //
                        WebChannelFactory<NoOp> factory = new WebChannelFactory<NoOp>(new Uri("http://localhost/"));
                        NoOp channel = factory.CreateChannel();
                        using (new OperationContextScope((IContextChannel)channel))
                        {
                            result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object)));
                            if (WebOperationContext.Current.OutgoingResponse.ContentType != null)
                                context.Response.ContentType = WebOperationContext.Current.OutgoingResponse.ContentType;
                        }
                    }
                    else
                    {
                        //
                        // This is a standard method call, just call it.
                        //
                        result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object)));
                    }

                    // NPM: Log the call as a SUCCESS
                    ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "SUCCESS", "");
                }
                catch (Exception e)
                {
                    // NPM: Log the call as a FAILURE
                    ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "FAILURE", e.InnerException.Message);

                    RESTException restEx;

                    if (e.InnerException != null)
                        e = e.InnerException;

                    restEx = e as RESTException;
                    if (restEx != null)
                    {
                        result = new RestErrorMessage(restEx);
                    }
                    else
                    {
                        result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty);
                    }
                }
            }

            //
            // Deal with the response that was generated.
            //
            try
            {
                if (result != null)
                {
                    //
                    // There is probably a better way to do this, but this is the best
                    // I can come up with. Somebody feel free to make this cleaner.
                    //
                    if (typeof(Stream).IsAssignableFrom(result.GetType()) == true)
                    {
                        Stream s = (Stream)result;
                        int count;

                        //
                        // Response is a data stream, just copy it to the response
                        // stream.
                        //
                        do
                        {
                            byte[] buf = new byte[8192];

                            count = s.Read(buf, 0, 8192);
                            context.Response.BinaryWrite(buf);
                        } while (count > 0);
                    }
                    else if (typeof(Message).IsAssignableFrom(result.GetType()) == true)
                    {
                        Message msg = (Message)result;
                        StringBuilder sb = new StringBuilder();
                        StringWriter sw = new StringWriter(sb);
                        XmlTextWriter xtw = new XmlTextWriter(sw);

                        //
                        // Response is a Message object. Write it out as an XML
                        // stream.
                        //
                        msg.WriteMessage(xtw);
                        context.Response.Write(sb.ToString());

                    }
                    else
                    {
                        DataContractSerializer serializer = new DataContractSerializer(result.GetType());

                        //
                        // Otherwise, use the DataContractSerializer to convert the object into
                        // an XML stream.
                        //
                        serializer.WriteObject(context.Response.OutputStream, result);
                    }
                }
            }
            catch (Exception e)
            {
                context.Response.Write(String.Format("Exception sending response: {0}", e.Message));
                return;
            }
        }