INT-821: implemented namespace support for file locking
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user