Replaced FileFilter usage with FileListFilter
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
class AcceptOnceFileFilter implements FileFilter {
|
||||
public class AcceptOnceFileFilter implements FileListFilter {
|
||||
|
||||
private final Queue<File> seen;
|
||||
|
||||
@@ -19,7 +20,17 @@ class AcceptOnceFileFilter implements FileFilter {
|
||||
seen = new LinkedBlockingQueue<File>();
|
||||
}
|
||||
|
||||
public boolean accept(File pathname) {
|
||||
public List<File> filterFiles(File[] files) {
|
||||
List<File> accepted = new ArrayList<File>();
|
||||
for (File file : files) {
|
||||
if (accept(file)) {
|
||||
accepted.add(file);
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
private boolean accept(File pathname) {
|
||||
synchronized (monitor) {
|
||||
if (!seen.contains(pathname)) {
|
||||
if (!seen.offer(pathname)) {
|
||||
|
||||
@@ -20,37 +20,37 @@ import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Composition that delegates to multiple {@link FileFilter}s. The compostition
|
||||
* is AND based, meaning that all filters must {@link #accept(File)} in order
|
||||
* for a file to be accepted by the composite.
|
||||
* is AND based, meaning that all filters must {@link #filterFiles(File)} in
|
||||
* order for a file to be accepted by the composite.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
class CompositeFileFilter implements FileFilter {
|
||||
public class CompositeFileFilter implements FileListFilter {
|
||||
|
||||
private final Set<FileFilter> fileFilters;
|
||||
private final Set<FileListFilter> fileFilters;
|
||||
|
||||
public CompositeFileFilter(FileFilter... fileFilters) {
|
||||
this.fileFilters = new HashSet<FileFilter>(Arrays.asList(fileFilters));
|
||||
public CompositeFileFilter(FileListFilter... fileFilters) {
|
||||
this.fileFilters = new HashSet<FileListFilter>(Arrays.asList(fileFilters));
|
||||
}
|
||||
|
||||
public CompositeFileFilter(HashSet<FileFilter> fileFilters) {
|
||||
this.fileFilters = new HashSet<FileFilter>(fileFilters);
|
||||
public CompositeFileFilter(Collection<FileListFilter> fileFilters) {
|
||||
this.fileFilters = new HashSet<FileListFilter>(fileFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean accept(File pathname) {
|
||||
for (FileFilter fileFilter : fileFilters) {
|
||||
if (!fileFilter.accept(pathname)) {
|
||||
return false;
|
||||
}
|
||||
public List<File> filterFiles(File[] files) {
|
||||
List<File> leftOver = Arrays.asList(files);
|
||||
for (FileListFilter fileFilter : fileFilters) {
|
||||
leftOver = fileFilter.filterFiles(leftOver.toArray(new File[] {}));
|
||||
}
|
||||
return true;
|
||||
return leftOver;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ class CompositeFileFilter implements FileFilter {
|
||||
* @param filters one or more new filters to be used
|
||||
* @return a new CompositeFileFilter with the additional filters
|
||||
*/
|
||||
public CompositeFileFilter addFilter(FileFilter... filters) {
|
||||
public CompositeFileFilter addFilter(FileListFilter... filters) {
|
||||
return addFilters(Arrays.asList(filters));
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ class CompositeFileFilter implements FileFilter {
|
||||
* @param filtersToAdd
|
||||
* @return a new CompositeFileFilter with the added filters
|
||||
*/
|
||||
public CompositeFileFilter addFilters(Collection<FileFilter> filtersToAdd) {
|
||||
HashSet<FileFilter> newFilterSet = new HashSet<FileFilter>(filtersToAdd);
|
||||
public CompositeFileFilter addFilters(Collection<FileListFilter> filtersToAdd) {
|
||||
HashSet<FileListFilter> newFilterSet = new HashSet<FileListFilter>(filtersToAdd);
|
||||
newFilterSet.addAll(fileFilters);
|
||||
return new CompositeFileFilter(newFilterSet);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileListFilter {
|
||||
|
||||
List<File> filterFiles(File[] files);
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
class ModificationTimeFileFilter implements FileFilter {
|
||||
|
||||
private final long modificationTime;
|
||||
public ModificationTimeFileFilter(long modificationTime) {
|
||||
this.modificationTime = modificationTime;
|
||||
}
|
||||
public boolean accept(File file) {
|
||||
return modificationTime <= file.lastModified();
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,10 @@ package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -50,21 +50,13 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class PollableFileSource implements PollableSource<File>, MessageDeliveryAware<File>, InitializingBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(PollableFileSource.class);
|
||||
private static final Log logger = LogFactory.getLog(PollableFileSource.class);
|
||||
|
||||
private volatile File inputDirectory;
|
||||
|
||||
private volatile Queue<File> fileQueue = new PriorityBlockingQueue<File>();
|
||||
private final Queue<File> toBeReceived = new PriorityBlockingQueue<File>();
|
||||
|
||||
private volatile FileFilter filter = new AcceptOnceFileFilter();
|
||||
|
||||
/**
|
||||
* Sets a queue to be used to hold files that are not processed yet. By
|
||||
* default a {@link PriorityBlockingQueue} with natural ordering is used.
|
||||
*/
|
||||
public void setQueue(Queue<File> queue) {
|
||||
this.fileQueue = queue;
|
||||
}
|
||||
private volatile FileListFilter filter = new AcceptOnceFileFilter();
|
||||
|
||||
public void setInputDirectory(File inputDirectory) {
|
||||
this.inputDirectory = inputDirectory;
|
||||
@@ -78,8 +70,8 @@ public class PollableFileSource implements PollableSource<File>, MessageDelivery
|
||||
* {@link CompositeFileFilter} can be used to group them together <p/>
|
||||
* <b>Note that the supplied filter must be thread safe</b>.
|
||||
*/
|
||||
public void setFilter(FileFilter filter) {
|
||||
Assert.notNull(filter);
|
||||
public void setFilter(FileListFilter filter) {
|
||||
Assert.notNull(filter, "'filter' should not be null");
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@@ -89,23 +81,16 @@ public class PollableFileSource implements PollableSource<File>, MessageDelivery
|
||||
Assert.isTrue(this.inputDirectory.canRead(), "No read permissions on " + inputDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return the Message created by the {@link #messageCreator} based on the
|
||||
* next file from the {@link #fileQueue}. Existence of the file is not
|
||||
* guaranteed, so the consumer of the message needs to check this.
|
||||
*/
|
||||
public Message<File> receive() throws MessagingException {
|
||||
traceState();
|
||||
refreshQueue();
|
||||
Message<File> message = null;
|
||||
File file = fileQueue.poll();
|
||||
File file = toBeReceived.poll();
|
||||
// we can't rely on isEmpty for concurrency reasons
|
||||
if (file != null) {
|
||||
message = new GenericMessage<File>(file);
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Created message: [" + message + "]");
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created message: [" + message + "]");
|
||||
}
|
||||
}
|
||||
traceState();
|
||||
@@ -113,41 +98,33 @@ public class PollableFileSource implements PollableSource<File>, MessageDelivery
|
||||
}
|
||||
|
||||
private void refreshQueue() {
|
||||
List<File> freshFiles = new ArrayList<File>(processFileList(Arrays.asList(inputDirectory.listFiles(filter))));
|
||||
List<File> filteredFiles = filter.filterFiles((inputDirectory.listFiles()));
|
||||
Set<File> freshFiles = new HashSet<File>(filteredFiles);
|
||||
if (!freshFiles.isEmpty()) {
|
||||
// don't duplicate what's on the queue already
|
||||
freshFiles.removeAll(fileQueue);
|
||||
fileQueue.addAll(freshFiles);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Added to queue: " + freshFiles);
|
||||
freshFiles.removeAll(toBeReceived);
|
||||
toBeReceived.addAll(freshFiles);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Added to queue: " + freshFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO point to FileFilter options
|
||||
* @param files
|
||||
* @return
|
||||
*/
|
||||
protected List<File> processFileList(List<File> files) {
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* In concurrent scenarios onFailure() might cause failing files to be
|
||||
* ignored. If this is not acceptable access to this method should be
|
||||
* synchronized on this instance externally.
|
||||
*/
|
||||
public void onFailure(Message<File> failedMessage, Throwable t) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Failed to send: " + failedMessage);
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to send: " + failedMessage);
|
||||
}
|
||||
fileQueue.add(failedMessage.getPayload());
|
||||
toBeReceived.add(failedMessage.getPayload());
|
||||
}
|
||||
|
||||
public void onSend(Message<File> sentMessage) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Sent: " + sentMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Sent: " + sentMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +132,8 @@ public class PollableFileSource implements PollableSource<File>, MessageDelivery
|
||||
* utility method to trace the stateful collections of this instance.
|
||||
*/
|
||||
private void traceState() {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Files to be received: [" + fileQueue + "]");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Files to be received: [" + toBeReceived + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,20 @@
|
||||
*/
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import static org.easymock.classextension.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.createNiceMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -28,40 +36,41 @@ import org.junit.Test;
|
||||
*/
|
||||
public class CompositeFileFilterTest {
|
||||
|
||||
private FileFilter fileFilterMock1 = createMock(FileFilter.class);
|
||||
private FileFilter fileFilterMock2 = createMock(FileFilter.class);
|
||||
private FileListFilter fileFilterMock1 = createMock(FileListFilter.class);
|
||||
|
||||
private FileListFilter fileFilterMock2 = createMock(FileListFilter.class);
|
||||
|
||||
private File fileMock = createNiceMock(File.class);
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void forwardedToFilters() throws Exception {
|
||||
CompositeFileFilter compositeFileFilter = new CompositeFileFilter(fileFilterMock1, fileFilterMock2);
|
||||
expect(fileFilterMock1.accept(fileMock)).andReturn(true).times(1);
|
||||
expect(fileFilterMock2.accept(fileMock)).andReturn(true).times(1);
|
||||
List<File> returnedFiles = Arrays.asList(new File[] { fileMock });
|
||||
expect(fileFilterMock1.filterFiles(isA(File[].class))).andReturn(returnedFiles).times(1);
|
||||
expect(fileFilterMock2.filterFiles(isA(File[].class))).andReturn(returnedFiles).times(1);
|
||||
replay(fileFilterMock1, fileFilterMock2);
|
||||
assertTrue(compositeFileFilter.accept(fileMock));
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterFiles(new File[]{fileMock}));
|
||||
verify(fileFilterMock1, fileFilterMock2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardedToAddedFilters() throws Exception {
|
||||
CompositeFileFilter compositeFileFilter = new CompositeFileFilter().addFilter(fileFilterMock1, fileFilterMock2);
|
||||
expect(fileFilterMock1.accept(fileMock)).andReturn(true).times(1);
|
||||
expect(fileFilterMock2.accept(fileMock)).andReturn(true).times(1);
|
||||
List<File> returnedFiles = Arrays.asList(new File[] { fileMock });
|
||||
expect(fileFilterMock1.filterFiles(isA(File[].class))).andReturn(returnedFiles).times(1);
|
||||
expect(fileFilterMock2.filterFiles(isA(File[].class))).andReturn(returnedFiles).times(1);
|
||||
replay(fileFilterMock1, fileFilterMock2);
|
||||
assertTrue(compositeFileFilter.accept(fileMock));
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterFiles(new File[]{fileMock}));
|
||||
verify(fileFilterMock1, fileFilterMock2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negative() throws Exception {
|
||||
CompositeFileFilter compositeFileFilter = new CompositeFileFilter(fileFilterMock1, fileFilterMock2);
|
||||
expect(fileFilterMock1.accept(fileMock)).andReturn(false).times(1);
|
||||
expect(fileFilterMock2.accept(fileMock)).andReturn(true).anyTimes();
|
||||
expect(fileFilterMock2.filterFiles(isA(File[].class))).andReturn(new ArrayList<File>()).times(1);
|
||||
expect(fileFilterMock1.filterFiles(isA(File[].class))).andReturn(new ArrayList<File>()).times(1);
|
||||
replay(fileFilterMock1, fileFilterMock2);
|
||||
assertFalse(compositeFileFilter.accept(fileMock));
|
||||
assertTrue(compositeFileFilter.filterFiles(new File[]{fileMock}).isEmpty());
|
||||
verify(fileFilterMock1, fileFilterMock2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<!-- under test -->
|
||||
<bean id="pollableFileSource" class="org.springframework.integration.file.PollableFileSource"
|
||||
p:inputDirectory="${java.io.tmpdir}/PollableFileSourceIntegrationTests"
|
||||
p:filter-ref="compositeFilter" />
|
||||
<!-- customized filter -->
|
||||
<bean id="compositeFilter"
|
||||
class="org.springframework.integration.file.CompositeFileFilter">
|
||||
<constructor-arg>
|
||||
|
||||
@@ -16,16 +16,14 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.reset;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -43,11 +41,9 @@ public class PollableFileSourceTests {
|
||||
|
||||
private File inputDirectory;
|
||||
|
||||
private FileFilter filterMock = createMock(FileFilter.class);
|
||||
|
||||
private File fileMock = createMock(File.class);
|
||||
|
||||
private Object[] allMocks = new Object[] { inputDirectoryMock, filterMock, fileMock };
|
||||
private Object[] allMocks = new Object[] { inputDirectoryMock, fileMock };
|
||||
|
||||
@Before
|
||||
public void initialize() throws Exception {
|
||||
@@ -56,15 +52,9 @@ public class PollableFileSourceTests {
|
||||
pollableFileSource.setInputDirectory(inputDirectoryMock);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setDefaultExpectations() {
|
||||
expect(fileMock.exists()).andReturn(true).anyTimes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void straightProcess() throws Exception {
|
||||
reset(fileMock);
|
||||
expect(inputDirectoryMock.listFiles(isA(FileFilter.class))).andReturn(new File[] { fileMock });
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock });
|
||||
replay(allMocks);
|
||||
pollableFileSource.onSend(pollableFileSource.receive());
|
||||
verify(allMocks);
|
||||
@@ -72,10 +62,11 @@ public class PollableFileSourceTests {
|
||||
|
||||
@Test
|
||||
public void requeueOnFailure() throws Exception {
|
||||
expect(inputDirectoryMock.listFiles(isA(FileFilter.class))).andReturn(new File[] { fileMock });
|
||||
expect(inputDirectoryMock.listFiles(isA(FileFilter.class))).andReturn(new File[] {});
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock });
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] {});
|
||||
replay(allMocks);
|
||||
Message received = pollableFileSource.receive();
|
||||
assertNotNull(received);
|
||||
pollableFileSource.onFailure(received, new RuntimeException("failed"));
|
||||
assertEquals(received.getPayload(), pollableFileSource.receive().getPayload());
|
||||
verify(allMocks);
|
||||
@@ -83,10 +74,12 @@ public class PollableFileSourceTests {
|
||||
|
||||
@Test
|
||||
public void noDuplication() throws Exception {
|
||||
expect(inputDirectoryMock.listFiles(isA(FileFilter.class))).andReturn(new File[] { fileMock });
|
||||
expect(inputDirectoryMock.listFiles(isA(FileFilter.class))).andReturn(new File[] {});
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] { fileMock });
|
||||
expect(inputDirectoryMock.listFiles()).andReturn(new File[] {});
|
||||
replay(allMocks);
|
||||
assertEquals(fileMock, pollableFileSource.receive().getPayload());
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
assertNotNull(received);
|
||||
assertEquals(fileMock, received.getPayload());
|
||||
assertNull(pollableFileSource.receive());
|
||||
verify(allMocks);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TestFileFilter implements FileFilter{
|
||||
public class TestFileFilter implements FileListFilter {
|
||||
|
||||
public boolean accept(File pathname) {
|
||||
return true;
|
||||
public List<File> filterFiles(File[] files) {
|
||||
return Arrays.asList(files);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user