/** * Implements processing and all related listener calls. * * @param item the item to be processed * @return the processed item * @throws Exception thrown if error occurs during processing. */ protected final O doTransform(I item) throws Exception { try { listener.beforeProcess(item); O result = itemProcessor.process(item); listener.afterProcess(item, result); return result; } catch (Exception e) { listener.onProcessError(item, e); throw e; } }
/** * Surrounds the read call with listener callbacks. * @return the item or {@code null} if the data source is exhausted * @throws Exception is thrown if error occurs during read. */ @Nullable protected final I doRead() throws Exception { try { listener.beforeRead(); I item = itemReader.read(); if(item != null) { listener.afterRead(item); } return item; } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage() + " : " + e.getClass().getName()); } listener.onReadError(e); throw e; } }
/** * Implements writing and all related listener calls * * @param contribution a {@link StepContribution} * @param chunk a {@link Chunk} * @throws Exception thrown if error occurs during the writing portion of the chunking loop. */ protected final void doPersist(final StepContribution contribution, final Chunk<O> chunk) throws Exception { try { List<O> items = chunk.getItems(); listener.beforeWrite(items); itemWriter.write(items); listener.afterWrite(items); } catch (Exception e) { listener.onWriteError(e, chunk.getItems()); throw e; } } }
@Override public I doWithRetry(RetryContext arg0) throws Exception { while (true) { try { return doProvide(contribution, chunk); } catch (Exception e) { if (shouldSkip(skipPolicy, e, contribution.getStepSkipCount())) { // increment skip count and try again contribution.incrementReadSkipCount(); chunk.skip(e); getListener().onSkipInRead(e); logger.debug("Skipping failed input", e); } else { getListener().onRetryReadException(e); if(rollbackClassifier.classify(e)) { throw e; } else { throw e; } } } } } };
@Override public O doWithRetry(RetryContext context) throws Exception { try { return doTransform(item); } catch (Exception e) { if (shouldSkip(skipPolicy, e, contribution.getStepSkipCount())) { // If we are not re-throwing then we should check if // this is skippable contribution.incrementProcessSkipCount(); logger.debug("Skipping after failed process with no rollback", e); // If not re-throwing then the listener will not be // called in next chunk. getListener().onSkipInProcess(item, e); } else { getListener().onRetryProcessException(item, e); if (rollbackClassifier.classify(e)) { // Default is to rollback unless the classifier // allows us to continue throw e; } else { throw e; } } } return null; }
@Override @SuppressWarnings({ "unchecked", "rawtypes" }) public Object doWithRetry(RetryContext context) throws Exception { chunkMonitor.setChunkSize(chunk.size()); try { doPersist(contribution, chunk); } catch (Exception e) { if (shouldSkip(skipPolicy, e, contribution.getStepSkipCount())) { // Per section 9.2.7 of JSR-352, the SkipListener receives all the items within the chunk ((MulticasterBatchListener) getListener()).onSkipInWrite(chunk.getItems(), e); } else { getListener().onRetryWriteException((List<Object>) chunk.getItems(), e); if (rollbackClassifier.classify(e)) { throw e; } } /* * If the exception is marked as no-rollback, we need to * override that, otherwise there's no way to write the * rest of the chunk or to honour the skip listener * contract. */ throw new ForceRollbackForWriteSkipException( "Force rollback on skippable exception so that skipped item can be located.", e); } contribution.incrementWriteCount(chunk.size()); return null; } };
/** * Test method for * {@link org.springframework.batch.core.listener.MulticasterBatchListener#onSkipInWrite(java.lang.Object, java.lang.Throwable)} * . */ @Test public void testOnSkipInProcess() { multicast.register(new SkipListenerSupport<Object,Object>() { @Override public void onSkipInProcess(Object item, Throwable t) { count++; super.onSkipInWrite(item, t); } }); multicast.onSkipInProcess(null, new RuntimeException("foo")); assertEquals(1, count); }
/** * Test method for * {@link org.springframework.batch.core.listener.MulticasterBatchListener#onSkipInWrite(java.lang.Object, java.lang.Throwable)} * . */ @Test public void testOnSkipInWrite() { multicast.register(new SkipListenerSupport<Object,Object>() { @Override public void onSkipInWrite(Object item, Throwable t) { count++; super.onSkipInWrite(item, t); } }); multicast.onSkipInWrite(null, new RuntimeException("foo")); assertEquals(1, count); }
/** * Test method for * {@link org.springframework.batch.core.listener.MulticasterBatchListener#onSkipInRead(java.lang.Throwable)} * . */ @Test public void testOnSkipInRead() { multicast.register(new SkipListenerSupport<Object,Object>() { @Override public void onSkipInRead(Throwable t) { count++; super.onSkipInRead(t); } }); multicast.onSkipInRead(new RuntimeException("foo")); assertEquals(1, count); }
@Test public void testAfterProcessFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.afterProcess(null, null); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
@Test public void testBeforeReadFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.beforeRead(); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
@Test public void testBeforeProcessFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.beforeProcess(null); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
@Test public void testBeforeWriteFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.beforeWrite(null); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
@Test public void testAfterReadFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.afterRead(null); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
@Test public void testAfterWriteFails_withAnnotatedListener() { StepListener listener = StepListenerFactoryBean.getListener(new AnnotationBasedStepListener()); multicast.register(listener); try { multicast.afterWrite(null); fail("Expected StepListenerFailedException"); } catch (StepListenerFailedException e) { // expected Throwable cause = e.getCause(); String message = cause.getMessage(); assertTrue(cause instanceof IllegalStateException); assertEquals("Wrong message: " + message, "listener error", message); } }
/** * Call the listener's after write method. * * @param items list of items that were just written. */ protected final void doAfterWrite(List<O> items) { listener.afterWrite(items); }
/** * Surrounds the actual write call with listener callbacks. * * @param items list of items to be written. * @throws Exception thrown if error occurs. */ protected final void doWrite(List<O> items) throws Exception { if (itemWriter == null) { return; } try { listener.beforeWrite(items); writeItems(items); doAfterWrite(items); } catch (Exception e) { doOnWriteError(e, items); throw e; } }
@Override public void postProcess(StepContribution contribution, Chunk<I> chunk) { for (Exception e : chunk.getErrors()) { try { getListener().onSkipInRead(e); } catch (RuntimeException ex) { throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); } } }
/** * Convenience method for calling process skip listener, so that it can be * called from multiple places. * * @param item the item that is skipped * @param e the cause of the skip */ private void callProcessSkipListener(I item, Throwable e) { try { getListener().onSkipInProcess(item, e); } catch (RuntimeException ex) { throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); } }
private void callSkipListeners(final Chunk<I> inputs, final Chunk<O> outputs) { for (SkipWrapper<I> wrapper : inputs.getSkips()) { I item = wrapper.getItem(); if (item == null) { continue; } Throwable e = wrapper.getException(); callProcessSkipListener(item, e); } for (SkipWrapper<O> wrapper : outputs.getSkips()) { Throwable e = wrapper.getException(); try { getListener().onSkipInWrite(wrapper.getItem(), e); } catch (RuntimeException ex) { throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); } } // Clear skips if we are possibly going to process this chunk again outputs.clearSkips(); inputs.clearSkips(); }