Skip to content

Commit bd8f9a8

Browse files
elimelecfmbenhassine
authored andcommitted
Attempt to close all delegate writers even when some fail
Issue #4750 Signed-off-by: Elimelec Burghelea <elimelec1@protonmail.com>
1 parent f758d14 commit bd8f9a8

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-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.
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.factory.InitializingBean;
2626
import org.springframework.util.Assert;
2727

28+
import java.util.ArrayList;
2829
import java.util.Arrays;
2930
import java.util.List;
3031

@@ -37,6 +38,7 @@
3738
* @author Robert Kasanicky
3839
* @author Dave Syer
3940
* @author Mahmoud Ben Hassine
41+
* @author Elimelec Burghelea
4042
*/
4143
public class CompositeItemWriter<T> implements ItemStreamWriter<T>, InitializingBean {
4244

@@ -105,11 +107,25 @@ public void setDelegates(List<ItemWriter<? super T>> delegates) {
105107

106108
@Override
107109
public void close() throws ItemStreamException {
110+
List<Exception> exceptions = new ArrayList<>();
111+
108112
for (ItemWriter<? super T> writer : delegates) {
109113
if (!ignoreItemStream && (writer instanceof ItemStream)) {
110-
((ItemStream) writer).close();
114+
try {
115+
((ItemStream) writer).close();
116+
}
117+
catch (Exception e) {
118+
exceptions.add(e);
119+
}
111120
}
112121
}
122+
123+
if (!exceptions.isEmpty()) {
124+
String message = String.format("Failed to close %d delegate(s) due to exceptions", exceptions.size());
125+
ItemStreamException holder = new ItemStreamException(message);
126+
exceptions.forEach(holder::addSuppressed);
127+
throw holder;
128+
}
113129
}
114130

115131
@Override

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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2022 the original author or authors.
2+
* Copyright 2008-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.
@@ -18,21 +18,26 @@
1818
import java.util.ArrayList;
1919
import java.util.List;
2020

21+
import org.junit.jupiter.api.Assertions;
2122
import org.junit.jupiter.api.Test;
2223

2324
import org.springframework.batch.item.Chunk;
2425
import org.springframework.batch.item.ExecutionContext;
26+
import org.springframework.batch.item.ItemStreamException;
2527
import org.springframework.batch.item.ItemStreamWriter;
2628
import org.springframework.batch.item.ItemWriter;
2729

30+
import static org.mockito.Mockito.doThrow;
2831
import static org.mockito.Mockito.mock;
32+
import static org.mockito.Mockito.verify;
2933

3034
/**
3135
* Tests for {@link CompositeItemWriter}
3236
*
3337
* @author Robert Kasanicky
3438
* @author Will Schipp
3539
* @author Mahmoud Ben Hassine
40+
* @author Elimelec Burghelea
3641
*/
3742
class CompositeItemWriterTests {
3843

@@ -94,4 +99,36 @@ private void doTestItemStream(boolean expectOpen) throws Exception {
9499
itemWriter.write(data);
95100
}
96101

102+
@Test
103+
void testCloseWithMultipleDelegate() {
104+
AbstractFileItemWriter<String> delegate1 = mock();
105+
AbstractFileItemWriter<String> delegate2 = mock();
106+
CompositeItemWriter<String> itemWriter = new CompositeItemWriter<>(List.of(delegate1, delegate2));
107+
108+
itemWriter.close();
109+
110+
verify(delegate1).close();
111+
verify(delegate2).close();
112+
}
113+
114+
@Test
115+
void testCloseWithMultipleDelegatesThatThrow() {
116+
AbstractFileItemWriter<String> delegate1 = mock();
117+
AbstractFileItemWriter<String> delegate2 = mock();
118+
CompositeItemWriter<String> itemWriter = new CompositeItemWriter<>(List.of(delegate1, delegate2));
119+
120+
doThrow(new ItemStreamException("A failure")).when(delegate1).close();
121+
122+
try {
123+
itemWriter.close();
124+
Assertions.fail("Expected an ItemStreamException");
125+
}
126+
catch (ItemStreamException ignored) {
127+
128+
}
129+
130+
verify(delegate1).close();
131+
verify(delegate2).close();
132+
}
133+
97134
}

0 commit comments

Comments
 (0)