/// <summary>
        /// The run until bookmark.
        /// </summary>
        /// <param name="bookmarkName">
        /// The bookmark name.
        /// </param>
        /// <exception cref="HttpResponseException">
        /// Timeout while waiting for bookmark
        /// </exception>
        private void RunUntilBookmark(string bookmarkName)
        {
            this.pendingBookmark = bookmarkName;
            this.workflowApplication.Run();

            if (!this.protocolEvent.WaitOne(this.serviceHost.WorkflowTimeout))
            {
                throw HttpExceptionHelper.CreateResponseException(
                          HttpStatusCode.InternalServerError,
                          "Timeout waiting for idle with bookmark {0}",
                          this.pendingBookmark);
            }
        }
        /// <summary>
        /// The select matching receive activity.
        /// </summary>
        /// <returns>
        /// </returns>
        private HttpReceive SelectMatchingReceiveActivity()
        {
            var match = this.serviceHost.MatchSingle(this.Request);

            if (match == null)
            {
                HttpExceptionHelper.CreateResponseException(
                    HttpStatusCode.NotFound,
                    "No Http Receive activity found with Uri template matching Uri {0}",
                    this.Request.RequestUri);
            }

            return(match != null ? (HttpReceive)match.Data : null);
        }
        /// <summary>
        /// Delivers the Http message to the receive activity and waits until the receive is closed before returning
        /// </summary>
        /// <param name="bookmarkName">
        /// The name of the bookmark to wait for
        /// </param>
        /// <param name="receive">
        /// The HttpReceive activity to deliver to
        /// </param>
        private void DeliverMessage(string bookmarkName, HttpReceive receive)
        {
            HttpExceptionHelper.WriteThread(
                "Delivering message to HttpReceive activity {0} ID {1} \"{2}\"",
                bookmarkName,
                receive.Id,
                receive.DisplayName);
            var timeLeft   = this.serviceHost.WorkflowTimeout;
            var retryMsecs = 10;

            while (timeLeft > TimeSpan.Zero)
            {
                // When resuming, the workflow might not be ready
                var result = this.workflowApplication.ResumeBookmark(bookmarkName, this);
                switch (result)
                {
                case BookmarkResumptionResult.Success:
                    if (!this.responseEvent.WaitOne(timeLeft))
                    {
                        throw HttpExceptionHelper.CreateResponseException(
                                  HttpStatusCode.InternalServerError,
                                  "Workflow timeout while waiting for response for {0}",
                                  bookmarkName);
                    }

                    // If the workflow terminated with an exception throw it
                    if (this.terminationException != null)
                    {
                        throw this.terminationException;
                    }

                    return;

                case BookmarkResumptionResult.NotFound:
                    throw HttpExceptionHelper.CreateResponseException(
                              HttpStatusCode.InternalServerError,
                              "Workflow was not waiting for bookmark {0}",
                              bookmarkName);

                case BookmarkResumptionResult.NotReady:
                    HttpExceptionHelper.WriteThread(
                        "Workflow Instance {0} was not ready to receive bookmark {1} retrying...",
                        bookmarkName,
                        this.workflowApplication.Id);

                    // The workflow was not ready to receive the resumption
                    Thread.Sleep(retryMsecs);
                    timeLeft   = timeLeft - TimeSpan.FromMilliseconds(retryMsecs);
                    retryMsecs = retryMsecs * 2;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            throw HttpExceptionHelper.CreateResponseException(
                      HttpStatusCode.InternalServerError,
                      "Unable to deliver message to Workflow ID {0} for bookmark {1}",
                      this.workflowApplication.Id,
                      bookmarkName);
        }
        public HttpResponseMessage InvokeWorkflow()
        {
#if DEBUG
            this.testName = this.Request.Headers.GetHeaderValue("TestName");
            this.testInfo = this.Request.Headers.GetHeaderValue("TestInfo");
            HttpExceptionHelper.WriteThread(
                "HttpResponseMessage InvokeWorkflow() message received {0} {1} Test: \"{2}\" Info:{3}",
                this.Request.Method,
                this.Request.RequestUri,
                this.testName,
                this.testInfo);
#endif

            // Step 1 - locate a receive activity that matches the Uri template
            // This will throw if no matching activity is found
            var receive = this.SelectMatchingReceiveActivity();

            if (receive == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            // Step 2 - Load or Create a workflow instance
            this.LoadOrCreateWorkflowInstance();

            HttpExceptionHelper.WriteThread(
                "Trying to Lock instance {0} {1} {2} Test: \"{3}\" Info:{4}",
                this.workflowApplication.Id,
                this.Request.Method,
                this.Request.RequestUri,
                this.testName,
                this.testInfo);
            if (!Monitor.TryEnter(this.workflowApplication, this.ServiceHost.WorkflowTimeout))
            {
                throw HttpExceptionHelper.CreateResponseException(
                          HttpStatusCode.InternalServerError,
                          "Timeout waiting for lock on instance {0}",
                          this.workflowApplication.Id);
            }

            try
            {
                HttpExceptionHelper.WriteThread(
                    "Successfully Locked instance {0} {1} {2} Test: \"{3}\" Info:{4}",
                    this.workflowApplication.Id,
                    this.Request.Method,
                    this.Request.RequestUri,
                    this.testName,
                    this.testInfo);

                // There may be a bookmark pending for this receive activity
                var bookmarkName =
                    receive.GetBookmarkName(this.serviceHost.GetMatchingBaseAddress(this.Request.RequestUri));

                // If the instance does not have the bookmark, create and run it
                if (!this.workflowApplication.ContainsBookmark(bookmarkName))
                {
                    // This will run the workflow until it becomes idle with the named bookmark
                    // Or it aborts, times out or completes
                    this.RunUntilBookmark(bookmarkName);
                }

                // Deliver the message to the receive activity by resuming the bookmark
                this.DeliverMessage(bookmarkName, receive);

                // The receive activity may or may not set a response
                HttpExceptionHelper.WriteThread("Returning response {0}", this.Response);

                // TODO: This makes IQueryable not work - can we return a response as an object?
                // If we do return an object, what if it is already an HttpResponse?
                return(this.Response);
            }
            finally
            {
                HttpExceptionHelper.WriteThread(
                    "Unlocking instance {0} {1} {2} Test: \"{3}\" Info:{4}",
                    this.workflowApplication.Id,
                    this.Request.Method,
                    this.Request.RequestUri,
                    this.testName,
                    this.testInfo);
                Monitor.Exit(this.workflowApplication);
            }
        }