INT-821: implemented namespace support for file locking

This commit is contained in:
Iwein Fuld
2009-12-14 21:11:58 +00:00
parent 277ffcca60
commit 647c825cad
20 changed files with 380 additions and 346 deletions

View File

@@ -16,8 +16,6 @@
package org.springframework.integration.file.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
@@ -25,53 +23,76 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for the <inbound-channel-adapter> element of the 'file' namespace.
*
*
* @author Iwein Fuld
* @author Mark Fisher
*/
public class FileInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
private static final String PACKAGE_NAME = "org.springframework.integration.file";
private static final String PACKAGE_NAME = "org.springframework.integration.file";
@Override
protected String parseSource(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_NAME + ".config.FileReadingMessageSourceFactoryBean");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "comparator");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "scanner");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory");
String filterBeanName = this.registerFileListFilter(element, parserContext);
builder.addPropertyReference("filter", filterBeanName);
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
}
@Override
protected String parseSource(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_NAME + ".config.FileReadingMessageSourceFactoryBean");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "comparator");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "scanner");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory");
String filterBeanName = this.registerFilter(element, parserContext);
String lockerBeanName = registerLocker(element, parserContext);
if (lockerBeanName != null) {
builder.addPropertyReference("locker", lockerBeanName);
}
builder.addPropertyReference("filter", filterBeanName);
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
}
private String registerFileListFilter(Element element, ParserContext parserContext) {
BeanDefinitionBuilder factoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_NAME + ".config.FileListFilterFactoryBean");
factoryBeanBuilder.setRole(BeanDefinition.ROLE_SUPPORT);
String filter = element.getAttribute("filter");
if (StringUtils.hasText(filter)) {
factoryBeanBuilder.addPropertyReference("filterReference", filter);
}
String filenamePattern = element.getAttribute("filename-pattern");
if (StringUtils.hasText(filenamePattern)) {
if (StringUtils.hasText(filter)) {
parserContext.getReaderContext().error(
"At most one of 'filter' and 'filename-pattern' may be provided.", element);
}
factoryBeanBuilder.addPropertyValue("filenamePattern", filenamePattern);
}
String preventDuplicates = element.getAttribute("prevent-duplicates");
if (StringUtils.hasText(preventDuplicates)) {
factoryBeanBuilder.addPropertyValue("preventDuplicates", preventDuplicates);
}
return BeanDefinitionReaderUtils.registerWithGeneratedName(
factoryBeanBuilder.getBeanDefinition(), parserContext.getRegistry());
}
private String registerLocker(Element element, ParserContext parserContext) {
String lockerBeanName = null;
Element nioLocker = DomUtils.getChildElementByTagName(element, "nio-locker");
if (nioLocker != null) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(PACKAGE_NAME + ".locking.NioFileLocker");
lockerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(),
parserContext.getRegistry());
} else {
Element locker = DomUtils.getChildElementByTagName(element, "locker");
if (locker != null) {
lockerBeanName = locker.getAttribute("ref");
}
}
return lockerBeanName;
}
private String registerFilter(Element element, ParserContext parserContext) {
BeanDefinitionBuilder factoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_NAME + ".config.FileListFilterFactoryBean");
factoryBeanBuilder.setRole(BeanDefinition.ROLE_SUPPORT);
String filter = element.getAttribute("filter");
if (StringUtils.hasText(filter)) {
factoryBeanBuilder.addPropertyReference("filterReference", filter);
}
String filenamePattern = element.getAttribute("filename-pattern");
if (StringUtils.hasText(filenamePattern)) {
if (StringUtils.hasText(filter)) {
parserContext.getReaderContext().error(
"At most one of 'filter' and 'filename-pattern' may be provided.", element);
}
factoryBeanBuilder.addPropertyValue("filenamePattern", filenamePattern);
}
String preventDuplicates = element.getAttribute("prevent-duplicates");
if (StringUtils.hasText(preventDuplicates)) {
factoryBeanBuilder.addPropertyValue("preventDuplicates", preventDuplicates);
}
return BeanDefinitionReaderUtils.registerWithGeneratedName(
factoryBeanBuilder.getBeanDefinition(), parserContext.getRegistry());
}
}

View File

