/// <summary> /// Registers a context with the current thread - always put a matching /// <see cref="Close"/> call in a finally block to ensure that the correct /// context is available in the enclosing block. /// </summary> /// <param name="stepExecution">the step context to register</param> /// <returns>a new StepContext or the current one if it has the same StepExecution</returns> public static StepContext Register(StepExecution stepExecution) { var context = Manager.Register(stepExecution); StepScopeSynchronization.ResetInstances(); return(context); }
/// <summary> /// Calls the registered listeners in reverse order, respecting and /// prioritizing those that hold an order metadata information. /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> public ExitStatus AfterStep(StepExecution stepExecution) { if (Logger.IsTraceEnabled) { Logger.Trace("CompositeStepExecutionListener :: Entering AfterStep"); } IEnumerator <IStepExecutionListener> enumerator = _list.Reverse(); while (enumerator.MoveNext()) { if (Logger.IsTraceEnabled) { Logger.Trace("CompositeStepExecutionListener :: Before Executing AfterStep - stepExecution.ExitStatus ={0}", stepExecution.ExitStatus); } IStepExecutionListener listener = enumerator.Current; ExitStatus close = listener.AfterStep(stepExecution); stepExecution.ExitStatus = stepExecution.ExitStatus.And(close); if (Logger.IsTraceEnabled) { Logger.Trace("CompositeStepExecutionListener :: After Executing AfterStep - stepExecution.ExitStatus ={0}", stepExecution.ExitStatus); } } if (Logger.IsTraceEnabled) { Logger.Trace("CompositeStepExecutionListener :: Exiting AfterStep"); } return(stepExecution.ExitStatus); }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> private ExitStatus HandleExecution(StepExecution stepExecution) { _stepExecutionListener.BeforeStep(stepExecution); Open(stepExecution.ExecutionContext); try { DoExecute(stepExecution); } catch (RepeatException e) { throw e.InnerException; } var exitStatus = ExitStatus.Completed.And(stepExecution.ExitStatus); // Check if someone is trying to stop us if (stepExecution.TerminateOnly) { throw new JobInterruptedException("JobExecution interrupted."); } // Need to upgrade here not set, in case the execution was stopped stepExecution.UpgradeStatus(BatchStatus.Completed); Logger.Debug("Step execution success: id= {0}", stepExecution.Id); return(exitStatus); }
/// <summary> /// @see AbstractPartitionHandler#DoHandle . /// </summary> /// <param name="masterStepExecution"></param> /// <param name="partitionStepExecutions"></param> /// <returns></returns> protected override ICollection<StepExecution> DoHandle(StepExecution masterStepExecution, ICollection<StepExecution> partitionStepExecutions) { Assert.NotNull(Step, "A Step must be provided."); HashSet<Task<StepExecution>> tasks = new HashSet<Task<StepExecution>>(); HashSet<StepExecution> result = new HashSet<StepExecution>(); foreach (StepExecution stepExecution in partitionStepExecutions) { Task<StepExecution> task = CreateTask(Step, stepExecution); try { _taskExecutor.Execute(task); tasks.Add(task); } catch (TaskRejectedException) { // couldn't execute one of the tasks ExitStatus exitStatus = ExitStatus.Failed.AddExitDescription("TaskExecutor rejected the task for this step."); // Set the status in case the caller is tracking it through the // JobExecution. stepExecution.BatchStatus = BatchStatus.Failed; stepExecution.ExitStatus = exitStatus; result.Add(stepExecution); } } foreach (Task<StepExecution> task in tasks) { // Accessing Result is blocking (waits for asynchronous execution to complete) result.Add(task.Result); } return result; }
/// <summary> /// Launch Worker method in the controlThread. /// </summary> /// <param name="stepExecution"></param> /// <param name="threadWait"></param> public void Worker(StepExecution stepExecution, AutoResetEvent threadWait) { // workerStartedMessage includes stepname and worker id string workerStartedMessage = stepExecution.StepName + dot + stepExecution.remoteChunking.WorkerID.ToString(); // worker send workerStartedMessage in the workerStarted queue for master job to check stepExecution.remoteChunking._workerStartedQueue.Send(workerStartedMessage); int maxMasterWaitWorkerSecond = stepExecution.remoteChunking.MaxMasterWaitWorkerSecond; int _workerTimerseconds = maxMasterWaitWorkerSecond * 1000; // start a timer to let master check worker is still alive or not in every defualt seconds Timer timer = new Timer(WorkerTimerMethod, stepExecution, 0, _workerTimerseconds); bool Isterminate = false; while (!Isterminate) { // send back signal to the afterStep and stop timer when batch failed ControlQueue workerCompletedQueue = stepExecution.remoteChunking._workerCompletedQueue; if ("FAILED".Equals(stepExecution.ExitStatus.ExitCode)) { timer.Dispose(); Isterminate = true; } // when batch completed send back signal to the afterStep and stop timer after send completedmessage to message queue else if ("COMPLETED".Equals(stepExecution.ExitStatus.ExitCode)) { string workerCompletedMessage = stepExecution.JobExecution.JobInstance.JobName + dot + stepExecution.remoteChunking.WorkerID + dot + "COMPLETED"; workerCompletedQueue.Send(workerCompletedMessage); timer.Dispose(); threadWait.Set(); Isterminate = true; } } }
/// <summary> /// Execute the job provided by delegating to the <see cref="IJobLauncher"/>to /// prevent duplicate executions. The job parameters will be generated by the /// <see cref="IJobParametersExtractor"/>provided (if any), otherwise empty. On a /// restart, the job parameters will be the same as the last (failed) execution. /// </summary> /// <param name="stepExecution"></param> protected override void DoExecute(StepExecution stepExecution) { ExecutionContext executionContext = stepExecution.ExecutionContext; executionContext.Put(StepConstants.StepTypeKey, GetType().Name); JobParameters jobParameters; if (executionContext.ContainsKey(JobParametersKey)) { jobParameters = (JobParameters)executionContext.Get(JobParametersKey); } else { jobParameters = _jobParametersExtractor.GetJobParameters(Job, stepExecution); executionContext.Put(JobParametersKey, jobParameters); } JobExecution jobExecution = JobLauncher.Run(Job, jobParameters); if (jobExecution.Status.IsUnsuccessful()) { // AbstractStep will take care of the step execution status throw new UnexpectedJobExecutionException("Step failure: the delegate Job failed in JobStep."); } }
public void Initialize() { _jobInstance = new JobInstance(1, "testJob"); _jobExecution = new JobExecution(_jobInstance, new JobParameters()); _stepExecution1 = new StepExecution("testStep1", _jobExecution, 1); _stepExecution2 = new StepExecution("testStep2", _jobExecution, 2); }
private void When_customer_adds_it_to_the_basket() { var product = "product"; StepExecution.Comment(string.Format("Transferring '{0}' to the basket", product)); _transferResult = _stock.TransferToBasket(_basket, product); }
/// <summary> /// Logic launched after the step. Will launch the postprocessor. /// @see IStepExecutionListener#AfterStep /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> public virtual ExitStatus AfterStep(StepExecution stepExecution) { ExitStatus returnStatus = stepExecution.ExitStatus; if (!"FAILED".Equals(returnStatus.ExitCode)) { MethodInfo post = this.GetType().GetMethod("Postprocess", BindingFlags.Instance | BindingFlags.NonPublic); if (post.DeclaringType != typeof(AbstractExecutionListener)) { using (var scope = TransactionScopeManager.CreateScope()) { try { returnStatus = Postprocess(); } catch (Exception e) { // Need to catch exception to log and set status to FAILED, while // Spring batch would only log and keep the status COMPLETED Logger.Error(e, "Exception during postprocessor"); stepExecution.UpgradeStatus(BatchStatus.Failed); throw; } scope.Complete(); } } } return(returnStatus); }
/// <summary> /// @see IStepExecutionDao#SaveStepExecution. /// </summary> /// <param name="stepExecution"></param> public void SaveStepExecution(StepExecution stepExecution) { Assert.IsTrue(stepExecution.Id == null); Assert.IsTrue(stepExecution.Version == null); var jobExecutionId = stepExecution.GetJobExecutionId(); if (jobExecutionId == null) { throw new ArgumentException("The corresponding job execution must have already been saved."); } IDictionary <long?, StepExecution> executions; if (!_executionsByJobExecutionId.TryGetValue(jobExecutionId, out executions)) { executions = new Dictionary <long?, StepExecution>(); _executionsByJobExecutionId[jobExecutionId] = executions; } stepExecution.Id = Interlocked.Increment(ref _currentId); stepExecution.IncrementVersion(); StepExecution copy = Copy(stepExecution); executions[stepExecution.Id] = copy; _executionsByStepExecutionId[stepExecution.Id] = copy; }
private void Given_product_is_in_stock() { var product = "product"; _stock.Add(product); StepExecution.Comment(string.Format("Added '{0}' to the stock", product)); }
/// <summary> /// @see AbstractPartitionHandler#DoHandle . /// </summary> /// <param name="masterStepExecution"></param> /// <param name="partitionStepExecutions"></param> /// <returns></returns> protected override ICollection <StepExecution> DoHandle(StepExecution masterStepExecution, ICollection <StepExecution> partitionStepExecutions) { Assert.NotNull(Step, "A Step must be provided."); HashSet <Task <StepExecution> > tasks = new HashSet <Task <StepExecution> >(); HashSet <StepExecution> result = new HashSet <StepExecution>(); foreach (StepExecution stepExecution in partitionStepExecutions) { Task <StepExecution> task = CreateTask(Step, stepExecution); try { _taskExecutor.Execute(task); tasks.Add(task); } catch (TaskRejectedException) { // couldn't execute one of the tasks ExitStatus exitStatus = ExitStatus.Failed.AddExitDescription("TaskExecutor rejected the task for this step."); // Set the status in case the caller is tracking it through the // JobExecution. stepExecution.BatchStatus = BatchStatus.Failed; stepExecution.ExitStatus = exitStatus; result.Add(stepExecution); } } foreach (Task <StepExecution> task in tasks) { // Accessing Result is blocking (waits for asynchronous execution to complete) result.Add(task.Result); } return(result); }
/// <summary> /// Checks if step execution has been interrupted. Throws a JobInterrupdeException in that case. /// </summary> /// <param name="stepExecution"></param> /// <exception cref="JobInterruptedException"> </exception> public void CheckInterrupted(StepExecution stepExecution) { if (IsInterrupted(stepExecution)) { throw new JobInterruptedException("Job interrupted status detected."); } }
/// <summary> /// Creates a row mapper that for step executions. /// </summary> /// <param name="jobExecution">the job execution to use when creating the step executions</param> /// <returns>a row mapper</returns> private static RowMapper <StepExecution> GetStepExecutionRowMapper(JobExecution jobExecution) { return((dataRecord, i) => { var wrapper = new DataRecordWrapper(dataRecord); var stepExecution = new StepExecution(wrapper.Get <string>((1)), jobExecution, wrapper.Get <long>((0))) { StartTime = wrapper.Get <DateTime>((2)), EndTime = wrapper.Get <DateTime>((3)), BatchStatus = BatchStatus.ValueOf(wrapper.Get <string>((4))), CommitCount = wrapper.Get <int>((5)), ReadCount = wrapper.Get <int>((6)), FilterCount = wrapper.Get <int>((7)), WriteCount = wrapper.Get <int>((8)), ExitStatus = new ExitStatus(wrapper.Get <string>((9)), wrapper.Get <string>(10)), ReadSkipCount = wrapper.Get <int>((11)), WriteSkipCount = wrapper.Get <int>((12)), ProcessSkipCount = wrapper.Get <int>((13)), RollbackCount = wrapper.Get <int>((14)), LastUpdated = wrapper.Get <DateTime?>(15), Version = wrapper.Get <int?>((16)) }; return stepExecution; }); }
public ExitStatus AfterStep(StepExecution stepExecution) { JobExecution job = stepExecution.JobExecution; var jobName = job.JobInstance.JobName; var filePathName = Path.Combine(TestDataDirectoryLogs, "1_" + jobName + "_" + job.StartTime.Value.Ticks + ".log"); FileInfo fInfo = new FileInfo(filePathName); using (StreamWriter logSW = new StreamWriter(fInfo.FullName, true)) { logSW.WriteLine("Job Name: " + jobName + ", Id: " + job.Id + " ended with BatchError."); for (int i = job.StepExecutions.Count - 1; i > 0; i--) { var stepName = job.StepExecutions.ElementAt(i).StepName; var exitCode = job.StepExecutions.ElementAt(i).ExitStatus.ExitCode; var batchStatus = job.StepExecutions.ElementAt(i).BatchStatus; var summary = job.StepExecutions.ElementAt(i).GetSummary(); ICollection <Exception> exceptions = job.StepExecutions.ElementAt(i).GetFailureExceptions(); logSW.WriteLine(summary); logSW.WriteLine("Step: " + stepName + ", Batch Status: " + batchStatus + ", Exit Status: " + exitCode); for (int j = 0; j < exceptions.Count; j++) { logSW.WriteLine("Exception @ Step[" + stepName + "]: " + exceptions.ElementAt(j).InnerException); } } } return(ExitStatus.Completed); }
/// <summary> /// Creates the parameters for inserting a new step execution in the database. /// </summary> /// <param name="stepExecution">the step execution to insert</param> /// <returns>an array containing the parameters</returns> private IDictionary <string, object> BuildStepExecutionParameters(StepExecution stepExecution) { Assert.NotNull(stepExecution, "the step execution must not be null"); Assert.NotNull(stepExecution.StepName, "the step execution name must not be null"); Assert.IsNull(stepExecution.Id, "the step execution must not already have an id"); Assert.IsNull(stepExecution.Version, "the step execution must not already have a version"); stepExecution.Id = _stepExecutionIncrementer.NextLong(); stepExecution.IncrementVersion(); var exitDescription = TruncateExitDescription(stepExecution.ExitStatus.ExitDescription); var parameters = new Dictionary <string, object> { { "id", stepExecution.Id }, { "version", stepExecution.Version }, { "stepName", stepExecution.StepName }, { "jobId", stepExecution.GetJobExecutionId() }, { "startTime", stepExecution.StartTime }, { "endTime", (object)stepExecution.EndTime ?? DBNull.Value }, { "status", stepExecution.BatchStatus.ToString() }, { "commitCount", stepExecution.CommitCount }, { "readCount", stepExecution.ReadCount }, { "filterCount", stepExecution.FilterCount }, { "writeCount", stepExecution.FilterCount }, { "exitCode", stepExecution.ExitStatus.ExitCode }, { "exitMessage", exitDescription }, { "readSkipCount", stepExecution.ReadSkipCount }, { "writeSkipCount", stepExecution.WriteSkipCount }, { "processSkipCount", stepExecution.ProcessSkipCount }, { "rollbackCount", stepExecution.RollbackCount }, { "lastUpdated", (object)stepExecution.LastUpdated ?? DBNull.Value } }; return(parameters); }
/// <summary> /// Gets execution dependencies for a given StepExecution. /// </summary> /// <param name="stepExecution"></param> private void GetStepExecutionDependencies(StepExecution stepExecution) { if (stepExecution != null) { stepExecution.ExecutionContext = _executionContextDao.GetExecutionContext(stepExecution); } }
/// <summary> /// Verifies that the <see cref="ProgressControllerStep"/> was initialized correctly /// </summary> /// <param name="testSubject">The step to verify</param> /// <param name="attributes">Step attributes</param> /// <param name="displayText">Step display text</param> public static void VerifyInitialized(ProgressControllerStep testSubject, StepAttributes attributes, string displayText = null) { StepExecution expectedExecution = (attributes & StepAttributes.BackgroundThread) != 0 ? StepExecution.BackgroundThread : StepExecution.ForegroundThread; bool expectedHidden = (attributes & StepAttributes.Hidden) != 0 ? true : false; bool expectedCancellable = (attributes & StepAttributes.NonCancellable) != 0 ? false : true; bool expectedImpactingProgress = (attributes & StepAttributes.NoProgressImpact) != 0 ? false : true; bool expectedIndeterminate = (attributes & StepAttributes.Indeterminate) != 0 ? true : false; CheckState(testSubject, StepExecutionState.NotStarted); Assert.AreEqual(displayText, testSubject.DisplayText, "Unexpected display text"); Assert.AreEqual(expectedCancellable, testSubject.Cancellable, "Cancellable: Unexpected post initialization value"); Assert.AreEqual(expectedIndeterminate, testSubject.Indeterminate, "Indeterminate: Unexpected post initialization value"); Assert.AreEqual(expectedExecution, testSubject.Execution, "Execution: Unexpected post initialization value"); Assert.AreEqual(expectedHidden, testSubject.Hidden, "Hidden: Unexpected post initialization value"); Assert.AreEqual(expectedImpactingProgress, testSubject.ImpactsProgress, "ImpactingProgress: Unexpected post initialization value"); if (expectedIndeterminate) { Assert.IsTrue(ProgressControllerHelper.IsIndeterminate(testSubject.Progress), "Progess: Should be Indeterminate"); } else { Assert.AreEqual(0, testSubject.Progress, "Progress: Unexpected post initialization value"); } }
/// <summary> /// registers both job and step contexts. /// </summary> /// <param name="stepExecution"></param> private void RegisterContexts(StepExecution stepExecution) { JobExecution jobExecution = stepExecution.JobExecution; JobContextManager.Context = jobExecution.ExecutionContext; StepContextManager.Context = stepExecution.ExecutionContext; }
/// <summary> /// Launch Master timer method in the controlThread. /// </summary> /// <param name="stepExectuionObject"></param> private void MasterTimerMethod(object stepExectuionObject) { StepExecution stepExecution = (StepExecution)stepExectuionObject; Logger.Debug("-------------------------------------------- Master Timer --------------------------------------------"); string stepName = stepExecution.StepName; ControlQueue masterLifeLineQueue = stepExecution.remoteChunking._masterLifeLineQueue; string masterNoAliveMessage = stepName + dot + bool.FalseString; // stop timer when batch failed if ("FAILED".Equals(stepExecution.ExitStatus.ExitCode)) { masterLifeLineQueue.Send(masterNoAliveMessage); return; } else // continue update workerMap in the stepExecution and check workers alive { Dictionary <string, bool> workerMap = stepExecution.remoteChunking._workerMap; List <string> workerIDList = stepExecution.remoteChunking._workerStartedQueue.GetWorkerIDByMasterName(stepExecution.StepName); foreach (string workerID in workerIDList) { if (!workerMap.ContainsKey(workerID)) { workerMap[workerID] = true; } } ControlQueue workerLifeLineQueue = stepExecution.remoteChunking._workerLifeLineQueue; List <string> workerIDs = new List <string>(workerMap.Keys); workerLifeLineQueue.CheckMessageExistAndConsumeAll(workerIDs, workerMap); } }
/// <summary> /// Comments currently executed step with a <paramref name="comment"/> text. /// The comment would be included in progress notification, as well as in execution reports. /// <para>This feature has to be enabled in <see cref="LightBddConfiguration"/> via <see cref="CommentingConfigurationExtensions.EnableStepCommenting"/>() prior to usage.</para> /// </summary> /// <param name="execution">Current step execution instance.</param> /// <param name="comment">Comment to add. If comment is <c>null</c> or empty, it will not be added.</param> public static void Comment(this StepExecution execution, string comment) { if (!string.IsNullOrWhiteSpace(comment)) { ScenarioExecutionContext.Current.Get <CurrentStepProperty>().Step.Comment(comment); } }
/// <summary> /// Adds StepExecution. /// </summary> /// <param name="stepExecution"></param> public void Add(StepExecution stepExecution) { ValidateStepExecution(stepExecution); stepExecution.LastUpdated = DateTime.Now; _stepExecutionDao.SaveStepExecution(stepExecution); _executionContextDao.SaveExecutionContext(stepExecution); }
/// <summary> /// Handler of steps sequentially as provided, checking each one for success /// before moving to the next. Returns the last StepExecution /// successfully processed if it exists, and null if none were processed. /// </summary> /// <param name="execution"></param> protected override void DoExecute(JobExecution execution) { StepExecution stepExecution = null; foreach (IStep step in _steps) { stepExecution = HandleStep(step, execution); if (stepExecution.BatchStatus != BatchStatus.Completed) { break; } } // // Update the job status to be the same as the last step // if (stepExecution != null) { if (Logger.IsDebugEnabled) { Logger.Debug("Upgrading JobExecution status: {0}", stepExecution); } execution.UpgradeStatus(stepExecution.BatchStatus); execution.ExitStatus = stepExecution.ExitStatus; } }
/// <summary> /// Template method for step execution logic - calls abstract methods for resource initialization ( /// Open), execution Logic (DoExecute) and resource closing (Close). /// </summary> /// <param name="stepExecution"></param> /// <exception cref="JobInterruptedException"> </exception> /// <exception cref="UnexpectedJobExecutionException"> </exception> public void Execute(StepExecution stepExecution) { Logger.Debug("Executing: id={0}", stepExecution.Id); stepExecution.StartTime = DateTime.Now; stepExecution.BatchStatus = BatchStatus.Started; stepExecution.DelayConfig = DelayConfig; JobRepository.Update(stepExecution); // Start with a default value that will be trumped by anything ExitStatus exitStatus = ExitStatus.Executing; DoExecutionRegistration(stepExecution); try { exitStatus = HandleExecution(stepExecution); } catch (Exception e) { exitStatus = HandleExecutionException(stepExecution, e, exitStatus); } finally { exitStatus = HandleListener(stepExecution, exitStatus); exitStatus = HandleUpdateExecutionContext(stepExecution, exitStatus); HandleUpdateStepExecution(stepExecution, exitStatus); HandleCloseAndRelease(stepExecution); stepExecution.ExecutionContext.Put("batch.executed", true); if (Logger.IsDebugEnabled) { Logger.Debug("Step execution complete:{0}", stepExecution.GetSummary()); } } }
/// <summary> /// Given a step and configuration, return true if the step should start, /// false if it should not, and throw an exception if the job should finish. /// </summary> /// <param name="lastStepExecution"></param> /// <param name="jobExecution"></param> /// <param name="step"></param> /// <returns></returns> /// <exception cref="JobRestartException"> </exception> /// <exception cref="StartLimitExceededException"> </exception> protected bool ShouldStart(StepExecution lastStepExecution, JobExecution jobExecution, IStep step) { var stepStatus = lastStepExecution == null ? BatchStatus.Starting : lastStepExecution.BatchStatus; if (stepStatus == BatchStatus.Unknown) { throw new JobRestartException("Cannot restart step from UNKNOWN status. " + "The last execution ended with a failure that could not be rolled back, " + "so it may be dangerous to proceed. Manual intervention is probably necessary."); } if (stepStatus == BatchStatus.Completed && (step.AllowStartIfComplete != null && !step.AllowStartIfComplete.Value) || stepStatus == BatchStatus.Abandoned) { // step is complete, false should be returned, indicating that the // step should not be started Logger.Info("Step already complete or not restartable, so no action to execute: {0}", lastStepExecution); return(false); } if (JobRepository.GetStepExecutionCount(jobExecution.JobInstance, step.Name) < step.StartLimit) { // step start count is less than start max, return true return(true); } else { // start max has been exceeded, throw an exception. throw new StartLimitExceededException( string.Format("Maximum start limit exceeded for step: {0} StartMax: {1}", step.Name, step.StartLimit)); } }
/// <summary> /// Custom constructor /// </summary> /// <param name="chunkContext"></param> /// <param name="semaphore"></param> /// <param name="owner"></param> public ChunkTransactionCallback(ChunkContext chunkContext, Semaphore semaphore, TaskletStep owner) { _chunkContext = chunkContext; _stepExecution = chunkContext.StepContext.StepExecution; _semaphore = semaphore; _ownerStep = owner; }
/// <summary> /// Performs the actual check and returns the code "EMPTY" or "NOT_EMPTY" accordingly /// </summary> /// <param name="stepExecution"></param> /// <returns>"EMPTY" or "NOT_EMPTY"</returns> public ExitStatus AfterStep(StepExecution stepExecution) { string exitCode = Empty; try { FileInfo file = FileToCheck.GetFileInfo(); if (file.Exists && (file.Attributes & FileAttributes.Directory) != FileAttributes.Directory && file.Length > 0) { if (Logger.IsDebugEnabled) { Logger.Debug("File [{0}] is not empty ! length =[{1}]", file.FullName, file.Length); } exitCode = NotEmpty; } else { if (Logger.IsDebugEnabled) { Logger.Debug("File [{0}] is empty .", file.FullName); } } } catch (IOException) { Logger.Error("Error accessing file " + FileToCheck.GetFilename()); } return(new ExitStatus(exitCode)); }
/// <summary> /// Tests if step execution should start. /// </summary> /// <param name="allowStartIfComplete"></param> /// <param name="stepExecution"></param> /// <param name="lastStepExecution"></param> /// <returns></returns> /// <exception cref="JobExecutionException"></exception> private bool ShouldStart(bool allowStartIfComplete, StepExecution stepExecution, StepExecution lastStepExecution) { if (lastStepExecution == null) { return(true); } BatchStatus stepStatus = lastStepExecution.BatchStatus; //unknown status handling HandleUnknownStatus(stepStatus); //Completed status handling if (stepStatus == BatchStatus.Completed) { return(allowStartIfComplete || IsSameJobExecution(stepExecution, lastStepExecution)); } //Stopped or failed permits restart if (HandleStoppedOrFailedStatus(stepStatus)) { return(true); } //other statuses should not allow restart. PreventFromRestarting(stepStatus); throw new JobExecutionException( string.Format("Cannot restart step from {0} status. " + "We believe the old execution was abandoned and therefore has been marked as un-restartable.", stepStatus)); }
/// <summary> /// @see IStepExecutionDao#UpdateStepExecution. /// </summary> /// <param name="stepExecution"></param> public void UpdateStepExecution(StepExecution stepExecution) { IDictionary <long?, StepExecution> executions; StepExecution persisted; var jobExecutionId = stepExecution.GetJobExecutionId(); if (jobExecutionId == null || !_executionsByJobExecutionId.TryGetValue(jobExecutionId, out executions) || stepExecution.Id == null || !_executionsByStepExecutionId.TryGetValue(stepExecution.Id, out persisted)) { throw new ArgumentException("The step execution must have already been saved."); } lock (stepExecution) { if (persisted.Version != stepExecution.Version) { throw new ArgumentException(string.Format("Attempt to update step execution (id={0}) with version {1}, but current version is {2}.", stepExecution.Id, stepExecution.Version, persisted.Version)); } stepExecution.IncrementVersion(); StepExecution copy = Copy(stepExecution); executions[stepExecution.Id] = copy; _executionsByStepExecutionId[stepExecution.Id] = copy; } }
/// <summary> /// Checks if step execution has been interrupted. Throws a JobInterrupdeException in that case. /// </summary> /// <param name="stepExecution"></param> /// <exception cref="JobInterruptedException"></exception> public void CheckInterrupted(StepExecution stepExecution) { if (IsInterrupted(stepExecution)) { throw new JobInterruptedException("Job interrupted status detected."); } }
/// <summary> /// Logic launched before the step. Will register the contexts and launch the preprocessor. /// @see IStepExecutionListener#BeforeStep /// </summary> /// <param name="stepExecution"></param> public virtual void BeforeStep(StepExecution stepExecution) { RegisterContexts(stepExecution); if (stepExecution.remoteChunking != null) { AutoResetEvent threadWait = new AutoResetEvent(false); // master step create control thread if (stepExecution.remoteChunking._master) { // controlthread with callback method Thread thread = new Thread((ThreadStart)(() => Master(stepExecution, threadWait))); stepExecution.remoteChunking.controlThread = thread; stepExecution.remoteChunking.threadWait = threadWait; thread.Start(); TimeSpan _maxMasterWaitWorkerSecond = TimeSpan.FromSeconds(stepExecution.remoteChunking.MaxMasterWaitWorkerSecond); int _maxMasterWaitWorkerRetry = stepExecution.remoteChunking.MaxMasterWaitWorkerRetry; // wait for Master method send back workerStarted signal if (threadWait.WaitOne(_maxMasterWaitWorkerSecond * _maxMasterWaitWorkerRetry)) { Logger.Info("Worker is started."); } else { // clean all message queues when no work job provided Exception e = new JobExecutionException("No worker job provided"); Logger.Error(e, "Clean message in the Queue."); stepExecution.remoteChunking.CleanAllQueue(); throw e; } } else // worker step create control thread { Thread thread = new Thread((ThreadStart)(() => Worker(stepExecution, threadWait))); stepExecution.remoteChunking.controlThread = thread; stepExecution.remoteChunking.threadWait = threadWait; thread.Start(); } } if (!stepExecution.ExecutionContext.ContainsKey(PreProcessor)) { stepExecution.ExecutionContext.Put(PreProcessor, true); } if (!stepExecution.ExecutionContext.ContainsKey(Restart) || (stepExecution.ExecutionContext.ContainsKey(PreProcessor) && (bool)stepExecution.ExecutionContext.Get(PreProcessor))) { try { Preprocess(); stepExecution.ExecutionContext.Put(PreProcessor, false); } catch (Exception e) { throw; } } }
private void When_I_clear_it(ScenarioContext ctx) { foreach (var contact in ctx.ContactBook.Contacts.ToArray()) { ctx.ContactBook.Remove(contact.Name); } StepExecution.Bypass("Contact book clearing is not implemented yet. Contacts are removed one by one."); }
/// <summary> /// Check to determine whether or not the JobExecution that is the parent of /// the provided StepExecution has been interrupted. If, after synchronizing /// the status with the database, the status has been updated to STOPPING, /// then the job has been interrupted. /// </summary> /// <param name="stepExecution"></param> private void CheckForInterruption(StepExecution stepExecution) { JobExecution jobExecution = stepExecution.JobExecution; _jobExecutionDao.SynchronizeStatus(jobExecution); if (jobExecution.IsStopping()) { _logger.Info("Parent JobExecution is stopped, so passing message on to StepExecution"); stepExecution.SetTerminateOnly(); } }
/// <summary> /// Test for api or applicative interruption. /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> private bool IsInterrupted(StepExecution stepExecution) { bool interrupted = ThreadUtils.IsCurrentThreadInterrupted(); if (interrupted) { Logger.Info("Step interrupted through Thread API"); } else { interrupted = stepExecution.TerminateOnly; if (interrupted) { Logger.Info("Step interrupted through StepExecution"); } } return interrupted; }
/// <summary> /// Aggregates the input executions into the result <see cref="StepExecution"/>. /// The aggregated fields are: /// <list type="table"> /// <item> /// <term>BatchStatus</term> /// <description>using the highest value using <see cref="BatchStatus.Max"/></description> /// </item> /// <item> /// <term>ExitStatus</term> /// <description>using <see cref="ExitStatus.And"/></description> /// </item> /// <item> /// <term>counters (e.g., CommitCount, RollbackCount)</term> /// <description>by arithmetic sum</description> /// </item> /// </list> /// </summary> /// <param name="result">the result to overwrite</param> /// <param name="executions">the inputs</param> public void Aggregate(StepExecution result, ICollection<StepExecution> executions) { Assert.NotNull(result, "To aggregate into a result it must be non-null."); if (executions == null) { return; } foreach (var stepExecution in executions) { result.BatchStatus = BatchStatus.Max(result.BatchStatus, stepExecution.BatchStatus); result.ExitStatus = result.ExitStatus.And(stepExecution.ExitStatus); result.FilterCount = result.FilterCount + stepExecution.FilterCount; result.ProcessSkipCount = result.ProcessSkipCount + stepExecution.ProcessSkipCount; result.CommitCount = result.CommitCount + stepExecution.CommitCount; result.RollbackCount = result.RollbackCount + stepExecution.RollbackCount; result.ReadCount = result.ReadCount + stepExecution.ReadCount; result.ReadSkipCount = result.ReadSkipCount + stepExecution.ReadSkipCount; result.WriteCount = result.WriteCount + stepExecution.WriteCount; result.WriteSkipCount = result.WriteSkipCount + stepExecution.WriteSkipCount; } }
/// <summary> /// @see IJobParametersExtractor#GetJobParameters(Job, StepExecution). /// </summary> /// <param name="job"></param> /// <param name="stepExecution"></param> /// <returns></returns> public JobParameters GetJobParameters(IJob job, StepExecution stepExecution) { JobParametersBuilder builder = new JobParametersBuilder(); IDictionary<string, JobParameter> jobParameters = stepExecution.GetJobParameters().GetParameters(); ExecutionContext executionContext = stepExecution.ExecutionContext; if (_useAllParentParameters) { foreach (string key in jobParameters.Keys) { builder.AddParameter(key, jobParameters[key]); } } foreach (string akey in _keys) { string key = akey; if (key.EndsWith("(long)") || key.EndsWith("(int)")) { HandleLongOrIntKey(key, executionContext, builder, jobParameters); } else if (key.EndsWith("(double)")) { HandleDoubleKey(key, executionContext, builder, jobParameters); } else if (key.EndsWith("(string)")) { HandleStringKey(key, executionContext, builder, jobParameters); } else if (key.EndsWith("(date)")) { HandleDateKey(key, executionContext, builder, jobParameters); } else { DefaultHandle(executionContext, key, builder, jobParameters); } } return builder.ToJobParameters(); }
/// <summary> /// @see IStepExecutionDao#SaveStepExecution. /// </summary> /// <param name="stepExecution"></param> public void SaveStepExecution(StepExecution stepExecution) { Assert.IsTrue(stepExecution.Id == null); Assert.IsTrue(stepExecution.Version == null); var jobExecutionId = stepExecution.GetJobExecutionId(); if (jobExecutionId == null) { throw new ArgumentException("The corresponding job execution must have already been saved."); } IDictionary<long?, StepExecution> executions; if (!_executionsByJobExecutionId.TryGetValue(jobExecutionId, out executions)) { executions = new ConcurrentDictionary<long?, StepExecution>(); _executionsByJobExecutionId[jobExecutionId] = executions; } stepExecution.Id = Interlocked.Increment(ref _currentId); stepExecution.IncrementVersion(); StepExecution copy = Copy(stepExecution); executions[stepExecution.Id] = copy; _executionsByStepExecutionId[stepExecution.Id] = copy; }
/// <summary> /// Manage the StepContext lifecycle. Business processing should be /// delegated to #DoInChunkContext(RepeatContext, ChunkContext). This /// is to ensure that the current thread has a reference to the context, even /// if the callback is executed in a pooled thread. Handles the registration /// and unregistration of the step context, so clients should not duplicate /// those calls. /// </summary> /// <param name="stepExecution"></param> /// <param name="doInChunkContext"></param> /// <returns></returns> public static RepeatCallback GetRepeatCallback(StepExecution stepExecution, DoInChunkContext doInChunkContext) { BlockingCollection<ChunkContext> attributeQueue = new BlockingCollection<ChunkContext>(); return context => { // The StepContext has to be the same for all chunks, // otherwise step-scoped beans will be re-initialised for each chunk. StepContext stepContext = StepSynchronizationManager.Register(stepExecution); if (Logger.IsDebugEnabled) { Logger.Debug("Preparing chunk execution for StepContext: {0}", ObjectUtils.IdentityToString(stepContext)); } ChunkContext chunkContext; attributeQueue.TryTake(out chunkContext); if (chunkContext == null) { chunkContext = new ChunkContext(stepContext); } try { Logger.Debug("Chunk execution starting: queue size= {0}", attributeQueue.Count); return doInChunkContext(context, chunkContext); //Delegation } finally { // Still some stuff to do with the data in this chunk, // pass it back. if (!chunkContext.Complete) { attributeQueue.Add(chunkContext); } StepSynchronizationManager.Close(); } }; }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> private ExitStatus HandleExecution(StepExecution stepExecution) { _stepExecutionListener.BeforeStep(stepExecution); Open(stepExecution.ExecutionContext); try { DoExecute(stepExecution); } catch (RepeatException e) { throw e.InnerException; } var exitStatus = ExitStatus.Completed.And(stepExecution.ExitStatus); // Check if someone is trying to stop us if (stepExecution.TerminateOnly) { throw new JobInterruptedException("JobExecution interrupted."); } // Need to upgrade here not set, in case the execution was stopped stepExecution.UpgradeStatus(BatchStatus.Completed); Logger.Debug("Step execution success: id= {0}", stepExecution.Id); return exitStatus; }
/// <summary> /// Gets execution dependencies for a given StepExecution. /// </summary> /// <param name="stepExecution">the given step execution</param> private void GetStepExecutionDependencies(StepExecution stepExecution) { if (stepExecution != null) { stepExecution.ExecutionContext = _executionContextDao.GetExecutionContext(stepExecution); } }
/// <summary> /// Extension point for subclasses to execute business logic. Subclasses should set the ExitStatus on the /// StepExecution before returning. /// </summary> /// <param name="stepExecution"></param> /// <exception cref="Exception"></exception> protected abstract void DoExecute(StepExecution stepExecution);
/// <summary> /// Rollback StepExecution /// </summary> /// <param name="stepExecution"></param> private void Rollback(StepExecution stepExecution) { if (!_rolledBack) { stepExecution.IncrementRollbackCount(); _rolledBack = true; } }
/// <summary> /// Copy from source StepExecution to target StepExecution /// </summary> /// <param name="source"></param> /// <param name="target"></param> private void Copy(StepExecution source, StepExecution target) { target.Version = source.Version; target.WriteCount = source.WriteCount; target.FilterCount = source.FilterCount; target.CommitCount = source.CommitCount; target.ExecutionContext = new ExecutionContext(source.ExecutionContext); }
/// <summary> /// Given a step and configuration, return true if the step should start, /// false if it should not, and throw an exception if the job should finish. /// </summary> /// <param name="lastStepExecution"></param> /// <param name="jobExecution"></param> /// <param name="step"></param> /// <returns></returns> /// <exception cref="JobRestartException"></exception> /// <exception cref="StartLimitExceededException"></exception> protected bool ShouldStart(StepExecution lastStepExecution, JobExecution jobExecution, IStep step) { var stepStatus = lastStepExecution == null ? BatchStatus.Starting : lastStepExecution.BatchStatus; if (stepStatus == BatchStatus.Unknown) { throw new JobRestartException("Cannot restart step from UNKNOWN status. " + "The last execution ended with a failure that could not be rolled back, " + "so it may be dangerous to proceed. Manual intervention is probably necessary."); } if (stepStatus == BatchStatus.Completed && ( step.AllowStartIfComplete !=null && !step.AllowStartIfComplete.Value) || stepStatus == BatchStatus.Abandoned) { // step is complete, false should be returned, indicating that the // step should not be started Logger.Info("Step already complete or not restartable, so no action to execute: {0}",lastStepExecution); return false; } if (JobRepository.GetStepExecutionCount(jobExecution.JobInstance, step.Name) < step.StartLimit) { // step start count is less than start max, return true return true; } else { // start max has been exceeded, throw an exception. throw new StartLimitExceededException( string.Format("Maximum start limit exceeded for step: {0} StartMax: {1}", step.Name, step.StartLimit)); } }
/// <summary> /// Template method for step execution logic - calls abstract methods for resource initialization ( /// Open), execution Logic (DoExecute) and resource closing (Close). /// </summary> /// <param name="stepExecution"></param> /// <exception cref="JobInterruptedException"></exception> /// <exception cref="UnexpectedJobExecutionException"></exception> public void Execute(StepExecution stepExecution) { Logger.Debug("Executing: id={0}", stepExecution.Id); stepExecution.StartTime = DateTime.Now; stepExecution.BatchStatus = BatchStatus.Started; JobRepository.Update(stepExecution); // Start with a default value that will be trumped by anything ExitStatus exitStatus = ExitStatus.Executing; DoExecutionRegistration(stepExecution); try { exitStatus = HandleExecution(stepExecution); } catch (Exception e) { exitStatus = HandleExecutionException(stepExecution, e, exitStatus); } finally { exitStatus = HandleListener(stepExecution, exitStatus); exitStatus = HandleUpdateExecutionContext(stepExecution, exitStatus); HandleUpdateStepExecution(stepExecution, exitStatus); HandleCloseAndRelease(stepExecution); if (Logger.IsDebugEnabled) { Logger.Debug("Step execution complete:{0}", stepExecution.GetSummary()); } } }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> private void HandleCloseAndRelease(StepExecution stepExecution) { try { Close(stepExecution.ExecutionContext); } catch (Exception e) { Logger.Error(e, "Exception while closing step execution resources in step {0} in job {1}", Name, stepExecution.JobExecution.JobInstance.JobName); stepExecution.AddFailureException(e); } DoExecutionRelease(); }
/// <summary> /// Handle restartability if needed. /// </summary> /// <param name="lastStepExecution"></param> /// <param name="currentStepExecution"></param> private void HandleRestart(StepExecution lastStepExecution, StepExecution currentStepExecution) { bool isRestart = (lastStepExecution != null && !lastStepExecution.BatchStatus.Equals(BatchStatus.Completed)); if (isRestart) { currentStepExecution.ExecutionContext = lastStepExecution.ExecutionContext; if (lastStepExecution.ExecutionContext.ContainsKey("batch.executed")) { currentStepExecution.ExecutionContext.Remove("batch.executed"); } } else { currentStepExecution.ExecutionContext = new ExecutionContext(_executionContext); } }
/// <summary> /// @see IStepExecutionListener#BeforeStep . /// </summary> /// <param name="stepExecution"></param> public virtual void BeforeStep(StepExecution stepExecution) { }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <param name="e"></param> /// <param name="exitStatus"></param> /// <returns></returns> private ExitStatus HandleExecutionException(StepExecution stepExecution, Exception e, ExitStatus exitStatus) { stepExecution.UpgradeStatus(DetermineBatchStatus(e)); ExitStatus returnedExitStatus = exitStatus.And(GetDefaultExitStatusForFailure(e)); stepExecution.AddFailureException(e); if (stepExecution.BatchStatus == BatchStatus.Stopped) { Logger.Info("Encountered interruption executing step {0} in job {1} : {2}", Name, stepExecution.JobExecution.JobInstance.JobName, e.Message); if (Logger.IsDebugEnabled) { Logger.Debug(e, "Full exception"); } } else { Logger.Error(e, "Encountered an error executing step {0} in job {1} :", Name, stepExecution.JobExecution.JobInstance.JobName); } return returnedExitStatus; }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <param name="exitStatus"></param> /// <returns></returns> private ExitStatus HandleListener(StepExecution stepExecution, ExitStatus exitStatus) { ExitStatus returnedExitStatus = exitStatus; try { // Update the step execution to the latest known value so the // listeners can act on it returnedExitStatus = returnedExitStatus.And(stepExecution.ExitStatus); stepExecution.ExitStatus = returnedExitStatus; Logger.Trace("_stepExecutionListener.AfterStep CALL"); returnedExitStatus = returnedExitStatus.And(_stepExecutionListener.AfterStep(stepExecution)); } catch (Exception e) { Logger.Error(e, "Exception in afterStep callback in step {0} in job {1}", Name, stepExecution.JobExecution.JobInstance.JobName); } return returnedExitStatus; }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <param name="exitStatus"></param> /// <returns></returns> private ExitStatus HandleUpdateExecutionContext(StepExecution stepExecution, ExitStatus exitStatus) { ExitStatus returnedExitStatus = exitStatus; try { JobRepository.UpdateExecutionContext(stepExecution); } catch (Exception e) { stepExecution.BatchStatus = BatchStatus.Unknown; returnedExitStatus = exitStatus.And(ExitStatus.Unknown); stepExecution.AddFailureException(e); Logger.Error(e, "Encountered an error saving batch meta data for step {0} in job {1}. This job is now in an unknown state and should not be restarted.", Name, stepExecution.JobExecution.JobInstance.JobName); } stepExecution.EndTime = DateTime.Now; stepExecution.ExitStatus = returnedExitStatus; return returnedExitStatus; }
/// <summary> /// /// </summary> /// <param name="stepExecution"></param> /// <param name="exitStatus"></param> private void HandleUpdateStepExecution(StepExecution stepExecution, ExitStatus exitStatus) { try { JobRepository.Update(stepExecution); } catch (Exception e) { stepExecution.BatchStatus = BatchStatus.Unknown; stepExecution.ExitStatus = exitStatus.And(ExitStatus.Unknown); stepExecution.AddFailureException(e); Logger.Error(e, "Encountered an error saving batch meta data for step {0} in job {1}. This job is now in an unknown state and should not be restarted.", Name, stepExecution.JobExecution.JobInstance.JobName); } }
/// <summary> /// @see IStepExecutionListener#AfterStep . /// </summary> /// <param name="stepExecution"></param> /// <returns></returns> public virtual ExitStatus AfterStep(StepExecution stepExecution) { return null; }
/// <summary> /// Registers the StepExecution for property resolution via StepScope /// </summary> /// <param name="stepExecution"></param> protected void DoExecutionRegistration(StepExecution stepExecution) { StepSynchronizationManager.Register(stepExecution); }
/// <summary> /// Handle normal step execution. /// </summary> /// <param name="step"></param> /// <param name="execution"></param> /// <param name="currentStepExecution"></param> private void HandleStepExecution(IStep step, JobExecution execution, StepExecution currentStepExecution) { JobRepository.Add(currentStepExecution); Logger.Info("Executing step: [ {0} ]", step.Name); try { step.Execute(currentStepExecution); currentStepExecution.ExecutionContext.Put("batch.executed", true); } catch (JobInterruptedException) { // Ensure that the job gets the message that it is stopping // and can pass it on to other steps that are executing // concurrently. execution.Status = BatchStatus.Stopping; throw; } JobRepository.UpdateExecutionContext(execution); }
/// <summary> /// Actual taskletstep execution. /// </summary> /// <param name="stepExecution"></param> /// <exception cref="Exception"></exception> protected override void DoExecute(StepExecution stepExecution) { stepExecution.ExecutionContext.Put(TaskletTypeKey, _tasklet.GetType().Name); stepExecution.ExecutionContext.Put(StepConstants.StepTypeKey, GetType().Name); _stream.Update(stepExecution.ExecutionContext); JobRepository.UpdateExecutionContext(stepExecution); // Shared semaphore per step execution, so other step executions can run // in parallel without needing the lock Semaphore semaphore = CreateSemaphore(); _stepOperations.Iterate(StepContextRepeatCallback.GetRepeatCallback(stepExecution, (context, chunkContext) => { StepExecution lStepExecution = chunkContext.StepContext.StepExecution; _interruptionPolicy.CheckInterrupted(lStepExecution); ChunkTransactionCallback callback = new ChunkTransactionCallback(chunkContext, semaphore, this); RepeatStatus result; using (var scope = TransactionScopeManager.CreateScope()) { TransactionScopeManager.RegisterTransactionSynchronization(scope, callback); try { result = callback.DoInTransaction(); } catch (Exception e) { //Log and rethrow Logger.Error(e, "Transaction will be rollbacked because of an unexpected exception."); throw; // throw to ensure rollback will occur (no complete) } scope.Complete(); } // Release connections since the transaction has ended ConnectionUtil.ReleaseConnections(); _interruptionPolicy.CheckInterrupted(stepExecution); return result; } )); }
private bool StepExecutionPartOfExistingJobExecution(JobExecution jobExecution, StepExecution stepExecution) { return stepExecution != null && stepExecution.GetJobExecutionId() != null && stepExecution.GetJobExecutionId().Equals(jobExecution.Id); }
/// <summary> /// Create a new instance of StepContext for this StepExecution. /// </summary> /// <param name="stepExecution"></param> public StepContext(StepExecution stepExecution) { Assert.NotNull(stepExecution, "A StepContext must have a non-null StepExecution"); _stepExecution = stepExecution; }
/// <summary> /// Wraps logic into a transactional context. /// </summary> /// <returns></returns> public RepeatStatus DoInTransaction() { RepeatStatus result; StepContribution contribution = _stepExecution.CreateStepContribution(); // In case we need to push it back to its old value // after a commit fails... _oldVersion = new StepExecution(_stepExecution.StepName, _stepExecution.JobExecution); Copy(_stepExecution, _oldVersion); try { try { try { result = _ownerStep._tasklet.Execute(contribution, _chunkContext) ?? RepeatStatus.Finished; } catch (Exception e) { _chunkContext.SetAttribute(StepListenerConstant.RollbackExceptionKey, e); throw; } } finally { // If the step operations are asynchronous then we need // to synchronize changes to the step execution (at a // minimum). Take the lock *before* changing the step // execution. try { _semaphore.WaitOne(); _locked = true; } catch (Exception) { Logger.Error("Thread interrupted while locking for repository update"); _stepExecution.BatchStatus = BatchStatus.Stopped; _stepExecution.SetTerminateOnly(); Thread.CurrentThread.Interrupt(); } // Apply the contribution to the step // even if unsuccessful if (Logger.IsDebugEnabled) { Logger.Debug("Applying contribution: {0}", contribution); } _stepExecution.Apply(contribution); } _stepExecutionUpdated = true; _ownerStep._stream.Update(_stepExecution.ExecutionContext); try { // Going to attempt a commit. If it fails this flag will // stay false and we can use that later. _ownerStep.JobRepository.UpdateExecutionContext(_stepExecution); _stepExecution.IncrementCommitCount(); if (Logger.IsDebugEnabled) { Logger.Debug("Saving step execution before commit: {0}", _stepExecution); } _ownerStep.JobRepository.Update(_stepExecution); } catch (Exception e) { // If we get to here there was a problem saving the step // execution and we have to fail. Logger.Error(e, JobRepositoryForcedRollbackMsg); throw new FatalStepExecutionException(JobRepositoryForcedRollbackMsg, e); } } catch (Exception e) { if (Logger.IsDebugEnabled) { Logger.Debug("Rollback for Exception: {0} : {1} ", e.GetType().Name, e.Message); } Rollback(_stepExecution); throw; } return result; }