Skip to content

Commit

Permalink
GH-9594: Fix FileReadingMessageSource for FileHeaders.RELATIVE_PATH
Browse files Browse the repository at this point in the history
Fixes: #9594
Issue link: #9594

The `String.replaceFirst()` for directory with `[]` or `()` leads to a regex execution
which does not really replace the root path because of mismatch between regex and file path.
Essentially, the `Matcher.quoteReplacement()` does not do the trick we would expect from it.

* Use `Path.relativize()` API instead which works in canonical paths and proper file separators

(cherry picked from commit 9149409)
  • Loading branch information
artembilan authored and spring-builds committed Oct 25, 2024
1 parent 3eb380b commit 7170f27
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.regex.Matcher;

import org.springframework.context.Lifecycle;
import org.springframework.integration.endpoint.AbstractMessageSource;
Expand Down Expand Up @@ -389,10 +388,7 @@ protected AbstractIntegrationMessageBuilder<File> doReceive() {
if (file != null) {
return getMessageBuilderFactory()
.withPayload(file)
.setHeader(FileHeaders.RELATIVE_PATH,
file.getAbsolutePath()
.replaceFirst(Matcher.quoteReplacement(
this.directory.getAbsolutePath() + File.separator), ""))
.setHeader(FileHeaders.RELATIVE_PATH, this.directory.toPath().relativize(file.toPath()).toString())
.setHeader(FileHeaders.FILENAME, file.getName())
.setHeader(FileHeaders.ORIGINAL_FILE, file);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
Expand All @@ -17,15 +17,12 @@
package org.springframework.integration.file;

import java.io.File;
import java.nio.file.Path;
import java.util.Comparator;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.messaging.Message;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -42,43 +39,40 @@
* @author Artem Bilan
* @author Gary Russell
*/
@RunWith(MockitoJUnitRunner.class)
public class FileReadingMessageSourceTests {
class FileReadingMessageSourceTests {

private FileReadingMessageSource source;

@Mock
private File inputDirectoryMock;
private final File inputDirectoryMock = mock();

@Mock
private File fileMock;
private final File fileMock = mock();

@Mock
private FileLocker locker;
private final FileLocker locker = mock();

@Mock
private Comparator<File> comparator;
private final Comparator<File> comparator = mock();

public void prepResource() {
when(inputDirectoryMock.getAbsolutePath()).thenReturn("foo/bar");
when(fileMock.getAbsolutePath()).thenReturn("foo/bar/fileMock");
when(inputDirectoryMock.toPath()).thenReturn(Path.of("[dir]"));
when(fileMock.toPath()).thenReturn(Path.of("[dir]/fileMock"));
when(locker.lock(isA(File.class))).thenReturn(true);
}

@Before
@BeforeEach
public void initialize() {
prepResource();
this.source = new FileReadingMessageSource(comparator);
this.source.setDirectory(inputDirectoryMock);
this.source.setLocker(locker);
this.source.setBeanFactory(mock(BeanFactory.class));
this.source.setBeanFactory(mock());
this.source.afterPropertiesSet();
}

@Test
public void straightProcess() {
when(inputDirectoryMock.listFiles()).thenReturn(new File[] {fileMock});
assertThat(source.receive().getPayload()).isEqualTo(fileMock);
Message<File> fileMessage = source.receive();
assertThat(fileMessage.getPayload()).isEqualTo(fileMock);
assertThat(fileMessage.getHeaders()).containsEntry(FileHeaders.RELATIVE_PATH, "fileMock");
}

@Test
Expand All @@ -88,13 +82,13 @@ public void requeueOnFailure() {
assertThat(received).isNotNull();
source.onFailure(received);
assertThat(source.receive().getPayload()).isEqualTo(received.getPayload());
verify(inputDirectoryMock, times(1)).listFiles();
verify(inputDirectoryMock).listFiles();
}

@Test
public void scanEachPoll() {
File anotherFileMock = mock(File.class);
when(anotherFileMock.getAbsolutePath()).thenReturn("foo/bar/anotherFileMock");
File anotherFileMock = mock();
when(anotherFileMock.toPath()).thenReturn(Path.of("[dir]/anotherFileMock"));
when(inputDirectoryMock.listFiles()).thenReturn(new File[] {fileMock, anotherFileMock});
source.setScanEachPoll(true);
assertThat(source.receive()).isNotNull();
Expand Down Expand Up @@ -139,12 +133,12 @@ public void lockedFilesAreIgnored() {

@Test
public void orderedReception() {
File file1 = mock(File.class);
when(file1.getAbsolutePath()).thenReturn("foo/bar/file1");
File file2 = mock(File.class);
when(file2.getAbsolutePath()).thenReturn("foo/bar/file2");
File file3 = mock(File.class);
when(file3.getAbsolutePath()).thenReturn("foo/bar/file3");
File file1 = mock();
when(file1.toPath()).thenReturn(Path.of("[dir]/file1"));
File file2 = mock();
when(file2.toPath()).thenReturn(Path.of("[dir]/file2"));
File file3 = mock();
when(file3.toPath()).thenReturn(Path.of("[dir]/file3"));

// record the comparator to reverse order the files
when(comparator.compare(file1, file2)).thenReturn(1);
Expand Down

0 comments on commit 7170f27

Please sign in to comment.