@Named()
public class MyJobListener extends AbstractJobListener {
public MyJobListener();
@Override()
public void beforeJob();
@Override()
public void afterJob();
}
cd javaee7-samples/batch/batch-listeners/Now we are ready to start testing. You can run all the tests in this sample by executing:
mvn test
Or you can run individual tests by executing one of the following:mvn test -Dtest=BatchListenersTest
The Batch specification, provides several listeners to notify about specific event occurring during the batch processing execution.
Events can be caught via extending the following classes, for the appropriate batch lifecycle event:
The Job Listener:
@Named()
public class MyJobListener extends AbstractJobListener {
public MyJobListener();
@Override()
public void beforeJob();
@Override()
public void afterJob();
}
Allows you to execute code before and after the job execution. Useful to setup and clear resources needed by the job.
The Step Listener:
@Named()
public class MyStepListener extends AbstractStepListener {
public MyStepListener();
@Override()
public void beforeStep() throws Exception;
@Override()
public void afterStep() throws Exception;
}
Allows you to execute code before and after the step execution. Useful to setup and clear resources needed by the step.
The Chunk Listener:
@Named()
public class MyChunkListener extends AbstractChunkListener {
public MyChunkListener();
@Override()
public void beforeChunk() throws Exception;
@Override()
public void afterChunk() throws Exception;
}
Allows you to execute code before and after the chunk processing. Useful to setup and clear resources needed by the chunk.
The Read Listener:
@Named()
public class MyItemReadListener extends AbstractItemReadListener {
public MyItemReadListener();
@Override()
public void beforeRead() throws Exception;
@Override()
public void afterRead(Object item) throws Exception;
@Override()
public void onReadError(Exception ex) throws Exception;
}
Allows you to execute code before and after reading a element as well if an error occurs reading that element. Useful to setup additional resources and add additional information to the object reading. You can also provide some logic to treat a failed object read.
The Processor Listener:
@Named()
public class MyItemProcessorListener extends AbstractItemProcessListener {
public MyItemProcessorListener();
@Override()
public void beforeProcess(Object item) throws Exception;
@Override()
public void afterProcess(Object item, Object result) throws Exception;
@Override()
public void onProcessError(Object item, Exception ex) throws Exception;
}
Allows you to execute code before and after processing a element as well if an error occurs processing that element. Useful to setup additional resources and add additional information to the object processing. You can also provide some logic to treat a failed object processing.
The Writer Listener:
@Named()
public class MyItemWriteListener extends AbstractItemWriteListener {
public MyItemWriteListener();
@Override()
public void beforeWrite(List items) throws Exception;
@Override()
public void afterWrite(List items) throws Exception;
@Override()
public void onWriteError(List items, Exception ex) throws Exception;
}
Allows you to execute code before and after writing a element as well if an error occurs writing that element. Useful to setup additional resources and add additional information to the object writing. You can also provide some logic to treat a failed object write.
The listeners
element can be used at the step
level or the job
level to define which listeners to run for each
batch processing event.
<?xml version="1.0" encoding="UTF-8"?>
<job id="myJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<listeners>
<listener ref="myJobListener"/>
</listeners>
<step id="myStep" >
<listeners>
<listener ref="myStepListener"/>
<listener ref="myChunkListener"/>
<listener ref="myItemReadListener"/>
<listener ref="myItemProcessorListener"/>
<listener ref="myItemWriteListener"/>
</listeners>
<chunk item-count="3">
<reader ref="myItemReader"/>
<processor ref="myItemProcessor"/>
<writer ref="myItemWriter"/>
</chunk>
</step>
</job>
We’re just going to deploy the application as a web archive
. Note the inclusion of the following files:
/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.batch.listeners")
.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 Metric
object available in the step execution and
also verify if the listeners were executed correctly via a CountDownLatch
wait.
The batch process itself will read and process 10 elements from numbers 1 to 10, but only write the odd elements.
Each listener will decrement the total value of the CountDownLatch
, until all the predicted events are
executed. The number of predicted events is 60:
MyJobListener
executes 2 times, 1 for MyJobListener#beforeJob
and 1 more for MyJobListener#afterJob
.
MyStepListener
executes 2 times, 1 for MyStepListener#beforeStep
and 1 more for MyStepListener#afterStep
.
MyChunkListener
executes 8 times, 4 for MyChunkListener#beforeChunk
and 4 more
for MyChunkListener#afterChunk
. Chunk size is set to 3 and the total elements is 10, so 10/3 = 3 and 1 more
for the last element, means 4 for each chunk listener event.
MyItemReader
executes 22 times, 10 elements in total plus an empty read, so MyItemReadListener#beforeRead
executes 11 times and MyItemReadListener#afterRead
the other 11 times.
MyItemProcessorListener
executes 20 times, 10 elements read in total,
so MyItemProcessorLister#beforeProcess
executes 10 times
and MyItemProcessorLister#afterProcess
the other 10 times.
MyItemWriterListener
executed 6 times, 3 times for MyItemWriterListener#beforeWrite
and another 3 times
for MyItemWriterListener#afterWrite
. This one is a bit more tricky, since not every element needs to be
written. Looking at MyItemProcessor
, only even records are going to be written. We also need to take into
account the elements read per chunk, so: Chunk[1] read and process [1,2,3] and wrote [2,6], Chunk[2] read and
process [4,5,6] and wrote [10], Chunk[3] read and process [7,8,9] and wrote [14,18], Chunk[4] read and process
[10] and did not wrote anything, so only 3 writes for the full processing.
Total: 2 + 2 + 8 + 22 + 20 + 6 = 60
@Test
public void testBatchListeners() 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(10L, metricsMap.get(Metric.MetricType.READ_COUNT).longValue());
assertEquals(10L / 2L, metricsMap.get(Metric.MetricType.WRITE_COUNT).longValue());
assertEquals(10L / 3 + (10L % 3 > 0 ? 1 : 0), metricsMap.get(Metric.MetricType.COMMIT_COUNT).longValue());
}
}
assertTrue(BatchListenerRecorder.batchListenersCountDownLatch.await(0, TimeUnit.SECONDS));
assertEquals(jobExecution.getBatchStatus(), BatchStatus.COMPLETED);
}
There's a lot more about JavaEE to cover. If you're ready to learn more, check out the other available samples.
git clone git://github.com/javaee-samples/javaee7-samples.git
cd javaee7-samples/batch/batch-listeners/
Do the changes as you see fit and send a pull request!
Good Luck!