Announcement Announcement Module
No announcement yet.
How might one get access to the JOB_EXECUTION_ID in onWriteError? Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • How might one get access to the JOB_EXECUTION_ID in onWriteError?


    I have an "public class ErrorClassifyingStepListener extends StepListenerSupport<Long, Long>" I use on all my steps to map Exceptions to different Enums that I can write to an errors table in the database (our db error schema has an "error_type" column with enum values like BAD_PHONE, BAD_MSG, etc).

    I'd like to keep track of which JOB_EXECUTION_ID the thread was running when I write the error to my error table, but I don't see an obvious way to get access to that information from within a StepListenerSupport child.

    Any thoughts?

    Thanks in advance,

  • #2
    Sort of solved my own problem...

    This isn't as nice or as elegant as I'd want it to be, but to get access to the job execution ID so that I might link up that ID with an error that was encountered during a step in that job's execution, I did the following:
    • wrote an error row to my DB in onWriteError
    • Saved off the ID of the error_row I just wrote...stored that ID in a ThreadLocal ArrayList in my listener
    • In the afterStep() method (which does have access to the job execution object), I ran through the accumulated error rows and updated them with the job_execution_id

    Here's the code, in case my description doesn't make sense. Not guaranteeing this is bug proof, as I haven't stressed it or reviewed it much yet. It seems to work on first blush, though:

    public class AlertErrorClassifyingStepListener extends StepListenerSupport<Long, Long> {
        private CustomerAlertInstanceErrorDAO customerAlertInstanceErrorDAO;
        protected final Log log = LogFactory.getLog(this.getClass());
         * <p>Using a ThreadLocal array list here because this StepListener is a singleton shared by multiple running Alert Batch
         * Jobs in multiple running threads.  For now, each job is single-threaded.  Thus, any error we detect in the
         * onWriteError() method can be added to this thread local list so that when the step is complete we can update the
         * database in the afterStep() method to let us know what job execution ID the error happened in.</p>
         * <p>We want to tie step errors with job execution IDs for display and management purposes.  Note that the Long values in
         * this array list are the fields, and <strong>not</strong>
         * the customer_alert_instance_error.customer_alert_instance_id fields.</p>
         * <p>This data is garbage collected when the thread running the current job dies.</p>
        private ThreadLocal<ArrayList<Long>> alertErrorThreadLocalList = new ThreadLocal<ArrayList<Long>>() {
            protected ArrayList<Long> initialValue() {
                return new ArrayList<Long>();
         * <p>Update our  customer alert instance error data store with the currently running job's execution ID for
         * every error our onWriteError() method saved off to the thread-local alertErrorThreadLocalList array</p>
         * @see org.springframework.batch.core.StepExecutionListener#afterStep(org.springframework.batch.core.StepExecution)
        public ExitStatus afterStep(StepExecution stepExecution) {
            Long jobExecutionId = stepExecution.getJobExecutionId();
            for(Long errorId : alertErrorThreadLocalList.get()) {
                customerAlertInstanceErrorDAO.updateErrorWithJobExecution(errorId, jobExecutionId);
            debug(log, "in afterStep for jobExecution Id %d. New errors in the customer_alert_instance table written during " +
                    "this step are %s", jobExecutionId, StringUtils.join(alertErrorThreadLocalList.get(), " "));
            return super.afterStep(stepExecution);
        public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
            return super.onErrorInStep(stepExecution, e);
         * This is run within the transaction of the failed alert job, which will be rolled back.
         * So run this method in a new transaction.
         * See
         * @see org.springframework.batch.core.ItemWriteListener#onWriteError(java.lang.Exception, java.lang.Object)
        @Transactional(propagation = REQUIRES_NEW)
        public void onWriteError(Exception ex, List<? extends Long> items) {
            CustomerAlertInstanceError.SkipReason skipReason = translateException(ex);
            debug(log, "Requesting alertErrorDAO logging of exception of type %s",;
            Long caiErrorId = customerAlertInstanceErrorDAO.logError(items.get(0), skipReason, ex);
        private CustomerAlertInstanceError.SkipReason translateException(final Exception ex) {
            // ...does stuff
        public void setCustomerAlertInstanceErrorDAO(CustomerAlertInstanceErrorDAO customerAlertInstanceErrorDAO) {
            this.customerAlertInstanceErrorDAO = customerAlertInstanceErrorDAO;