Results 1 to 2 of 2

Thread: How might one get access to the JOB_EXECUTION_ID in onWriteError?

  1. #1

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

    Hi,

    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,
    Tom

  2. #2

    Default 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:

    Code:
    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 customer_alert_instance_error.id 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>>() {
            @Override
            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)
         */
        @Override
        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);
        }
    
        @Override
        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 http://static.springframework.org/spring-batch/spring-batch-docs/reference/html-single/index.html#d0e4511
         * @see org.springframework.batch.core.ItemWriteListener#onWriteError(java.lang.Exception, java.lang.Object)
         */
        @Override
        @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", skipReason.name());
    
            Long caiErrorId = customerAlertInstanceErrorDAO.logError(items.get(0), skipReason, ex);
            this.alertErrorThreadLocalList.get().add(caiErrorId);
        }
    
        private CustomerAlertInstanceError.SkipReason translateException(final Exception ex) {
            // ...does stuff
        }
    
        @Autowired
        public void setCustomerAlertInstanceErrorDAO(CustomerAlertInstanceErrorDAO customerAlertInstanceErrorDAO) {
            this.customerAlertInstanceErrorDAO = customerAlertInstanceErrorDAO;
        }
    }

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •