INT-1073: Added HeadDirectoryScanner as an answer to the queue overflow problem

This commit is contained in:
Iwein Fuld
2010-04-09 12:30:01 +00:00
parent 5d7a76e822
commit ce479439ed
5 changed files with 66 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2010 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.
@@ -28,6 +28,7 @@ import java.util.concurrent.LinkedBlockingQueue;
* This implementation is thread safe.
*
* @author Iwein Fuld
* @since 1.0.0
*/
public class AcceptOnceFileListFilter extends AbstractFileListFilter {

View File

@@ -29,7 +29,6 @@ import org.springframework.util.Assert;
import java.io.File;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
/**
@@ -76,14 +75,15 @@ public class FileReadingMessageSource implements MessageSource<File>,
private boolean scanEachPoll = false;
/**
* Creates a FileReadingMessageSource with a naturally ordered queue of default capacity.
* Creates a FileReadingMessageSource with a naturally ordered queue of unbounded capacity.
*/
public FileReadingMessageSource() {
this(-1);
this(null);
}
/**
* Creates a FileReadingMessageSource with a bounded queue of the given capacity.
* Creates a FileReadingMessageSource with a bounded queue of the given capacity. This can be used to reduce the
* memory footprint of this component when reading from a large directory.
*
* @param internalQueueCapacity the size of the queue used to cache files to be received internally. This queue can
* be made larger to optimize the directory scanning. With scanEachPoll set to false
@@ -92,8 +92,9 @@ public class FileReadingMessageSource implements MessageSource<File>,
* of large numbers of files in a directory.
*/
public FileReadingMessageSource(int internalQueueCapacity) {
toBeReceived = new ArrayBlockingQueue<File>(
internalQueueCapacity < 0 ? DEFAULT_INTERNAL_QUEUE_CAPACITY : internalQueueCapacity);
this(null);
Assert.isTrue(internalQueueCapacity>0, "Cannot create a queue with non positive capacity");
this.setScanner(new HeadDirectoryScanner(internalQueueCapacity));
}
/**

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2010 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.file;
import java.io.File;
import java.util.Arrays;
import java.util.List;
/**
* A custom scanner that only returns the first <code>maxNumberOfFiles</code> elements from a directory listing. This is
* useful to limit the number of File objects in memory and therefore mutually exclusive with AcceptOnceFileListFilter.
*
* @author Iwein Fuld
* @since 2.0.0
*/
public class HeadDirectoryScanner extends DefaultDirectoryScanner {
public HeadDirectoryScanner(int maxNumberOfFiles) {
this.setFilter(new HeadFilter(maxNumberOfFiles));
}
private class HeadFilter implements FileListFilter {
private final int maxNumberOfFiles;
public HeadFilter(int maxNumberOfFiles) {
this.maxNumberOfFiles = maxNumberOfFiles;
}
public List<File> filterFiles(File[] files) {
return Arrays.asList(files).subList(0, Math.min(files.length, maxNumberOfFiles));
}
}
}

View File

@@ -100,14 +100,13 @@
</xsd:attribute>
<xsd:attribute name="queue-size" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
Specify the queue size used internally by the underlying FileReadingMessageSource to store files
listed on a poll. A larger queue size reduces the number of directory listings needed, but it
increases the chances of the internal queue being out of whack with the actual files listed in
the directory. Use 0 for small but volatile directories, use a large number for large
directories that are only written to. MUTUALLY EXCLUSIVE with comparator, if comparator is set
this attribute will be ignored.
</xsd:documentation>
<xsd:documentation>
Specify the maximum number of files stored in memory by the underlying FileReadingMessageSource.
This is useful to limit the memory footprint of this endpoint. Using a stateful filter would counter
this benefit, so AcceptOnceFileListFilter is not used when this attribute is specified.
MUTUALLY EXCLUSIVE with comparator, if comparator is set this attribute will be ignored.
MUTUALLY EXCLUSIVE with stateful filtering.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>

View File

@@ -22,11 +22,10 @@ import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.HeadDirectoryScanner;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.BlockingQueue;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@@ -49,9 +48,7 @@ public class FileInboundChannelAdapterWithQueueSizeTests {
@Test
public void queueSize() {
Object queue = accessor.getPropertyValue("toBeReceived");
assertThat(queue, is(BlockingQueue.class));
BlockingQueue blockingQueue = (BlockingQueue) queue;
assertThat(blockingQueue.remainingCapacity()+blockingQueue.size(), is(30));
Object scanner = accessor.getPropertyValue("scanner");
assertThat(scanner, is(HeadDirectoryScanner.class));
}
}