Inheritance: ICloneable
        /// <summary>
        /// Run a workflow step
        /// </summary>
        /// <param name="workflowMessage">Message to pass to the step class</param>
        /// <param name="retryStepTimes">Times to retry on error</param>
        /// <param name="workflowStep">The step to run</param>
        internal void RunFrameworkStep(IWorkflowMessage workflowMessage, int retryStepTimes, ProcessorStep workflowStep, ProcessorJob currentJob, bool isCheckDepends)
        {
            bool impersonated = false;
            if (!string.IsNullOrEmpty(workflowStep.RunAsDomain) && !string.IsNullOrEmpty(workflowStep.RunAsUser) && !string.IsNullOrEmpty(workflowStep.RunAsPassword) && workflowStep.RunMode == FrameworkStepRunMode.STA)
                impersonated = impersonate.ImpersonateValidUser(workflowStep.RunAsUser, workflowStep.RunAsDomain, workflowStep.RunAsPassword);
            using (IStep step = StepFactory.GetStep(workflowStep.InvokeClass))
            {
                try
                {
                    workflowStep.RunStatus = FrameworkStepRunStatus.Loaded;
                    if (isCheckDepends)
                        WaitForDependents(workflowStep, currentJob);
                    workflowStep.StartDate = DateTime.Now;
                    step.RunStep(workflowMessage);
                    workflowStep.EndDate = DateTime.Now;
                    workflowStep.RunStatus = FrameworkStepRunStatus.Complete;
                    workflowStep.ExitMessage = "Complete";
                }
                catch (Exception e)
                {
                    workflowStep.RunStatus = FrameworkStepRunStatus.CompleteWithErrors;
                    workflowStep.ExitMessage = e.Message;
                    while (e.InnerException != null)
                    {
                        e = e.InnerException;
                        workflowStep.ExitMessage += '|' + e.Message;
                    }
                    switch (workflowStep.OnError)
                    {
                        case OnFrameworkStepError.RetryStep:
                            if (workflowStep.WaitBetweenRetriesMilliseconds > 0)
                                Thread.Sleep(workflowStep.WaitBetweenRetriesMilliseconds);
                            // Try the step again
                            retryStepTimes++;
                            if (retryStepTimes <= workflowStep.RetryTimes)
                                RunFrameworkStep(workflowMessage, retryStepTimes, workflowStep, currentJob, isCheckDepends);
                            break;
                        default:
                            // If this is running from a thread, do not throw the error up
                            if (workflowStep.RunMode == FrameworkStepRunMode.STA)
                                throw e;
                            else
                                Processor.ReportJobError(e, workflowStep, workflowMessage, currentJob);
                            break;
                    }
                }
            }

            if (impersonated)
                impersonate.UndoImpersonation();
        }
        /// <summary>
        /// Load the framework config from workflow.xml
        /// </summary>
        internal static void LoadFrameworkConfig(ProcessorJob processorJob)
        {
            XmlDocument doc = new XmlDocument();
            XmlTextReader reader = new XmlTextReader(EngineConfiguration.FrameworkConfigLocation);
            doc.Load(reader);
            reader.Close();
            // Load the job
            XmlNode jobNode = doc.SelectSingleNode("//Job[@Name='" + processorJob.JobName + "']");
            processorJob.MessageClass = jobNode.Attributes["MessageClass"].Value;
            if (jobNode.Attributes["MessageQueue"] != null && !string.IsNullOrEmpty(jobNode.Attributes["MessageQueue"].Value))
                processorJob.MessageQueue = jobNode.Attributes["MessageQueue"].Value;
            if (jobNode.Attributes["ErrorQueue"] != null && !string.IsNullOrEmpty(jobNode.Attributes["ErrorQueue"].Value))
                processorJob.ErrorQueue = jobNode.Attributes["ErrorQueue"].Value;
            if (jobNode.Attributes["PoisonQueue"] != null && !string.IsNullOrEmpty(jobNode.Attributes["PoisonQueue"].Value))
                processorJob.PoisonQueue = jobNode.Attributes["PoisonQueue"].Value;
            if (jobNode.Attributes["CompletedQueue"] != null && !string.IsNullOrEmpty(jobNode.Attributes["CompletedQueue"].Value))
                processorJob.CompletedQueue = jobNode.Attributes["CompletedQueue"].Value;
            if (jobNode.Attributes["NotifyComplete"] != null && !string.IsNullOrEmpty(jobNode.Attributes["NotifyComplete"].Value))
            {
                bool notifyComplete = false;
                if (bool.TryParse(jobNode.Attributes["NotifyComplete"].Value, out notifyComplete))
                    processorJob.NotifyComplete = notifyComplete;
            }

            if (jobNode.Attributes["MaxRunTimeMilliseconds"] != null && !string.IsNullOrEmpty(jobNode.Attributes["MaxRunTimeMilliseconds"].Value))
            {
                int maxRunTimeMilliseconds = 0;
                if (int.TryParse(jobNode.Attributes["MaxRunTimeMilliseconds"].Value, out maxRunTimeMilliseconds))
                    processorJob.MaxRunTimeMilliseconds = maxRunTimeMilliseconds;
            }
            if (jobNode.Attributes["MessageQueueType"] != null && !string.IsNullOrEmpty(jobNode.Attributes["MessageQueueType"].Value))
                processorJob.MessageQueueType = (MessageQueueType)Enum.Parse(typeof(MessageQueueType), jobNode.Attributes["MessageQueueType"].Value, true);
            //Load the Queues
            XmlNode queuesNode = jobNode.SelectSingleNode("//Job[@Name='" + processorJob.JobName + "']/Queues");
            if (queuesNode != null)
                LoadJobQueues(processorJob, queuesNode);
            //Load the steps
            XmlNode stepsNode = jobNode.SelectSingleNode("//Job[@Name='" + processorJob.JobName + "']/Steps");
            if (stepsNode != null)
                LoadJobSteps(processorJob, stepsNode);
            else // For backwards compatibility
                LoadJobSteps(processorJob, jobNode);
        }
        /// <summary>
        /// Load the Job's Queues
        /// </summary>
        /// <param name="processorJob">The ProcessorJob to load to</param>
        /// <param name="queuesNode">The XmlNode to load from</param>
        private static void LoadJobQueues(ProcessorJob processorJob, XmlNode queuesNode)
        {
            foreach (XmlNode queueNode in queuesNode.ChildNodes)
            {
                ProcessorQueue processorQueue = new ProcessorQueue();

                if (!string.IsNullOrEmpty(queueNode.Attributes["MessageQueue"].Value)) //Required
                    processorQueue.MessageQueue = queueNode.Attributes["MessageQueue"].Value;

                if (!string.IsNullOrEmpty(queueNode.Attributes["ErrorQueue"].Value)) //Required
                    processorQueue.ErrorQueue = queueNode.Attributes["ErrorQueue"].Value;

                if (!string.IsNullOrEmpty(queueNode.Attributes["PoisonQueue"].Value)) //Required
                    processorQueue.PoisonQueue = queueNode.Attributes["PoisonQueue"].Value;

                if (!string.IsNullOrEmpty(queueNode.Attributes["CompletedQueue"].Value)) //Required
                    processorQueue.CompletedQueue = queueNode.Attributes["CompletedQueue"].Value;

                if (!string.IsNullOrEmpty(queueNode.Attributes["MessageQueueType"].Value)) //Required
                    processorQueue.MessageQueueType = (MessageQueueType)Enum.Parse(typeof(MessageQueueType), queueNode.Attributes["MessageQueueType"].Value, true);

                processorJob.ProcessorQueues.Add(processorQueue);
            }
        }
        /// <summary>
        /// Block untill all steps and groups this step depends on have finished
        /// </summary>
        /// <param name="workflowStep"></param>
        private void WaitForDependents(ProcessorStep workflowStep, ProcessorJob currentJob)
        {
            if (string.IsNullOrEmpty(workflowStep.DependsOn) &&
                string.IsNullOrEmpty(workflowStep.DependsOnGroup))
                return;

            var result = from ProcessorStep in currentJob.WorkFlowSteps
                         where ProcessorStep.RunStatus != FrameworkStepRunStatus.Complete &&
                         (workflowStep.DependsOn.Split(',').Contains(ProcessorStep.StepName) ||
                         workflowStep.DependsOnGroup.Split(',').Contains(ProcessorStep.Group))
                         select ProcessorStep;
            if (result.Count() > 0) //There are still steps to wait for
            {
                Thread.Sleep(100);
                workflowStep.RunStatusTime += 100;
                if (workflowStep.RunStatusTime <= workflowStep.WaitForDependsOnMilliseconds)
                    WaitForDependents(workflowStep, currentJob);
            }
        }
        /// <summary>
        /// Load the Job's steps
        /// </summary>
        /// <param name="processorJob">The ProcessorJob to load to</param>
        /// <param name="queuesNode">The XmlNode to load from</param>
        private static void LoadJobSteps(ProcessorJob processorJob, XmlNode stepsNode)
        {
            foreach (XmlNode stepNode in stepsNode.ChildNodes)
            {
                ProcessorStep workFlowStep = new ProcessorStep();

                if (!string.IsNullOrEmpty(stepNode.Attributes["Name"].Value)) //Required
                    workFlowStep.StepName = stepNode.Attributes["Name"].Value;
                if (!string.IsNullOrEmpty(stepNode.Attributes["Group"].Value)) //Required
                    workFlowStep.Group = stepNode.Attributes["Group"].Value;
                if (!string.IsNullOrEmpty(stepNode.Attributes["InvokeClass"].Value)) //Required
                    workFlowStep.InvokeClass = stepNode.Attributes["InvokeClass"].Value;
                if (stepNode.Attributes["OnError"] != null && !string.IsNullOrEmpty(stepNode.Attributes["OnError"].Value))
                    workFlowStep.OnError = (OnFrameworkStepError)Enum.Parse(typeof(OnFrameworkStepError), stepNode.Attributes["OnError"].Value, true);
                if (stepNode.Attributes["RetryTimes"] != null && !string.IsNullOrEmpty(stepNode.Attributes["RetryTimes"].Value))
                {
                    int retryTimes = 0;
                    int.TryParse(stepNode.Attributes["RetryTimes"].Value, out retryTimes);
                    workFlowStep.RetryTimes = retryTimes;
                }
                if (stepNode.Attributes["WaitBetweenRetriesMilliseconds"] != null && !string.IsNullOrEmpty(stepNode.Attributes["WaitBetweenRetriesMilliseconds"].Value))
                {
                    int waitBetweenRetriesMilliseconds = 0;
                    int.TryParse(stepNode.Attributes["WaitBetweenRetriesMilliseconds"].Value, out waitBetweenRetriesMilliseconds);
                    workFlowStep.WaitBetweenRetriesMilliseconds = waitBetweenRetriesMilliseconds;
                }
                if (stepNode.Attributes["RunMode"] != null && !string.IsNullOrEmpty(stepNode.Attributes["RunMode"].Value))
                    workFlowStep.RunMode = (FrameworkStepRunMode)Enum.Parse(typeof(FrameworkStepRunMode), stepNode.Attributes["RunMode"].Value, true);
                if (stepNode.Attributes["DependsOn"] != null && !string.IsNullOrEmpty(stepNode.Attributes["DependsOn"].Value))
                    workFlowStep.DependsOn = stepNode.Attributes["DependsOn"].Value;
                if (stepNode.Attributes["DependsOnGroup"] != null && !string.IsNullOrEmpty(stepNode.Attributes["DependsOnGroup"].Value))
                    workFlowStep.DependsOnGroup = stepNode.Attributes["DependsOnGroup"].Value;
                if (stepNode.Attributes["WaitForDependsOnMilliseconds"] != null && !string.IsNullOrEmpty(stepNode.Attributes["WaitForDependsOnMilliseconds"].Value))
                {
                    int waitForDependsOnMilliseconds = int.MaxValue;
                    if (!string.IsNullOrEmpty(stepNode.Attributes["WaitForDependsOnMilliseconds"].Value))
                        int.TryParse(stepNode.Attributes["WaitForDependsOnMilliseconds"].Value, out waitForDependsOnMilliseconds);
                    workFlowStep.WaitForDependsOnMilliseconds = waitForDependsOnMilliseconds;
                }
                if (stepNode.Attributes["RunAsDomain"] != null && !string.IsNullOrEmpty(stepNode.Attributes["RunAsDomain"].Value))
                    workFlowStep.RunAsDomain = Utils.GetDecryptedString(stepNode.Attributes["RunAsDomain"].Value);
                if (stepNode.Attributes["RunAsUser"] != null && !string.IsNullOrEmpty(stepNode.Attributes["RunAsUser"].Value))
                    workFlowStep.RunAsUser = Utils.GetDecryptedString(stepNode.Attributes["RunAsUser"].Value);
                if (stepNode.Attributes["RunAsPassword"] != null && !string.IsNullOrEmpty(stepNode.Attributes["RunAsPassword"].Value))
                    workFlowStep.RunAsPassword = Utils.GetDecryptedString(stepNode.Attributes["RunAsPassword"].Value);
                processorJob.WorkFlowSteps.Add(workFlowStep);
            }
        }
        /// <summary>
        /// Reports a job error
        /// </summary>
        /// <param name="e">The exception to report</param>
        /// <param name="step">The step the exception accrued</param>
        /// <param name="workflowMessage">The original message pulled from the queue</param>
        public static void ReportJobError(Exception e, ProcessorStep workflowStep, IWorkflowMessage workflowMessage, ProcessorJob currentJob)
        {
            // Push an error message to the error Queue
            WorkflowErrorMessage errorMessage = new WorkflowErrorMessage() { ExceptionMessage = e.Message, JobName = currentJob.JobName, StepName = workflowStep.StepName };

            //if e contains inner exceptions, add those messages
            while (e.InnerException != null)
            {
                //assign e to the inner exception - recursive
                e = e.InnerException;
                errorMessage.ExceptionMessage += '|' + e.Message;
            }

            FrameworkManager.AddFrameworkError(currentJob, workflowMessage, errorMessage);
        }
 /// <summary>
 /// This method starts the framework and blocks.
 /// Call this from an exe or a windows service on a new thread.
 /// </summary>
 /// <param name="jobName">The workflow job name</param>
 public void StartFramework(string jobName)
 {
     processorJob = new ProcessorJob();
     processorJob.JobName = jobName;
     pool = new Semaphore(EngineConfiguration.FrameworkMaxThreads, EngineConfiguration.FrameworkMaxThreads);
     JobsRunning = 0;
     // Load the config file and start processing jobs
     WorkflowConfiguration.LoadFrameworkConfig(processorJob);
     FrameworkOn = true;
     LoadActiveQueue();
     ProcessorQueue.ProcessorQueueChanged += new ProcessorQueue.ProcessorQueueEventHandler(ProcessorQueue_ProcessorQueueChanged);
     executor.ProcessorJob = processorJob;
     RunFramework(5, 0, 5);
     FrameworkOn = false;
 }
 public static void ReportJobComplete(IWorkflowMessage workflowMessage, ProcessorJob currentJob)
 {
     if (currentJob.NotifyComplete &&
         currentJob.WorkFlowSteps.Where(s => s.RunStatus == FrameworkStepRunStatus.Complete).Count() == currentJob.WorkFlowSteps.Count())
         FrameworkManager.AddFrameworkJobComplete(currentJob, workflowMessage);
 }
 /// <summary>
 /// Add a job for the framework to process
 /// </summary>
 /// <param name="jobName">The name of the job in the framework's workflow file</param>
 /// <param name="message">A class containing the message data</param>
 public static void AddFrameworkJob(string jobName, IWorkflowMessage message)
 {
     // Add a message to the Queue
     ProcessorJob processorJob = new ProcessorJob() { JobName = jobName, CreatedDate = DateTime.Now };
     WorkflowConfiguration.LoadFrameworkConfig(processorJob);
     ProcessorQueue processorQueue = GetActiveQueue(processorJob, QueueOperationType.Delivery);
     MessageQueue workflowQueue = new MessageQueue(processorQueue.MessageQueue);
     MessageQueueTransaction transaction = new MessageQueueTransaction();
     try
     {
         if (processorQueue.MessageQueueType == MessageQueueType.Transactional)
         {
             transaction.Begin();
             workflowQueue.Send(message, jobName, transaction);
             transaction.Commit();
         }
         else
         {
             workflowQueue.Send(message, jobName);
         }
     }
     catch (Exception e)
     {
         if (processorQueue.MessageQueueType == MessageQueueType.Transactional &&
             transaction.Status == MessageQueueTransactionStatus.Pending)
             transaction.Abort();
         throw new WorkflowException("Error adding message to Queue", e);
     }
     finally
     {
         transaction.Dispose();
         workflowQueue.Dispose();
     }
 }
        internal static void AddFrameworkJobComplete(ProcessorJob processorJob, IWorkflowMessage message)
        {
            try
            {
                WorkflowConfiguration.LoadFrameworkConfig(processorJob);
                ProcessorQueue processorQueue = GetActiveQueue(processorJob, QueueOperationType.Delivery);
                QueueOperationsHandler.HandleComplete(processorQueue, message);
            }
            catch (Exception)
            {

            }
        }
        /// <summary>
        /// Add a message containing the error to the error queue and the original message to the poison queue
        /// </summary>
        /// <param name="jobName"></param>
        /// <param name="message"></param>
        internal static void AddFrameworkError(ProcessorJob processorJob, IWorkflowMessage message, WorkflowErrorMessage errorMessage)
        {
            try
            {
                WorkflowConfiguration.LoadFrameworkConfig(processorJob);
                ProcessorQueue processorQueue = GetActiveQueue(processorJob, QueueOperationType.Delivery);
                MsmqPoisonMessageException error = new MsmqPoisonMessageException() { Source = processorJob.JobName };
                error.Data["processorQueue"] = processorQueue;
                error.Data["message"] = message;
                error.Data["errorMessage"] = errorMessage;

                QueueOperationsHandler.HandleError(error);
            }
            catch (Exception)
            {

            }
        }
        /// <summary>
        /// Get the currently active Queue to pickup or deliver messages
        /// </summary>
        /// <param name="processorJob">The current loaded configuration</param>
        /// <param name="queueOperationType">The type of operation to execute on the Queue</param>
        /// <returns>The current active ProcessorQueue for the Operation Type</returns>
        public static ProcessorQueue GetActiveQueue(ProcessorJob processorJob, QueueOperationType queueOperationType)
        {
            // For backward compatibility if the master Queue is present return it
            if (!string.IsNullOrEmpty(processorJob.MessageQueue) && !string.IsNullOrEmpty(processorJob.ErrorQueue))
                return new ProcessorQueue()
                {
                    MessageQueue = processorJob.MessageQueue,
                    ErrorQueue = processorJob.ErrorQueue,
                    PoisonQueue = processorJob.PoisonQueue,
                    CompletedQueue = processorJob.CompletedQueue,
                    MessageQueueType = processorJob.MessageQueueType
                };

            //If this is a delivery, deliver to the first available queue
            //If this is a pickup, pick it from the queues in a reverse order
            // Get the first queue with messages
            ProcessorQueue queue = new ProcessorQueue();

            lock (activeQueueSearchLock)
            {
                if (queueOperationType == QueueOperationType.Pickup)
                    processorJob.ProcessorQueues.Reverse();

                foreach (ProcessorQueue processorQueue in processorJob.ProcessorQueues)
                {
                    MessageQueue workflowQueue = new MessageQueue(processorQueue.MessageQueue);
                    queue = processorQueue;

                    try
                    {
                        using (MessageQueue msmq = new MessageQueue(processorQueue.MessageQueue))
                        {
                            msmq.Peek(new TimeSpan(0));
                            break;
                        }
                    }
                    catch (MessageQueueException e)
                    {
                        if (e.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
                        {
                            // The queue is is available and empty it can be used for delivery
                            if (queueOperationType == QueueOperationType.Delivery)
                                break;
                        }
                    }
                    catch (Exception) { }
                }
                // Reverse the queue list back to the original order
                if (queueOperationType == QueueOperationType.Pickup)
                    processorJob.ProcessorQueues.Reverse();
            }
            return queue;
        }