/// <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); } }