Attempt to close all delegate readers even when some fail

Signed-off-by: Elimelec Burghelea <elimelec1@protonmail.com>
This commit is contained in:
Elimelec Burghelea
2025-02-20 21:00:38 +02:00
committed by Mahmoud Ben Hassine
parent 982ccc1c1c
commit 1eac9e9fee
2 changed files with 46 additions and 3 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-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.
@@ -15,6 +15,7 @@
*/
package org.springframework.batch.item.support;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -27,6 +28,7 @@ import org.springframework.batch.item.ItemStreamReader;
* implementation is not thread-safe.
*
* @author Mahmoud Ben Hassine
* @author Elimelec Burghelea
* @param <T> type of objects to read
* @since 5.2
*/
@@ -79,8 +81,22 @@ public class CompositeItemReader<T> implements ItemStreamReader<T> {
@Override
public void close() throws ItemStreamException {
List<Exception> exceptions = new ArrayList<>();
for (ItemStreamReader<? extends T> delegate : delegates) {
delegate.close();
try {
delegate.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;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-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.
@@ -17,11 +17,14 @@ package org.springframework.batch.item.support;
import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -32,6 +35,7 @@ import static org.mockito.Mockito.when;
* Test class for {@link CompositeItemReader}.
*
* @author Mahmoud Ben Hassine
* @author Elimelec Burghelea
*/
public class CompositeItemReaderTests {
@@ -107,4 +111,27 @@ public class CompositeItemReaderTests {
verify(reader2).close();
}
@Test
void testCompositeItemReaderCloseWithDelegateThatThrowsException() {
// given
ItemStreamReader<String> reader1 = mock();
ItemStreamReader<String> reader2 = mock();
CompositeItemReader<String> compositeItemReader = new CompositeItemReader<>(Arrays.asList(reader1, reader2));
doThrow(new ItemStreamException("A failure")).when(reader1).close();
// when
try {
compositeItemReader.close();
Assertions.fail("Expected an ItemStreamException");
}
catch (ItemStreamException ignored) {
}
// then
verify(reader1).close();
verify(reader2).close();
}
}