@@ -17,11 +17,11 @@
package org.springframework.integration.file.config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.integration.file.CompositeFileListFilter;
import org.springframework.integration.file.DirectoryScanner;
import org.springframework.integration.file.FileListFilter;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.locking.BaseLockingFilter;
import java.io.File;
import java.util.Comparator;
@@ -31,92 +31,96 @@ import java.util.Comparator;
* @author Iwein Fuld
* @since 1.0.3
*/
public class FileReadingMessageSourceFactoryBean implements FactoryBean, ResourceLoaderAware {
public class FileReadingMessageSourceFactoryBean implements FactoryBean {
private volatile FileReadingMessageSource source;
private volatile FileReadingMessageSource source;
private volatile ResourceLoader resourceLoader;
private volatile File directory;
private volatile File directory;
private volatile FileListFilter filter;
private volatile FileListFilter filter;
private volatile BaseLockingFilter locker;
private volatile Comparator<File> comparator;
private volatile Comparator<File> comparator;
private volatile DirectoryScanner scanner;
private volatile Boolean scanEachPoll;
private volatile Boolean scanEachPoll;
private volatile Boolean autoCreateDirectory;
private volatile Boolean autoCreateDirectory;
private final Object initializationMonitor = new Object();
private final Object initializationMonitor = new Object();
public void setDirectory(File directory) {
this.directory = directory;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void setDirectory(File directory) {
this.directory = directory;
}
public void setComparator(Comparator<File> comparator) {
this.comparator = comparator;
}
public void setComparator(Comparator<File> comparator) {
this.comparator = comparator;
}
public void setScanner(DirectoryScanner scanner) {
this.scanner = scanner;
}
public void setFilter(FileListFilter filter) {
this.filter = filter;
}
this.filter = filter;
}
public void setScanEachPoll(Boolean scanEachPoll) {
this.scanEachPoll = scanEachPoll;
}
public void setScanEachPoll(Boolean scanEachPoll) {
this.scanEachPoll = scanEachPoll;
}
public void setAutoCreateDirectory(Boolean autoCreateDirectory) {
this.autoCreateDirectory = autoCreateDirectory;
}
public void setAutoCreateDirectory(Boolean autoCreateDirectory) {
this.autoCreateDirectory = autoCreateDirectory;
}
public Object getObject() throws Exception {
if (this.source == null) {
initSource();
}
return this.source;
}
public void setLocker(BaseLockingFilter locker) {
this.locker = locker;
}
public Class<?> getObjectType() {
return FileReadingMessageSource.class;
}
public Object getObject() throws Exception {
if (this.source == null) {
initSource();
}
return this.source;
}
public boolean isSingleton() {
return true;
}
public Class<?> getObjectType() {
return FileReadingMessageSource.class;
}
private void initSource() {
synchronized (this.initializationMonitor) {
if (this.source != null) {
return;
}
this.source = (this.comparator != null) ?
new FileReadingMessageSource(this.comparator) : new FileReadingMessageSource();
this.source.setDirectory(this.directory);
if (this.filter != null) {
this.source.setFilter(this.filter);
}
if (this.scanEachPoll != null) {
this.source.setScanEachPoll(this.scanEachPoll);
}
if (this.autoCreateDirectory != null) {
this.source.setAutoCreateDirectory(this.autoCreateDirectory);
}
public boolean isSingleton() {
return true;
}
private void initSource() {
synchronized (this.initializationMonitor) {
if (this.source != null) {
return;
}
this.source = (this.comparator != null) ?
new FileReadingMessageSource(this.comparator) : new FileReadingMessageSource();
this.source.setDirectory(this.directory);
if (this.filter != null) {
if (this.locker == null) {
this.source.setFilter(this.filter);
} else {
this.source.setFilter(new CompositeFileListFilter(this.filter, this.locker));
this.source.setLocker(locker);
}
}
if (this.scanEachPoll != null) {
this.source.setScanEachPoll(this.scanEachPoll);
}
if (this.autoCreateDirectory != null) {
this.source.setAutoCreateDirectory(this.autoCreateDirectory);
}
if (this.scanner != null) {
this.source.setScanner(this.scanner);
}
this.source.afterPropertiesSet();
}
}
this.source.afterPropertiesSet();
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2002-2008 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.locking;
import org.springframework.integration.file.AbstractFileListFilter;
import java.io.File;
/**
* Convenience base class for implementing FileLockers that acquires a lock upon accepting a file. This is required
* when used in combination with a FileReadingMessageSource.
*
* @author Iwein Fuld
* @since 2.0
*
*/
public abstract class BaseLockingFilter extends AbstractFileListFilter implements FileLocker {
protected final boolean accept(File file) {
return lock(file);
}
}

View File

@@ -26,6 +26,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Static cache of FileLocks that can be used to ensure that only a single lock is used inside this ClassLoader.
*
* @author Iwein Fuld
* @since 2.0
*/
@@ -38,7 +40,7 @@ final class FileChannelCache {
* Try to get a lock for this file while guaranteeing that the same channel will be used for all file locks in this
* VM. If the lock could not be acquired this method will return <code>null</code>.
* <p/>
* Locks acquired through this method should be passed back to #closeChannelFor to prevent memory leak.
* Locks acquired through this method should be passed back to #closeChannelFor to prevent memory leaks.
* <p/>
* Thread safe.
*/

View File

@@ -1,104 +0,0 @@
/*
* Copyright 2002-2009 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.locking;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.file.AbstractFileListFilter;
import org.springframework.util.Assert;
import java.io.File;
import java.io.IOException;
/**
* LockFileFileListFilter keeps track of the files it should pick up by adding lock files in a directory. If this
* directory is the same as the directory of the original files additional precautions need to be taken to avoid
* treating the lock files as normal input.
*
* If different LockFileFileListFilters share their lock directory they are unlikely to pass the same file.
*
* @author Iwein Fuld
* @since 2.0
*/
public class LockFileFileListFilter extends AbstractFileListFilter implements FileLocker{
private static final Log logger = LogFactory.getLog(LockFileFileListFilter.class);
private static final String LOCK_SUFFIX = ".lock";
private static final String PRELOCK_SUFFIX = ".prelock";
private File workdir;
public LockFileFileListFilter(File workdir) {
Assert.notNull(workdir, "Work directory must not be null.");
Assert.isTrue(workdir.isDirectory(), "Work directory must be a directory.");
Assert.isTrue(workdir.canWrite(), "Work directory must be write accessible.");
this.workdir = workdir;
}
protected boolean accept(File file) {
File lockFile = new File(workdir, file.getName() + LOCK_SUFFIX);
File preLockFile = new File(workdir, file.getName() + PRELOCK_SUFFIX);
if (lockFile.exists() || preLockFile.exists()) {
return false;
}
String name = file.getName();
return !name.endsWith(LOCK_SUFFIX) && !name.endsWith(PRELOCK_SUFFIX);
}
/**
* Makes a best effort attempt at locking the file atomically.
*
* The locking mechanism will create a prelock file and move it to a lock
* file location.
*/
public boolean lock(File fileToLock) {
File lockFile = new File(workdir, fileToLock.getName() + LOCK_SUFFIX);
File preLockFile = new File(workdir, fileToLock.getName() + PRELOCK_SUFFIX);
if (lockFile.exists() || preLockFile.exists()) {
return false;
}
try {
preLockFile.createNewFile();
}
catch (IOException e) {
logger.warn("Failed to lock file", e);
return false;
}
// there is still a small chance that the next line will be done concurrently and the file will be picked up twice
if (!lockFile.exists() && preLockFile.renameTo(lockFile)) {
lockFile.deleteOnExit();
return true;
}
preLockFile.delete();
logger.warn("Failed to lock file [" + fileToLock + "]");
return false;
}
/**
* Deletes the lockFile for this file.
*/
public void unlock(File fileToUnlock) {
File lockFile = new File(workdir, fileToUnlock.getName() + LOCK_SUFFIX);
File preLockFile = new File(workdir, fileToUnlock.getName() + PRELOCK_SUFFIX);
if (!preLockFile.exists()) {
lockFile.delete();
}
}
}

View File

@@ -16,15 +16,14 @@
package org.springframework.integration.file.locking;
import org.springframework.integration.file.AbstractFileListFilter;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.file.FileReadingMessageSource;
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* File locking strategy that uses java.nio. The locks taken by FileChannel are
@@ -41,11 +40,11 @@ import java.util.concurrent.ConcurrentHashMap;
* @author Mark Fisher
* @since 2.0
*/
public class NioFileLocker extends AbstractFileListFilter implements FileLocker {
public class NioFileLocker extends BaseLockingFilter {
private final ConcurrentMap<File, FileLock> lockCache = new ConcurrentHashMap<File, FileLock>();
/**
/**
* {@inheritDoc}
*
*/
@@ -79,9 +78,4 @@ public class NioFileLocker extends AbstractFileListFilter implements FileLocker
+ fileToUnlock, e);
}
}
protected boolean accept(File file) {
return this.lock(file);
}
}

View File

@@ -26,9 +26,13 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element ref="locker" minOccurs="0" maxOccurs="1"/>
<xsd:element ref="nio-locker" minOccurs="0" maxOccurs="1"/>
</xsd:choice>
</xsd:choice>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
@@ -249,4 +253,20 @@
<xsd:attribute name="delete-files" type="xsd:string"/>
</xsd:complexType>
<xsd:element name="locker">
<xsd:complexType>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.file.locking.FileLocker"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="nio-locker"/>
</xsd:schema>

View File

@@ -5,13 +5,13 @@
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<si:channel id="copyInput" />
@@ -32,6 +32,7 @@
directory="${java.io.tmpdir}/anyDir"
delete-source-files="true"/>
<context:property-placeholder />
<!--suppress SpringModelInspection -->
<context:property-placeholder />
</beans:beans>

View File

@@ -16,23 +16,16 @@
package org.springframework.integration.file.config;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.integration.file.*;
import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.regex.Pattern;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.integration.file.AbstractFileListFilter;
import org.springframework.integration.file.AcceptOnceFileListFilter;
import org.springframework.integration.file.CompositeFileListFilter;
import org.springframework.integration.file.FileListFilter;
import org.springframework.integration.file.PatternMatchingFileListFilter;
import static org.junit.Assert.*;
/**
* @author Mark Fisher

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<!-- under test -->
<file:inbound-channel-adapter id="customLockingAdapter" directory="#{directory.root}">
<file:locker ref="customLocker"/>
</file:inbound-channel-adapter>
<file:inbound-channel-adapter id="nioLockingAdapter" directory="#{directory.root}">
<file:nio-locker/>
</file:inbound-channel-adapter>
<bean id="customLocker" class="org.springframework.integration.file.locking.FileLockingNamespaceTests$StubLocker"/>
<bean id="directory" class="org.junit.rules.TemporaryFolder" init-method="create" destroy-method="delete"/>
<si:poller default="true">
<si:interval-trigger interval="100000000"/>
</si:poller>
</beans>

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2002-2008 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.locking;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.CompositeFileListFilter;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.File;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* @author Iwein Fuld
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileLockingNamespaceTests {
@Autowired
@Qualifier("nioLockingAdapter.adapter")
SourcePollingChannelAdapter nioAdapter;
FileReadingMessageSource nioLockingSource;
@Autowired
@Qualifier("customLockingAdapter.adapter")
SourcePollingChannelAdapter customAdapter;
FileReadingMessageSource customLockingSource;
@Before public void extractSources() {
nioLockingSource = (FileReadingMessageSource) new DirectFieldAccessor( nioAdapter).getPropertyValue("source");
customLockingSource = (FileReadingMessageSource) new DirectFieldAccessor( customAdapter).getPropertyValue("source");
}
@Test
public void shouldLoadConfig() {
//verify Spring can load the configuration
}
@Test
public void shouldSetCustomLockerProperly() {
DirectFieldAccessor accessor = new DirectFieldAccessor(customLockingSource);
assertThat(accessor.getPropertyValue("locker"), is(StubLocker.class));
assertThat(accessor.getPropertyValue("filter"), is(CompositeFileListFilter.class));
}
@Test
public void shouldSetNioLockerProperly() {
DirectFieldAccessor accessor = new DirectFieldAccessor(nioLockingSource);
assertThat(accessor.getPropertyValue("locker"), is(NioFileLocker.class));
assertThat(accessor.getPropertyValue("filter"), is(CompositeFileListFilter.class));
}
public static class StubLocker extends BaseLockingFilter {
public boolean lock(File fileToLock) {
return true;
}
public void unlock(File fileToUnlock) {
//
}
}
}

View File

@@ -8,15 +8,18 @@
<!-- under test -->
<bean id="fileSource1" class="org.springframework.integration.file.FileReadingMessageSource"
p:directory="file:${java.io.tmpdir}/FileLockingWithMultipleSourcesIntegrationTests"
p:filter-ref="filter"/>
p:filter-ref="filter1"/>
<bean id="fileSource2" class="org.springframework.integration.file.FileReadingMessageSource"
p:directory="file:${java.io.tmpdir}/FileLockingWithMultipleSourcesIntegrationTests"
p:filter-ref="filter"/>
p:filter-ref="filter2"/>
<bean id="filter" class="org.springframework.integration.file.locking.LockFileFileListFilter">
<constructor-arg value="file:${java.io.tmpdir}/FileLockingWithMultipleSourcesIntegrationTests"/>
</bean>
<bean id="fileSource3" class="org.springframework.integration.file.FileReadingMessageSource"
p:directory="file:${java.io.tmpdir}/FileLockingWithMultipleSourcesIntegrationTests"
p:filter-ref="filter1"/>
<bean id="filter1" class="org.springframework.integration.file.locking.NioFileLocker"/>
<bean id="filter2" class="org.springframework.integration.file.locking.NioFileLocker"/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.integration.file.locking;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -24,12 +22,15 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.file.FileReadingMessageSource;
import static org.springframework.integration.test.matcher.PayloadMatcher.hasPayload;
import org.springframework.test.context.ContextConfiguration;
import java.io.File;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.integration.test.matcher.PayloadMatcher.hasPayload;
/**
* @author Iwein Fuld
*/
@@ -50,10 +51,15 @@ public class FileLockingWithMultipleSourcesIntegrationTests {
@Autowired
@Qualifier("fileSource1")
private FileReadingMessageSource fileSource1;
@Autowired
@Qualifier("fileSource2")
private FileReadingMessageSource fileSource2;
@Autowired
@Qualifier("fileSource2")
private FileReadingMessageSource fileSource3;
@Before
public void cleanoutWorkDir() {
for (File file : workdir.listFiles()) {
@@ -62,10 +68,20 @@ public class FileLockingWithMultipleSourcesIntegrationTests {
}
@Test
public void filePickedUpOnlyOnce() throws IOException {
public void filePickedUpOnceWithDistinctFilters() throws IOException {
File testFile = new File(workdir, "test");
testFile.createNewFile();
assertThat(fileSource1.receive(), hasPayload(testFile));
assertThat(fileSource2.receive(), nullValue());
FileChannelCache.closeChannelFor(testFile);
}
@Test
public void filePickedUpTwiceWithSharedFilter() throws Exception {
File testFile = new File(workdir, "test");
testFile.createNewFile();
assertThat(fileSource1.receive(), hasPayload(testFile));
assertThat(fileSource3.receive(), hasPayload(testFile));
FileChannelCache.closeChannelFor(testFile);
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2002-2008 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.locking;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.BeforeClass;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Iwein Fuld
*/
public class LockFileFileListFilterTests {
private static File workdir = new File(new File(System.getProperty("java.io.tmpdir")), LockFileFileListFilterTests.class.getSimpleName());
@BeforeClass
public static void setupWorkDir() {
workdir.mkdir();
}
private FileLocker locker = new LockFileFileListFilter(workdir);
@Before
public void cleanDirectory() {
File[] files = workdir.listFiles();
for (File file : files) {
file.delete();
}
}
@Test
public void fileListedOnlyWhenNotLocked() throws IOException {
LockFileFileListFilter filter = new LockFileFileListFilter(workdir);
File testFile = new File(workdir, "test0");
testFile.createNewFile();
assertThat(filter.filterFiles(workdir.listFiles()).get(0), is(testFile));
locker.lock(testFile);
assertThat(filter.filterFiles(workdir.listFiles()), is((List)new ArrayList<File>()));
}
@Test
public void fileListedByOneFilterOnly() throws IOException {
LockFileFileListFilter filter1 = new LockFileFileListFilter(workdir);
LockFileFileListFilter filter2 = new LockFileFileListFilter(workdir);
File testFile = new File(workdir, "test1");
testFile.createNewFile();
assertThat(filter1.filterFiles(workdir.listFiles()).get(0), is(testFile));
locker.lock(testFile);
assertThat(filter2.filterFiles(workdir.listFiles()), is((List)new ArrayList<File>()));
}
}

View File

@@ -0,0 +1,8 @@
log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%c{1}: %m%n
log4j.category.org.springframework.integration=INFO
log4j.category.org.springframework.integration.file=DEBUG