/// <summary> /// Cancels a running workflow. /// </summary> /// <remarks> /// This happened because of an explicit cancel request by a user. /// </remarks> public Guid CancelJob(Job job) { EnsureNotStopping(); JobInstance ji; using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; context.JobGuid = job.Guid; ji = LoadJobInstance(context, job); // Update registry ji.JobExecutionStatus = JobExecutionState.Cancelling; ji.Save(); } return CancelWorkflow(ji.WorkflowInstanceId); }
/// <summary> /// Do bookkeeping required when a workflow starts /// </summary> /// <param name="wfapp"></param> private void RegisterWorkflow(Job job, WorkflowApplication wfapp) { lock (syncRoot) { workflows.Add(wfapp.Id, new WorkflowDetails() { Job = job, WorkflowApplication = wfapp, }); } }
private void SaveWorkflowParameters(Job job, IDictionary<string, object> outputs) { // Load job data from the registry using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; context.JobGuid = job.Guid; JobInstance ji = LoadJobInstance(context, job); foreach (var name in outputs.Keys) { ji.Parameters[name].SetValue(outputs[name]); } ji.Save(); } }
/// <summary> /// Instructes the workflow host to start a job. /// </summary> /// <param name="jobGuid"></param> /// <returns></returns> public Guid PrepareStartJob(Job job) { lastTimeActive = DateTime.Now; return workflowHost.PrepareStartJob(job); // *** TODO: handle initialization errors here }
/// <summary> /// Starts a job workflow /// </summary> /// <param name="wftype"></param> /// <param name="par"></param> /// <returns></returns> private Guid PrepareStartWorkflow(Job job, Dictionary<string, object> par) { var wftype = GetWorkflowType(job); var wfapp = CreateWorkflowApplication(wftype, par); RegisterWorkflow(job, wfapp); return wfapp.Id; }
/// <summary> /// Returns an AppDomainHost to run the job. /// </summary> /// <remarks> /// Either an existing AppDomainHost is returned that has the /// required assembly, or a new AppDomainHost is created. /// </remarks> /// <param name="job"></param> /// <returns></returns> private AppDomainHost GetOrCreateAppDomainHost(Job job) { AppDomain ad; Components.AppDomainManager.Instance.GetAppDomainForType(job.WorkflowTypeName, true, out ad); if (!appDomains.ContainsKey(ad.Id)) { // New app domain, create host var adh = new AppDomainHost(ad, contextGuid); adh.WorkflowEvent += new EventHandler<HostEventArgs>(adh_WorkflowEvent); appDomains.Add(ad.Id, adh); adh.Start(Scheduler, interactive); return adh; } else { // Old app domain, return existing host return appDomains[ad.Id]; } }
/// <summary> /// Queries the registry for new jobs to schedule /// </summary> private void PollNewJobs() { List<Job> temp = new List<Job>(); using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; foreach (var queue in Cluster.Queues.Values) { var qi = new QueueInstance(context); qi.Guid = queue.Guid; qi.Load(); var ji = qi.GetNextJobInstance(); if (ji != null) { var user = new User(context); user.Guid = ji.UserGuidOwner; user.Load(); var job = new Job() { Guid = ji.Guid, JobID = ji.JobID, UserGuid = user.Guid, UserName = user.Name, QueueGuid = ji.ParentReference.Guid, WorkflowTypeName = ji.WorkflowTypeName, }; if ((ji.JobExecutionStatus & JobExecutionState.Scheduled) != 0) { job.Status = JobStatus.Starting; ji.JobExecutionStatus = JobExecutionState.Starting; } else if ((ji.JobExecutionStatus & JobExecutionState.Persisted) != 0) { // Save cancel requested flag here ji.JobExecutionStatus ^= JobExecutionState.Persisted; ji.JobExecutionStatus |= JobExecutionState.Starting; job.Status = JobStatus.Resuming; } else { throw new NotImplementedException(); } ji.Save(); lock (queue) { queue.Jobs.Add(job.Guid, job); } temp.Add(job); } } } foreach (var job in temp) { StartOrResumeJob(job); } }
/// <summary> /// Sets up a new workflow and starts the new job /// </summary> /// <param name="jobGuid"></param> /// <returns></returns> public Guid PrepareStartJob(Job job) { EnsureNotStopping(); // Load job data from the registry using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; context.JobGuid = job.Guid; JobInstance ji = LoadJobInstance(context, job); // Deserialize parameters Dictionary<string, object> pars = new Dictionary<string, object>(); foreach (var par in ji.Parameters.Values) { if ((par.Direction & JobParameterDirection.In) != 0) { pars.Add(par.Name, ji.Parameters[par.Name].GetValue()); } } // Set default parameters pars.Add("UserGuid", ji.UserGuidOwner); pars.Add("JobGuid", ji.Guid); // Start the workflow Guid wfguid = PrepareStartWorkflow(job, pars); // Update registry ji.DateStarted = DateTime.Now; ji.WorkflowInstanceId = wfguid; ji.JobExecutionStatus = JobExecutionState.Executing; ji.Save(); return wfguid; } }
public void RunJob(Job job) { EnsureNotStopping(); lock (syncRoot) { workflows[job.WorkflowInstanceId].WorkflowApplication.Run(); } }
public Guid PersistJob(Job job) { EnsureNotStopping(); JobInstance ji; using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; context.JobGuid = job.Guid; ji = LoadJobInstance(context, job); // Update registry ji.JobExecutionStatus = JobExecutionState.Persisting; ji.Save(); } // For some reason, unloading happens synchronously so to avoid deadlock with // the previous registry update, this has to be called outside the context return PersistWorkflow(ji.WorkflowInstanceId); }
/// <summary> /// Resumes a previously persisted workflow /// </summary> /// <param name="jobGuid"></param> /// <returns></returns> public Guid PrepareResumeJob(Job job) { EnsureNotStopping(); using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.ContextGuid = contextGuid; context.JobGuid = job.Guid; JobInstance ji = LoadJobInstance(context, job); // Load assembly and create workflow instance Type wftype = Type.GetType(ji.WorkflowTypeName); // Resume the workflow Guid wfguid = PrepareResumeWorkflow(job, ji.WorkflowInstanceId); // Update registry ji.JobExecutionStatus = JobExecutionState.Executing; ji.Save(); return wfguid; } }
/// <summary> /// Instructs the workflow host to cancel a /// running job. /// </summary> /// <param name="workflowInstanceId"></param> public void CancelJob(Job job) { lastTimeActive = DateTime.Now; workflowHost.CancelJob(job); }
/// <summary> /// Instructs the workflow host to cancel the job and mark it as timed-out. /// </summary> /// <param name="job"></param> public void TimeOutJob(Job job) { lastTimeActive = DateTime.Now; workflowHost.TimeOutJob(job); }
public void RunJob(Job job) { lastTimeActive = DateTime.Now; workflowHost.RunJob(job); }
private void CancelOrTimeOutJob(Job job, bool timeout) { using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.JobGuid = job.Guid; context.ContextGuid = contextGuid; var ji = new JobInstance(context); ji.Guid = job.Guid; ji.Load(); // Update registry ji.JobExecutionStatus = JobExecutionState.Cancelling; ji.Save(); } // Update job status if (timeout) { job.Status = JobStatus.TimedOut; appDomains[job.AppDomainID].TimeOutJob(job); } else { job.Status = JobStatus.Cancelled; appDomains[job.AppDomainID].CancelJob(job); } }
private Type GetWorkflowType(Job job) { // Implicitely load the assembly. // TODO: If this fails, assembly loading failure // is the cause. Check ReflectionHelperInternal event handlers. Type wftype = Type.GetType(job.WorkflowTypeName); if (wftype == null) { throw new TypeLoadException(ExceptionMessages.ErrorLoadingWorkflowType); } return wftype; }
/// <summary> /// Finished the execution of a job and records the results in the registry. /// </summary> /// <param name="workflowInstanceId"></param> /// <param name="eventType"></param> private void FinishJob(Job job, HostEventArgs e) { using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.JobGuid = job.Guid; context.ContextGuid = contextGuid; JobInstance ji = new JobInstance(context); ji.Guid = job.Guid; ji.Load(); // Update execution status, error message and finish time switch (e.EventType) { case WorkflowEventType.Completed: ji.JobExecutionStatus = JobExecutionState.Completed; break; case WorkflowEventType.Cancelled: ji.JobExecutionStatus = JobExecutionState.Cancelled; break; case WorkflowEventType.TimedOut: ji.JobExecutionStatus = JobExecutionState.TimedOut; break; case WorkflowEventType.Persisted: ji.JobExecutionStatus = JobExecutionState.Persisted; break; case WorkflowEventType.Failed: ji.JobExecutionStatus = JobExecutionState.Failed; ji.ExceptionMessage = e.ExceptionMessage; break; } // Update registry ji.DateFinished = DateTime.Now; ji.Save(); ji.ReleaseLock(false); ji.RescheduleIfRecurring(); // Do local bookkeeping lock (runningJobs) { lock (Cluster.Queues[job.QueueGuid].Jobs) { Cluster.Queues[job.QueueGuid].Jobs.Remove(job.Guid); } runningJobs.Remove(job.WorkflowInstanceId); } if (interactive) { Console.WriteLine("Finishing job: {0}", ji.Guid); } } }
private JobInstance LoadJobInstance(Context context, Job job) { var ji = new JobInstance(context); ji.Guid = job.Guid; ji.Load(); return ji; }
private void PersistJob(Job job) { using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.JobGuid = job.Guid; context.ContextGuid = contextGuid; var ji = new JobInstance(context); ji.Guid = job.Guid; ji.Load(); // Update registry ji.JobExecutionStatus = JobExecutionState.Persisting; ji.Save(); } // Update job status job.Status = JobStatus.Persisted; appDomains[job.AppDomainID].PersistJob(job); }
/// <summary> /// Resumes a previously persisted job workflow /// </summary> /// <param name="wftype"></param> /// <param name="par"></param> /// <param name="instanceId"></param> /// <returns></returns> private Guid PrepareResumeWorkflow(Job job, Guid instanceId) { var wftype = GetWorkflowType(job); var wfapp = CreateWorkflowApplication(wftype, null); wfapp.Load(instanceId); RegisterWorkflow(job, wfapp); return wfapp.Id; }
/// <summary> /// Starts a single job /// </summary> /// <param name="job"></param> private void StartOrResumeJob(Job job) { AppDomainHost adh = null; using (Context context = ContextManager.Instance.CreateContext(ConnectionMode.AutoOpen, TransactionMode.AutoCommit)) { context.JobGuid = job.Guid; context.ContextGuid = contextGuid; JobInstance ji = new JobInstance(context); ji.Guid = job.Guid; ji.Load(); // Lock the job, so noone else can pick it up ji.DateStarted = DateTime.Now; ji.ObtainLock(); ji.Save(); } // Schedule job in the appropriate app domain adh = GetOrCreateAppDomainHost(job); // Check if job is a new instance or previously persisted and // has to be resumed switch (job.Status) { case JobStatus.Starting: if (interactive) { Console.WriteLine("Starting job: {0}", job.Guid); } job.WorkflowInstanceId = adh.PrepareStartJob(job); break; case JobStatus.Resuming: if (interactive) { Console.WriteLine("Resuming job: {0}", job.Guid); } job.WorkflowInstanceId = adh.PrepareResumeJob(job); break; default: throw new NotImplementedException(); } // Update job status job.TimeStarted = DateTime.Now; job.Status = JobStatus.Executing; job.AppDomainID = adh.ID; // TODO: this has to happen before starting the job lock (runningJobs) { runningJobs.Add(job.WorkflowInstanceId, job); } adh.RunJob(job); }
/// <summary> /// Instructs the workflow host to persist and suspend a job. /// </summary> /// <param name="jobGuid"></param> public void PersistJob(Job job) { lastTimeActive = DateTime.Now; workflowHost.PersistJob(job); }