/// <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
        }
Ejemplo n.º 2
0
 /// <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);
            }
        }