Skip to content

Commit 1eac9e9

Browse files
elimelecfmbenhassine
authored andcommitted
Attempt to close all delegate readers even when some fail
Signed-off-by: Elimelec Burghelea <[email protected]>
1 parent 982ccc1 commit 1eac9e9

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemReader.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 the original author or authors.
2+
* Copyright 2024-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.batch.item.support;
1717

18+
import java.util.ArrayList;
1819
import java.util.Iterator;
1920
import java.util.List;
2021

@@ -27,6 +28,7 @@
2728
* implementation is not thread-safe.
2829
*
2930
* @author Mahmoud Ben Hassine
31+
* @author Elimelec Burghelea
3032
* @param <T> type of objects to read
3133
* @since 5.2
3234
*/
@@ -79,8 +81,22 @@ public void update(ExecutionContext executionContext) throws ItemStreamException
7981

8082
@Override
8183
public void close() throws ItemStreamException {
84+
List<Exception> exceptions = new ArrayList<>();
85+
8286
for (ItemStreamReader<? extends T> delegate : delegates) {
83-
delegate.close();
87+
try {
88+
delegate.close();
89+
}
90+
catch (Exception e) {
91+
exceptions.add(e);
92+
}
93+
}
94+
95+
if (!exceptions.isEmpty()) {
96+
String message = String.format("Failed to close %d delegate(s) due to exceptions", exceptions.size());
97+
ItemStreamException holder = new ItemStreamException(message);
98+
exceptions.forEach(holder::addSuppressed);
99+
throw holder;
84100
}
85101
}
86102

spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemReaderTests.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 the original author or authors.
2+
* Copyright 2024-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,11 +17,14 @@
1717

1818
import java.util.Arrays;
1919

20+
import org.junit.jupiter.api.Assertions;
2021
import org.junit.jupiter.api.Test;
2122

2223
import org.springframework.batch.item.ExecutionContext;
24+
import org.springframework.batch.item.ItemStreamException;
2325
import org.springframework.batch.item.ItemStreamReader;
2426

27+
import static org.mockito.Mockito.doThrow;
2528
import static org.mockito.Mockito.mock;
2629
import static org.mockito.Mockito.times;
2730
import static org.mockito.Mockito.verify;
@@ -32,6 +35,7 @@
3235
* Test class for {@link CompositeItemReader}.
3336
*
3437
* @author Mahmoud Ben Hassine
38+
* @author Elimelec Burghelea
3539
*/
3640
public class CompositeItemReaderTests {
3741

@@ -107,4 +111,27 @@ void testCompositeItemReaderClose() {
107111
verify(reader2).close();
108112
}
109113

114+
@Test
115+
void testCompositeItemReaderCloseWithDelegateThatThrowsException() {
116+
// given
117+
ItemStreamReader<String> reader1 = mock();
118+
ItemStreamReader<String> reader2 = mock();
119+
CompositeItemReader<String> compositeItemReader = new CompositeItemReader<>(Arrays.asList(reader1, reader2));
120+
121+
doThrow(new ItemStreamException("A failure")).when(reader1).close();
122+
123+
// when
124+
try {
125+
compositeItemReader.close();
126+
Assertions.fail("Expected an ItemStreamException");
127+
}
128+
catch (ItemStreamException ignored) {
129+
130+
}
131+
132+
// then
133+
verify(reader1).close();
134+
verify(reader2).close();
135+
}
136+
110137
}

0 commit comments

Comments
 (0)