package org.javaee7.batch.chunk.exception; import org.javaee7.util.BatchTestHelper; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Test; import org.junit.runner.RunWith; import javax.batch.operations.JobOperator; import javax.batch.runtime.*; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Exceptions are a natural part of Batch Processing, and the batch itself should be prepared to deal with * exceptions during processing. * * Batch Processing deals with two kinds of exceptions: skippable and retryable. Skippable Exceptions are used to skip * elements during reading, processing and writing and continue to the next element. Retryable Exceptions on the other * hand when thrown will try to retry the chunk on which the exception occurred. * * When the same exception is specified as both retryable and skippable, retryable takes precedence over skippable * during regular processing of the chunk. While the chunk is retrying, skippable takes precedence over retryable since * the exception is already being retried. * * The Reader: * include::MyItemReader[] * * Just reads elements from a list and simulate a retry exception. * * The Processor: * include::MyItemProcessor[] * * Process and simulate a skip exception. * * The Writer: * include::MyItemWriter[] * * The writer will retry an exception and then skip it. * * The batch specification also allows you to provide listeners for skipping and retrying for every operation. Have a * look into the following classes: * * * +MySkipReadListener+ * * +MySkipProcessorListener+ * * +MySkipWriteListener+ * * +MyRetryReadListener+ * * +MyRetryProcessorListener+ * * +MyRetryWriteListener+ * * Events can be caught via extending the following classes, for the appropriate batch lifecycle event: * * * +javax.batch.api.chunk.listener.SkipReadListener+ * * +javax.batch.api.chunk.listener.SkipProcessListener+ * * +javax.batch.api.chunk.listener.SkipWriteListener+ * * +javax.batch.api.chunk.listener.RetryReadListener+ * * +javax.batch.api.chunk.listener.RetryProcessListener+ * * +javax.batch.api.chunk.listener.RetryWriteListener+ * * include::myJob.xml[] * * A very simple job is defined in the +myJob.xml+ file. Just a single step with a reader, a processor and a writer. For * this sample we are going to process a few records and mix some exceptions during read, processing and write of the * chunk. Batch exception handling is achieved by defining the elements +skippable-exception-classes+ and * +retryable-exception-classes+ into the +chunk+. Both elements should indicate the full qualified name of the * exceptions that we are trying to catch. The +listeners+ element can be used at the +step+ level to define which * listeners to run for each batch processing event. * * @author Roberto Cortez */ @RunWith(Arquillian.class) public class BatchChunkExceptionTest { /** * We're just going to deploy the application as a +web archive+. Note the inclusion of the following files: * * [source,file] * ---- * /META-INF/batch-jobs/myJob.xml * ---- * * The +myJob.xml+ file is needed for running the batch definition. */ @Deployment public static WebArchive createDeployment() { WebArchive war = ShrinkWrap.create(WebArchive.class) .addClass(BatchTestHelper.class) .addPackage("org.javaee7.batch.chunk.exception") .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")) .addAsResource("META-INF/batch-jobs/myJob.xml"); System.out.println(war.toString(true)); return war; } /** * In the test, we're just going to invoke the batch execution and wait for completion. To validate the test * expected behaviour we need to query the +javax.batch.runtime.Metric+ object available in the step execution. * * @throws Exception an exception if the batch could not complete successfully. */ @Test public void testBatchChunkException() throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); Long executionId = jobOperator.start("myJob", new Properties()); JobExecution jobExecution = jobOperator.getJobExecution(executionId); jobExecution = BatchTestHelper.keepTestAlive(jobExecution); List<StepExecution> stepExecutions = jobOperator.getStepExecutions(executionId); for (StepExecution stepExecution : stepExecutions) { if (stepExecution.getStepName().equals("myStep")) { Map<Metric.MetricType, Long> metricsMap = BatchTestHelper.getMetricsMap(stepExecution.getMetrics()); assertEquals(1L, metricsMap.get(Metric.MetricType.PROCESS_SKIP_COUNT).longValue()); // There are a few differences between Glassfish and Wildfly. Needs investigation. //assertEquals(1L, metricsMap.get(Metric.MetricType.WRITE_SKIP_COUNT).longValue()); assertEquals(1L, ChunkExceptionRecorder.retryReadExecutions); } } assertTrue(ChunkExceptionRecorder.chunkExceptionsCountDownLatch.await(0, TimeUnit.SECONDS)); assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus()); } }