OPEN - issue INT-583: FileReadingMessageSource rescanning directory every pass
http://jira.springframework.org/browse/INT-583 Fixed issue, added flag to go back to old behavior. Refactored test to Mockito.
This commit is contained in:
@@ -54,8 +54,8 @@ import org.springframework.util.Assert;
|
||||
* a {@link Resequencer}, but in cases where writing files and failure
|
||||
* downstream are rare it might be sufficient.
|
||||
* <p/>
|
||||
* FileReadingMessageSource is fully thread-safe under concurrent receives and
|
||||
* message delivery callbacks.
|
||||
* FileReadingMessageSource is fully thread-safe under concurrent
|
||||
* <code>receive()</code> invocations and message delivery callbacks.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
@@ -76,6 +76,8 @@ public class FileReadingMessageSource implements MessageSource<File> {
|
||||
|
||||
private volatile FileListFilter filter = new AcceptOnceFileListFilter();
|
||||
|
||||
private boolean scanEachPoll = false;
|
||||
|
||||
/**
|
||||
* Creates a FileReadingMessageSource with a naturally ordered queue.
|
||||
*/
|
||||
@@ -109,8 +111,8 @@ public class FileReadingMessageSource implements MessageSource<File> {
|
||||
/**
|
||||
* Sets a {@link FileListFilter}. By default a
|
||||
* {@link AcceptOnceFileListFilter} with no bounds is used. In most cases a
|
||||
* customized {@link FileListFilter} will be needed to deal with modification
|
||||
* and duplication concerns. If multiple filters are required a
|
||||
* customized {@link FileListFilter} will be needed to deal with
|
||||
* modification and duplication concerns. If multiple filters are required a
|
||||
* {@link CompositeFileListFilter} can be used to group them together
|
||||
* <p/>
|
||||
* <b>The supplied filter must be thread safe.</b>.
|
||||
@@ -120,9 +122,23 @@ public class FileReadingMessageSource implements MessageSource<File> {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional. Set this flag if you want to make sure the internal queue is
|
||||
* refreshed with the latest content of the input directory on each poll.
|
||||
* <p/>
|
||||
* By default this implementation will empty its queue before looking at the
|
||||
* directory again. In cases where order
|
||||
*/
|
||||
public void setScanEachPoll(boolean scanEachPoll) {
|
||||
this.scanEachPoll = scanEachPoll;
|
||||
}
|
||||
|
||||
public Message<File> receive() throws MessagingException {
|
||||
refreshQueue();
|
||||
Message<File> message = null;
|
||||
// rescan only if needed or explicitly configured
|
||||
if (toBeReceived.isEmpty() || scanEachPoll) {
|
||||
scanInputDirectory();
|
||||
}
|
||||
File file = toBeReceived.poll();
|
||||
// we can't rely on isEmpty for concurrency reasons
|
||||
if (file != null) {
|
||||
@@ -134,7 +150,7 @@ public class FileReadingMessageSource implements MessageSource<File> {
|
||||
return message;
|
||||
}
|
||||
|
||||
private void refreshQueue() {
|
||||
private void scanInputDirectory() {
|
||||
List<File> filteredFiles = filter.filterFiles((inputDirectory.listFiles()));
|
||||
Set<File> freshFiles = new HashSet<File>(filteredFiles);
|
||||
if (!freshFiles.isEmpty()) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import static org.easymock.classextension.EasyMock.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -26,6 +26,9 @@ import java.util.Comparator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnit44Runner;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
@@ -33,25 +36,27 @@ import org.springframework.integration.core.Message;
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@RunWith(MockitoJUnit44Runner.class)
|
||||
public class FileReadingMessageSourceTests {
|
||||
|
||||
private FileReadingMessageSource source;
|
||||
|
||||
private File inputDirectoryMock = createMock(File.class);
|
||||
@Mock
|
||||
private File inputDirectoryMock;
|
||||
|
||||
private Resource inputDirectoryResourceMock = createNiceMock(Resource.class);
|
||||
@Mock
|
||||
private Resource inputDirectoryResourceMock;
|
||||
|
||||
private File fileMock = createMock(File.class);
|
||||
@Mock
|
||||
private File fileMock;
|
||||
|
||||
private Comparator<File> comparator = createMock(Comparator.class);
|
||||
|
||||
private Object[] allMocks = new Object[] { inputDirectoryMock, fileMock, inputDirectoryResourceMock, comparator };
|
||||
@Mock
|
||||
private Comparator<File> comparator;
|
||||
|
||||
public void prepResource() throws Exception {
|
||||
expect(inputDirectoryResourceMock.exists()).andReturn(true).anyTimes();
|
||||
expect(inputDirectoryResourceMock.getFile()).andReturn(inputDirectoryMock).anyTimes();
|
||||
expect(inputDirectoryMock.canRead()).andReturn(true);
|
||||
replay(inputDirectoryResourceMock, inputDirectoryMock);
|
||||
when(inputDirectoryResourceMock.exists()).thenReturn(true);
|
||||
when(inputDirectoryResourceMock.getFile()).thenReturn(inputDirectoryMock);
|
||||
when(inputDirectoryMock.canRead()).thenReturn(true);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -59,59 +64,64 @@ public class FileReadingMessageSourceTests {
|
||||
prepResource();
|
||||
this.source = new FileReadingMessageSource(comparator);
|
||||
source.setInputDirectory(inputDirectoryResourceMock);
|
||||
reset(allMocks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void straightProcess() throws Exception {
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock });
|
||||
replay(allMocks);
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[] { fileMock });
|
||||
source.onSend(source.receive());
|
||||
verify(allMocks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requeueOnFailure() throws Exception {
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock }).times(2);
|
||||
replay(allMocks);
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[] { fileMock });
|
||||
Message received = source.receive();
|
||||
assertNotNull(received);
|
||||
source.onFailure(received, new RuntimeException("failed"));
|
||||
assertEquals(received.getPayload(), source.receive().getPayload());
|
||||
verify(allMocks);
|
||||
verify(inputDirectoryMock,times(1)).listFiles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanEachPoll() throws Exception {
|
||||
File anotherFileMock = mock(File.class);
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[] { fileMock, anotherFileMock });
|
||||
source.setScanEachPoll(true);
|
||||
assertNotNull(source.receive());
|
||||
assertNotNull(source.receive());
|
||||
assertNull(source.receive());
|
||||
verify(inputDirectoryMock,times(3)).listFiles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDuplication() throws Exception {
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock }).times(2);
|
||||
replay(allMocks);
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[] { fileMock });
|
||||
Message<File> received = source.receive();
|
||||
assertNotNull(received);
|
||||
assertEquals(fileMock, received.getPayload());
|
||||
assertNull(source.receive());
|
||||
verify(allMocks);
|
||||
verify(inputDirectoryMock,times(2)).listFiles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderedReception() throws Exception {
|
||||
File file1 = createMock(File.class);
|
||||
File file2 = createMock(File.class);
|
||||
File file3 = createMock(File.class);
|
||||
|
||||
//record the comparator to reverse order the files
|
||||
expect(comparator.compare(file1, file2)).andReturn(1).anyTimes();
|
||||
expect(comparator.compare(file1, file3)).andReturn(1).anyTimes();
|
||||
expect(comparator.compare(file2, file3)).andReturn(1).anyTimes();
|
||||
expect(comparator.compare(file2, file1)).andReturn(-1).anyTimes();
|
||||
expect(comparator.compare(file3, file1)).andReturn(-1).anyTimes();
|
||||
expect(comparator.compare(file3, file2)).andReturn(-1).anyTimes();
|
||||
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { file2, file3, file1 }).anyTimes();
|
||||
replay(allMocks);
|
||||
File file1 = mock(File.class);
|
||||
File file2 = mock(File.class);
|
||||
File file3 = mock(File.class);
|
||||
|
||||
// record the comparator to reverse order the files
|
||||
when(comparator.compare(file1, file2)).thenReturn(1);
|
||||
when(comparator.compare(file1, file3)).thenReturn(1);
|
||||
when(comparator.compare(file2, file3)).thenReturn(1);
|
||||
when(comparator.compare(file2, file1)).thenReturn(-1);
|
||||
when(comparator.compare(file3, file1)).thenReturn(-1);
|
||||
when(comparator.compare(file3, file2)).thenReturn(-1);
|
||||
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[] { file2, file3, file1 });
|
||||
assertSame(file3, source.receive().getPayload());
|
||||
assertSame(file2, source.receive().getPayload());
|
||||
assertSame(file1, source.receive().getPayload());
|
||||
assertNull(source.receive());
|
||||
verify(allMocks);
|
||||
verify(inputDirectoryMock,times(2)).listFiles();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user