|
15 | 15 | */
|
16 | 16 | package org.springframework.batch.integration.chunk;
|
17 | 17 |
|
| 18 | +import java.io.IOException; |
18 | 19 | import java.util.Arrays;
|
| 20 | +import java.util.List; |
19 | 21 |
|
20 | 22 | import org.junit.Assert;
|
21 | 23 | import org.junit.Rule;
|
22 | 24 | import org.junit.Test;
|
23 | 25 | import org.junit.rules.ExpectedException;
|
24 | 26 | import org.junit.runner.RunWith;
|
25 | 27 |
|
| 28 | +import org.springframework.batch.core.ChunkListener; |
| 29 | +import org.springframework.batch.core.ItemReadListener; |
| 30 | +import org.springframework.batch.core.ItemWriteListener; |
| 31 | +import org.springframework.batch.core.JobExecution; |
| 32 | +import org.springframework.batch.core.JobParameters; |
| 33 | +import org.springframework.batch.core.SkipListener; |
| 34 | +import org.springframework.batch.core.StepExecution; |
| 35 | +import org.springframework.batch.core.StepExecutionListener; |
26 | 36 | import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
27 |
| -import org.springframework.batch.core.listener.ChunkListenerSupport; |
28 |
| -import org.springframework.batch.core.listener.CompositeItemReadListener; |
29 |
| -import org.springframework.batch.core.listener.CompositeItemWriteListener; |
30 |
| -import org.springframework.batch.core.listener.SkipListenerSupport; |
31 |
| -import org.springframework.batch.core.listener.StepExecutionListenerSupport; |
32 | 37 | import org.springframework.batch.core.repository.JobRepository;
|
| 38 | +import org.springframework.batch.core.step.item.ChunkOrientedTasklet; |
| 39 | +import org.springframework.batch.core.step.item.SimpleChunkProcessor; |
| 40 | +import org.springframework.batch.core.step.item.SimpleChunkProvider; |
33 | 41 | import org.springframework.batch.core.step.tasklet.TaskletStep;
|
| 42 | +import org.springframework.batch.item.ItemProcessor; |
34 | 43 | import org.springframework.batch.item.ItemReader;
|
35 | 44 | import org.springframework.batch.item.ItemStreamSupport;
|
| 45 | +import org.springframework.batch.item.ItemWriter; |
| 46 | +import org.springframework.batch.item.support.CompositeItemStream; |
36 | 47 | import org.springframework.batch.item.support.ListItemReader;
|
37 |
| -import org.springframework.batch.repeat.exception.DefaultExceptionHandler; |
38 | 48 | import org.springframework.batch.repeat.support.RepeatTemplate;
|
39 | 49 | import org.springframework.beans.factory.annotation.Autowired;
|
40 | 50 | import org.springframework.context.annotation.Configuration;
|
41 | 51 | import org.springframework.integration.channel.DirectChannel;
|
42 | 52 | import org.springframework.integration.channel.QueueChannel;
|
| 53 | +import org.springframework.integration.core.MessagingTemplate; |
43 | 54 | import org.springframework.messaging.PollableChannel;
|
| 55 | +import org.springframework.retry.RetryListener; |
44 | 56 | import org.springframework.retry.backoff.NoBackOffPolicy;
|
45 |
| -import org.springframework.retry.listener.RetryListenerSupport; |
46 | 57 | import org.springframework.retry.policy.MapRetryContextCache;
|
47 | 58 | import org.springframework.test.context.ContextConfiguration;
|
48 | 59 | import org.springframework.test.context.junit4.SpringRunner;
|
| 60 | +import org.springframework.test.util.ReflectionTestUtils; |
49 | 61 | import org.springframework.transaction.PlatformTransactionManager;
|
50 | 62 | import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
51 | 63 |
|
| 64 | +import static org.mockito.Mockito.any; |
| 65 | +import static org.mockito.Mockito.atLeastOnce; |
| 66 | +import static org.mockito.Mockito.mock; |
| 67 | +import static org.mockito.Mockito.verify; |
| 68 | +import static org.mockito.Mockito.when; |
52 | 69 | /**
|
53 | 70 | * @author Mahmoud Ben Hassine
|
54 | 71 | */
|
@@ -216,44 +233,136 @@ public void testMasterStepCreation() {
|
216 | 233 | * The following test is to cover setters that override those from parent builders.
|
217 | 234 | */
|
218 | 235 | @Test
|
219 |
| - public void testSetters() { |
| 236 | + public void testSetters() throws Exception { |
220 | 237 | // when
|
| 238 | + DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(); |
| 239 | + |
| 240 | + Object annotatedListener = new Object(); |
| 241 | + MapRetryContextCache retryCache = new MapRetryContextCache(); |
| 242 | + RepeatTemplate stepOperations = new RepeatTemplate(); |
| 243 | + NoBackOffPolicy backOffPolicy = new NoBackOffPolicy(); |
| 244 | + ItemStreamSupport stream = new ItemStreamSupport() { |
| 245 | + }; |
| 246 | + StepExecutionListener stepExecutionListener = mock(StepExecutionListener.class); |
| 247 | + ItemReadListener<String> itemReadListener = mock(ItemReadListener.class); |
| 248 | + ItemWriteListener<String> itemWriteListener = mock(ItemWriteListener.class); |
| 249 | + ChunkListener chunkListener = mock(ChunkListener.class); |
| 250 | + SkipListener<String, String> skipListener = mock(SkipListener.class); |
| 251 | + RetryListener retryListener = mock(RetryListener.class); |
| 252 | + |
| 253 | + when(retryListener.open(any(), any())).thenReturn(true); |
| 254 | + |
| 255 | + ItemProcessor<String, String> itemProcessor = item -> { |
| 256 | + System.out.println("processing item " + item); |
| 257 | + if(item.equals("b")) { |
| 258 | + throw new Exception("b was found"); |
| 259 | + } |
| 260 | + else { |
| 261 | + return item; |
| 262 | + } |
| 263 | + }; |
| 264 | + |
| 265 | + ItemReader<String> itemReader = new ItemReader<String>() { |
| 266 | + |
| 267 | + int count = 0; |
| 268 | + List<String> items = Arrays.asList("a", "b", "c", "d", "d", "e", "f", "g", "h", "i"); |
| 269 | + |
| 270 | + @Override |
| 271 | + public String read() throws Exception { |
| 272 | + System.out.println(">> count == " + count); |
| 273 | + if(count == 6) { |
| 274 | + count++; |
| 275 | + throw new IOException("6th item"); |
| 276 | + } |
| 277 | + else if(count == 7) { |
| 278 | + count++; |
| 279 | + throw new RuntimeException("7th item"); |
| 280 | + } |
| 281 | + else if(count < items.size()){ |
| 282 | + String item = items.get(count++); |
| 283 | + System.out.println(">> item read was " + item); |
| 284 | + return item; |
| 285 | + } |
| 286 | + else { |
| 287 | + return null; |
| 288 | + } |
| 289 | + } |
| 290 | + }; |
| 291 | + |
221 | 292 | TaskletStep taskletStep = new RemoteChunkingMasterStepBuilder<String, String>("step")
|
222 |
| - .reader(this.itemReader) |
| 293 | + .reader(itemReader) |
223 | 294 | .readerIsTransactionalQueue()
|
| 295 | + .processor(itemProcessor) |
224 | 296 | .repository(this.jobRepository)
|
225 | 297 | .transactionManager(this.transactionManager)
|
226 |
| - .transactionAttribute(new DefaultTransactionAttribute()) |
| 298 | + .transactionAttribute(transactionAttribute) |
227 | 299 | .inputChannel(this.inputChannel)
|
228 | 300 | .outputChannel(this.outputChannel)
|
229 |
| - .listener(new Object()) |
230 |
| - .listener(new SkipListenerSupport<>()) |
231 |
| - .listener(new ChunkListenerSupport()) |
232 |
| - .listener(new StepExecutionListenerSupport()) |
233 |
| - .listener(new CompositeItemReadListener<>()) |
234 |
| - .listener(new CompositeItemWriteListener<>()) |
235 |
| - .listener(new RetryListenerSupport()) |
| 301 | + .listener(annotatedListener) |
| 302 | + .listener(skipListener) |
| 303 | + .listener(chunkListener) |
| 304 | + .listener(stepExecutionListener) |
| 305 | + .listener(itemReadListener) |
| 306 | + .listener(itemWriteListener) |
| 307 | + .listener(retryListener) |
236 | 308 | .skip(Exception.class)
|
237 | 309 | .noSkip(RuntimeException.class)
|
238 | 310 | .skipLimit(10)
|
239 |
| - .retry(Exception.class) |
| 311 | + .retry(IOException.class) |
240 | 312 | .noRetry(RuntimeException.class)
|
241 | 313 | .retryLimit(10)
|
242 |
| - .retryContextCache(new MapRetryContextCache()) |
| 314 | + .retryContextCache(retryCache) |
243 | 315 | .noRollback(Exception.class)
|
244 |
| - .chunk(10) |
245 | 316 | .startLimit(3)
|
246 | 317 | .allowStartIfComplete(true)
|
247 |
| - .exceptionHandler(new DefaultExceptionHandler()) |
248 |
| - .stepOperations(new RepeatTemplate()) |
249 |
| - .chunkOperations(new RepeatTemplate()) |
250 |
| - .backOffPolicy(new NoBackOffPolicy()) |
251 |
| - .stream(new ItemStreamSupport() {}) |
| 318 | + .stepOperations(stepOperations) |
| 319 | + .chunk(3) |
| 320 | + .backOffPolicy(backOffPolicy) |
| 321 | + .stream(stream) |
252 | 322 | .keyGenerator(Object::hashCode)
|
253 | 323 | .build();
|
254 | 324 |
|
| 325 | + JobExecution jobExecution = this.jobRepository.createJobExecution("job1", new JobParameters()); |
| 326 | + StepExecution stepExecution = new StepExecution("step1", jobExecution); |
| 327 | + this.jobRepository.add(stepExecution); |
| 328 | + |
| 329 | + taskletStep.execute(stepExecution); |
| 330 | + |
255 | 331 | // then
|
256 | 332 | Assert.assertNotNull(taskletStep);
|
| 333 | + ChunkOrientedTasklet tasklet = (ChunkOrientedTasklet) ReflectionTestUtils.getField(taskletStep, "tasklet"); |
| 334 | + SimpleChunkProvider provider = (SimpleChunkProvider) ReflectionTestUtils.getField(tasklet, "chunkProvider"); |
| 335 | + SimpleChunkProcessor processor = (SimpleChunkProcessor) ReflectionTestUtils.getField(tasklet, "chunkProcessor"); |
| 336 | + ItemWriter itemWriter = (ItemWriter) ReflectionTestUtils.getField(processor, "itemWriter"); |
| 337 | + MessagingTemplate messagingTemplate = (MessagingTemplate) ReflectionTestUtils.getField(itemWriter, "messagingGateway"); |
| 338 | + CompositeItemStream compositeItemStream = (CompositeItemStream) ReflectionTestUtils.getField(taskletStep, "stream"); |
| 339 | + |
| 340 | + Assert.assertEquals(ReflectionTestUtils.getField(provider, "itemReader"), itemReader); |
| 341 | + Assert.assertFalse((Boolean) ReflectionTestUtils.getField(tasklet, "buffering")); |
| 342 | + Assert.assertEquals(ReflectionTestUtils.getField(taskletStep, "jobRepository"), this.jobRepository); |
| 343 | + Assert.assertEquals(ReflectionTestUtils.getField(taskletStep, "transactionManager"), this.transactionManager); |
| 344 | + Assert.assertEquals(ReflectionTestUtils.getField(taskletStep, "transactionAttribute"), transactionAttribute); |
| 345 | + Assert.assertEquals(ReflectionTestUtils.getField(itemWriter, "replyChannel"), this.inputChannel); |
| 346 | + Assert.assertEquals(ReflectionTestUtils.getField(messagingTemplate, "defaultDestination"), this.outputChannel); |
| 347 | + Assert.assertEquals(ReflectionTestUtils.getField(processor, "itemProcessor"), itemProcessor); |
| 348 | + |
| 349 | + Assert.assertEquals((int) ReflectionTestUtils.getField(taskletStep, "startLimit"), 3); |
| 350 | + Assert.assertTrue((Boolean) ReflectionTestUtils.getField(taskletStep, "allowStartIfComplete")); |
| 351 | + Object stepOperationsUsed = ReflectionTestUtils.getField(taskletStep, "stepOperations"); |
| 352 | + Assert.assertEquals(stepOperationsUsed, stepOperations); |
| 353 | + |
| 354 | + Assert.assertEquals(((List)ReflectionTestUtils.getField(compositeItemStream, "streams")).size(), 2); |
| 355 | + Assert.assertNotNull(ReflectionTestUtils.getField(processor, "keyGenerator")); |
| 356 | + |
| 357 | + verify(skipListener, atLeastOnce()).onSkipInProcess(any(), any()); |
| 358 | + verify(retryListener, atLeastOnce()).open(any(), any()); |
| 359 | + verify(stepExecutionListener, atLeastOnce()).beforeStep(any()); |
| 360 | + verify(chunkListener, atLeastOnce()).beforeChunk(any()); |
| 361 | + verify(itemReadListener, atLeastOnce()).beforeRead(); |
| 362 | + verify(itemWriteListener, atLeastOnce()).beforeWrite(any()); |
| 363 | + |
| 364 | + Assert.assertEquals(stepExecution.getSkipCount(), 2); |
| 365 | + Assert.assertEquals(stepExecution.getRollbackCount(), 3); |
257 | 366 | }
|
258 | 367 |
|
259 | 368 | @Configuration
|
|
0 commit comments