Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.batch.infrastructure.item.file.builder;

import java.io.IOException;
import java.util.Comparator;

import org.jspecify.annotations.Nullable;
Expand All @@ -25,6 +26,8 @@
import org.springframework.batch.infrastructure.item.file.MultiResourceItemReader;
import org.springframework.batch.infrastructure.item.file.ResourceAwareItemReaderItemStream;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

Expand All @@ -34,6 +37,7 @@
* @author Glenn Renfro
* @author Drummond Dawson
* @author Stefano Cordio
* @author Sanghyuk Jung
* @since 4.0
* @see MultiResourceItemReader
*/
Expand All @@ -43,6 +47,8 @@ public class MultiResourceItemReaderBuilder<T> {

private Resource @Nullable [] resources;

private @Nullable String filesPattern;

private boolean strict = false;

private @Nullable Comparator<Resource> comparator;
Expand Down Expand Up @@ -90,6 +96,19 @@ public MultiResourceItemReaderBuilder<T> resources(Resource... resources) {
return this;
}

/**
* The location pattern of files that the {@link MultiResourceItemReader} will use to
* retrieve items. This is an Ant-style pattern that supports wildcards like `*`, `**`
* and `?`(for example `/data/*.csv`or `data/**\/user?.txt`).
* @param filesPattern the location pattern of files to use.
* @return this instance for method chaining.
*/
public MultiResourceItemReaderBuilder<T> filesPattern(String filesPattern) {
this.filesPattern = filesPattern;

return this;
}

/**
* Establishes the delegate to use for reading the resources provided.
* @param delegate reads items from single {@link Resource}.
Expand Down Expand Up @@ -135,14 +154,31 @@ public MultiResourceItemReaderBuilder<T> comparator(Comparator<Resource> compara
* @return a {@link MultiResourceItemReader}
*/
public MultiResourceItemReader<T> build() {
Assert.notNull(this.resources, "resources array is required.");
Assert.isTrue(this.resources != null || this.filesPattern != null,
"resources array or filesPattern is required.");

Assert.notNull(this.delegate, "delegate is required.");
if (this.saveState) {
Assert.state(StringUtils.hasText(this.name), "A name is required when saveState is set to true.");
}

MultiResourceItemReader<T> reader = new MultiResourceItemReader<>(this.delegate);
reader.setResources(this.resources);

if (this.resources != null) {
reader.setResources(this.resources);
}
else if (this.filesPattern != null) {
ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = patternResolver.getResources("file:" + this.filesPattern);
reader.setResources(resources);
}
catch (IOException e) {
throw new IllegalArgumentException("Unable to initialize resources by the pattern " + this.filesPattern,
e);
}
}

reader.setSaveState(this.saveState);
reader.setStrict(this.strict);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
import org.springframework.batch.infrastructure.item.file.FlatFileItemReader;
import org.springframework.batch.infrastructure.item.file.LineMapper;
import org.springframework.batch.infrastructure.item.file.MultiResourceItemReader;
import org.springframework.batch.infrastructure.item.file.mapping.PassThroughLineMapper;
import org.springframework.batch.infrastructure.item.sample.Foo;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -79,7 +82,26 @@ void testNullDelegate() {
void testNullResources() {
Exception exception = assertThrows(IllegalArgumentException.class,
() -> new MultiResourceItemReaderBuilder<String>().delegate(mock(FlatFileItemReader.class)).build());
assertEquals("resources array is required.", exception.getMessage());
assertEquals("resources array or filesPattern is required.", exception.getMessage());
}

@Test
void testFilesPattern() throws Exception {
FlatFileItemReader<String> delegate = new FlatFileItemReaderBuilder<String>().name("textReader")
.lineMapper(new PassThroughLineMapper())
.build();

String basePath = new ClassPathResource("", this.getClass()).getFile().getPath();
MultiResourceItemReader<String> reader = new MultiResourceItemReaderBuilder<String>().delegate(delegate)
.filesPattern(basePath + "/test?.txt")
.name("multiFileReader")
.build();

reader.open(new ExecutionContext());
assertEquals("1", reader.read());
assertEquals("2", reader.read());
assertNull(reader.read());
reader.close();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2