Attempt to close all delegate writers even when some fail

Issue #4750

Signed-off-by: Elimelec Burghelea <elimelec1@protonmail.com>
This commit is contained in:
Elimelec Burghelea
2025-01-23 01:59:10 +02:00
committed by Mahmoud Ben Hassine
parent f758d14ee6
commit bd8f9a8d76
2 changed files with 56 additions and 3 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -37,6 +38,7 @@ import java.util.List;
* @author Robert Kasanicky
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @author Elimelec Burghelea
*/
public class CompositeItemWriter<T> implements ItemStreamWriter<T>, InitializingBean {
@@ -105,11 +107,25 @@ public class CompositeItemWriter<T> implements ItemStreamWriter<T>, Initializing
@Override
public void close() throws ItemStreamException {
List<Exception> exceptions = new ArrayList<>();
for (ItemWriter<? super T> writer : delegates) {
if (!ignoreItemStream && (writer instanceof ItemStream)) {
((ItemStream) writer).close();
try {
((ItemStream) writer).close();
}
catch (Exception e) {
exceptions.add(e);
}
}
}
if (!exceptions.isEmpty()) {
String message = String.format("Failed to close %d delegate(s) due to exceptions", exceptions.size());
ItemStreamException holder = new ItemStreamException(message);
exceptions.forEach(holder::addSuppressed);
throw holder;
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2022 the original author or authors.
* Copyright 2008-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,14 +18,18 @@ package org.springframework.batch.item.support;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamWriter;
import org.springframework.batch.item.ItemWriter;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link CompositeItemWriter}
@@ -33,6 +37,7 @@ import static org.mockito.Mockito.mock;
* @author Robert Kasanicky
* @author Will Schipp
* @author Mahmoud Ben Hassine
* @author Elimelec Burghelea
*/
class CompositeItemWriterTests {
@@ -94,4 +99,36 @@ class CompositeItemWriterTests {
itemWriter.write(data);
}
@Test
void testCloseWithMultipleDelegate() {
AbstractFileItemWriter<String> delegate1 = mock();
AbstractFileItemWriter<String> delegate2 = mock();
CompositeItemWriter<String> itemWriter = new CompositeItemWriter<>(List.of(delegate1, delegate2));
itemWriter.close();
verify(delegate1).close();
verify(delegate2).close();
}
@Test
void testCloseWithMultipleDelegatesThatThrow() {
AbstractFileItemWriter<String> delegate1 = mock();
AbstractFileItemWriter<String> delegate2 = mock();
CompositeItemWriter<String> itemWriter = new CompositeItemWriter<>(List.of(delegate1, delegate2));
doThrow(new ItemStreamException("A failure")).when(delegate1).close();
try {
itemWriter.close();
Assertions.fail("Expected an ItemStreamException");
}
catch (ItemStreamException ignored) {
}
verify(delegate1).close();
verify(delegate2).close();
}
}