/// <summary> /// The initialize template tables. /// </summary> /// <exception cref="ValidationException"> /// The workflow is invalid /// </exception> private void InitializeTemplateTables() { try { // Cache definitions so we don't run CacheMetadata more than once. if (!UriTemplateTables.ContainsKey(this.Activity)) { WorkflowInspectionServices.CacheMetadata(this.Activity); UriTemplateTables.Add(this.Activity, new List <UriTemplateTable>()); foreach (var uriTemplateTable in this.BaseAddresses.Select(baseAddress => new UriTemplateTable(baseAddress))) { UriTemplateTables[this.Activity].Add(uriTemplateTable); this.LocateHttpReceiveActivities(this.Activity, uriTemplateTable); // No UriTemplates in this activity if (uriTemplateTable.KeyValuePairs.Count == 0) { throw new ValidationException( "Activity must contain at least one HttpReceive activity with a valid Uri template"); } } } } #if DEBUG catch (Exception ex) { HttpExceptionHelper.WriteThread(ex.Message); throw; } #endif }
/// <summary> /// The wait for unload. /// </summary> /// <param name="timeout"> /// The timeout. /// </param> /// <exception cref="TimeoutException"> /// The unload event did not occur within the timeout /// </exception> public void WaitForUnload(TimeSpan timeout) { if (!this.unloadEvent.WaitOne(timeout)) { HttpExceptionHelper.WriteThread( "Timeout waiting for workflow to unload"); throw new TimeoutException(); } }
/// <summary> /// The on idle. /// </summary> /// <param name="args"> /// The args. /// </param> private void OnIdle(WorkflowApplicationIdleEventArgs args) { HttpExceptionHelper.WriteThread( "OnWorkflowIdle waiting for bookmark {0} instance Id {1} with bookmarks {2}", this.pendingBookmark, args.InstanceId, args.BookmarksToDelimitedList()); this.Idle = true; if (args.ContainsBookmark(this.pendingBookmark)) { this.pendingBookmark = null; this.protocolEvent.Set(); } }
/// <summary> /// The unload if idle. /// </summary> /// <param name="state"> /// The state. /// </param> private void UnloadIfIdle(object state) { if (this.idle) { HttpExceptionHelper.WriteThread( "Unloading Idle Instance {0} at {1}", this.workflowApplication.Id, DateTime.Now.ToString("mm:ss")); if (this.serviceHost.OnUnload != null) { // The host may decide it does not want to unload this instance. if (!this.serviceHost.OnUnload(this.workflowApplication)) { HttpExceptionHelper.WriteThread( "Host OnUnload returned false for Instance {0}", this.workflowApplication.Id); return; } } // Once unloaded you cannot use the instance again WorkflowApplicationCache.Remove(this.workflowApplication.Id); this.workflowApplication.Unload(); } }
/// <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); } }