merging from the branch for the changes made to support EntryListFilters instead of FileListFilters.
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.file.entries.AcceptAllEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
|
||||
/**
|
||||
* Strategy class charged with knowing how to connect to a remote file system, scan it for new files and then downloading the file.
|
||||
* <p/>
|
||||
* The implementation should run through any configured {@link org.springframework.integration.file.entries.EntryListFilter}s
|
||||
* to ensure the entry is worth downloading.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractInboundRemoteFileSystemSychronizer<T> extends AbstractEndpoint {
|
||||
/**
|
||||
* Should we <emphasis>delete</emphasis> the <b>source</b> file?
|
||||
* For an FTP server, for example, this would delete the original FTPFile instance
|
||||
* <p/>
|
||||
* At the moment I can simply see this triggering an implementation specific {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy}
|
||||
* implementation that knows how to delete an entry on the remote file system.
|
||||
*/
|
||||
protected boolean shouldDeleteSourceFile;
|
||||
|
||||
/**
|
||||
* the directory we're writing our synchronizations to
|
||||
*/
|
||||
protected volatile Resource localDirectory;
|
||||
|
||||
/**
|
||||
* a {@link org.springframework.integration.file.entries.EntryListFilter} that we're running against the <emphasis>remote</emphasis> file system view!
|
||||
*/
|
||||
protected volatile EntryListFilter<T> filter = new AcceptAllEntryListFilter<T>();
|
||||
|
||||
/**
|
||||
* the {@link java.util.concurrent.ScheduledFuture} instance we get when we schedule our {@link AbstractInboundRemoteFileSystemSychronizer.SynchronizeTask}
|
||||
*/
|
||||
protected ScheduledFuture<?> scheduledFuture;
|
||||
|
||||
/**
|
||||
* Used to store the {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} implementation
|
||||
*/
|
||||
protected EntryAcknowledgmentStrategy<T> entryAcknowledgmentStrategy;
|
||||
|
||||
/**
|
||||
* Obviously thread safe - simply provides a NOOP impl so we don't have to keep dancing around NPE's
|
||||
*/
|
||||
private EntryAcknowledgmentStrategy<T> noOpEntryAcknowledgmentStrategy = new EntryAcknowledgmentStrategy<T>() {
|
||||
public void acknowledge(Object o, T msg) {
|
||||
}
|
||||
};
|
||||
|
||||
public void setEntryAcknowledgmentStrategy(EntryAcknowledgmentStrategy<T> entryAcknowledgmentStrategy) {
|
||||
this.entryAcknowledgmentStrategy = entryAcknowledgmentStrategy;
|
||||
}
|
||||
|
||||
public void setShouldDeleteSourceFile(boolean shouldDeleteSourceFile) {
|
||||
this.shouldDeleteSourceFile = shouldDeleteSourceFile;
|
||||
}
|
||||
|
||||
public void setLocalDirectory(Resource localDirectory) {
|
||||
this.localDirectory = localDirectory;
|
||||
}
|
||||
|
||||
public void setFilter(EntryListFilter<T> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param usefulContextOrClientData this is context information to be passed to the individual {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} implementation.
|
||||
* {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy#acknowledge(Object, Object)} will be called
|
||||
* in line with the {@link org.springframework.integration.core.MessageSource#receive()} call so this could conceivably be a 'live' stateful
|
||||
* client (a connection?) that is inappropriate to cache as it has per-request state.
|
||||
* @param t leverages strategy implementations to enable different behavior. It's a hook to the entry ({@link T}) after it's been successfully downloaded.
|
||||
* Conceptually, you might delete the remote one or rename it or something
|
||||
* @throws Throwable escape hatch exception, let the adapter deal with it.
|
||||
*/
|
||||
protected void acknowledge(Object usefulContextOrClientData, T t)
|
||||
throws Throwable {
|
||||
Assert.notNull(this.entryAcknowledgmentStrategy != null, "entryAcknowledgmentStrategy can't be null!");
|
||||
this.entryAcknowledgmentStrategy.acknowledge(usefulContextOrClientData, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the callback where we need the implementation to do some specific work
|
||||
*
|
||||
* @throws Exception thrown if anything goes wrong
|
||||
*/
|
||||
protected abstract void syncRemoteToLocalFileSystem()
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected void doStop() {
|
||||
Assert.notNull(this.scheduledFuture, "the 'scheduledFuture' can't be null!");
|
||||
this.scheduledFuture.cancel(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value in millis dictating how frequently the trigger should fire
|
||||
*
|
||||
* @return a {@link org.springframework.scheduling.Trigger} implementation (likely,
|
||||
* {@link org.springframework.scheduling.support.PeriodicTrigger})
|
||||
*/
|
||||
protected abstract Trigger getTrigger();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected void doStart() {
|
||||
if (this.entryAcknowledgmentStrategy == null) {
|
||||
this.entryAcknowledgmentStrategy = noOpEntryAcknowledgmentStrategy;
|
||||
}
|
||||
|
||||
this.scheduledFuture = this.getTaskScheduler().schedule(new SynchronizeTask(), this.getTrigger());
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy interface to expose a hook for dispatching, moving, or deleting the file once it's been delivered.
|
||||
* This will typically be a NOOP for the implementation. Adapters should (for consistency) expose an attribute
|
||||
* dictating whether the adapter will delete the <emphasis>source</emphasis> entry on the remote file system.
|
||||
* This is the file-system version of an <code>ack-mode</code>. Future implementations should consider
|
||||
* exposing a custom attribute that plugs a custom {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy}
|
||||
* into the pipeline and also some more advanced scenarios (i.e., 'move file to another folder on delete ', or 'rename on delete')
|
||||
*
|
||||
* @param <T> the entry type (file, sftp, ftp, ...)
|
||||
*/
|
||||
public static interface EntryAcknowledgmentStrategy<T> {
|
||||
/**
|
||||
* Semantics are simple. You get a pointer to the entry just processed and any kind of helper data you could ask for. Since the strategy is a
|
||||
* singleton and the clients you might ask for as context data are pooled, it's not recommended that you try to cache them.
|
||||
*
|
||||
* @param useful any context data
|
||||
* @param msg the data / file / entry you want to process -- specific to sublcasses
|
||||
* @throws Exception thrown for any old reason
|
||||
*/
|
||||
void acknowledge(Object useful, T msg) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* This {@link Runnable} is launched as a background thread and is used to babysit the
|
||||
* {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer#localDirectory},
|
||||
* queueing and delivering accumulated files as possible.
|
||||
*/
|
||||
class SynchronizeTask implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
syncRemoteToLocalFileSystem();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.file.entries.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Ultimately, this factors out a lot of the common logic between the FTP and SFTP adapters. Designed to be extendable to handle
|
||||
* adapters whose task it is to synchronize a remote file system with a local file system (NB: this does *NOT* handle pushing files TO the remote
|
||||
* file system that exist uniquely in the local file system. It only handles bringing down the remote file system - as you'd expect
|
||||
* an 'inbound' adapter would).
|
||||
* <p/>
|
||||
* The base class supports configuration of whether the remote file system and local file system's directories should
|
||||
* be created on start (what 'creating a directory' means to the specific adapter is of course implementaton specific).
|
||||
* <p/>
|
||||
* This class is to be used as a pair with an implementation of
|
||||
* {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer<T>}. This synchronizer
|
||||
* must handle the work of actually connecting to the remote file system and delivering new {@link java.io.File}s.
|
||||
* The synchronizer is designed to be
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractInboundRemoteFileSystemSynchronizingMessageSource<Y, T extends AbstractInboundRemoteFileSystemSychronizer<Y>> extends AbstractEndpoint implements MessageSource<File> {
|
||||
/**
|
||||
* Extension used when downloading files. We change it right after we know it's downloaded
|
||||
*/
|
||||
public static final String INCOMPLETE_EXTENSION = ".INCOMPLETE";
|
||||
|
||||
/**
|
||||
* Should the endpoint attempt to create the local directory and / or the remote directory?
|
||||
*/
|
||||
protected volatile boolean autoCreateDirectories = true;
|
||||
|
||||
/**
|
||||
* An implementation that will handle the chores of actually connecting to and syncing up the remote FS with the local one, in an inbound direction
|
||||
*/
|
||||
protected volatile T synchronizer;
|
||||
|
||||
/**
|
||||
* What directory should things be synced to locally ?
|
||||
*/
|
||||
protected volatile Resource localDirectory;
|
||||
|
||||
/**
|
||||
* The actual {@link FileReadingMessageSource} that we continue to trust to do the job monitoring the filesystem once files are moved down
|
||||
*/
|
||||
protected volatile FileReadingMessageSource fileSource;
|
||||
|
||||
/**
|
||||
* The predicate to use in scanning the remote Fs for downloads
|
||||
*/
|
||||
protected EntryListFilter<Y> remotePredicate;
|
||||
|
||||
public void setAutoCreateDirectories(boolean autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
public void setSynchronizer(T synchronizer) {
|
||||
this.synchronizer = synchronizer;
|
||||
}
|
||||
|
||||
public void setLocalDirectory(Resource localDirectory) {
|
||||
this.localDirectory = localDirectory;
|
||||
}
|
||||
|
||||
public void setRemotePredicate(EntryListFilter<Y> remotePredicate) {
|
||||
this.remotePredicate = remotePredicate;
|
||||
}
|
||||
|
||||
private EntryListFilter<File> buildFilter() {
|
||||
FileEntryNamer fileEntryNamer = new FileEntryNamer();
|
||||
Pattern completePattern = Pattern.compile("^.*(?<!" + INCOMPLETE_EXTENSION + ")$");
|
||||
return new CompositeEntryListFilter<File>(new AcceptOnceEntryFileListFilter<File>(), new PatternMatchingEntryListFilter<File>(fileEntryNamer, completePattern));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
if (this.remotePredicate != null) {
|
||||
this.synchronizer.setFilter(this.remotePredicate);
|
||||
}
|
||||
|
||||
if (this.autoCreateDirectories) {
|
||||
if ((this.localDirectory != null) && !this.localDirectory.exists() && this.localDirectory.getFile().mkdirs())
|
||||
logger.debug("the localDirectory " + this.localDirectory + " doesn't exist");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles making sure the remote files get here in one piece
|
||||
*/
|
||||
this.synchronizer.setLocalDirectory(this.localDirectory);
|
||||
this.synchronizer.setTaskScheduler(this.getTaskScheduler());
|
||||
this.synchronizer.setBeanFactory(this.getBeanFactory());
|
||||
this.synchronizer.setPhase(this.getPhase());
|
||||
this.synchronizer.setBeanName(this.getComponentName());
|
||||
|
||||
/**
|
||||
* Handles forwarding files once they ultimately appear in the {@link #localDirectory}
|
||||
*/
|
||||
this.fileSource = new FileReadingMessageSource();
|
||||
this.fileSource.setFilter(buildFilter());
|
||||
this.fileSource.setDirectory(this.localDirectory.getFile());
|
||||
this.fileSource.afterPropertiesSet();
|
||||
this.synchronizer.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public Message<File> receive() {
|
||||
return this.fileSource.receive();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* {@link FileListFilter} that passes files only one time. This can
|
||||
* conveniently be used to prevent duplication of files, as is done in
|
||||
* {@link FileReadingMessageSource}.
|
||||
* <p/>
|
||||
* This implementation is thread safe.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AcceptOnceFileListFilter extends AbstractFileListFilter {
|
||||
|
||||
private final Queue<File> seen;
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Creates an AcceptOnceFileFilter that is based on a bounded queue. If the
|
||||
* queue overflows, files that fall out will be passed through this filter
|
||||
* again if passed to the {@link #filterFiles(File[])} method.
|
||||
*
|
||||
* @param maxCapacity the maximum number of Files to maintain in the 'seen'
|
||||
* queue.
|
||||
*/
|
||||
public AcceptOnceFileListFilter(int maxCapacity) {
|
||||
this.seen = new LinkedBlockingQueue<File>(maxCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AcceptOnceFileFilter based on an unbounded queue.
|
||||
*/
|
||||
public AcceptOnceFileListFilter() {
|
||||
this.seen = new LinkedBlockingQueue<File>();
|
||||
}
|
||||
|
||||
|
||||
protected boolean accept(File pathname) {
|
||||
synchronized (this.monitor) {
|
||||
if (seen.contains(pathname)) {
|
||||
return false;
|
||||
}
|
||||
if (!seen.offer(pathname)) {
|
||||
seen.poll();
|
||||
seen.add(pathname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,14 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Default directory scanner and base class for other directory scanners. It takes care of the default interrelations
|
||||
* between filtering, scanning and locking.
|
||||
@@ -29,16 +32,17 @@ import java.util.List;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultDirectoryScanner implements DirectoryScanner {
|
||||
private FileListFilter filter = new AcceptOnceFileListFilter();
|
||||
private EntryListFilter<File> filter = new AcceptOnceEntryFileListFilter<File>();
|
||||
private FileLocker locker;
|
||||
|
||||
public final List<File> listFiles(File directory) throws IllegalArgumentException {
|
||||
File[] files = listEligibleFiles(directory);
|
||||
|
||||
if (files == null) {
|
||||
throw new MessagingException("The path [" + directory
|
||||
+ "] does not denote a properly accessible directory.");
|
||||
throw new MessagingException("The path [" + directory + "] does not denote a properly accessible directory.");
|
||||
}
|
||||
return this.filter.filterFiles(files);
|
||||
|
||||
return this.filter.filterEntries(files);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,10 +56,7 @@ public class DefaultDirectoryScanner implements DirectoryScanner {
|
||||
return directory.listFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void setFilter(FileListFilter filter) {
|
||||
public void setFilter(EntryListFilter<File> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ public class DefaultDirectoryScanner implements DirectoryScanner {
|
||||
* This class takes the minimal implementation and merely delegates to the locker if set.
|
||||
*/
|
||||
public final boolean tryClaim(File file) {
|
||||
return locker != null ? locker.lock(file) : true;
|
||||
return (locker == null) || locker.lock(file);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Strategy for scanning directories. Implementations may select all children and grandchildren of the scanned directory
|
||||
* in any order. This interface is intended to enable the customization of selection, locking and ordering of files in a
|
||||
* directory like RecursiveDirectoryScanner. If the only requirement is to ignore certain files a FileListFilter
|
||||
* directory like RecursiveDirectoryScanner. If the only requirement is to ignore certain files a EntryListFilter
|
||||
* implementation should suffice.
|
||||
*
|
||||
*
|
||||
@@ -37,6 +39,7 @@ public interface DirectoryScanner {
|
||||
*
|
||||
* @param directory the directory to scan for files
|
||||
* @return a list of files representing the content of the directory
|
||||
* @throws IllegalArgumentException thrown if the input is incorrect
|
||||
*/
|
||||
List<File> listFiles(File directory) throws IllegalArgumentException;
|
||||
|
||||
@@ -47,7 +50,7 @@ public interface DirectoryScanner {
|
||||
*
|
||||
* @param filter the custom filter to be used
|
||||
*/
|
||||
void setFilter(FileListFilter filter);
|
||||
void setFilter(EntryListFilter<File> filter);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -24,22 +23,25 @@ import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.aggregator.ResequencingMessageGroupProcessor;
|
||||
import org.springframework.integration.core.MessageBuilder;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
|
||||
/**
|
||||
* {@link MessageSource} that creates messages from a file system directory. To prevent messages for certain files, you
|
||||
* may supply a {@link FileListFilter}. By default, an {@link AcceptOnceFileListFilter} is used. It ensures files are
|
||||
* may supply a {@link org.springframework.integration.file.entries.EntryListFilter}. By default,
|
||||
* an {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} is used. It ensures files are
|
||||
* picked up only once from the directory.
|
||||
* <p/>
|
||||
* A common problem with reading files is that a file may be detected before it is ready. The default {@link
|
||||
* AcceptOnceFileListFilter} does not prevent this. In most cases, this can be prevented if the file-writing process
|
||||
* org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} does not prevent this. In most cases, this can be prevented if the file-writing process
|
||||
* renames each file as soon as it is ready for reading. A pattern-matching filter that accepts only files that are
|
||||
* ready (e.g. based on a known suffix), composed with the default {@link AcceptOnceFileListFilter} would allow for
|
||||
* this. See {@link org.springframework.integration.file.CompositeFileListFilter} for a way to do this.
|
||||
* ready (e.g. based on a known suffix), composed with the default {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} would allow for
|
||||
* this. See {@link org.springframework.integration.file.entries.CompositeEntryListFilter} for a way to do this.
|
||||
* <p/>
|
||||
* A {@link Comparator} can be used to ensure internal ordering of the Files in a {@link PriorityBlockingQueue}. This
|
||||
* does not provide the same guarantees as a {@link ResequencingMessageGroupProcessor}, but in cases where writing files and failure
|
||||
@@ -51,18 +53,11 @@ import java.util.concurrent.PriorityBlockingQueue;
|
||||
* @author Iwein Fuld
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class FileReadingMessageSource implements MessageSource<File>,
|
||||
InitializingBean {
|
||||
|
||||
public class FileReadingMessageSource implements MessageSource<File>, InitializingBean {
|
||||
private static final int DEFAULT_INTERNAL_QUEUE_CAPACITY = 5;
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(FileReadingMessageSource.class);
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FileReadingMessageSource.class);
|
||||
private volatile File directory;
|
||||
|
||||
private volatile DirectoryScanner scanner = new DefaultDirectoryScanner();
|
||||
|
||||
private volatile boolean autoCreateDirectory = true;
|
||||
|
||||
/*
|
||||
@@ -70,7 +65,6 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
* There is no locking around the queue, so there is also no iteration.
|
||||
*/
|
||||
private final Queue<File> toBeReceived;
|
||||
|
||||
private boolean scanEachPoll = false;
|
||||
|
||||
/**
|
||||
@@ -92,7 +86,7 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
*/
|
||||
public FileReadingMessageSource(int internalQueueCapacity) {
|
||||
this(null);
|
||||
Assert.isTrue(internalQueueCapacity>0, "Cannot create a queue with non positive capacity");
|
||||
Assert.isTrue(internalQueueCapacity > 0, "Cannot create a queue with non positive capacity");
|
||||
this.setScanner(new HeadDirectoryScanner(internalQueueCapacity));
|
||||
}
|
||||
|
||||
@@ -108,12 +102,13 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
* @param receptionOrderComparator the comparator to be used to order the files in the internal queue
|
||||
*/
|
||||
public FileReadingMessageSource(Comparator<File> receptionOrderComparator) {
|
||||
toBeReceived = new PriorityBlockingQueue<File>(DEFAULT_INTERNAL_QUEUE_CAPACITY,
|
||||
receptionOrderComparator);
|
||||
toBeReceived = new PriorityBlockingQueue<File>(DEFAULT_INTERNAL_QUEUE_CAPACITY, receptionOrderComparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the input directory.
|
||||
*
|
||||
* @param directory to monitor
|
||||
*/
|
||||
public void setDirectory(File directory) {
|
||||
Assert.notNull(directory, "directory must not be null");
|
||||
@@ -122,6 +117,8 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
|
||||
/**
|
||||
* Optionally specify a custom scanner, for example the {@link org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner}
|
||||
*
|
||||
* @param scanner scanner impl
|
||||
*/
|
||||
public void setScanner(DirectoryScanner scanner) {
|
||||
this.scanner = scanner;
|
||||
@@ -131,19 +128,23 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
* Specify whether to create the source directory automatically if it does not yet exist upon initialization. By
|
||||
* default, this value is <emphasis>true</emphasis>. If set to <emphasis>false</emphasis> and the source directory
|
||||
* does not exist, an Exception will be thrown upon initialization.
|
||||
*
|
||||
* @param autoCreateDirectory should the directory to be monitored be created when this component starts up?
|
||||
*/
|
||||
public void setAutoCreateDirectory(boolean autoCreateDirectory) {
|
||||
this.autoCreateDirectory = autoCreateDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link CompositeFileListFilter} can be used to group them together.
|
||||
* Sets a {@link org.springframework.integration.file.entries.EntryListFilter}. By default a {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} with no bounds is used. In most
|
||||
* cases a customized {@link org.springframework.integration.file.entries.EntryListFilter} will be needed to deal with modification and duplication concerns. If
|
||||
* multiple filters are required a {@link org.springframework.integration.file.entries.CompositeEntryListFilter} can be used to group them together.
|
||||
* <p/>
|
||||
* <b>The supplied filter must be thread safe.</b>.
|
||||
*
|
||||
* @param filter a filter
|
||||
*/
|
||||
public void setFilter(FileListFilter filter) {
|
||||
public void setFilter(EntryListFilter<File> filter) {
|
||||
Assert.notNull(filter, "'filter' must not be null");
|
||||
this.scanner.setFilter(filter);
|
||||
}
|
||||
@@ -153,6 +154,8 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
* against duplicate processing.
|
||||
* <p/>
|
||||
* <b>The supplied FileLocker must be thread safe</b>
|
||||
*
|
||||
* @param locker a locker
|
||||
*/
|
||||
public void setLocker(FileLocker locker) {
|
||||
Assert.notNull(locker, "'fileLocker' must not be null.");
|
||||
@@ -168,50 +171,60 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
* java.util.concurrent.BlockingQueue} that this class is keeping will more likely be out of sync with the file
|
||||
* system if this flag is set to <code>false</code>, but it will change more often (causing expensive reordering) if
|
||||
* it is set to <code>true</code>.
|
||||
*
|
||||
* @param scanEachPoll whether or not the component should re-scan (as opposed to not rescanning until the entire backlog has been delivered)
|
||||
*/
|
||||
public void setScanEachPoll(boolean scanEachPoll) {
|
||||
this.scanEachPoll = scanEachPoll;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ResultOfMethodCallIgnored"})
|
||||
public final void afterPropertiesSet() {
|
||||
Assert.notNull(directory, "'directory' must not be set before initialization");
|
||||
|
||||
if (!this.directory.exists() && this.autoCreateDirectory) {
|
||||
this.directory.mkdirs();
|
||||
}
|
||||
Assert.isTrue(this.directory.exists(), "Source directory ["
|
||||
+ directory + "] does not exist.");
|
||||
Assert.isTrue(this.directory.isDirectory(), "Source path ["
|
||||
+ this.directory + "] does not point to a directory.");
|
||||
Assert.isTrue(this.directory.canRead(), "Source directory ["
|
||||
+ this.directory + "] is not readable.");
|
||||
|
||||
Assert.isTrue(this.directory.exists(), "Source directory [" + directory + "] does not exist.");
|
||||
Assert.isTrue(this.directory.isDirectory(), "Source path [" + this.directory + "] does not point to a directory.");
|
||||
Assert.isTrue(this.directory.canRead(), "Source directory [" + this.directory + "] is not readable.");
|
||||
}
|
||||
|
||||
public Message<File> receive() throws MessagingException {
|
||||
Message<File> message = null;
|
||||
|
||||
// rescan only if needed or explicitly configured
|
||||
if (scanEachPoll || toBeReceived.isEmpty()) {
|
||||
scanInputDirectory();
|
||||
}
|
||||
|
||||
File file = toBeReceived.poll();
|
||||
|
||||
// file == null means the queue was empty
|
||||
// we can't rely on isEmpty for concurrency reasons
|
||||
while (file != null && !scanner.tryClaim(file)) {
|
||||
while ((file != null) && !scanner.tryClaim(file)) {
|
||||
file = toBeReceived.poll();
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
message = MessageBuilder.withPayload(file).build();
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created message: [" + message + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private void scanInputDirectory() {
|
||||
List<File> filteredFiles = scanner.listFiles(directory);
|
||||
Set<File> freshFiles = new HashSet<File>(filteredFiles);
|
||||
|
||||
if (!freshFiles.isEmpty()) {
|
||||
toBeReceived.addAll(freshFiles);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Added to queue: " + freshFiles);
|
||||
}
|
||||
@@ -220,16 +233,21 @@ public class FileReadingMessageSource implements MessageSource<File>,
|
||||
|
||||
/**
|
||||
* Adds the failed message back to the 'toBeReceived' queue if there is room.
|
||||
*
|
||||
* @param failedMessage the {@link org.springframework.integration.Message} that blew up
|
||||
*/
|
||||
public void onFailure(Message<File> failedMessage, Throwable t) {
|
||||
public void onFailure(Message<File> failedMessage) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to send: " + failedMessage);
|
||||
}
|
||||
|
||||
toBeReceived.offer(failedMessage.getPayload());
|
||||
}
|
||||
|
||||
/**
|
||||
* The message is just logged. It was already removed from the queue during the call to <code>receive()</code>
|
||||
*
|
||||
* @param sentMessage the message that was successfully delivered
|
||||
*/
|
||||
public void onSend(Message<File> sentMessage) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
@@ -13,13 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
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.
|
||||
@@ -28,19 +31,18 @@ import java.util.List;
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HeadDirectoryScanner extends DefaultDirectoryScanner {
|
||||
|
||||
public HeadDirectoryScanner(int maxNumberOfFiles) {
|
||||
this.setFilter(new HeadFilter(maxNumberOfFiles));
|
||||
}
|
||||
|
||||
private class HeadFilter implements FileListFilter {
|
||||
private class HeadFilter implements EntryListFilter<File> {
|
||||
private final int maxNumberOfFiles;
|
||||
|
||||
public HeadFilter(int maxNumberOfFiles) {
|
||||
this.maxNumberOfFiles = maxNumberOfFiles;
|
||||
}
|
||||
|
||||
public List<File> filterFiles(File[] files) {
|
||||
public List<File> filterEntries(File[] files) {
|
||||
return Arrays.asList(files).subList(0, Math.min(files.length, maxNumberOfFiles));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,110 +13,115 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
import org.springframework.integration.file.entries.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.3
|
||||
*/
|
||||
public class FileListFilterFactoryBean implements FactoryBean<FileListFilter> {
|
||||
public class FileListFilterFactoryBean implements FactoryBean<EntryListFilter<File>> {
|
||||
private volatile EntryListFilter<File> fileListFilter;
|
||||
private volatile EntryListFilter<File> filterReference;
|
||||
private volatile Pattern filenamePattern;
|
||||
private volatile Boolean preventDuplicates;
|
||||
private final Object monitor = new Object();
|
||||
private volatile Collection<EntryListFilter<File>> filterReferences;
|
||||
private FileEntryNamer fileNamer = new FileEntryNamer();
|
||||
|
||||
private volatile FileListFilter fileListFilter;
|
||||
public void setFilterReferences(Collection<EntryListFilter<File>> filterReferences) {
|
||||
this.filterReferences = filterReferences;
|
||||
}
|
||||
|
||||
private volatile FileListFilter filterReference;
|
||||
public void setFilterReference(EntryListFilter<File> filterReference) {
|
||||
this.filterReference = filterReference;
|
||||
}
|
||||
|
||||
private volatile Pattern filenamePattern;
|
||||
public void setFilenamePattern(Pattern filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
private volatile Boolean preventDuplicates;
|
||||
public void setPreventDuplicates(Boolean preventDuplicates) {
|
||||
this.preventDuplicates = preventDuplicates;
|
||||
}
|
||||
|
||||
private final Object monitor = new Object();
|
||||
public EntryListFilter<File> getObject() throws Exception {
|
||||
if (this.fileListFilter == null) {
|
||||
synchronized (this.monitor) {
|
||||
this.intializeFileListFilter();
|
||||
}
|
||||
}
|
||||
|
||||
return this.fileListFilter;
|
||||
}
|
||||
|
||||
public void setFilterReference(FileListFilter filterReference) {
|
||||
this.filterReference = filterReference;
|
||||
}
|
||||
public Class<?> getObjectType() {
|
||||
return (this.fileListFilter != null) ? this.fileListFilter.getClass() : EntryListFilter.class;
|
||||
}
|
||||
|
||||
public void setFilenamePattern(Pattern filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setPreventDuplicates(Boolean preventDuplicates) {
|
||||
this.preventDuplicates = preventDuplicates;
|
||||
}
|
||||
private void intializeFileListFilter() {
|
||||
if (this.fileListFilter != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public FileListFilter getObject() throws Exception {
|
||||
if (this.fileListFilter == null) {
|
||||
synchronized (this.monitor) {
|
||||
this.intializeFileListFilter();
|
||||
}
|
||||
}
|
||||
return this.fileListFilter;
|
||||
}
|
||||
EntryListFilter<File> flf=null;
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return (this.fileListFilter != null)
|
||||
? this.fileListFilter.getClass() : FileListFilter.class;
|
||||
}
|
||||
if ((this.filterReference != null) && (this.filenamePattern != null)) {
|
||||
throw new IllegalArgumentException("The 'filter' reference and " + "'filename-pattern' attributes are mutually exclusive.");
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
if (this.filterReference != null) {
|
||||
if (Boolean.TRUE.equals(this.preventDuplicates)) {
|
||||
flf = this.createCompositeWithAcceptOnceFilter(this.filterReference);
|
||||
} else { // preventDuplicates is either FALSE or NULL
|
||||
flf = this.filterReference;
|
||||
}
|
||||
} else if (this.filenamePattern != null) {
|
||||
PatternMatchingEntryListFilter<File> patternFilter = new PatternMatchingEntryListFilter<File>(fileNamer, this.filenamePattern);
|
||||
|
||||
private void intializeFileListFilter() {
|
||||
if (this.fileListFilter != null) {
|
||||
return;
|
||||
}
|
||||
FileListFilter flf = null;
|
||||
if (this.filterReference != null && this.filenamePattern != null) {
|
||||
throw new IllegalArgumentException("The 'filter' reference and " +
|
||||
"'filename-pattern' attributes are mutually exclusive.");
|
||||
}
|
||||
if (this.filterReference != null) {
|
||||
if (Boolean.TRUE.equals(this.preventDuplicates)) {
|
||||
flf = this.createCompositeWithAcceptOnceFilter(this.filterReference);
|
||||
}
|
||||
else { // preventDuplicates is either FALSE or NULL
|
||||
flf = this.filterReference;
|
||||
}
|
||||
}
|
||||
else if (this.filenamePattern != null) {
|
||||
PatternMatchingFileListFilter patternFilter = new PatternMatchingFileListFilter(this.filenamePattern);
|
||||
if (Boolean.FALSE.equals(this.preventDuplicates)) {
|
||||
flf = patternFilter;
|
||||
}
|
||||
else { // preventDuplicates is either TRUE or NULL
|
||||
flf = this.createCompositeWithAcceptOnceFilter(patternFilter);
|
||||
}
|
||||
}
|
||||
else if (Boolean.FALSE.equals(this.preventDuplicates)) {
|
||||
flf = new AbstractFileListFilter() {
|
||||
@Override
|
||||
protected boolean accept(File file) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
else { // preventDuplicates is either TRUE or NULL
|
||||
flf = new AcceptOnceFileListFilter();
|
||||
}
|
||||
this.fileListFilter = flf;
|
||||
}
|
||||
if (Boolean.FALSE.equals(this.preventDuplicates)) {
|
||||
flf = patternFilter;
|
||||
} else { // preventDuplicates is either TRUE or NULL
|
||||
flf = this.createCompositeWithAcceptOnceFilter(patternFilter);
|
||||
}
|
||||
} else if (Boolean.FALSE.equals(this.preventDuplicates)) {
|
||||
flf = new AcceptAllEntryListFilter<File>();
|
||||
} else { // preventDuplicates is either TRUE or NULL
|
||||
flf = new AcceptOnceEntryFileListFilter<File>();
|
||||
}
|
||||
|
||||
private FileListFilter createCompositeWithAcceptOnceFilter(FileListFilter otherFilter) {
|
||||
CompositeFileListFilter compositeFilter = new CompositeFileListFilter();
|
||||
compositeFilter.addFilter(new AcceptOnceFileListFilter(), otherFilter);
|
||||
return compositeFilter;
|
||||
}
|
||||
// finally, it might be that they simply want a {@link CompositeEntryListFilter}
|
||||
if ((this.filterReferences != null) && (this.filterReferences.size() > 0) ) {
|
||||
CompositeEntryListFilter<File> flfc = new CompositeEntryListFilter<File>();
|
||||
|
||||
for (EntryListFilter<File> ff : filterReferences)
|
||||
flfc.addFilter(ff);
|
||||
|
||||
flf = flfc;
|
||||
}
|
||||
if( flf== null)flf =new CompositeEntryListFilter<File>();
|
||||
|
||||
this.fileListFilter = flf;
|
||||
}
|
||||
|
||||
private CompositeEntryListFilter<File> createCompositeWithAcceptOnceFilter(EntryListFilter<File> otherFilter) {
|
||||
CompositeEntryListFilter<File> compositeFilter = new CompositeEntryListFilter<File>();
|
||||
compositeFilter.addFilter(new AcceptOnceEntryFileListFilter<File>() );
|
||||
compositeFilter.addFilter(otherFilter);
|
||||
|
||||
return compositeFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ package org.springframework.integration.file.config;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
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.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.locking.AbstractFileLockerFilter;
|
||||
|
||||
import java.io.File;
|
||||
@@ -41,7 +41,7 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean<FileRead
|
||||
|
||||
private volatile File directory;
|
||||
|
||||
private volatile FileListFilter filter;
|
||||
private volatile EntryListFilter<File> filter;
|
||||
|
||||
private volatile AbstractFileLockerFilter locker;
|
||||
|
||||
@@ -69,7 +69,7 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean<FileRead
|
||||
this.scanner = scanner;
|
||||
}
|
||||
|
||||
public void setFilter(FileListFilter filter) {
|
||||
public void setFilter(EntryListFilter<File> filter) {
|
||||
if (filter instanceof AbstractFileLockerFilter && this.locker == null) {
|
||||
this.setLocker((AbstractFileLockerFilter) filter);
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean<FileRead
|
||||
if (this.locker == null) {
|
||||
this.source.setFilter(this.filter);
|
||||
} else {
|
||||
this.source.setFilter(new CompositeFileListFilter(this.filter, this.locker));
|
||||
this.source.setFilter(new CompositeEntryListFilter<File>(this.filter, this.locker));
|
||||
this.source.setLocker(locker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file.entries;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class AbstractEntryListFilter<T> implements EntryListFilter<T> {
|
||||
|
||||
protected abstract boolean accept(T t);
|
||||
/**
|
||||
* A convenience base class for any {@link EntryListFilter} whose criteria can be
|
||||
* evaluated against each File in isolation. If the entire List of files is
|
||||
* required for evaluation, implement the {@link EntryListFilter} interface directly.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractEntryListFilter<T> implements InitializingBean, EntryListFilter<T> {
|
||||
public abstract boolean accept(T t);
|
||||
|
||||
public List<T> filterEntries(T[] entries) {
|
||||
List<T> accepted = new ArrayList<T>();
|
||||
@@ -37,4 +47,8 @@ public abstract class AbstractEntryListFilter<T> implements EntryListFilter<T> {
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// its all you!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.entries;
|
||||
|
||||
|
||||
/**
|
||||
* Simple NOOP implementation for {@link org.springframework.integration.file.entries.EntryListFilter} implementation.
|
||||
* Suitable as a default in implementations.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @param <T>
|
||||
*/
|
||||
public class AcceptAllEntryListFilter<T> extends AbstractEntryListFilter<T> {
|
||||
@Override
|
||||
public boolean accept(T t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,20 +13,29 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file.entries;
|
||||
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@link EntryListFilter} that passes files only one time. This can
|
||||
* conveniently be used to prevent duplication of files, as is done in
|
||||
* {@link org.springframework.integration.file.FileReadingMessageSource}.
|
||||
* <p/>
|
||||
* This implementation is thread safe.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AcceptOnceEntryFileListFilter<T> extends AbstractEntryListFilter<T> {
|
||||
private final Queue<T> seen;
|
||||
private final Object monitor = new Object();
|
||||
|
||||
/**
|
||||
* Creates an AcceptOnceFileFilter that is based on a bounded queue. If the
|
||||
* Creates an {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} that is based on a bounded queue. If the
|
||||
* queue overflows, files that fall out will be passed through this filter
|
||||
* again if passed to the {@link #filterEntries(Object[])} method.
|
||||
*
|
||||
@@ -44,7 +53,7 @@ public class AcceptOnceEntryFileListFilter<T> extends AbstractEntryListFilter<T>
|
||||
this.seen = new LinkedBlockingQueue<T>();
|
||||
}
|
||||
|
||||
protected boolean accept(T pathname) {
|
||||
public boolean accept(T pathname) {
|
||||
synchronized (this.monitor) {
|
||||
if (seen.contains(pathname)) {
|
||||
return false;
|
||||
|
||||
@@ -13,41 +13,51 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file.entries;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class CompositeEntryListFilter<T> implements EntryListFilter<T> {
|
||||
private final Set<EntryListFilter> fileFilters;
|
||||
private final Set<EntryListFilter<T>> fileFilters;
|
||||
|
||||
public CompositeEntryListFilter(EntryListFilter... fileFilters) {
|
||||
this.fileFilters = new LinkedHashSet<EntryListFilter>(Arrays.asList(fileFilters));
|
||||
public CompositeEntryListFilter(EntryListFilter<T>... fileFilters) {
|
||||
this.fileFilters = new LinkedHashSet<EntryListFilter<T>>(Arrays.asList(fileFilters));
|
||||
}
|
||||
|
||||
public CompositeEntryListFilter(Collection<EntryListFilter> fileFilters) {
|
||||
this.fileFilters = new LinkedHashSet<EntryListFilter>(fileFilters);
|
||||
public CompositeEntryListFilter(Collection<?extends EntryListFilter<T>> fileFilters) {
|
||||
this.fileFilters = new LinkedHashSet<EntryListFilter<T>>(fileFilters);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<T> filterEntries(T[] entries) {
|
||||
Assert.notNull(entries, "'files' should not be null");
|
||||
List<T> leftOver = Arrays.asList(entries);
|
||||
for (EntryListFilter fileFilter : this.fileFilters) {
|
||||
T[] ts =(T[]) leftOver.toArray();
|
||||
|
||||
List<T> leftOver = Arrays.asList(entries);
|
||||
|
||||
for (EntryListFilter<T> fileFilter : this.fileFilters) {
|
||||
T[] ts = (T[]) leftOver.toArray();
|
||||
leftOver = fileFilter.filterEntries(ts);
|
||||
}
|
||||
|
||||
return leftOver;
|
||||
}
|
||||
|
||||
public CompositeEntryListFilter<T> addFilter(EntryListFilter<T> filter) {
|
||||
return this.addFilters(Arrays.asList(filter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filters one or more new filters to add
|
||||
* @return this CompositeFileFilter instance with the added filters
|
||||
* @see #addFilters(Collection)
|
||||
*/
|
||||
public CompositeEntryListFilter addFilter(EntryListFilter... filters) {
|
||||
@SuppressWarnings("unused")
|
||||
public CompositeEntryListFilter<T> addFilters(EntryListFilter<T>[] filters) {
|
||||
return addFilters(Arrays.asList(filters));
|
||||
}
|
||||
|
||||
@@ -59,7 +69,16 @@ public class CompositeEntryListFilter<T> implements EntryListFilter<T> {
|
||||
* @param filtersToAdd a list of filters to add
|
||||
* @return this CompositeEntryListFilter instance with the added filters
|
||||
*/
|
||||
public CompositeEntryListFilter addFilters(Collection<EntryListFilter> filtersToAdd) {
|
||||
public CompositeEntryListFilter<T> addFilters(Collection<EntryListFilter<T>> filtersToAdd) {
|
||||
for (EntryListFilter<T> elf : filtersToAdd)
|
||||
if (elf instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) elf).afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.fileFilters.addAll(filtersToAdd);
|
||||
|
||||
return this;
|
||||
|
||||
@@ -18,6 +18,19 @@ package org.springframework.integration.file.entries;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface EntryListFilter <T> {
|
||||
List<T> filterEntries(T [] entries );
|
||||
/**
|
||||
* Strategy interface for filtering a group of entries / files.
|
||||
* <p/>
|
||||
* {@link EntryListFilter} that passes file entries only one time. This can
|
||||
* conveniently be used to prevent duplication of files, as is done in
|
||||
* {@link org.springframework.integration.file.FileReadingMessageSource}.
|
||||
* <p/>
|
||||
* This implementation is thread safe.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface EntryListFilter<T> {
|
||||
List<T> filterEntries(T[] entries);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,21 @@
|
||||
*/
|
||||
package org.springframework.integration.file.entries;
|
||||
|
||||
/**
|
||||
* Responsible for coercing a String identification out of the {@link T} entry.
|
||||
* @param <T> the type of entry (there's an implementation for FTP, SFTP, and plain-old java.io.Files)
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public interface EntryNamer<T> {
|
||||
|
||||
/**
|
||||
* This is the one place I couldn't spackle over the interface differences between an FTPFile (FTP adapter), File (File adapter), and LsEntry (SFTP adapter)
|
||||
* with generics alone. So we have a typed strategy implementation for accessing a property ....
|
||||
*
|
||||
*
|
||||
* @param entry the entry in a file system listing
|
||||
* @return the String name that might be used to reference that entry or to do regular expression checks against
|
||||
*/
|
||||
String nameOf(T entry);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.entries;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* {@link java.io.File} implementation of the {@link org.springframework.integration.file.entries.EntryNamer} strategy.
|
||||
*
|
||||
* This part feels a little over-engineered...
|
||||
*
|
||||
* @author Josh Long
|
||||
*
|
||||
*/
|
||||
public class FileEntryNamer implements EntryNamer<File> {
|
||||
public String nameOf(File entry) {
|
||||
return (entry != null) ? entry.getName() : null;
|
||||
}
|
||||
}
|
||||
@@ -24,16 +24,29 @@ import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* experimental
|
||||
* <emphasis>experimental</emphasis>
|
||||
* <p/>
|
||||
* Filters a listing of entries (T) by qualifying their 'name' (as determined by {@link org.springframework.integration.file.entries.EntryNamer})
|
||||
* against a regular expression (an instance of {@link java.util.regex.Pattern})
|
||||
*
|
||||
* @author Josh Long
|
||||
* @param <T> the type of entry
|
||||
*/
|
||||
public abstract class PatternMatchingEntryListFilter<T> extends AbstractEntryListFilter<T> implements InitializingBean {
|
||||
public class PatternMatchingEntryListFilter<T> extends AbstractEntryListFilter<T> implements InitializingBean {
|
||||
private Pattern pattern;
|
||||
private String patternExpression;
|
||||
private EntryNamer<T> entryNamer;
|
||||
|
||||
public PatternMatchingEntryListFilter(EntryNamer<T> en, String p) {
|
||||
this.entryNamer = en;
|
||||
this.patternExpression = p;
|
||||
}
|
||||
|
||||
public PatternMatchingEntryListFilter(EntryNamer<T> en, Pattern p) {
|
||||
this.entryNamer = en;
|
||||
this.pattern = p;
|
||||
}
|
||||
|
||||
public void setPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
@@ -46,13 +59,12 @@ public abstract class PatternMatchingEntryListFilter<T> extends AbstractEntryLis
|
||||
if (StringUtils.hasText(this.patternExpression) && (pattern == null)) {
|
||||
this.pattern = Pattern.compile(this.patternExpression);
|
||||
}
|
||||
|
||||
Assert.notNull(this.entryNamer,"'entryNamer' must not be null!");
|
||||
Assert.notNull(this.entryNamer, "'entryNamer' must not be null!");
|
||||
Assert.notNull(this.pattern, "'pattern' mustn't be null!");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(T t) {
|
||||
public boolean accept(T t) {
|
||||
return (t != null) && this.pattern.matcher(this.entryNamer.nameOf(t)).matches();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.entries;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* this simply takes an {@link org.springframework.integration.file.entries.EntryListFilter}
|
||||
* and produces an object that can field just <b>one</b> argument instea of an array
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SingleEntryAdaptingEntryListFilter<T> extends AbstractEntryListFilter<T> {
|
||||
|
||||
/**
|
||||
* the {@link org.springframework.integration.file.entries.EntryListFilter} that you'd like to delegate to
|
||||
*/
|
||||
private volatile EntryListFilter<T> entryFilter;
|
||||
|
||||
public SingleEntryAdaptingEntryListFilter(EntryListFilter<T> ef) {
|
||||
this.entryFilter = ef;
|
||||
Assert.notNull(this.entryFilter, "the entryFilter can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean accept(T t) {
|
||||
T[] ts = (T[]) new Object[] { t };
|
||||
return this.entryFilter.filterEntries(ts).size() == 1;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@@ -24,30 +24,31 @@ import java.util.List;
|
||||
* A convenience base class for any {@link FileListFilter} whose criteria can be
|
||||
* evaluated against each File in isolation. If the entire List of files is
|
||||
* required for evaluation, implement the FileListFilter interface directly.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractFileListFilter implements FileListFilter {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final List<File> filterFiles(File[] files) {
|
||||
List<File> accepted = new ArrayList<File>();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (this.accept(file)) {
|
||||
accepted.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final List<File> filterFiles(File[] files) {
|
||||
List<File> accepted = new ArrayList<File>();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (this.accept(file)) {
|
||||
accepted.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method.
|
||||
*/
|
||||
protected abstract boolean accept(File file);
|
||||
/**
|
||||
* Subclasses must implement this method.
|
||||
*/
|
||||
protected abstract boolean accept(File file);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.filters;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
|
||||
/**
|
||||
* {@link FileListFilter} that passes files only one time. This can
|
||||
* conveniently be used to prevent duplication of files, as is done in
|
||||
* {@link org.springframework.integration.file.FileReadingMessageSource}.
|
||||
* <p/>
|
||||
* This implementation is thread safe.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class AcceptOnceFileListFilter extends AbstractFileListFilter {
|
||||
private final Queue<File> seen;
|
||||
private final Object monitor = new Object();
|
||||
|
||||
/**
|
||||
* Creates an AcceptOnceFileFilter that is based on a bounded queue. If the
|
||||
* queue overflows, files that fall out will be passed through this filter
|
||||
* again if passed to the {@link #filterFiles(File[])} method.
|
||||
*
|
||||
* @param maxCapacity the maximum number of Files to maintain in the 'seen'
|
||||
* queue.
|
||||
*/
|
||||
public AcceptOnceFileListFilter(int maxCapacity) {
|
||||
this.seen = new LinkedBlockingQueue<File>(maxCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AcceptOnceFileFilter based on an unbounded queue.
|
||||
*/
|
||||
public AcceptOnceFileListFilter() {
|
||||
this.seen = new LinkedBlockingQueue<File>();
|
||||
}
|
||||
|
||||
protected boolean accept(File pathname) {
|
||||
synchronized (this.monitor) {
|
||||
if (seen.contains(pathname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!seen.offer(pathname)) {
|
||||
seen.poll();
|
||||
seen.add(pathname);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -22,6 +21,7 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Composition that delegates to multiple {@link FileFilter}s. The composition is AND based, meaning that a file must
|
||||
* pass through each filter's {@link #filterFiles(java.io.File[])} method in order to be accepted by the composite.
|
||||
@@ -29,11 +29,10 @@ import java.util.*;
|
||||
* @author Iwein Fuld
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeFileListFilter implements FileListFilter {
|
||||
|
||||
private final Set<FileListFilter> fileFilters;
|
||||
|
||||
|
||||
public CompositeFileListFilter(FileListFilter... fileFilters) {
|
||||
this.fileFilters = new LinkedHashSet<FileListFilter>(Arrays.asList(fileFilters));
|
||||
}
|
||||
@@ -42,7 +41,6 @@ public class CompositeFileListFilter implements FileListFilter {
|
||||
this.fileFilters = new LinkedHashSet<FileListFilter>(fileFilters);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
@@ -50,33 +48,35 @@ public class CompositeFileListFilter implements FileListFilter {
|
||||
*/
|
||||
public List<File> filterFiles(File[] files) {
|
||||
Assert.notNull(files, "'files' should not be null");
|
||||
|
||||
List<File> leftOver = Arrays.asList(files);
|
||||
|
||||
for (FileListFilter fileFilter : this.fileFilters) {
|
||||
leftOver = fileFilter.filterFiles(leftOver.toArray(new File[]{}));
|
||||
}
|
||||
|
||||
return leftOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filters one or more new filters to add
|
||||
* @return this CompositeFileFilter instance with the added filters
|
||||
* @see #addFilters(Collection)
|
||||
*/
|
||||
public CompositeFileListFilter addFilter(FileListFilter... filters) {
|
||||
/* public CompositeFileListFilter addFilter(FileListFilter... filters) {
|
||||
return addFilters(Arrays.asList(filters));
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Not thread safe. Only a single thread may add filters at a time.
|
||||
*
|
||||
* <p/>
|
||||
* Add the new filters to this CompositeFileFilter while maintaining the existing filters.
|
||||
*
|
||||
* @param filtersToAdd a list of filters to add
|
||||
* @return this CompositeFileFilter instance with the added filters
|
||||
*/
|
||||
public CompositeFileListFilter addFilters(Collection<FileListFilter> filtersToAdd) {
|
||||
/* public CompositeFileListFilter addFilters(Collection<FileListFilter> filtersToAdd) {
|
||||
this.fileFilters.addAll(filtersToAdd);
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}*/
|
||||
}
|
||||
@@ -14,22 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Strategy interface for filtering a group of files.
|
||||
*
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
@Deprecated
|
||||
public interface FileListFilter {
|
||||
|
||||
/**
|
||||
* Filters out files and returns the files that are left in a list, or an
|
||||
* empty list when a null is passed in.
|
||||
*/
|
||||
List<File> filterFiles(File[] files);
|
||||
/**
|
||||
* Filters out files and returns the files that are left in a list, or an
|
||||
* empty list when a null is passed in.
|
||||
*/
|
||||
List<File> filterFiles(File[] files);
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -26,6 +26,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Deprecated
|
||||
public class PatternMatchingFileListFilter extends AbstractFileListFilter {
|
||||
|
||||
private final Pattern pattern;
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file.locking;
|
||||
|
||||
import org.springframework.integration.file.AbstractFileListFilter;
|
||||
import org.springframework.integration.file.FileLocker;
|
||||
import org.springframework.integration.file.entries.AbstractEntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -29,9 +29,10 @@ import java.io.File;
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractFileLockerFilter extends AbstractFileListFilter implements FileLocker {
|
||||
public abstract class AbstractFileLockerFilter extends AbstractEntryListFilter<File> implements FileLocker {
|
||||
|
||||
protected final boolean accept(File file) {
|
||||
return isLockable(file);
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return this.isLockable(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
package org.springframework.integration.file.monitors;
|
||||
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.file.entries.*;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
|
||||
/**
|
||||
* This component will support event-based (not poller based) notifications of files from a file system.
|
||||
* Immediate implementations will center around supporting other adapters's delivery of files once they've been synced from a remote system.
|
||||
* Potential future expansions for consumer consumption might be a push / event-based file adapter based on either native code
|
||||
* or Java 7's NIO.2 WaterService or other third party implementations.
|
||||
* <p/>
|
||||
* In the meantime, this provides us with a base class for building event driven file adapters quickly. The two cases I see are:
|
||||
* <p/>
|
||||
* <ol>
|
||||
* <li></li>
|
||||
* <li></li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractEventDrivenFileMonitor extends IntegrationObjectSupport implements EventDrivenDirectoryMonitor {
|
||||
/**
|
||||
* when this component starts up, we can perform a scan of the folder this first time and to pre-seed the #additions queue
|
||||
*/
|
||||
private boolean scanDirectoryOnLoad;
|
||||
|
||||
/**
|
||||
* How many files we'll support in the backlog at a time
|
||||
*/
|
||||
private volatile int maxQueueSize = 100;
|
||||
|
||||
/**
|
||||
* the backlog
|
||||
*/
|
||||
private volatile LinkedBlockingQueue<File> additions;
|
||||
|
||||
/**
|
||||
* An {@link java.util.concurrent.Executor} implementation. Default is {@link org.springframework.core.task.SimpleAsyncTaskExecutor}
|
||||
*/
|
||||
private volatile Executor executor;
|
||||
|
||||
/**
|
||||
* Should the director be automatically created?
|
||||
*/
|
||||
private volatile boolean autoCreateDirectory;
|
||||
|
||||
/**
|
||||
* The directory to monitor (a {@link java.io.File})
|
||||
*/
|
||||
private volatile File directoryToMonitorCached;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.file.entries.EntryListFilter} reference
|
||||
*/
|
||||
private volatile SingleEntryAdaptingEntryListFilter<File> filter;
|
||||
|
||||
/**
|
||||
* state guard (extra for post-init state)
|
||||
*/
|
||||
private final Object guard = new Object();
|
||||
|
||||
public void setScanDirectoryOnLoad(boolean scanDirectoryOnLoad) {
|
||||
this.scanDirectoryOnLoad = scanDirectoryOnLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* installs directory, and then kicks of an event pump
|
||||
*
|
||||
* @param directory the directory to start watching from. Unspecified if this implies recursion or not.
|
||||
* @param fileAdditionListener the callback
|
||||
* @throws Exception
|
||||
*/
|
||||
public void monitor(File directory, FileAdditionListener fileAdditionListener)
|
||||
throws Exception {
|
||||
this.installDirectoryIfRequired(directory);
|
||||
this.prescan();
|
||||
this.executor.execute(new FileDeliveryPump(fileAdditionListener));
|
||||
}
|
||||
|
||||
public void setMaxQueueSize(int maxQueueSize) {
|
||||
this.maxQueueSize = maxQueueSize;
|
||||
}
|
||||
|
||||
public void setFilter(EntryListFilter<File> filter) {
|
||||
this.filter = new SingleEntryAdaptingEntryListFilter<File>(filter);
|
||||
}
|
||||
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectory(boolean autoCreateDirectory) {
|
||||
this.autoCreateDirectory = autoCreateDirectory;
|
||||
}
|
||||
|
||||
protected void publishNewFileReceived(String path) {
|
||||
this.publishNewFileReceived(new File(path));
|
||||
}
|
||||
|
||||
protected void publishNewFileReceived(File file) {
|
||||
this.additions.add(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obviously, we're trying to keep away from scanning, but this may be necessary at startup to catch up the backlog
|
||||
*/
|
||||
protected void prescan() {
|
||||
synchronized (this.guard) {
|
||||
if (this.scanDirectoryOnLoad) {
|
||||
for (File f : this.directoryToMonitorCached.listFiles())
|
||||
this.publishNewFileReceived(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ensuring that the directory we're monitroring exists or can be created
|
||||
*
|
||||
* @param directoryToMonitor the directory to monitor
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void installDirectoryIfRequired(File directoryToMonitor)
|
||||
throws Exception {
|
||||
synchronized (this.guard) {
|
||||
this.directoryToMonitorCached = directoryToMonitor;
|
||||
Assert.state(null != this.directoryToMonitorCached, "the directory to monitor can't be null");
|
||||
|
||||
boolean directoryIsReady = this.directoryToMonitorCached.exists();
|
||||
|
||||
if (!directoryIsReady) {
|
||||
if (!directoryToMonitorCached.exists()) {
|
||||
if (this.autoCreateDirectory) {
|
||||
Assert.state(directoryToMonitorCached.mkdirs() && directoryToMonitorCached.exists(),
|
||||
String.format("Couldn't create the directory %s", directoryToMonitorCached.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom initialization hook - override at your discretion
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void start() throws Exception{
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
additions = new LinkedBlockingQueue<File>(this.maxQueueSize);
|
||||
|
||||
if (this.executor == null) {
|
||||
this.executor = new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
if (this.filter == null) {
|
||||
this.filter = new SingleEntryAdaptingEntryListFilter<File>(new AcceptAllEntryListFilter<File>());
|
||||
}
|
||||
|
||||
Assert.notNull(this.filter, "the filter can't be null");
|
||||
|
||||
this.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* a way to remove the responsibility of reacting to the file system from implementations while still being thread safe and handling backlog
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
class FileDeliveryPump implements Runnable {
|
||||
private volatile FileAdditionListener fileAdditionListener;
|
||||
|
||||
public FileDeliveryPump(FileAdditionListener fileAdditionListener) {
|
||||
this.fileAdditionListener = fileAdditionListener;
|
||||
Assert.notNull(this.fileAdditionListener, "the FileAdditionListener can't be null");
|
||||
}
|
||||
|
||||
public void run() {
|
||||
do {
|
||||
try {
|
||||
File taken = additions.take();
|
||||
|
||||
if (filter.accept(taken)) {
|
||||
fileAdditionListener.fileAdded(taken);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
|
||||
class MyEDFRM extends AbstractEventDrivenFileMonitor {
|
||||
@Override
|
||||
protected void start() throws Exception {
|
||||
System.out.println("start()");
|
||||
}
|
||||
|
||||
public void addToHeap(File file) {
|
||||
this.publishNewFileReceived(file);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
|
||||
|
||||
final MyEDFRM myEDFRM = new MyEDFRM();
|
||||
myEDFRM.setExecutor(simpleAsyncTaskExecutor);
|
||||
myEDFRM.setAutoCreateDirectory(true);
|
||||
myEDFRM.afterPropertiesSet();
|
||||
|
||||
AcceptOnceEntryFileListFilter <File> acceptOnceEntryFileListFilter=new AcceptOnceEntryFileListFilter<File>() ;
|
||||
acceptOnceEntryFileListFilter.afterPropertiesSet();
|
||||
PatternMatchingEntryListFilter <File> patternMatchingEntryListFilter=
|
||||
new PatternMatchingEntryListFilter<File>(new FileEntryNamer(), ".*?jpg");
|
||||
patternMatchingEntryListFilter.afterPropertiesSet();
|
||||
|
||||
Collection<? extends EntryListFilter<File>> l=Arrays.asList( acceptOnceEntryFileListFilter, patternMatchingEntryListFilter);
|
||||
CompositeEntryListFilter compositeEntryListFilter = new CompositeEntryListFilter<File>(l );
|
||||
|
||||
|
||||
myEDFRM.setFilter(compositeEntryListFilter);
|
||||
|
||||
final File desktop = new File(System.getProperty("user.home"), "Desktop");
|
||||
myEDFRM.monitor(desktop, new FileAdditionListener() {
|
||||
public void fileAdded(File f) {
|
||||
System.out.println("Got one! " + f.getAbsolutePath());
|
||||
}
|
||||
});
|
||||
System.out.println("enjoying the world");
|
||||
simpleAsyncTaskExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000 * 10);
|
||||
|
||||
for (File f : desktop.listFiles())
|
||||
myEDFRM.addToHeap(f);
|
||||
} catch (InterruptedException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.integration.file.monitors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* simply takes a cue / hint (something <emphasis>tells</emphasis> it outright that something has
|
||||
* been added to a directory, and it and publishes an event as appropriate). This is useful for adapters
|
||||
* that know when the file's been downloaded and want to deliver data as soon as its downloaded, but to poll the
|
||||
* remote system only at a certain interval.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class DirectedEventDrivenFileMonitor extends AbstractEventDrivenFileMonitor {
|
||||
public void directlyNotifyOfNewFile(File file) {
|
||||
this.publishNewFileReceived(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.integration.file.monitors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Defines an interface for a component that reacts to file system events
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public interface EventDrivenDirectoryMonitor {
|
||||
/**
|
||||
* the implementation should know how to publish events on the {@link FileAdditionListener}
|
||||
* for a given #directory
|
||||
*
|
||||
* @param directory the directory to start watching from. Unspecified if this implies recursion or not.
|
||||
* @param fileAdditionListener the callback
|
||||
* @throws Exception if anything should go wrong
|
||||
*/
|
||||
void monitor(File directory, FileAdditionListener fileAdditionListener) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.integration.file.monitors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A generic hook into the arrival of a new {@link java.io.File}
|
||||
*
|
||||
* @author Josh Long
|
||||
* @see org.springframework.integration.file.monitors.MessageSendingFileAdditionListener
|
||||
*/
|
||||
public interface FileAdditionListener {
|
||||
|
||||
/**
|
||||
* a callback method that's invoked when a new {@link java.io.File} is detected.
|
||||
*
|
||||
* @param f the {@link java.io.File} that was detected
|
||||
*/
|
||||
void fileAdded(File f);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.springframework.integration.file.monitors;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.MessageBuilder;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Supports propagating a {@link org.springframework.integration.Message} on the receipt of a new {@link java.io.File}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class MessageSendingFileAdditionListener extends IntegrationObjectSupport implements FileAdditionListener {
|
||||
private MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
private MessageChannel channel;
|
||||
private PlatformTransactionManager platformTransactionManager;
|
||||
|
||||
public void setChannel(MessageChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.channel, "'channel' can't be null!");
|
||||
|
||||
if (this.platformTransactionManager != null) {
|
||||
this.messagingTemplate.setTransactionManager(platformTransactionManager);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
|
||||
this.platformTransactionManager = platformTransactionManager;
|
||||
}
|
||||
|
||||
public void fileAdded(File f) {
|
||||
Message<File> fileMsg = MessageBuilder.withPayload(f).build();
|
||||
this.messagingTemplate.send(fileMsg);
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.file.FileListFilter"/>
|
||||
<tool:expected-type type="org.springframework.integration.file.filters.FileListFilter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.file.FileListFilter"/>
|
||||
<tool:expected-type type="org.springframework.integration.file.entries.EntryListFilter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
|
||||
@@ -16,57 +16,64 @@
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class CompositeFileListFilterTests {
|
||||
|
||||
private FileListFilter fileFilterMock1 = mock(FileListFilter.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
private EntryListFilter<File> fileFilterMock1 = mock(EntryListFilter.class);
|
||||
|
||||
private FileListFilter fileFilterMock2 = mock(FileListFilter.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
private EntryListFilter<File> fileFilterMock2 = mock(EntryListFilter.class);
|
||||
|
||||
private File fileMock = mock(File.class);
|
||||
private File fileMock = mock(File.class);
|
||||
|
||||
@Test
|
||||
public void forwardedToFilters() throws Exception {
|
||||
CompositeFileListFilter compositeFileFilter = new CompositeFileListFilter(fileFilterMock1, fileFilterMock2);
|
||||
List<File> returnedFiles = Arrays.asList(new File[] { fileMock });
|
||||
when(fileFilterMock1.filterFiles(isA(File[].class))).thenReturn(returnedFiles);
|
||||
when(fileFilterMock2.filterFiles(isA(File[].class))).thenReturn(returnedFiles);
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterFiles(new File[]{fileMock}));
|
||||
verify(fileFilterMock1).filterFiles(isA(File[].class));
|
||||
verify(fileFilterMock2).filterFiles(isA(File[].class));
|
||||
}
|
||||
@Test
|
||||
public void forwardedToFilters() throws Exception {
|
||||
CompositeEntryListFilter<File> compositeFileFilter = new CompositeEntryListFilter<File>(fileFilterMock1, fileFilterMock2);
|
||||
List<File> returnedFiles = Arrays.asList( fileMock);
|
||||
when(fileFilterMock1.filterEntries(isA(File[].class))).thenReturn(returnedFiles);
|
||||
when(fileFilterMock2.filterEntries(isA(File[].class))).thenReturn(returnedFiles);
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterEntries(new File[]{fileMock}));
|
||||
verify(fileFilterMock1).filterEntries(isA(File[].class));
|
||||
verify(fileFilterMock2).filterEntries(isA(File[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardedToAddedFilters() throws Exception {
|
||||
CompositeFileListFilter compositeFileFilter = new CompositeFileListFilter().addFilter(fileFilterMock1, fileFilterMock2);
|
||||
List<File> returnedFiles = Arrays.asList(new File[] { fileMock });
|
||||
when(fileFilterMock1.filterFiles(isA(File[].class))).thenReturn(returnedFiles);
|
||||
when(fileFilterMock2.filterFiles(isA(File[].class))).thenReturn(returnedFiles);
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterFiles(new File[]{fileMock}));
|
||||
verify(fileFilterMock1).filterFiles(isA(File[].class));
|
||||
verify(fileFilterMock2).filterFiles(isA(File[].class));
|
||||
}
|
||||
@Test
|
||||
public void forwardedToAddedFilters() throws Exception {
|
||||
CompositeEntryListFilter<File> compositeFileFilter = new CompositeEntryListFilter<File>();
|
||||
compositeFileFilter.addFilter(fileFilterMock1);
|
||||
compositeFileFilter.addFilter( fileFilterMock2);
|
||||
List<File> returnedFiles = Arrays.asList(fileMock);
|
||||
when(fileFilterMock1.filterEntries(isA(File[].class))).thenReturn(returnedFiles);
|
||||
when(fileFilterMock2.filterEntries(isA(File[].class))).thenReturn(returnedFiles);
|
||||
assertEquals(returnedFiles, compositeFileFilter.filterEntries(new File[]{fileMock}));
|
||||
verify(fileFilterMock1).filterEntries(isA(File[].class));
|
||||
verify(fileFilterMock2).filterEntries(isA(File[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negative() throws Exception {
|
||||
CompositeFileListFilter compositeFileFilter = new CompositeFileListFilter(fileFilterMock1, fileFilterMock2);
|
||||
when(fileFilterMock2.filterFiles(isA(File[].class))).thenReturn(new ArrayList<File>());
|
||||
when(fileFilterMock1.filterFiles(isA(File[].class))).thenReturn(new ArrayList<File>());
|
||||
assertTrue(compositeFileFilter.filterFiles(new File[]{fileMock}).isEmpty());
|
||||
verify(fileFilterMock1).filterFiles(isA(File[].class));
|
||||
verify(fileFilterMock2).filterFiles(isA(File[].class));
|
||||
}
|
||||
@Test
|
||||
public void negative() throws Exception {
|
||||
CompositeEntryListFilter<File> compositeFileFilter = new CompositeEntryListFilter<File>();
|
||||
compositeFileFilter.addFilter(fileFilterMock1);
|
||||
compositeFileFilter.addFilter(fileFilterMock2);
|
||||
|
||||
when(fileFilterMock2.filterEntries(isA(File[].class))).thenReturn(new ArrayList<File>());
|
||||
when(fileFilterMock1.filterEntries(isA(File[].class))).thenReturn(new ArrayList<File>());
|
||||
assertTrue(compositeFileFilter.filterEntries(new File[]{fileMock}).isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,49 @@
|
||||
<?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:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
|
||||
|
||||
<!-- under test -->
|
||||
<bean id="pollableFileSource" class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:directory="file:${java.io.tmpdir}/FileReadingMessageSourceIntegrationTests"
|
||||
p:filter-ref="compositeFilter"/>
|
||||
<!-- under test -->
|
||||
<bean id="pollableFileSource" class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:directory="file:${java.io.tmpdir}/FileReadingMessageSourceIntegrationTests"
|
||||
p:filter-ref="compositeFilter"/>
|
||||
|
||||
<!-- customized filter -->
|
||||
<bean id="compositeFilter" class="org.springframework.integration.file.CompositeFileListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.integration.file.AcceptOnceFileListFilter" />
|
||||
<bean class="org.springframework.integration.file.TestFileListFilter" />
|
||||
<bean class="org.springframework.integration.file.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
<bean class="org.springframework.integration.file.entries.FileEntryNamer" id="entryNamer"/>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
|
||||
<!-- customized filter -->
|
||||
<!--
|
||||
<bean id="compositeFilter" class="org.springframework.integration.file.entries.CompositeEntryListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter"/>
|
||||
<bean class="org.springframework.integration.file.TestFileListFilter"/>
|
||||
<bean class="org.springframework.integration.file.entries.PatternMatchingEntryListFilter">
|
||||
<constructor-arg ref="entryNamer"/>
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
</beans>
|
||||
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" id="compositeFilter">
|
||||
<property name="filterReferences">
|
||||
|
||||
<util:list>
|
||||
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:preventDuplicates="true"/>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:preventDuplicates="false"/>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:filenamePattern="^test.*$"/>
|
||||
|
||||
</util:list>
|
||||
|
||||
</property>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -37,148 +37,155 @@ import static org.junit.Assert.*;
|
||||
@ContextConfiguration
|
||||
public class FileReadingMessageSourceIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
FileReadingMessageSource pollableFileSource;
|
||||
@Autowired
|
||||
FileReadingMessageSource pollableFileSource;
|
||||
|
||||
private static File inputDir;
|
||||
private static File inputDir;
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() throws Throwable {
|
||||
if(inputDir.exists())
|
||||
inputDir.delete();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setupInputDir() {
|
||||
inputDir = new File(System.getProperty("java.io.tmpdir") + "/"
|
||||
+ FileReadingMessageSourceIntegrationTests.class.getSimpleName());
|
||||
inputDir.mkdir();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void generateTestFiles() throws Exception {
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanoutInputDir() throws Exception {
|
||||
File[] listFiles = inputDir.listFiles();
|
||||
for (int i = 0; i < listFiles.length; i++) {
|
||||
listFiles[i].delete();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void removeInputDir() throws Exception {
|
||||
inputDir.delete();
|
||||
}
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void setupInputDir() {
|
||||
inputDir = new File(System.getProperty("java.io.tmpdir") + "/"
|
||||
+ FileReadingMessageSourceIntegrationTests.class.getSimpleName());
|
||||
inputDir.mkdir();
|
||||
}
|
||||
@Test
|
||||
public void configured() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(pollableFileSource);
|
||||
assertEquals(inputDir, accessor.getPropertyValue("directory"));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void generateTestFiles() throws Exception {
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
}
|
||||
@Test
|
||||
public void getFiles() throws Exception {
|
||||
Message<File> received1 = pollableFileSource.receive();
|
||||
System.out.println("receive files round 1");
|
||||
assertNotNull("This should return the first message", received1);
|
||||
pollableFileSource.onSend(received1);
|
||||
Message<File> received2 = pollableFileSource.receive();
|
||||
assertNotNull(received2);
|
||||
pollableFileSource.onSend(received2);
|
||||
Message<File> received3 = pollableFileSource.receive();
|
||||
assertNotNull(received3);
|
||||
pollableFileSource.onSend(received3);
|
||||
assertNotSame(received1 + " == " + received2, received1.getPayload(), received2.getPayload());
|
||||
assertNotSame(received1 + " == " + received3, received1.getPayload(), received3.getPayload());
|
||||
assertNotSame(received2 + " == " + received3, received2.getPayload(), received3.getPayload());
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanoutInputDir() throws Exception {
|
||||
File[] listFiles = inputDir.listFiles();
|
||||
for (int i = 0; i < listFiles.length; i++) {
|
||||
listFiles[i].delete();
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void parallelRetrieval() throws Exception {
|
||||
Message<File> received1 = pollableFileSource.receive();
|
||||
Message<File> received2 = pollableFileSource.receive();
|
||||
Message<File> received3 = pollableFileSource.receive();
|
||||
assertNotSame(received1 + " == " + received2, received1, received2);
|
||||
assertNotSame(received1 + " == " + received3, received1, received3);
|
||||
assertNotSame(received2 + " == " + received3, received2, received3);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void removeInputDir() throws Exception {
|
||||
inputDir.delete();
|
||||
}
|
||||
@Test
|
||||
public void inputDirExhausted() throws Exception {
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNull(pollableFileSource.receive());
|
||||
}
|
||||
|
||||
@Test(timeout = 6000)
|
||||
@Repeat(5)
|
||||
public void concurrentProcessing() throws Exception {
|
||||
CountDownLatch go = new CountDownLatch(1);
|
||||
Runnable succesfulConsumer = new Runnable() {
|
||||
public void run() {
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
while (received == null) {
|
||||
Thread.yield();
|
||||
received = pollableFileSource.receive();
|
||||
}
|
||||
pollableFileSource.onSend(received);
|
||||
}
|
||||
};
|
||||
Runnable failingConsumer = new Runnable() {
|
||||
public void run() {
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
if (received != null) {
|
||||
pollableFileSource.onFailure(received);
|
||||
}
|
||||
}
|
||||
};
|
||||
CountDownLatch succesfulDone = doConcurrently(3, succesfulConsumer, go);
|
||||
CountDownLatch failingDone = doConcurrently(10, failingConsumer, go);
|
||||
go.countDown();
|
||||
try {
|
||||
succesfulDone.await();
|
||||
failingDone.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
// make sure three different files were taken
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
if (received != null) {
|
||||
pollableFileSource.onSend(received);
|
||||
}
|
||||
assertNull(received);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configured() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(pollableFileSource);
|
||||
assertEquals(inputDir, accessor.getPropertyValue("directory"));
|
||||
}
|
||||
/**
|
||||
* Convenience method to run part of a test concurrently in multiple threads
|
||||
*
|
||||
* @param numberOfThreads how many threads to spawn
|
||||
* @param runnable the runnable that should be run by all the threads
|
||||
* @param start the {@link java.util.concurrent.CountDownLatch} instance telling it when to assume everything works
|
||||
* @return a latch that will be counted down once all threads have run their
|
||||
* runnable.
|
||||
*/
|
||||
private CountDownLatch doConcurrently(int numberOfThreads, final Runnable runnable, final CountDownLatch start) {
|
||||
final CountDownLatch started = new CountDownLatch(numberOfThreads);
|
||||
final CountDownLatch done = new CountDownLatch(numberOfThreads);
|
||||
for (int i = 0; i < numberOfThreads; i++) {
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Test
|
||||
public void getFiles() throws Exception {
|
||||
Message<File> received1 = pollableFileSource.receive();
|
||||
assertNotNull("This should return the first message", received1);
|
||||
pollableFileSource.onSend(received1);
|
||||
Message<File> received2 = pollableFileSource.receive();
|
||||
assertNotNull(received2);
|
||||
pollableFileSource.onSend(received2);
|
||||
Message<File> received3 = pollableFileSource.receive();
|
||||
assertNotNull(received3);
|
||||
pollableFileSource.onSend(received3);
|
||||
assertNotSame(received1 + " == " + received2, received1.getPayload(), received2.getPayload());
|
||||
assertNotSame(received1 + " == " + received3, received1.getPayload(), received3.getPayload());
|
||||
assertNotSame(received2 + " == " + received3, received2.getPayload(), received3.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parallelRetrieval() throws Exception {
|
||||
Message<File> received1 = pollableFileSource.receive();
|
||||
Message<File> received2 = pollableFileSource.receive();
|
||||
Message<File> received3 = pollableFileSource.receive();
|
||||
assertNotSame(received1 + " == " + received2, received1, received2);
|
||||
assertNotSame(received1 + " == " + received3, received1, received3);
|
||||
assertNotSame(received2 + " == " + received3, received2, received3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputDirExhausted() throws Exception {
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNotNull(pollableFileSource.receive());
|
||||
assertNull(pollableFileSource.receive());
|
||||
}
|
||||
|
||||
@Test(timeout = 6000)
|
||||
@Repeat(10)
|
||||
public void concurrentProcessing() throws Exception {
|
||||
CountDownLatch go = new CountDownLatch(1);
|
||||
Runnable succesfulConsumer = new Runnable() {
|
||||
public void run() {
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
while (received == null) {
|
||||
Thread.yield();
|
||||
received = pollableFileSource.receive();
|
||||
}
|
||||
pollableFileSource.onSend(received);
|
||||
}
|
||||
};
|
||||
Runnable failingConsumer = new Runnable() {
|
||||
public void run() {
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
if (received != null) {
|
||||
pollableFileSource.onFailure(received, new RuntimeException("nothing"));
|
||||
}
|
||||
}
|
||||
};
|
||||
CountDownLatch succesfulDone = doConcurrently(3, succesfulConsumer, go);
|
||||
CountDownLatch failingDone = doConcurrently(10, failingConsumer, go);
|
||||
go.countDown();
|
||||
try {
|
||||
succesfulDone.await();
|
||||
failingDone.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
// make sure three different files were taken
|
||||
Message<File> received = pollableFileSource.receive();
|
||||
if (received != null) {
|
||||
pollableFileSource.onSend(received);
|
||||
}
|
||||
assertNull(received);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to run part of a test concurrently in multiple threads
|
||||
*
|
||||
* @param numberOfThreads
|
||||
* @param todo the runnable that should be run by all the threads
|
||||
* @return a latch that will be counted down once all threads have run their
|
||||
* runnable.
|
||||
*/
|
||||
private CountDownLatch doConcurrently(int numberOfThreads, final Runnable todo, final CountDownLatch start) {
|
||||
final CountDownLatch started = new CountDownLatch(numberOfThreads);
|
||||
final CountDownLatch done = new CountDownLatch(numberOfThreads);
|
||||
for (int i = 0; i < numberOfThreads; i++) {
|
||||
new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
started.countDown();
|
||||
try {
|
||||
started.await();
|
||||
start.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
todo.run();
|
||||
done.countDown();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
return done;
|
||||
}
|
||||
public void run() {
|
||||
started.countDown();
|
||||
try {
|
||||
started.await();
|
||||
start.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
runnable.run();
|
||||
done.countDown();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class FileReadingMessageSourceTests {
|
||||
when(inputDirectoryMock.listFiles()).thenReturn(new File[]{fileMock});
|
||||
Message received = source.receive();
|
||||
assertNotNull(received);
|
||||
source.onFailure(received, new RuntimeException("failed"));
|
||||
source.onFailure(received);
|
||||
assertEquals(received.getPayload(), source.receive().getPayload());
|
||||
verify(inputDirectoryMock, times(1)).listFiles();
|
||||
}
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns:file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:si="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:p="http://www.springframework.org/schema/p" xmlns:si="http://www.springframework.org/schema/integration" xmlns:util="http://www.springframework.org/schema/util"
|
||||
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">
|
||||
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
|
||||
|
||||
<!-- under test -->
|
||||
<file:inbound-channel-adapter
|
||||
directory="#{inputDirectory.path}"
|
||||
channel="fileMessages" filter="compositeFilter"/>
|
||||
<!-- under test -->
|
||||
<file:inbound-channel-adapter
|
||||
directory="#{inputDirectory.path}"
|
||||
channel="fileMessages" filter="compositeFilter"/>
|
||||
|
||||
<bean id="temp" class="org.junit.rules.TemporaryFolder"
|
||||
init-method="create" destroy-method="delete"/>
|
||||
<bean id="temp" class="org.junit.rules.TemporaryFolder"
|
||||
init-method="create" destroy-method="delete"/>
|
||||
|
||||
<bean id="inputDirectory" class="java.io.File">
|
||||
<constructor-arg value="#{temp.newFolder('FileToChannelIntegrationTests').path}"/>
|
||||
</bean>
|
||||
<bean id="inputDirectory" class="java.io.File">
|
||||
<constructor-arg value="#{temp.newFolder('FileToChannelIntegrationTests').path}"/>
|
||||
</bean>
|
||||
|
||||
<si:channel id="fileMessages">
|
||||
<si:queue capacity="10" />
|
||||
</si:channel>
|
||||
<si:channel id="fileMessages">
|
||||
<si:queue capacity="10"/>
|
||||
</si:channel>
|
||||
|
||||
<!-- customized filter -->
|
||||
<bean id="compositeFilter"
|
||||
class="org.springframework.integration.file.CompositeFileListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.integration.file.AcceptOnceFileListFilter"/>
|
||||
<bean class="org.springframework.integration.file.TestFileListFilter"/>
|
||||
<bean class="org.springframework.integration.file.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<si:poller default="true" fixed-rate="10"/>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" id="compositeFilter">
|
||||
<property name="filterReferences">
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
<util:list>
|
||||
|
||||
</beans>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:preventDuplicates="true"/>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:preventDuplicates="false"/>
|
||||
<bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" p:filenamePattern="^test.*$"/>
|
||||
|
||||
</util:list>
|
||||
|
||||
</property>
|
||||
|
||||
</bean>
|
||||
|
||||
<si:poller default="true" fixed-rate="10"/>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -16,75 +16,75 @@
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.FileEntryNamer;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PatternMatchingFileListFilterTests {
|
||||
|
||||
@Test
|
||||
public void matchSingleFile() {
|
||||
File[] files = new File[] { new File("/some/path/test.txt") };
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingFileListFilter filter = new PatternMatchingFileListFilter(pattern);
|
||||
List<File> accepted = filter.filterFiles(files);
|
||||
assertEquals(1, accepted.size());
|
||||
}
|
||||
private FileEntryNamer fileEntryNamer = new FileEntryNamer();
|
||||
|
||||
@Test
|
||||
public void noMatchWithSingleFile() {
|
||||
File[] files = new File[] { new File("/some/path/Test.txt") };
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingFileListFilter filter = new PatternMatchingFileListFilter(pattern);
|
||||
List<File> accepted = filter.filterFiles(files);
|
||||
assertEquals(0, accepted.size());
|
||||
}
|
||||
@Test
|
||||
public void matchSingleFile() {
|
||||
File[] files = new File[]{new File("/some/path/test.txt")};
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingEntryListFilter<File> filter = new PatternMatchingEntryListFilter<File>(fileEntryNamer, pattern);
|
||||
List<File> accepted = filter.filterEntries(files);
|
||||
assertEquals(1, accepted.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchSubset() {
|
||||
File[] files = new File[] {
|
||||
new File("/some/path/foo.txt"),
|
||||
new File("/some/path/foo.not"),
|
||||
new File("/some/path/bar.txt"),
|
||||
new File("/some/path/bar.not")
|
||||
};
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingFileListFilter filter = new PatternMatchingFileListFilter(pattern);
|
||||
List<File> accepted = filter.filterFiles(files);
|
||||
assertEquals(2, accepted.size());
|
||||
assertTrue(accepted.contains(new File("/some/path/foo.txt")));
|
||||
assertTrue(accepted.contains(new File("/some/path/bar.txt")));
|
||||
}
|
||||
@Test
|
||||
public void noMatchWithSingleFile() {
|
||||
File[] files = new File[]{new File("/some/path/Test.txt")};
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingEntryListFilter<File> filter = new PatternMatchingEntryListFilter<File>(fileEntryNamer, pattern);
|
||||
List<File> accepted = filter.filterEntries(files);
|
||||
assertEquals(0, accepted.size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nullPattern() {
|
||||
new PatternMatchingFileListFilter(null);
|
||||
}
|
||||
@Test
|
||||
public void matchSubset() {
|
||||
File[] files = new File[]{
|
||||
new File("/some/path/foo.txt"),
|
||||
new File("/some/path/foo.not"),
|
||||
new File("/some/path/bar.txt"),
|
||||
new File("/some/path/bar.not")
|
||||
};
|
||||
Pattern pattern = Pattern.compile("[a-z]+\\.txt");
|
||||
PatternMatchingEntryListFilter<File> filter = new PatternMatchingEntryListFilter<File>(this.fileEntryNamer, pattern);
|
||||
List<File> accepted = filter.filterEntries(files);
|
||||
assertEquals(2, accepted.size());
|
||||
assertTrue(accepted.contains(new File("/some/path/foo.txt")));
|
||||
assertTrue(accepted.contains(new File("/some/path/bar.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternEditorInContext() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"patternMatchingFileListFilterTests.xml", this.getClass());
|
||||
FileListFilter filter = (FileListFilter) context.getBean("filter");
|
||||
File[] files = new File[] { new File("/some/path/foo.txt") };
|
||||
List<File> accepted = filter.filterFiles(files);
|
||||
assertEquals(1, accepted.size());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void invalidPatternSyntax() throws Throwable {
|
||||
new ClassPathXmlApplicationContext("invalidPatternMatchingFileListFilterTests.xml", this.getClass());
|
||||
}
|
||||
@Test
|
||||
public void patternEditorInContext() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"patternMatchingFileListFilterTests.xml", this.getClass());
|
||||
EntryListFilter<File> filter = (EntryListFilter<File>) context.getBean("filter");
|
||||
File[] files = new File[]{new File("/some/path/foo.txt")};
|
||||
List<File> accepted = filter.filterEntries(files);
|
||||
assertEquals(1, accepted.size());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void invalidPatternSyntax() throws Throwable {
|
||||
new ClassPathXmlApplicationContext("invalidPatternMatchingFileListFilterTests.xml", this.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -23,10 +25,8 @@ import java.util.List;
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class TestFileListFilter implements FileListFilter {
|
||||
|
||||
public List<File> filterFiles(File[] files) {
|
||||
return Arrays.asList(files);
|
||||
}
|
||||
|
||||
public class TestFileListFilter implements EntryListFilter<File> {
|
||||
public List<File> filterEntries(File[] entries) {
|
||||
return Arrays.asList(entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,6 @@
|
||||
|
||||
<context:property-placeholder/>
|
||||
|
||||
<si:poller default="true">
|
||||
<si:interval-trigger interval="10000"/>
|
||||
</si:poller>
|
||||
<si:poller default="true" fixed-rate="10000"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -16,16 +16,10 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -34,6 +28,11 @@ import org.springframework.integration.file.FileWritingMessageHandler;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@@ -41,92 +40,92 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class AutoCreateDirectoryIntegrationTests {
|
||||
|
||||
private static final String BASE_PATH =
|
||||
System.getProperty("java.io.tmpdir") + File.separator + AutoCreateDirectoryIntegrationTests.class.getSimpleName();
|
||||
private static final String BASE_PATH =
|
||||
System.getProperty("java.io.tmpdir") + File.separator + AutoCreateDirectoryIntegrationTests.class.getSimpleName();
|
||||
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void setupNonAutoCreatedDirectories() {
|
||||
new File(BASE_PATH).delete();
|
||||
new File(BASE_PATH + File.separator + "customInbound").mkdirs();
|
||||
new File(BASE_PATH + File.separator + "customOutbound").mkdirs();
|
||||
new File(BASE_PATH + File.separator + "customOutboundGateway").mkdirs();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void setupNonAutoCreatedDirectories() {
|
||||
new File(BASE_PATH).delete();
|
||||
new File(BASE_PATH + File.separator + "customInbound").mkdirs();
|
||||
new File(BASE_PATH + File.separator + "customOutbound").mkdirs();
|
||||
new File(BASE_PATH + File.separator + "customOutboundGateway").mkdirs();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void deleteBaseDirectory() {
|
||||
new File(BASE_PATH).delete();
|
||||
}
|
||||
@AfterClass
|
||||
public static void deleteBaseDirectory() {
|
||||
new File(BASE_PATH).delete();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void defaultInbound() throws Exception {
|
||||
Object adapter = context.getBean("defaultInbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileReadingMessageSource source = (FileReadingMessageSource)
|
||||
adapterAccessor.getPropertyValue("source");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(source).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultInbound").exists());
|
||||
}
|
||||
@Test
|
||||
public void defaultInbound() throws Exception {
|
||||
Object adapter = context.getBean("defaultInbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileReadingMessageSource source = (FileReadingMessageSource)
|
||||
adapterAccessor.getPropertyValue("source");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(source).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultInbound").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customInbound() throws Exception {
|
||||
Object adapter = context.getBean("customInbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileReadingMessageSource source = (FileReadingMessageSource)
|
||||
adapterAccessor.getPropertyValue("source");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customInbound").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(source).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
@Test
|
||||
public void customInbound() throws Exception {
|
||||
Object adapter = context.getBean("customInbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileReadingMessageSource source = (FileReadingMessageSource)
|
||||
adapterAccessor.getPropertyValue("source");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customInbound").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(source).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultOutbound() throws Exception {
|
||||
Object adapter = context.getBean("defaultOutbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
adapterAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultOutbound").exists());
|
||||
}
|
||||
@Test
|
||||
public void defaultOutbound() throws Exception {
|
||||
Object adapter = context.getBean("defaultOutbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
adapterAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultOutbound").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customOutbound() throws Exception {
|
||||
Object adapter = context.getBean("customOutbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
adapterAccessor.getPropertyValue("handler");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customOutbound").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
@Test
|
||||
public void customOutbound() throws Exception {
|
||||
Object adapter = context.getBean("customOutbound");
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
adapterAccessor.getPropertyValue("handler");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customOutbound").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultOutboundGateway() throws Exception {
|
||||
Object gateway = context.getBean("defaultOutboundGateway");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultOutboundGateway").exists());
|
||||
}
|
||||
@Test
|
||||
public void defaultOutboundGateway() throws Exception {
|
||||
Object gateway = context.getBean("defaultOutboundGateway");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.TRUE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
assertTrue(new File(BASE_PATH + File.separator + "defaultOutboundGateway").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customOutboundGateway() throws Exception {
|
||||
Object gateway = context.getBean("customOutboundGateway");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customOutboundGateway").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
@Test
|
||||
public void customOutboundGateway() throws Exception {
|
||||
Object gateway = context.getBean("customOutboundGateway");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertTrue(new File(BASE_PATH + File.separator + "customOutboundGateway").exists());
|
||||
assertEquals(Boolean.FALSE,
|
||||
new DirectFieldAccessor(handler).getPropertyValue("autoCreateDirectory"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class CustomFileNameGenerator implements FileNameGenerator {
|
||||
|
||||
public String generateFileName(Message<?> message) {
|
||||
return "file" + new Date().getTime();
|
||||
}
|
||||
public String generateFileName(Message<?> message) {
|
||||
return "file" + new Date().getTime();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -33,6 +29,9 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.3
|
||||
@@ -41,32 +40,32 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@ContextConfiguration
|
||||
public class DefaultConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void verifyErrorChannel() {
|
||||
Object errorChannel = context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
|
||||
assertNotNull(errorChannel);
|
||||
assertEquals(PublishSubscribeChannel.class, errorChannel.getClass());
|
||||
}
|
||||
@Test
|
||||
public void verifyErrorChannel() {
|
||||
Object errorChannel = context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
|
||||
assertNotNull(errorChannel);
|
||||
assertEquals(PublishSubscribeChannel.class, errorChannel.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyNullChannel() {
|
||||
Object nullChannel = context.getBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
|
||||
assertNotNull(nullChannel);
|
||||
assertEquals(NullChannel.class, nullChannel.getClass());
|
||||
}
|
||||
@Test
|
||||
public void verifyNullChannel() {
|
||||
Object nullChannel = context.getBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
|
||||
assertNotNull(nullChannel);
|
||||
assertEquals(NullChannel.class, nullChannel.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyTaskScheduler() {
|
||||
Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME);
|
||||
assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass());
|
||||
Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler");
|
||||
assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass());
|
||||
Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel");
|
||||
assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel);
|
||||
}
|
||||
@Test
|
||||
public void verifyTaskScheduler() {
|
||||
Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME);
|
||||
assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass());
|
||||
Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler");
|
||||
assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass());
|
||||
Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel");
|
||||
assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,20 +15,22 @@
|
||||
filter="filter"
|
||||
comparator="testComparator"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="5000"/>
|
||||
</integration:poller>
|
||||
<integration:poller fixed-rate="5000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<beans:bean id="filter" class="org.springframework.integration.file.CompositeFileListFilter">
|
||||
<beans:bean id="filter" class="org.springframework.integration.file.config.FileListFilterFactoryBean">
|
||||
|
||||
</beans:bean>
|
||||
<!--
|
||||
<beans:bean id="filter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
|
||||
<beans:constructor-arg>
|
||||
<beans:list></beans:list>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
</beans:bean>-->
|
||||
|
||||
<beans:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
|
||||
<beans:bean id="testComparator"
|
||||
class="org.springframework.integration.file.config.FileInboundChannelAdapterParserTests$TestComparator"/>
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -16,27 +16,24 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
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.context.ApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.file.DefaultDirectoryScanner;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
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.context.ApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.file.CompositeFileListFilter;
|
||||
import org.springframework.integration.file.DefaultDirectoryScanner;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
@@ -46,57 +43,58 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileInboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired(required=true)
|
||||
private ApplicationContext context;
|
||||
@Autowired(required = true)
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private FileReadingMessageSource source;
|
||||
@Autowired
|
||||
private FileReadingMessageSource source;
|
||||
|
||||
private DirectFieldAccessor accessor;
|
||||
private DirectFieldAccessor accessor;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
accessor = new DirectFieldAccessor(source);
|
||||
}
|
||||
@Before
|
||||
public void init() {
|
||||
accessor = new DirectFieldAccessor(source);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void channelName() throws Exception {
|
||||
AbstractMessageChannel channel = context.getBean("inputDirPoller", AbstractMessageChannel.class);
|
||||
assertEquals("Channel should be available under specified id", "inputDirPoller", channel.getComponentName());
|
||||
}
|
||||
@Test
|
||||
public void channelName() throws Exception {
|
||||
AbstractMessageChannel channel = context.getBean("inputDirPoller", AbstractMessageChannel.class);
|
||||
assertEquals("Channel should be available under specified id", "inputDirPoller", channel.getComponentName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputDirectory() {
|
||||
File expected = new File(System.getProperty("java.io.tmpdir"));
|
||||
File actual = (File) accessor.getPropertyValue("directory");
|
||||
assertEquals("'directory' should be set", expected, actual);
|
||||
}
|
||||
@Test
|
||||
public void inputDirectory() {
|
||||
File expected = new File(System.getProperty("java.io.tmpdir"));
|
||||
File actual = (File) accessor.getPropertyValue("directory");
|
||||
assertEquals("'directory' should be set", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filter() throws Exception {
|
||||
DefaultDirectoryScanner scanner = (DefaultDirectoryScanner) accessor.getPropertyValue("scanner");
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(scanner);
|
||||
assertTrue("'filter' should be set",
|
||||
scannerAccessor.getPropertyValue("filter") instanceof CompositeFileListFilter);
|
||||
}
|
||||
@Test
|
||||
public void filter() throws Exception {
|
||||
DefaultDirectoryScanner scanner = (DefaultDirectoryScanner) accessor.getPropertyValue("scanner");
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(scanner);
|
||||
Object filter = scannerAccessor.getPropertyValue("filter");
|
||||
assertTrue("'filter' should be set",
|
||||
filter instanceof AcceptOnceEntryFileListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparator() throws Exception {
|
||||
Object priorityQueue = accessor.getPropertyValue("toBeReceived");
|
||||
assertEquals(PriorityBlockingQueue.class, priorityQueue.getClass());
|
||||
Object expected = context.getBean("testComparator");
|
||||
Object innerQueue = new DirectFieldAccessor(priorityQueue).getPropertyValue("q");
|
||||
Object actual = new DirectFieldAccessor(innerQueue).getPropertyValue("comparator");
|
||||
assertSame("comparator reference not set, ", expected, actual);
|
||||
}
|
||||
@Test
|
||||
public void comparator() throws Exception {
|
||||
Object priorityQueue = accessor.getPropertyValue("toBeReceived");
|
||||
assertEquals(PriorityBlockingQueue.class, priorityQueue.getClass());
|
||||
Object expected = context.getBean("testComparator");
|
||||
Object innerQueue = new DirectFieldAccessor(priorityQueue).getPropertyValue("q");
|
||||
Object actual = new DirectFieldAccessor(innerQueue).getPropertyValue("comparator");
|
||||
assertSame("comparator reference not set, ", expected, actual);
|
||||
}
|
||||
|
||||
|
||||
static class TestComparator implements Comparator<File> {
|
||||
static class TestComparator implements Comparator<File> {
|
||||
|
||||
public int compare(File f1, File f2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public int compare(File f1, File f2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
<inbound-channel-adapter id="inputDirPoller"
|
||||
directory="${inputdir}"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="5000"/>
|
||||
</integration:poller>
|
||||
<integration:poller fixed-rate="5000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<context:property-placeholder location="classpath:org/springframework/integration/file/config/test.properties" />
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
<inbound-channel-adapter id="adapterWithPattern"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern=".*\.txt" auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
<integration:poller fixed-rate="1000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -16,32 +16,28 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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.context.ApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.file.AcceptOnceFileListFilter;
|
||||
import org.springframework.integration.file.CompositeFileListFilter;
|
||||
import org.springframework.integration.file.FileListFilter;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.integration.file.PatternMatchingFileListFilter;
|
||||
import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter;
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
@@ -50,90 +46,90 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileInboundChannelAdapterWithPatternParserTests {
|
||||
|
||||
@Autowired(required=true)
|
||||
private ApplicationContext context;
|
||||
@Autowired(required = true)
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired(required=true)
|
||||
@Qualifier("adapterWithPattern.adapter")
|
||||
private AbstractEndpoint endpoint;
|
||||
@Autowired(required = true)
|
||||
@Qualifier("adapterWithPattern.adapter")
|
||||
private AbstractEndpoint endpoint;
|
||||
|
||||
private DirectFieldAccessor accessor;
|
||||
private DirectFieldAccessor accessor;
|
||||
|
||||
|
||||
@Autowired(required=true)
|
||||
public void setSource(FileReadingMessageSource source) {
|
||||
this.accessor = new DirectFieldAccessor(source);
|
||||
}
|
||||
@Autowired(required = true)
|
||||
public void setSource(FileReadingMessageSource source) {
|
||||
this.accessor = new DirectFieldAccessor(source);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void channelName() {
|
||||
AbstractMessageChannel channel = context.getBean("adapterWithPattern", AbstractMessageChannel.class);
|
||||
assertEquals("adapterWithPattern", channel.getComponentName());
|
||||
}
|
||||
@Test
|
||||
public void channelName() {
|
||||
AbstractMessageChannel channel = context.getBean("adapterWithPattern", AbstractMessageChannel.class);
|
||||
assertEquals("adapterWithPattern", channel.getComponentName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoStartupDisabled() {
|
||||
assertFalse(this.endpoint.isRunning());
|
||||
assertEquals(Boolean.FALSE, new DirectFieldAccessor(endpoint).getPropertyValue("autoStartup"));
|
||||
}
|
||||
@Test
|
||||
public void autoStartupDisabled() {
|
||||
assertFalse(this.endpoint.isRunning());
|
||||
assertEquals(Boolean.FALSE, new DirectFieldAccessor(endpoint).getPropertyValue("autoStartup"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputDirectory() {
|
||||
File expected = new File(System.getProperty("java.io.tmpdir"));
|
||||
File actual = (File) accessor.getPropertyValue("directory");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
@Test
|
||||
public void inputDirectory() {
|
||||
File expected = new File(System.getProperty("java.io.tmpdir"));
|
||||
File actual = (File) accessor.getPropertyValue("directory");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeFilterType() {
|
||||
@Test
|
||||
public void compositeFilterType() {
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner"));
|
||||
assertTrue(scannerAccessor.getPropertyValue("filter") instanceof CompositeFileListFilter);
|
||||
}
|
||||
assertTrue(scannerAccessor.getPropertyValue("filter") instanceof CompositeEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void compositeFilterSetSize() {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void compositeFilterSetSize() {
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner"));
|
||||
|
||||
Set<FileListFilter> filters = (Set<FileListFilter>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
assertEquals(2, filters.size());
|
||||
}
|
||||
Set<EntryListFilter<File>> filters = (Set<EntryListFilter<File>>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
assertEquals(2, filters.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void acceptOnceFilter() {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void acceptOnceFilter() {
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner"));
|
||||
|
||||
Set<FileListFilter> filters = (Set<FileListFilter>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
boolean hasAcceptOnceFilter = false;
|
||||
for (FileListFilter filter : filters) {
|
||||
if (filter instanceof AcceptOnceFileListFilter) {
|
||||
hasAcceptOnceFilter = true;
|
||||
}
|
||||
}
|
||||
assertTrue("expected AcceptOnceFileListFilter", hasAcceptOnceFilter);
|
||||
}
|
||||
Set<EntryListFilter<File>> filters = (Set<EntryListFilter<File>>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
boolean hasAcceptOnceFilter = false;
|
||||
for (EntryListFilter<File> filter : filters) {
|
||||
if (filter instanceof AcceptOnceEntryFileListFilter) {
|
||||
hasAcceptOnceFilter = true;
|
||||
}
|
||||
}
|
||||
assertTrue("expected AcceptOnceFileListFilter", hasAcceptOnceFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void patternFilter() {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void patternFilter() {
|
||||
DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner"));
|
||||
|
||||
Set<FileListFilter> filters = (Set<FileListFilter>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
Pattern pattern = null;
|
||||
for (FileListFilter filter : filters) {
|
||||
if (filter instanceof PatternMatchingFileListFilter) {
|
||||
pattern = (Pattern) new DirectFieldAccessor(filter).getPropertyValue("pattern");
|
||||
}
|
||||
}
|
||||
assertNotNull("expected PatternMatchingFileListFilter", pattern);
|
||||
assertEquals(".*\\.txt", pattern.toString());
|
||||
assertFalse(pattern.matcher("foo").matches());
|
||||
assertTrue(pattern.matcher("foo.txt").matches());
|
||||
}
|
||||
Set<EntryListFilter> filters = (Set<EntryListFilter>) new DirectFieldAccessor(
|
||||
scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters");
|
||||
Pattern pattern = null;
|
||||
for (EntryListFilter filter : filters) {
|
||||
if (filter instanceof PatternMatchingEntryListFilter) {
|
||||
pattern = (Pattern) new DirectFieldAccessor(filter).getPropertyValue("pattern");
|
||||
}
|
||||
}
|
||||
assertNotNull("expected PatternMatchingFileListFilter", pattern);
|
||||
assertEquals(".*\\.txt", pattern.toString());
|
||||
assertFalse(pattern.matcher("foo").matches());
|
||||
assertTrue(pattern.matcher("foo.txt").matches());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,114 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
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">
|
||||
|
||||
<beans:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
|
||||
<beans:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
|
||||
<integration:channel id="channel">
|
||||
<integration:queue/>
|
||||
</integration:channel>
|
||||
<integration:channel id="channel">
|
||||
<integration:queue/>
|
||||
</integration:channel>
|
||||
|
||||
<inbound-channel-adapter id="filterAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="filterAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="filterAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="filterAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="filterAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="filterAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filter="testFilter"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
<inbound-channel-adapter id="patternAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="patternAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="patternAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
<inbound-channel-adapter id="patternAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="patternAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="10000"/>
|
||||
</integration:poller>
|
||||
</inbound-channel-adapter>
|
||||
<inbound-channel-adapter id="patternAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
filename-pattern="test"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
<beans:bean id="testFilter" class="org.springframework.integration.file.TestFileListFilter"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndNull"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndTrue"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
prevent-duplicates="true"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<inbound-channel-adapter id="defaultAndFalse"
|
||||
directory="file:${java.io.tmpdir}"
|
||||
prevent-duplicates="false"
|
||||
channel="channel"
|
||||
auto-startup="false">
|
||||
<integration:poller fixed-rate="10000"/>
|
||||
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<beans:bean id="testFilter" class="org.springframework.integration.file.TestFileListFilter"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -13,136 +13,146 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
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.context.ApplicationContext;
|
||||
import org.springframework.integration.file.*;
|
||||
|
||||
import org.springframework.integration.file.TestFileListFilter;
|
||||
import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter;
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("testFilter")
|
||||
private TestFileListFilter testFilter;
|
||||
|
||||
|
||||
@Test
|
||||
public void filterAndNull() {
|
||||
FileListFilter filter = this.extractFilter("filterAndNull");
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("filterAndNull");
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertSame(testFilter, filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void filterAndTrue() {
|
||||
FileListFilter filter = this.extractFilter("filterAndTrue");
|
||||
assertTrue(filter instanceof CompositeFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("filterAndTrue");
|
||||
assertTrue(filter instanceof CompositeEntryListFilter);
|
||||
|
||||
Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters");
|
||||
assertTrue(filters.iterator().next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(filters.iterator().next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(filters.contains(testFilter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAndFalse() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("filterAndFalse");
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("filterAndFalse");
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertSame(testFilter, filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void patternAndNull() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("patternAndNull");
|
||||
assertTrue(filter instanceof CompositeFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("patternAndNull");
|
||||
assertTrue(filter instanceof CompositeEntryListFilter);
|
||||
|
||||
Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters");
|
||||
Iterator<FileListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingFileListFilter);
|
||||
Iterator<EntryListFilter<File>> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void patternAndTrue() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("patternAndTrue");
|
||||
assertTrue(filter instanceof CompositeFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("patternAndTrue");
|
||||
assertTrue(filter instanceof CompositeEntryListFilter);
|
||||
|
||||
Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters");
|
||||
Iterator<FileListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingFileListFilter);
|
||||
Iterator<EntryListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternAndFalse() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("patternAndFalse");
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
assertTrue(filter instanceof PatternMatchingFileListFilter);
|
||||
EntryListFilter<File> filter = this.extractFilter("patternAndFalse");
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertTrue(filter instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultAndNull() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("defaultAndNull");
|
||||
EntryListFilter<File> filter = this.extractFilter("defaultAndNull");
|
||||
assertNotNull(filter);
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
assertTrue(filter instanceof AcceptOnceFileListFilter);
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertTrue(filter instanceof AcceptOnceEntryFileListFilter);
|
||||
|
||||
File testFile = new File("test");
|
||||
File[] files = new File[]{testFile, testFile, testFile};
|
||||
List<File> result = filter.filterFiles(files);
|
||||
File[] files = new File[] { testFile, testFile, testFile };
|
||||
List<File> result = filter.filterEntries(files);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void defaultAndTrue() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("defaultAndTrue");
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
assertTrue(filter instanceof AcceptOnceFileListFilter);
|
||||
EntryListFilter filter = this.extractFilter("defaultAndTrue");
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertTrue(filter instanceof AcceptOnceEntryFileListFilter);
|
||||
|
||||
File testFile = new File("test");
|
||||
File[] files = new File[]{testFile, testFile, testFile};
|
||||
List<File> result = filter.filterFiles(files);
|
||||
File[] files = new File[] { testFile, testFile, testFile };
|
||||
List<File> result = filter.filterEntries(files);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void defaultAndFalse() throws Exception {
|
||||
FileListFilter filter = this.extractFilter("defaultAndFalse");
|
||||
EntryListFilter filter = this.extractFilter("defaultAndFalse");
|
||||
assertNotNull(filter);
|
||||
assertFalse(filter instanceof CompositeFileListFilter);
|
||||
assertFalse(filter instanceof AcceptOnceFileListFilter);
|
||||
assertFalse(filter instanceof CompositeEntryListFilter);
|
||||
assertFalse(filter instanceof AcceptOnceEntryFileListFilter);
|
||||
|
||||
File testFile = new File("test");
|
||||
File[] files = new File[]{testFile, testFile, testFile};
|
||||
List<File> result = filter.filterFiles(files);
|
||||
File[] files = new File[] { testFile, testFile, testFile };
|
||||
List<File> result = filter.filterEntries(files);
|
||||
assertEquals(3, result.size());
|
||||
}
|
||||
|
||||
|
||||
private FileListFilter extractFilter(String beanName) {
|
||||
return (FileListFilter) new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(context.getBean(beanName))
|
||||
.getPropertyValue("source"))
|
||||
.getPropertyValue("scanner"))
|
||||
.getPropertyValue("filter");
|
||||
@SuppressWarnings("unchecked")
|
||||
private EntryListFilter<File> extractFilter(String beanName) {
|
||||
return (EntryListFilter<File>) new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(context.getBean(beanName)).getPropertyValue("source")).getPropertyValue("scanner")).getPropertyValue(
|
||||
"filter");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,11 @@
|
||||
filter="filter"
|
||||
queue-size="30"
|
||||
auto-startup="false">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="5000"/>
|
||||
</integration:poller>
|
||||
<integration:poller fixed-rate="1000"/>
|
||||
</inbound-channel-adapter>
|
||||
<beans:bean class="org.springframework.integration.file.config.FileListFilterFactoryBean" />
|
||||
|
||||
<beans:bean id="filter" class="org.springframework.integration.file.CompositeFileListFilter">
|
||||
<beans:bean id="filter" class="org.springframework.integration.file.entries.CompositeEntryListFilter">
|
||||
<beans:constructor-arg>
|
||||
<beans:list></beans:list>
|
||||
</beans:constructor-arg>
|
||||
@@ -47,4 +46,4 @@
|
||||
<beans:bean id="testComparator"
|
||||
class="org.springframework.integration.file.config.FileInboundChannelAdapterParserTests$TestComparator"/>
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.integration.file.config;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.integration.file.*;
|
||||
import org.springframework.integration.file.entries.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
@@ -32,93 +32,92 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class FileListFilterFactoryBeanTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void customFilterAndFilenamePatternAreMutuallyExclusive() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilterReference(new TestFilter());
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.getObject();
|
||||
}
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void customFilterAndFilenamePatternAreMutuallyExclusive() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilterReference(new TestFilter());
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.getObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customFilterAndPreventDuplicatesNull() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
FileListFilter result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeFileListFilter);
|
||||
assertSame(testFilter, result);
|
||||
}
|
||||
@Test
|
||||
public void customFilterAndPreventDuplicatesNull() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeEntryListFilter);
|
||||
assertSame(testFilter, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customFilterAndPreventDuplicatesTrue() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
factory.setPreventDuplicates(Boolean.TRUE);
|
||||
FileListFilter result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeFileListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
assertTrue(filters.iterator().next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(filters.contains(testFilter));
|
||||
}
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customFilterAndPreventDuplicatesTrue() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
factory.setPreventDuplicates(Boolean.TRUE);
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeEntryListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
assertTrue(filters.iterator().next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(filters.contains(testFilter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customFilterAndPreventDuplicatesFalse() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
factory.setPreventDuplicates(Boolean.FALSE);
|
||||
FileListFilter result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeFileListFilter);
|
||||
assertSame(testFilter, result);
|
||||
}
|
||||
@Test
|
||||
public void customFilterAndPreventDuplicatesFalse() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
TestFilter testFilter = new TestFilter();
|
||||
factory.setFilterReference(testFilter);
|
||||
factory.setPreventDuplicates(Boolean.FALSE);
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeEntryListFilter);
|
||||
assertSame(testFilter, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void filenamePatternAndPreventDuplicatesNull() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
FileListFilter result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeFileListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
Iterator<FileListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingFileListFilter);
|
||||
}
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void filenamePatternAndPreventDuplicatesNull() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeEntryListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
Iterator<EntryListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void filenamePatternAndPreventDuplicatesTrue() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.setPreventDuplicates(Boolean.TRUE);
|
||||
FileListFilter result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeFileListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
Iterator<FileListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingFileListFilter);
|
||||
}
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void filenamePatternAndPreventDuplicatesTrue() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.setPreventDuplicates(Boolean.TRUE);
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertTrue(result instanceof CompositeEntryListFilter);
|
||||
Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters");
|
||||
Iterator<EntryListFilter> iterator = filters.iterator();
|
||||
assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter);
|
||||
assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filenamePatternAndPreventDuplicatesFalse() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.setPreventDuplicates(Boolean.FALSE);
|
||||
FileListFilter result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeFileListFilter);
|
||||
assertTrue(result instanceof PatternMatchingFileListFilter);
|
||||
}
|
||||
@Test
|
||||
public void filenamePatternAndPreventDuplicatesFalse() throws Exception {
|
||||
FileListFilterFactoryBean factory = new FileListFilterFactoryBean();
|
||||
factory.setFilenamePattern(Pattern.compile("foo"));
|
||||
factory.setPreventDuplicates(Boolean.FALSE);
|
||||
EntryListFilter<File> result = factory.getObject();
|
||||
assertFalse(result instanceof CompositeEntryListFilter);
|
||||
assertTrue(result instanceof PatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
|
||||
private static class TestFilter extends AbstractFileListFilter {
|
||||
|
||||
@Override
|
||||
protected boolean accept(File file) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private static class TestFilter extends AbstractEntryListFilter<File> {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
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;
|
||||
@@ -31,6 +26,10 @@ import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
* @author Mark Fisher
|
||||
@@ -40,31 +39,31 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileOutboundAdaptersWithClasspathInPropertiesTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("adapter")
|
||||
private EventDrivenConsumer adapter;
|
||||
@Autowired
|
||||
@Qualifier("adapter")
|
||||
private EventDrivenConsumer adapter;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("gateway")
|
||||
private EventDrivenConsumer gateway;
|
||||
@Autowired
|
||||
@Qualifier("gateway")
|
||||
private EventDrivenConsumer gateway;
|
||||
|
||||
|
||||
@Test
|
||||
public void outboundChannelAdapter() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(adapter).getPropertyValue("handler"));
|
||||
File expected = new ClassPathResource("").getFile();
|
||||
File actual = (File) accessor.getPropertyValue("destinationDirectory");
|
||||
assertEquals("'destinationDirectory' should be set", expected, actual);
|
||||
}
|
||||
@Test
|
||||
public void outboundChannelAdapter() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(adapter).getPropertyValue("handler"));
|
||||
File expected = new ClassPathResource("").getFile();
|
||||
File actual = (File) accessor.getPropertyValue("destinationDirectory");
|
||||
assertEquals("'destinationDirectory' should be set", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outboundGateway() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(gateway).getPropertyValue("handler"));
|
||||
File expected = new ClassPathResource("").getFile();
|
||||
File actual = (File) accessor.getPropertyValue("destinationDirectory");
|
||||
assertEquals("'destinationDirectory' should be set", expected, actual);
|
||||
}
|
||||
@Test
|
||||
public void outboundGateway() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(gateway).getPropertyValue("handler"));
|
||||
File expected = new ClassPathResource("").getFile();
|
||||
File actual = (File) accessor.getPropertyValue("destinationDirectory");
|
||||
assertEquals("'destinationDirectory' should be set", expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -28,6 +25,8 @@ import org.springframework.integration.file.FileWritingMessageHandler;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@@ -35,19 +34,19 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileOutboundGatewayParserTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void checkOrderedGateway() throws Exception {
|
||||
Object gateway = context.getBean("ordered");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.FALSE, gatewayAccessor.getPropertyValue("autoStartup"));
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(777, handlerAccessor.getPropertyValue("order"));
|
||||
}
|
||||
@Test
|
||||
public void checkOrderedGateway() throws Exception {
|
||||
Object gateway = context.getBean("ordered");
|
||||
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
gatewayAccessor.getPropertyValue("handler");
|
||||
assertEquals(Boolean.FALSE, gatewayAccessor.getPropertyValue("autoStartup"));
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(777, handlerAccessor.getPropertyValue("order"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
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;
|
||||
@@ -30,6 +27,8 @@ import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@@ -37,20 +36,20 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FileToStringTransformerParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("transformer")
|
||||
EventDrivenConsumer endpoint;
|
||||
@Autowired
|
||||
@Qualifier("transformer")
|
||||
EventDrivenConsumer endpoint;
|
||||
|
||||
@Test
|
||||
public void checkDeleteFilesValue() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(endpoint);
|
||||
MessageTransformingHandler handler = (MessageTransformingHandler)
|
||||
endpointAccessor.getPropertyValue("handler");
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
FileToStringTransformer transformer = (FileToStringTransformer)
|
||||
handlerAccessor.getPropertyValue("transformer");
|
||||
DirectFieldAccessor transformerAccessor = new DirectFieldAccessor(transformer);
|
||||
assertEquals(Boolean.TRUE, transformerAccessor.getPropertyValue("deleteFiles"));
|
||||
}
|
||||
@Test
|
||||
public void checkDeleteFilesValue() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(endpoint);
|
||||
MessageTransformingHandler handler = (MessageTransformingHandler)
|
||||
endpointAccessor.getPropertyValue("handler");
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
FileToStringTransformer transformer = (FileToStringTransformer)
|
||||
handlerAccessor.getPropertyValue("transformer");
|
||||
DirectFieldAccessor transformerAccessor = new DirectFieldAccessor(transformer);
|
||||
assertEquals(Boolean.TRUE, transformerAccessor.getPropertyValue("deleteFiles"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<bean id="filter" class="org.springframework.integration.file.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="[fo+\.[tx]{3}"/>
|
||||
</bean>
|
||||
<bean id="filter" class="org.springframework.integration.file.entries.PatternMatchingEntryListFilter">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.integration.file.entries.FileEntryNamer"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg value="[fo+\.[tx]{3}"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -22,8 +22,8 @@ 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.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -51,9 +51,10 @@ public class FileLockingNamespaceTests {
|
||||
|
||||
FileReadingMessageSource customLockingSource;
|
||||
|
||||
@Before public void extractSources() {
|
||||
nioLockingSource = (FileReadingMessageSource) new DirectFieldAccessor( nioAdapter).getPropertyValue("source");
|
||||
customLockingSource = (FileReadingMessageSource) new DirectFieldAccessor( customAdapter).getPropertyValue("source");
|
||||
@Before
|
||||
public void extractSources() {
|
||||
nioLockingSource = (FileReadingMessageSource) new DirectFieldAccessor(nioAdapter).getPropertyValue("source");
|
||||
customLockingSource = (FileReadingMessageSource) new DirectFieldAccessor(customAdapter).getPropertyValue("source");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,17 +65,17 @@ public class FileLockingNamespaceTests {
|
||||
@Test
|
||||
public void shouldSetCustomLockerProperly() {
|
||||
assertThat(extractFromScanner("locker", customLockingSource), is(StubLocker.class));
|
||||
assertThat(extractFromScanner("filter", customLockingSource), is(CompositeFileListFilter.class));
|
||||
assertThat(extractFromScanner("filter", customLockingSource), is(CompositeEntryListFilter.class));
|
||||
}
|
||||
|
||||
private Object extractFromScanner(String propertyName, FileReadingMessageSource source) {
|
||||
return new DirectFieldAccessor( new DirectFieldAccessor(source).getPropertyValue("scanner") ).getPropertyValue(propertyName);
|
||||
return new DirectFieldAccessor(new DirectFieldAccessor(source).getPropertyValue("scanner")).getPropertyValue(propertyName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetNioLockerProperly() {
|
||||
assertThat(extractFromScanner("locker", nioLockingSource), is(NioFileLocker.class));
|
||||
assertThat(extractFromScanner("filter", nioLockingSource), is(CompositeFileListFilter.class));
|
||||
assertThat(extractFromScanner("filter", nioLockingSource), is(CompositeEntryListFilter.class));
|
||||
}
|
||||
|
||||
public static class StubLocker extends AbstractFileLockerFilter {
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.integration.file.locking;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.integration.file.FileListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -49,20 +49,20 @@ public class NioFileLockerTests {
|
||||
NioFileLocker filter = new NioFileLocker();
|
||||
File testFile = new File(workdir, "test0");
|
||||
testFile.createNewFile();
|
||||
assertThat(filter.filterFiles(workdir.listFiles()).get(0), is(testFile));
|
||||
assertThat(filter.filterEntries(workdir.listFiles()).get(0), is(testFile));
|
||||
filter.lock(testFile);
|
||||
assertThat(filter.filterFiles(workdir.listFiles()).get(0), is(testFile));
|
||||
assertThat(filter.filterEntries(workdir.listFiles()).get(0), is(testFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileNotListedWhenLockedByOtherFilter() throws IOException {
|
||||
NioFileLocker filter1 = new NioFileLocker();
|
||||
FileListFilter filter2 = new NioFileLocker();
|
||||
EntryListFilter<File> filter2 = new NioFileLocker();
|
||||
File testFile = new File(workdir, "test1");
|
||||
testFile.createNewFile();
|
||||
assertThat(filter1.filterFiles(workdir.listFiles()).get(0), is(testFile));
|
||||
assertThat(filter1.filterEntries(workdir.listFiles()).get(0), is(testFile));
|
||||
filter1.lock(testFile);
|
||||
assertThat(filter2.filterFiles(workdir.listFiles()), is((List<File>)new ArrayList<File>()));
|
||||
assertThat(filter2.filterEntries(workdir.listFiles()), is((List<File>)new ArrayList<File>()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<bean id="filter" class="org.springframework.integration.file.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="fo+\.[tx]{3}"/>
|
||||
</bean>
|
||||
<!--<bean class="org.springframework.integration.file.filters.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="fo+\.[tx]{3}"/>
|
||||
</bean>
|
||||
-->
|
||||
<bean id="filter" class="org.springframework.integration.file.entries.PatternMatchingEntryListFilter">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.integration.file.entries.FileEntryNamer"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg value="fo+\.[tx]{3}"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
auto-startup="true"
|
||||
scanner="recursiveScanner"
|
||||
channel="files">
|
||||
<integration:poller>
|
||||
<integration:interval-trigger interval="100"/>
|
||||
</integration:poller>
|
||||
<integration:poller fixed-rate="1000"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
<integration:channel id="files">
|
||||
@@ -29,4 +27,4 @@
|
||||
<beans:bean id="recursiveScanner" class="org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner"/>
|
||||
|
||||
<beans:bean id="directory" class="org.junit.rules.TemporaryFolder" init-method="create" destroy-method="delete"/>
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -10,4 +10,6 @@ Import-Template:
|
||||
org.springframework.core.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.util;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.util.xml;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.scheduling.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.transaction.*;version="[3.0.0, 4.0.0)",
|
||||
org.w3c.dom.*;version="0"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Convenience implementation patterned off {@link org.springframework.integration.file.FileListFilter}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractFtpFileListFilter implements FtpFileListFilter {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
abstract public boolean accept(FTPFile ftpFile);
|
||||
|
||||
public List<FTPFile> filterFiles(FTPFile[] files) {
|
||||
List<FTPFile> accepted = new ArrayList<FTPFile>();
|
||||
|
||||
if (files != null) {
|
||||
for (FTPFile f : files) {
|
||||
if (this.accept(f)) {
|
||||
accepted.add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return accepted;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Patterned very much on the {@link org.springframework.integration.file.CompositeFileListFilter}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class CompositeFtpFileListFilter implements FtpFileListFilter {
|
||||
private Set<FtpFileListFilter> filters;
|
||||
|
||||
public CompositeFtpFileListFilter(FtpFileListFilter... ftpFileListFilter) {
|
||||
this.filters = new LinkedHashSet<FtpFileListFilter>(Arrays.asList(ftpFileListFilter));
|
||||
}
|
||||
|
||||
public CompositeFtpFileListFilter(Collection<FtpFileListFilter> ftpFileListFilter) {
|
||||
this.filters = new LinkedHashSet<FtpFileListFilter>(ftpFileListFilter);
|
||||
}
|
||||
public void addFilter( FtpFileListFilter ftpFileListFilter ) {
|
||||
this.filters.add(ftpFileListFilter);
|
||||
}
|
||||
public List<FTPFile> filterFiles(FTPFile[] files) {
|
||||
Assert.notNull(files, "files[] can't be null!");
|
||||
|
||||
List<FTPFile> leftOver = Arrays.asList(files);
|
||||
|
||||
for (FtpFileListFilter ff : this.filters)
|
||||
leftOver = ff.filterFiles(leftOver.toArray(new FTPFile[leftOver.size()]));
|
||||
|
||||
return leftOver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.integration.file.entries.EntryNamer;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.file.entries.EntryNamer} for {@link org.apache.commons.net.ftp.FTPFile} objects
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpFileEntryNamer implements EntryNamer<FTPFile> {
|
||||
public String nameOf(FTPFile entry) {
|
||||
return entry.getName();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Filters out all the FTPFiles taken in a scan of the remote mount o
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public interface FtpFileListFilter {
|
||||
List<FTPFile> filterFiles (FTPFile [] files);
|
||||
}
|
||||
@@ -1,112 +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.ftp;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.file.AcceptOnceFileListFilter;
|
||||
import org.springframework.integration.file.CompositeFileListFilter;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.integration.file.PatternMatchingFileListFilter;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* A source adapter for receiving files via FTP.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class FtpFileSource implements MessageSource<File>, InitializingBean, Lifecycle {
|
||||
private FileReadingMessageSource fileSource;
|
||||
private FtpInboundSynchronizer synchronizer;
|
||||
|
||||
public FtpFileSource() {
|
||||
this(new FileReadingMessageSource(), new FtpInboundSynchronizer());
|
||||
}
|
||||
|
||||
public FtpFileSource(FileReadingMessageSource fileSource, FtpInboundSynchronizer synchronizer) {
|
||||
this.fileSource = fileSource;
|
||||
this.synchronizer = synchronizer;
|
||||
|
||||
Pattern completePattern = Pattern.compile("^.*(?<!" + FtpInboundSynchronizer.INCOMPLETE_EXTENSION + ")$");
|
||||
fileSource.setFilter(new CompositeFileListFilter(new AcceptOnceFileListFilter(), new PatternMatchingFileListFilter(completePattern)));
|
||||
}
|
||||
|
||||
public void setFileSource(FileReadingMessageSource fileSource) {
|
||||
this.fileSource = fileSource;
|
||||
}
|
||||
|
||||
public void setSynchronizer(FtpInboundSynchronizer synchronizer) {
|
||||
this.synchronizer = synchronizer;
|
||||
}
|
||||
|
||||
public void setLocalWorkingDirectory(Resource localWorkingDirectory) {
|
||||
this.synchronizer.setLocalDirectory(localWorkingDirectory);
|
||||
|
||||
try {
|
||||
this.fileSource.setDirectory(localWorkingDirectory.getFile());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrigger(Trigger trigger) {
|
||||
synchronizer.setTrigger(trigger);
|
||||
}
|
||||
|
||||
public void setTaskScheduler(TaskScheduler scheduler) {
|
||||
synchronizer.setTaskScheduler(scheduler);
|
||||
}
|
||||
|
||||
public void setClientPool(FtpClientPool pool) {
|
||||
synchronizer.setClientPool(pool);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
synchronizer.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public Message<File> receive() {
|
||||
return fileSource.receive();
|
||||
}
|
||||
|
||||
public void onFailure(Message<File> failedMessage, Throwable t) {
|
||||
fileSource.onFailure(failedMessage, t);
|
||||
}
|
||||
|
||||
public void onSend(Message<File> sentMessage) {
|
||||
fileSource.onSend(sentMessage);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return synchronizer.isRunning();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
synchronizer.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronizer.stop();
|
||||
}
|
||||
}
|
||||
@@ -1,179 +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.ftp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
|
||||
/**
|
||||
* <code>FtpInboundSynchronizer</code> will keep a local directory in sync with a remote Ftp directory.
|
||||
* It will NOT move new files put into the local directory to the remote server.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class FtpInboundSynchronizer implements InitializingBean, Lifecycle {
|
||||
private static final Log logger = LogFactory.getLog(FtpInboundSynchronizer.class);
|
||||
static final String INCOMPLETE_EXTENSION = ".INCOMPLETE";
|
||||
private static final long DEFAULT_REFRESH_RATE = 10000;
|
||||
private volatile TaskScheduler taskScheduler;
|
||||
private volatile Trigger trigger = new PeriodicTrigger(DEFAULT_REFRESH_RATE);
|
||||
private volatile FtpClientPool clientPool;
|
||||
private volatile Resource localDirectory;
|
||||
private boolean running = false;
|
||||
private ScheduledFuture<?> scheduledFuture;
|
||||
private FtpFileListFilter filter;
|
||||
private FtpFileListFilter acceptAllFtpFileListFilter = new FtpFileListFilter() {
|
||||
public List<FTPFile> filterFiles(FTPFile[] files) {
|
||||
return Arrays.asList(files);
|
||||
}
|
||||
};
|
||||
|
||||
public void setFilter(FtpFileListFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public void setTaskScheduler(TaskScheduler scheduler) {
|
||||
this.taskScheduler = scheduler;
|
||||
}
|
||||
|
||||
public void setTrigger(Trigger trigger) {
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
public void setLocalDirectory(Resource localDirectory) {
|
||||
this.localDirectory = localDirectory;
|
||||
}
|
||||
|
||||
public void setClientPool(FtpClientPool pool) {
|
||||
clientPool = pool;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(localDirectory, "'localDirectory' is required.");
|
||||
|
||||
if (this.filter == null) {
|
||||
this.filter = acceptAllFtpFileListFilter;
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronize() {
|
||||
try {
|
||||
FTPClient client = this.clientPool.getClient();
|
||||
Assert.state(client != null, FtpClientPool.class.getSimpleName() + " returned 'null' client this most likely a bug in the pool implementation.");
|
||||
|
||||
Collection<FTPFile> fileList = this.filter.filterFiles(client.listFiles());
|
||||
|
||||
try {
|
||||
for (FTPFile ftpFile : fileList) {
|
||||
/*
|
||||
* according to the FTPFile javadoc the list can contain
|
||||
* nulls if files couldn't be parsed
|
||||
*/
|
||||
if ((ftpFile != null) && ftpFile.isFile()) {
|
||||
copyFileToLocalDirectory(client, ftpFile, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.clientPool.releaseClient(client);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("Problem occurred while synchronizing remote to local directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyFileToLocalDirectory(FTPClient client, FTPFile ftpFile, Resource localDirectory)
|
||||
throws IOException, FileNotFoundException {
|
||||
String remoteFileName = ftpFile.getName();
|
||||
String localFileName = localDirectory.getFile().getPath() + "/" + remoteFileName;
|
||||
File localFile = new File(localFileName);
|
||||
|
||||
if (!localFile.exists()) {
|
||||
String tempFileName = localFileName + INCOMPLETE_EXTENSION;
|
||||
File file = new File(tempFileName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
|
||||
try {
|
||||
client.retrieveFile(remoteFileName, fos);
|
||||
} finally {
|
||||
fos.close();
|
||||
}
|
||||
|
||||
file.renameTo(localFile);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.state(taskScheduler != null, "'taskScheduler' is required");
|
||||
scheduledFuture = taskScheduler.schedule(new SynchronizeTask(), trigger);
|
||||
// future.get();
|
||||
this.running = true;
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Started " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scheduledFuture.cancel(true);
|
||||
this.running = false;
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Stopped " + this);
|
||||
}
|
||||
}
|
||||
|
||||
private class SynchronizeTask implements Runnable {
|
||||
public void run() {
|
||||
synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Makes it easier to assemble the moving pieces involved in standing up an {@link FtpMessageSourceFactoryBean}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpMessageSourceFactoryBean extends AbstractFactoryBean<FtpFileSource> implements ResourceLoaderAware, ApplicationContextAware {
|
||||
private int port;
|
||||
private boolean autoCreateDirectories;
|
||||
private String filenamePattern;
|
||||
private String username;
|
||||
private String password;
|
||||
private String host;
|
||||
private String remoteDirectory;
|
||||
private String localWorkingDirectory;
|
||||
private ApplicationContext applicationContext;
|
||||
private Resource localDirectoryResource;
|
||||
private FtpInboundSynchronizer ftpInboundSynchronizer;
|
||||
private TaskScheduler taskScheduler;
|
||||
private ResourceLoader resourceLoader;
|
||||
private FileReadingMessageSource fileReadingMessageSource;
|
||||
private int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
|
||||
/**
|
||||
* Used to teach the FTP adapter what files you are interested in receiving
|
||||
*/
|
||||
private FtpFileListFilter filter;
|
||||
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectories(boolean autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setLocalWorkingDirectory(String localWorkingDirectory) {
|
||||
this.localWorkingDirectory = localWorkingDirectory;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public Class<?extends FtpFileSource> getObjectType() {
|
||||
return FtpFileSource.class;
|
||||
}
|
||||
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
public void setFilter(FtpFileListFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FtpFileSource createInstance() throws Exception {
|
||||
// setup local dir
|
||||
if (!StringUtils.hasText(this.localWorkingDirectory)) {
|
||||
File tmp = SystemUtils.getJavaIoTmpDir();
|
||||
File ftpTmp = new File(tmp, "ftpInbound");
|
||||
this.localWorkingDirectory = "file://" + ftpTmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
Assert.hasText(this.localWorkingDirectory, "the local working directory can't be null!");
|
||||
|
||||
ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader);
|
||||
resourceEditor.setAsText(this.localWorkingDirectory);
|
||||
this.localDirectoryResource = (Resource) resourceEditor.getValue();
|
||||
fileReadingMessageSource = new FileReadingMessageSource();
|
||||
|
||||
this.ftpInboundSynchronizer = new FtpInboundSynchronizer();
|
||||
|
||||
CompositeFtpFileListFilter compositeFtpFileListFilter = new CompositeFtpFileListFilter();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingFtpFileListFilter patternMatchingFTPFileListFilter = new PatternMatchingFtpFileListFilter();
|
||||
patternMatchingFTPFileListFilter.setPattern(Pattern.compile(this.filenamePattern));
|
||||
compositeFtpFileListFilter.addFilter(patternMatchingFTPFileListFilter);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
this.ftpInboundSynchronizer.setFilter(compositeFtpFileListFilter);
|
||||
|
||||
if (this.taskScheduler == null) {
|
||||
Map<String, TaskScheduler> tss = null;
|
||||
|
||||
if ((tss = applicationContext.getBeansOfType(TaskScheduler.class)).keySet().size() != 0) {
|
||||
taskScheduler = tss.get(tss.keySet().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
if (null == taskScheduler) {
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPoolSize(10);
|
||||
threadPoolTaskScheduler.setErrorHandler(new ErrorHandler() {
|
||||
public void handleError(Throwable t) {
|
||||
logger.debug("Error! ", t);
|
||||
}
|
||||
});
|
||||
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
threadPoolTaskScheduler.initialize();
|
||||
this.taskScheduler = threadPoolTaskScheduler;
|
||||
}
|
||||
|
||||
DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory();
|
||||
defaultFtpClientFactory.setHost(this.host);
|
||||
defaultFtpClientFactory.setPassword(this.password);
|
||||
defaultFtpClientFactory.setPort(this.port);
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(this.remoteDirectory);
|
||||
defaultFtpClientFactory.setUsername(this.username);
|
||||
defaultFtpClientFactory.setClientMode(this.clientMode);
|
||||
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultFtpClientFactory);
|
||||
|
||||
this.ftpInboundSynchronizer.setClientPool(queuedFtpClientPool);
|
||||
this.ftpInboundSynchronizer.setLocalDirectory(this.localDirectoryResource);
|
||||
this.ftpInboundSynchronizer.setTaskScheduler(this.taskScheduler);
|
||||
assert this.localDirectoryResource != null : "the 'localDirectoryResource' can't be null at this point";
|
||||
|
||||
if (this.autoCreateDirectories) {
|
||||
if (!this.localDirectoryResource.exists()) {
|
||||
try {
|
||||
if (!localDirectoryResource.getFile().mkdirs()) {
|
||||
logger.debug("attempted to ensure the existence of the local directory '" + this.localDirectoryResource.getFile().getAbsolutePath() + "' but didn't succeed");
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
logger.debug("attempted to ensure the existence of the local directory '" + this.localDirectoryResource.getFile().getAbsolutePath() + "' but didn't succeed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.ftpInboundSynchronizer.afterPropertiesSet();
|
||||
|
||||
FtpFileSource ftpFileSource = new FtpFileSource(this.fileReadingMessageSource, this.ftpInboundSynchronizer);
|
||||
ftpFileSource.setClientPool(queuedFtpClientPool);
|
||||
ftpFileSource.setLocalWorkingDirectory(this.localDirectoryResource);
|
||||
ftpFileSource.setSynchronizer(this.ftpInboundSynchronizer);
|
||||
ftpFileSource.setTaskScheduler(this.taskScheduler);
|
||||
ftpFileSource.setFileSource(this.fileReadingMessageSource);
|
||||
ftpFileSource.afterPropertiesSet();
|
||||
ftpFileSource.start();
|
||||
|
||||
return ftpFileSource;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Validates {@link org.apache.commons.net.ftp.FTPFile}s against a {@link java.util.regex.Pattern}.
|
||||
* Patterned very much like {@link org.springframework.integration.file.PatternMatchingFileListFilter}.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class PatternMatchingFtpFileListFilter extends AbstractFtpFileListFilter implements InitializingBean {
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Pattern pattern;
|
||||
private String patternExpression;
|
||||
|
||||
public void setPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public void setPatternExpression(String patternExpression) {
|
||||
this.patternExpression = patternExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(FTPFile ftpFile) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("testing: " + ToStringBuilder.reflectionToString(ftpFile));
|
||||
}
|
||||
|
||||
return (ftpFile != null) && this.pattern.matcher(ftpFile.getName()).matches();
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (StringUtils.hasText(this.patternExpression) && (this.pattern == null)) {
|
||||
this.pattern = Pattern.compile(this.patternExpression);
|
||||
}
|
||||
|
||||
Assert.notNull(this.pattern, "the pattern must not be null");
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpSendingMessageHandlerFactoryBean;
|
||||
import org.springframework.integration.ftp.impl.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -57,7 +59,7 @@ public class FtpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
private static class FTPMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser {
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PACKAGE_NAME + ".FtpSendingMessageHandlerFactoryBean");
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpSendingMessageHandlerFactoryBean.class.getName());
|
||||
|
||||
for (String p : "auto-create-directories,username,port,password,host,key-file,key-file-password,remote-directory".split(",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p);
|
||||
@@ -79,17 +81,20 @@ public class FtpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PACKAGE_NAME + ".FtpMessageSourceFactoryBean");
|
||||
|
||||
// reference
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,"filter");
|
||||
|
||||
for (String p : ("filename-pattern,auto-create-directories,username,password,host,port," + "remote-directory,local-working-directory").split(",")) {
|
||||
for (String p : ("auto-delete-remote-files-on-sync,filename-pattern,auto-create-directories,username,password,host,port," +
|
||||
"remote-directory,local-working-directory").split(",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p);
|
||||
}
|
||||
|
||||
int clientMode = CLIENT_MODES.get(element.getAttribute("client-mode"));
|
||||
builder.addPropertyValue("clientMode", clientMode);
|
||||
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.springframework.integration.ftp.impl;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer;
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
import org.springframework.integration.ftp.FtpClientPool;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* An FTP-adapter implementation of {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer<FTPFile> {
|
||||
protected FtpClientPool clientPool;
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "clientPool can't be null");
|
||||
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link org.springframework.integration.ftp.FtpClientPool} that holds references to {@link org.apache.commons.net.ftp.FTPClient} instances
|
||||
*
|
||||
* @param clientPool the {@link org.springframework.integration.ftp.FtpClientPool}
|
||||
*/
|
||||
public void setClientPool(FtpClientPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
protected boolean copyFileToLocalDirectory(FTPClient client, FTPFile ftpFile, Resource localDirectory)
|
||||
throws IOException, FileNotFoundException {
|
||||
String remoteFileName = ftpFile.getName();
|
||||
String localFileName = localDirectory.getFile().getPath() + "/" + remoteFileName;
|
||||
File localFile = new File(localFileName);
|
||||
|
||||
if (!localFile.exists()) {
|
||||
String tempFileName = localFileName + AbstractInboundRemoteFileSystemSynchronizingMessageSource.INCOMPLETE_EXTENSION;
|
||||
File file = new File(tempFileName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
|
||||
try {
|
||||
client.retrieveFile(remoteFileName, fos);
|
||||
|
||||
// Perhaps we have some dispatch of hte source file to do?
|
||||
acknowledge(client, ftpFile);
|
||||
} catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
} finally {
|
||||
fos.close();
|
||||
}
|
||||
|
||||
file.renameTo(localFile);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void syncRemoteToLocalFileSystem() {
|
||||
try {
|
||||
FTPClient client = this.clientPool.getClient();
|
||||
Assert.state(client != null, FtpClientPool.class.getSimpleName() + " returned a 'null' client. " + "This most likely a bug in the pool implementation.");
|
||||
|
||||
Collection<FTPFile> fileList = this.filter.filterEntries(client.listFiles());
|
||||
|
||||
try {
|
||||
for (FTPFile ftpFile : fileList) {
|
||||
if ((ftpFile != null) && ftpFile.isFile()) {
|
||||
copyFileToLocalDirectory(client, ftpFile, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.clientPool.releaseClient(client);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("Problem occurred while synchronizing remote to local directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trigger getTrigger() {
|
||||
return new PeriodicTrigger(10 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* An ackowledgment strategy that deletes
|
||||
*/
|
||||
class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<FTPFile> {
|
||||
public void acknowledge(Object useful, FTPFile msg)
|
||||
throws Exception {
|
||||
FTPClient ftpClient = (FTPClient) useful;
|
||||
if ((msg != null) && ftpClient.deleteFile(msg.getName())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("deleted " + msg.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.springframework.integration.ftp.impl;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
import org.springframework.integration.ftp.FtpClientPool;
|
||||
|
||||
|
||||
/**
|
||||
* a {@link org.springframework.integration.core.MessageSource} implementation for FTP
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpInboundRemoteFileSystemSynchronizingMessageSource extends AbstractInboundRemoteFileSystemSynchronizingMessageSource<FTPFile, FtpInboundRemoteFileSystemSynchronizer> {
|
||||
private volatile FtpClientPool clientPool;
|
||||
|
||||
public void setClientPool(FtpClientPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
this.synchronizer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() {
|
||||
this.synchronizer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
this.synchronizer.setClientPool(this.clientPool);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package org.springframework.integration.ftp.impl;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
import org.springframework.integration.ftp.DefaultFtpClientFactory;
|
||||
import org.springframework.integration.ftp.FtpFileEntryNamer;
|
||||
import org.springframework.integration.ftp.QueuedFtpClientPool;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Factory to make building the namespace easier
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends AbstractFactoryBean<FtpInboundRemoteFileSystemSynchronizingMessageSource> implements ResourceLoaderAware {
|
||||
private volatile String port;
|
||||
private volatile String autoCreateDirectories;
|
||||
private volatile String filenamePattern;
|
||||
private volatile String username;
|
||||
private volatile String password;
|
||||
private volatile String host;
|
||||
private volatile String remoteDirectory;
|
||||
private volatile String localWorkingDirectory;
|
||||
private volatile ResourceLoader resourceLoader;
|
||||
private volatile Resource localDirectoryResource;
|
||||
private volatile EntryListFilter<FTPFile> filter;
|
||||
private volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
private volatile String autoDeleteRemoteFilesOnSync;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) {
|
||||
this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return FtpInboundRemoteFileSystemSynchronizingMessageSource.class;
|
||||
}
|
||||
|
||||
private Resource fromText(String path) {
|
||||
ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader);
|
||||
resourceEditor.setAsText(path);
|
||||
return (Resource) resourceEditor.getValue();
|
||||
}
|
||||
|
||||
private DefaultFtpClientFactory defaultFtpClientFactory() {
|
||||
DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory();
|
||||
defaultFtpClientFactory.setHost(this.host);
|
||||
defaultFtpClientFactory.setPassword(this.password);
|
||||
defaultFtpClientFactory.setPort(Integer.parseInt(this.port));
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(this.remoteDirectory);
|
||||
defaultFtpClientFactory.setUsername(this.username);
|
||||
defaultFtpClientFactory.setClientMode(this.clientMode);
|
||||
|
||||
return defaultFtpClientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FtpInboundRemoteFileSystemSynchronizingMessageSource createInstance()
|
||||
throws Exception {
|
||||
boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories);
|
||||
boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync);
|
||||
|
||||
FtpInboundRemoteFileSystemSynchronizingMessageSource ftpRemoteFileSystemSynchronizingMessageSource = new FtpInboundRemoteFileSystemSynchronizingMessageSource();
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setAutoCreateDirectories(autoCreatDirs);
|
||||
|
||||
if (!StringUtils.hasText(this.localWorkingDirectory)) {
|
||||
File tmp = new File(SystemUtils.getJavaIoTmpDir(), "ftpInbound");
|
||||
this.localWorkingDirectory = "file://" + tmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
this.localDirectoryResource = this.fromText(this.localWorkingDirectory);
|
||||
|
||||
FtpFileEntryNamer ftpFileEntryNamer = new FtpFileEntryNamer();
|
||||
CompositeEntryListFilter<FTPFile> compositeFtpFileListFilter = new CompositeEntryListFilter<FTPFile>();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingEntryListFilter<FTPFile> ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter<FTPFile>(ftpFileEntryNamer, filenamePattern);
|
||||
compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultFtpClientFactory());
|
||||
|
||||
FtpInboundRemoteFileSystemSynchronizer ftpRemoteFileSystemSynchronizer = new FtpInboundRemoteFileSystemSynchronizer();
|
||||
ftpRemoteFileSystemSynchronizer.setClientPool(queuedFtpClientPool);
|
||||
ftpRemoteFileSystemSynchronizer.setLocalDirectory(this.localDirectoryResource);
|
||||
ftpRemoteFileSystemSynchronizer.setShouldDeleteSourceFile(ackRemoteDir);
|
||||
|
||||
ftpRemoteFileSystemSynchronizer.setFilter(compositeFtpFileListFilter);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setRemotePredicate(compositeFtpFileListFilter);
|
||||
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setSynchronizer(ftpRemoteFileSystemSynchronizer);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setClientPool(queuedFtpClientPool);
|
||||
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setLocalDirectory(this.localDirectoryResource);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setBeanFactory(this.getBeanFactory());
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setAutoStartup(true);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.afterPropertiesSet();
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.start();
|
||||
|
||||
return ftpRemoteFileSystemSynchronizingMessageSource;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoCreateDirectories(String autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocalWorkingDirectory(String localWorkingDirectory) {
|
||||
this.localWorkingDirectory = localWorkingDirectory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilter(EntryListFilter<FTPFile> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.ftp.FtpFileListFilter"/>
|
||||
<tool:expected-type type="org.springframework.integration.file.entries.EntryListFilter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
|
||||
@@ -29,5 +29,6 @@ public class InboundFtpFileServiceActivator {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
ClassPathXmlApplicationContext classPathXmlApplicationContext =
|
||||
new ClassPathXmlApplicationContext("inbound-ftp-context.xml");
|
||||
classPathXmlApplicationContext.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
filename-pattern=".*?jpg"
|
||||
>
|
||||
<int:poller>
|
||||
<int:interval-trigger interval="10000" time-unit="MILLISECONDS"/>
|
||||
<int:interval-trigger interval="1000" time-unit="MILLISECONDS"/>
|
||||
</int:poller>
|
||||
</ftp:inbound-channel-adapter>
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Convenience implementation patterned off {@link org.springframework.integration.file.FileListFilter}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public abstract class AbstractSftpFileListFilter implements SftpFileListFilter {
|
||||
|
||||
abstract public boolean accept(ChannelSftp.LsEntry lsEntry);
|
||||
|
||||
public List<ChannelSftp.LsEntry> filterFiles(ChannelSftp.LsEntry[] files) {
|
||||
List<ChannelSftp.LsEntry> accepted = new ArrayList<ChannelSftp.LsEntry>();
|
||||
|
||||
if (files != null) {
|
||||
for (ChannelSftp.LsEntry lsEntry : files)
|
||||
if (this.accept(lsEntry)) {
|
||||
accepted.add(lsEntry);
|
||||
}
|
||||
}
|
||||
|
||||
return accepted;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Patterned very much on the {@link org.springframework.integration.file.CompositeFileListFilter}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class CompositeFtpFileListFilter implements SftpFileListFilter {
|
||||
private Set<SftpFileListFilter> filters;
|
||||
|
||||
public CompositeFtpFileListFilter(SftpFileListFilter... ftpFileListFilter) {
|
||||
this.filters = new LinkedHashSet<SftpFileListFilter>(Arrays.asList(ftpFileListFilter));
|
||||
}
|
||||
|
||||
public CompositeFtpFileListFilter(Collection<SftpFileListFilter> ftpFileListFilter) {
|
||||
this.filters = new LinkedHashSet<SftpFileListFilter>(ftpFileListFilter);
|
||||
}
|
||||
|
||||
public void addFilter(SftpFileListFilter ftpFileListFilter) {
|
||||
this.filters.add(ftpFileListFilter);
|
||||
}
|
||||
|
||||
public List<ChannelSftp.LsEntry> filterFiles(ChannelSftp.LsEntry[] files) {
|
||||
Assert.notNull(files, "files[] can't be null!");
|
||||
|
||||
List<ChannelSftp.LsEntry> leftOver = Arrays.asList(files);
|
||||
|
||||
for (SftpFileListFilter ff : this.filters)
|
||||
leftOver = ff.filterFiles(leftOver.toArray(new ChannelSftp.LsEntry[leftOver.size()]));
|
||||
|
||||
return leftOver;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Validates {@link com.jcraft.jsch.ChannelSftp.LsEntry}s against a {@link java.util.regex.Pattern}.
|
||||
* Patterned very much like {@link org.springframework.integration.file.PatternMatchingFileListFilter}.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class PatternMatchingSftpFileListFilter extends AbstractSftpFileListFilter implements InitializingBean {
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Pattern pattern;
|
||||
private String patternExpression;
|
||||
|
||||
public void setPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public void setPatternExpression(String patternExpression) {
|
||||
this.patternExpression = patternExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(ChannelSftp.LsEntry lsEntry) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("testing: " + ToStringBuilder.reflectionToString(lsEntry));
|
||||
}
|
||||
|
||||
return (lsEntry != null) && this.pattern.matcher(lsEntry.getFilename()).matches();
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (StringUtils.hasText(this.patternExpression) && (this.pattern == null)) {
|
||||
this.pattern = Pattern.compile(this.patternExpression);
|
||||
}
|
||||
|
||||
Assert.notNull(this.pattern, "the pattern must not be null");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.springframework.integration.file.entries.EntryNamer;
|
||||
|
||||
/**
|
||||
* Knows how to name a {@link com.jcraft.jsch.ChannelSftp.LsEntry} instance
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpEntryNamer implements EntryNamer<ChannelSftp.LsEntry>{
|
||||
|
||||
public String nameOf(ChannelSftp.LsEntry entry) {
|
||||
return entry.getFilename() ;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Filters out all the {@link com.jcraft.jsch.ChannelSftp.LsEntry} taken in a scan of the remote mount
|
||||
* and returns the balance. These are then sync'd to the local directory.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public interface SftpFileListFilter {
|
||||
List<ChannelSftp.LsEntry> filterFiles (ChannelSftp.LsEntry [] files);
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
|
||||
/**
|
||||
* This handles keeping the {@link #localDirectory} in sync with the contents of the remote mount. From there, files are deposited
|
||||
* into a folder where the {@link org.springframework.integration.file.FileReadingMessageSource} will eventually deliver them as events
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mario Gray
|
||||
*/
|
||||
public class SftpInboundSynchronizer implements InitializingBean {
|
||||
private static final long DEFAULT_REFRESH_RATE = 10 * 1000; // 10 seconds
|
||||
|
||||
// a lot of the approach for this (including the use of a FileReadingMessageSource and the regex / mask approach were lifted from FtpInboundSynchronizer
|
||||
static final String INCOMPLETE_EXTENSION = ".INCOMPLETE";
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
private volatile Resource localDirectory;
|
||||
private volatile SftpSessionPool pool;
|
||||
private volatile ScheduledFuture<?> scheduledFuture;
|
||||
private volatile String remotePath;
|
||||
private volatile TaskScheduler taskScheduler;
|
||||
private volatile Trigger trigger = new PeriodicTrigger(DEFAULT_REFRESH_RATE);
|
||||
private volatile boolean autoCreatePath;
|
||||
private volatile boolean running;
|
||||
private SftpFileListFilter filter;
|
||||
|
||||
public void setFilter(SftpFileListFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
private volatile boolean shouldDeleteDownloadedRemoteFiles; //.. this is false
|
||||
|
||||
private SftpFileListFilter acceptAllFilteListFilter = new SftpFileListFilter(){
|
||||
public List<ChannelSftp.LsEntry> filterFiles(ChannelSftp.LsEntry[] files) {
|
||||
return Arrays.asList( files);
|
||||
}
|
||||
} ;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.state(taskScheduler != null, "taskScheduler can't be null!");
|
||||
Assert.state(localDirectory != null, "the localDirectory property must not be null!");
|
||||
|
||||
File localDir = localDirectory.getFile();
|
||||
|
||||
if (!localDir.exists()) {
|
||||
if (autoCreatePath) {
|
||||
if (!localDir.mkdirs()) {
|
||||
throw new RuntimeException(String.format("couldn't create localDirectory %s", this.localDirectory.getFile().getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this.filter == null)
|
||||
this.filter = acceptAllFilteListFilter;
|
||||
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public boolean isShouldDeleteDownloadedRemoteFiles() {
|
||||
return shouldDeleteDownloadedRemoteFiles;
|
||||
}
|
||||
|
||||
public void setAutoCreatePath(boolean autoCreatePath) {
|
||||
this.autoCreatePath = autoCreatePath;
|
||||
}
|
||||
|
||||
public void setLocalDirectory(Resource localDirectory) {
|
||||
this.localDirectory = localDirectory;
|
||||
}
|
||||
|
||||
public void setPool(SftpSessionPool pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public void setRemotePath(String remotePath) {
|
||||
this.remotePath = remotePath;
|
||||
}
|
||||
|
||||
public void setScheduledFuture(ScheduledFuture<?> scheduledFuture) {
|
||||
this.scheduledFuture = scheduledFuture;
|
||||
}
|
||||
|
||||
public void setShouldDeleteDownloadedRemoteFiles(boolean shouldDeleteDownloadedRemoteFiles) {
|
||||
this.shouldDeleteDownloadedRemoteFiles = shouldDeleteDownloadedRemoteFiles;
|
||||
}
|
||||
|
||||
public void setTaskScheduler(TaskScheduler taskScheduler) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
public void setTrigger(Trigger t) {
|
||||
this.trigger = t;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.state(checkThatRemotePathExists(remotePath), "the remotePath should exist before we can sync with it!");
|
||||
Assert.state(taskScheduler != null, "'taskScheduler' is required");
|
||||
|
||||
scheduledFuture = taskScheduler.schedule(new SynchronizeTask(), trigger);
|
||||
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.state(scheduledFuture != null, "scheduledFuture is null!");
|
||||
this.scheduledFuture.cancel(true);
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void synchronize() throws Exception {
|
||||
SftpSession session = null;
|
||||
|
||||
try {
|
||||
session = pool.getSession();
|
||||
session.start();
|
||||
|
||||
ChannelSftp channelSftp = session.getChannel();
|
||||
Collection<ChannelSftp.LsEntry> beforeFilter = channelSftp.ls(remotePath);
|
||||
ChannelSftp.LsEntry [] entries = beforeFilter == null? new ChannelSftp.LsEntry[0] :
|
||||
beforeFilter.toArray(new ChannelSftp.LsEntry[ beforeFilter.size()]) ;
|
||||
Collection<ChannelSftp.LsEntry> files = this.filter.filterFiles( entries );
|
||||
|
||||
for (ChannelSftp.LsEntry lsEntry : files) {
|
||||
if ((lsEntry != null) && !lsEntry.getAttrs().isDir() && !lsEntry.getAttrs().isLink()) {
|
||||
copyFromRemoteToLocalDirectory(session, lsEntry, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("couldn't synchronize remote to local directory", e);
|
||||
} finally {
|
||||
if ((session != null) && (pool != null)) {
|
||||
pool.release(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* there be dragons this way ... This method will check to ensure that the remote directory exists. If the directory
|
||||
* doesnt exist, and autoCreatePath is 'true,' then this method makes a few reasonably sane attempts
|
||||
* to create it. Otherwise, it fails fast.
|
||||
*
|
||||
* @param remotePath the path on the remote SSH / SFTP server to create.
|
||||
* @return whether or not the directory is there (regardless of whether we created it in this method or it already
|
||||
* existed.)
|
||||
*/
|
||||
private boolean checkThatRemotePathExists(String remotePath) {
|
||||
SftpSession session = null;
|
||||
ChannelSftp channelSftp = null;
|
||||
|
||||
try {
|
||||
session = pool.getSession();
|
||||
Assert.state(session != null, "session as returned from the pool should not be null. " + "If it is, it is most likely an error in the pool implementation. ");
|
||||
session.start();
|
||||
channelSftp = session.getChannel();
|
||||
|
||||
SftpATTRS attrs = channelSftp.stat(remotePath);
|
||||
assert (attrs != null) && attrs.isDir() : "attrs can't be null, and should indicate that it's a directory!";
|
||||
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
if (this.autoCreatePath && (pool != null) && (session != null)) {
|
||||
try {
|
||||
if (channelSftp != null) {
|
||||
channelSftp.mkdir(remotePath);
|
||||
|
||||
if (channelSftp.stat(remotePath).isDir()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if ((pool != null) && (session != null)) {
|
||||
pool.release(session);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ignored")
|
||||
private boolean copyFromRemoteToLocalDirectory(SftpSession sftpSession, ChannelSftp.LsEntry entry, Resource localDir)
|
||||
throws Exception {
|
||||
File fileForLocalDir = localDir.getFile();
|
||||
|
||||
File localFile = new File(fileForLocalDir, entry.getFilename());
|
||||
|
||||
if (!localFile.exists()) {
|
||||
InputStream in = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
File tmpLocalTarget = new File(localFile.getAbsolutePath() + INCOMPLETE_EXTENSION);
|
||||
|
||||
fos = new FileOutputStream(tmpLocalTarget);
|
||||
|
||||
String remoteFqPath = this.remotePath + "/" + entry.getFilename();
|
||||
in = sftpSession.getChannel().get(remoteFqPath);
|
||||
IOUtils.copy(in, fos);
|
||||
|
||||
if (tmpLocalTarget.renameTo(localFile)) {
|
||||
// last step
|
||||
if (isShouldDeleteDownloadedRemoteFiles()) {
|
||||
sftpSession.getChannel().rm(remoteFqPath);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(fos);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class SynchronizeTask implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
synchronize();
|
||||
} catch (Throwable e) {
|
||||
// todo logger.debug("couldn't invoke synchronize()", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.file.AcceptOnceFileListFilter;
|
||||
import org.springframework.integration.file.CompositeFileListFilter;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.integration.file.PatternMatchingFileListFilter;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* this creates the message source that ultimately 'see's files on a local directory and forwards them on to the bus.
|
||||
* These files are asynchronously deposited into a folder via the SFTP synchronizer. This code is <i>very</i> influenced
|
||||
* by the FtpFileSource class from the Spring Integration FTP adapter.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpMessageSource implements MessageSource<File>, InitializingBean, Lifecycle {
|
||||
private FileReadingMessageSource fileReadingMessageSource;
|
||||
private Resource localDirectory;
|
||||
private SftpInboundSynchronizer synchronizer;
|
||||
private TaskScheduler taskScheduler;
|
||||
private Trigger trigger;
|
||||
|
||||
public SftpMessageSource(FileReadingMessageSource fileSource, SftpInboundSynchronizer synchronizer) {
|
||||
this.fileReadingMessageSource = fileSource;
|
||||
this.synchronizer = synchronizer;
|
||||
|
||||
Pattern completePattern = Pattern.compile("^.*(?<!" + SftpInboundSynchronizer.INCOMPLETE_EXTENSION + ")$");
|
||||
fileReadingMessageSource.setFilter(new CompositeFileListFilter(new AcceptOnceFileListFilter(), new PatternMatchingFileListFilter(completePattern)));
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
synchronizer.afterPropertiesSet();
|
||||
this.fileReadingMessageSource.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public FileReadingMessageSource getFileReadingMessageSource() {
|
||||
return fileReadingMessageSource;
|
||||
}
|
||||
|
||||
public Resource getLocalDirectory() {
|
||||
return localDirectory;
|
||||
}
|
||||
|
||||
public SftpInboundSynchronizer getSynchronizer() {
|
||||
return synchronizer;
|
||||
}
|
||||
|
||||
public TaskScheduler getTaskScheduler() {
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
public Trigger getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.synchronizer.isRunning();
|
||||
}
|
||||
|
||||
public Message<File> receive() {
|
||||
return this.fileReadingMessageSource.receive();
|
||||
}
|
||||
|
||||
public void setFileReadingMessageSource(final FileReadingMessageSource fileReadingMessageSource) {
|
||||
this.fileReadingMessageSource = fileReadingMessageSource;
|
||||
}
|
||||
|
||||
public void setLocalDirectory(final Resource localDirectory) {
|
||||
this.localDirectory = localDirectory;
|
||||
try {
|
||||
this.fileReadingMessageSource.setDirectory(localDirectory.getFile());
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
this.synchronizer.setLocalDirectory(localDirectory);
|
||||
}
|
||||
|
||||
public void setSynchronizer(final SftpInboundSynchronizer synchronizer) {
|
||||
this.synchronizer = synchronizer;
|
||||
}
|
||||
|
||||
public void setTaskScheduler(final TaskScheduler taskScheduler) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
synchronizer.setTaskScheduler(taskScheduler);
|
||||
}
|
||||
|
||||
public void setTrigger(final Trigger trigger) {
|
||||
this.trigger = trigger;
|
||||
this.synchronizer.setTrigger(trigger);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
synchronizer.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronizer.stop();
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.sftp.config;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.file.FileReadingMessageSource;
|
||||
import org.springframework.integration.sftp.*;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Building a {@link org.springframework.integration.sftp.SftpMessageSource} is a complicated because we also
|
||||
* use a {@link org.springframework.integration.file.FileReadingMessageSource} to handle the "receipt" of files in
|
||||
* a {@link #localWorkingDirectory}.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpMessageSourceFactoryBean extends AbstractFactoryBean<SftpMessageSource> implements ApplicationContextAware, ResourceLoaderAware {
|
||||
private ApplicationContext applicationContext;
|
||||
private FileReadingMessageSource fileReadingMessageSource;
|
||||
private Resource localDirectoryResource;
|
||||
private ResourceLoader resourceLoader;
|
||||
private SftpInboundSynchronizer synchronizer;
|
||||
private String host;
|
||||
private String keyFile;
|
||||
private String keyFilePassword;
|
||||
private String localWorkingDirectory;
|
||||
private String password;
|
||||
private String remoteDirectory;
|
||||
private String username;
|
||||
private TaskScheduler taskScheduler;
|
||||
private Trigger trigger;
|
||||
private boolean autoCreateDirectories;
|
||||
private boolean autoDeleteRemoteFilesOnSync;
|
||||
private int port = 22;
|
||||
private SftpFileListFilter filter;
|
||||
private String filenamePattern;
|
||||
|
||||
public FileReadingMessageSource getFileReadingMessageSource() {
|
||||
return fileReadingMessageSource;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getKeyFile() {
|
||||
return keyFile;
|
||||
}
|
||||
|
||||
public String getKeyFilePassword() {
|
||||
return keyFilePassword;
|
||||
}
|
||||
|
||||
public String getLocalWorkingDirectory() {
|
||||
return localWorkingDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?extends SftpMessageSource> getObjectType() {
|
||||
return SftpMessageSource.class;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getRemoteDirectory() {
|
||||
return remoteDirectory;
|
||||
}
|
||||
|
||||
public SftpInboundSynchronizer getSynchronizer() {
|
||||
return synchronizer;
|
||||
}
|
||||
|
||||
public TaskScheduler getTaskScheduler() {
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
public Trigger getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
// this is the ultimate layer of control
|
||||
// users will configure theeir entire experience using this class and trust that a working
|
||||
// component comes out as a result of their input
|
||||
// we need to support user/pw/keys/host/port/auto-delete properties
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean isAutoCreateDirectories() {
|
||||
return autoCreateDirectories;
|
||||
}
|
||||
|
||||
public boolean isAutoDeleteRemoteFilesOnSync() {
|
||||
return autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
public void setApplicationContext(final ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectories(final boolean autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
public void setAutoDeleteRemoteFilesOnSync(final boolean autoDeleteRemoteFilesOnSync) {
|
||||
this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
public void setFileReadingMessageSource(final FileReadingMessageSource fileReadingMessageSource) {
|
||||
this.fileReadingMessageSource = fileReadingMessageSource;
|
||||
}
|
||||
|
||||
public void setHost(final String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setKeyFile(final String keyFile) {
|
||||
this.keyFile = keyFile;
|
||||
}
|
||||
|
||||
public void setKeyFilePassword(final String keyFilePassword) {
|
||||
this.keyFilePassword = keyFilePassword;
|
||||
}
|
||||
|
||||
public void setLocalWorkingDirectory(final String lwd) {
|
||||
this.localWorkingDirectory = lwd;
|
||||
}
|
||||
|
||||
public void setPassword(final String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setPort(final int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setRemoteDirectory(final String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
public void setResourceLoader(final ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public void setSynchronizer(final SftpInboundSynchronizer synchronizer) {
|
||||
this.synchronizer = synchronizer;
|
||||
}
|
||||
|
||||
public void setTaskScheduler(final TaskScheduler taskScheduler) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
public void setTrigger(final Trigger trigger) {
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
public void setFilter(SftpFileListFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public void setUsername(final String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SftpMessageSource createInstance() throws Exception {
|
||||
try {
|
||||
if ((localWorkingDirectory == null) || !StringUtils.hasText(localWorkingDirectory)) {
|
||||
File tmp = SystemUtils.getJavaIoTmpDir();
|
||||
File sftpTmp = new File(tmp, "sftpInbound");
|
||||
this.localWorkingDirectory = "file://" + sftpTmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
// resource for local directory
|
||||
ResourceEditor editor = new ResourceEditor(this.resourceLoader);
|
||||
editor.setAsText(this.localWorkingDirectory);
|
||||
this.localDirectoryResource = (Resource) editor.getValue();
|
||||
|
||||
fileReadingMessageSource = new FileReadingMessageSource();
|
||||
|
||||
synchronizer = new SftpInboundSynchronizer();
|
||||
|
||||
CompositeFtpFileListFilter compositeFtpFileListFilter = new CompositeFtpFileListFilter();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingSftpFileListFilter flp = new PatternMatchingSftpFileListFilter();
|
||||
flp.setPatternExpression(this.filenamePattern);
|
||||
flp.afterPropertiesSet();
|
||||
compositeFtpFileListFilter.addFilter(flp);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
synchronizer.setFilter(compositeFtpFileListFilter);
|
||||
|
||||
if (null == taskScheduler) {
|
||||
Map<String, TaskScheduler> tss = null;
|
||||
|
||||
if ((tss = applicationContext.getBeansOfType(TaskScheduler.class)).keySet().size() != 0) {
|
||||
taskScheduler = tss.get(tss.keySet().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
if (null == taskScheduler) {
|
||||
ThreadPoolTaskScheduler ts = new ThreadPoolTaskScheduler();
|
||||
ts.setPoolSize(10);
|
||||
ts.setErrorHandler(new ErrorHandler() {
|
||||
public void handleError(Throwable t) {
|
||||
// todo make this forward a message onto the error channel (how does that work?)
|
||||
logger.debug("error! ", t);
|
||||
}
|
||||
});
|
||||
|
||||
ts.setWaitForTasksToCompleteOnShutdown(true);
|
||||
ts.initialize();
|
||||
this.taskScheduler = ts;
|
||||
}
|
||||
|
||||
SftpSessionFactory sessionFactory = SftpSessionUtils.buildSftpSessionFactory(
|
||||
this.getHost(), this.getPassword(), this.getUsername(), this.getKeyFile(), this.getKeyFilePassword(), this.getPort());
|
||||
|
||||
QueuedSftpSessionPool pool = new QueuedSftpSessionPool(15, sessionFactory);
|
||||
pool.afterPropertiesSet();
|
||||
synchronizer.setRemotePath(this.getRemoteDirectory());
|
||||
synchronizer.setPool(pool);
|
||||
synchronizer.setAutoCreatePath(this.isAutoCreateDirectories());
|
||||
synchronizer.setShouldDeleteDownloadedRemoteFiles(this.isAutoDeleteRemoteFilesOnSync());
|
||||
|
||||
SftpMessageSource sftpMessageSource = new SftpMessageSource(fileReadingMessageSource, synchronizer);
|
||||
|
||||
sftpMessageSource.setTaskScheduler(taskScheduler);
|
||||
|
||||
if (null != this.trigger) {
|
||||
sftpMessageSource.setTrigger(trigger);
|
||||
}
|
||||
|
||||
sftpMessageSource.setLocalDirectory(this.localDirectoryResource);
|
||||
sftpMessageSource.afterPropertiesSet();
|
||||
sftpMessageSource.start();
|
||||
|
||||
return sftpMessageSource;
|
||||
} catch (Throwable thr) {
|
||||
logger.debug("error occurred when trying to configure SFTPmessageSource ", thr);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.sftp.impl.SftpInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
import org.springframework.integration.sftp.impl.SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
@@ -63,7 +65,8 @@ public class SftpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
private static class SFTPMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( SftpMessageSourceFactoryBean.class.getName());
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter");
|
||||
|
||||
|
||||
@@ -19,12 +19,9 @@ import org.springframework.integration.sftp.SftpSessionFactory;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Provides a single place to handle this tedious chore.
|
||||
*
|
||||
* todo : replace all the ad-hoc definitions of {@link org.springframework.integration.sftp.SftpSessionFactory}
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpSessionUtils {
|
||||
/**
|
||||
@@ -43,7 +40,7 @@ public class SftpSessionUtils {
|
||||
* @throws Exception thrown in case of darned near <em>anything</em>
|
||||
*/
|
||||
public static SftpSessionFactory buildSftpSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port)
|
||||
throws Exception {
|
||||
throws Exception {
|
||||
SftpSessionFactory sftpSessionFactory = new SftpSessionFactory();
|
||||
sftpSessionFactory.setPassword(pw);
|
||||
sftpSessionFactory.setPort(port);
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.springframework.integration.sftp.impl;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer;
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
import org.springframework.integration.sftp.SftpSession;
|
||||
import org.springframework.integration.sftp.SftpSessionPool;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* This handles the synchronization between a remote SFTP endpoint and a local mount
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer<ChannelSftp.LsEntry> {
|
||||
/**
|
||||
* the path on the remote mount
|
||||
*/
|
||||
private volatile String remotePath;
|
||||
|
||||
/**
|
||||
* the pool of {@link org.springframework.integration.sftp.SftpSessionPool} SFTP sessions
|
||||
*/
|
||||
private volatile SftpSessionPool clientPool;
|
||||
|
||||
public void setRemotePath(String remotePath) {
|
||||
this.remotePath = remotePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "'clientPool' can't be null");
|
||||
Assert.notNull(this.remotePath, "'remotePath' can't be null");
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
@Required
|
||||
public void setClientPool(SftpSessionPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ignored")
|
||||
private boolean copyFromRemoteToLocalDirectory(SftpSession sftpSession, ChannelSftp.LsEntry entry, Resource localDir)
|
||||
throws Exception {
|
||||
File fileForLocalDir = localDir.getFile();
|
||||
|
||||
File localFile = new File(fileForLocalDir, entry.getFilename());
|
||||
|
||||
if (!localFile.exists()) {
|
||||
InputStream in = null;
|
||||
FileOutputStream fileOutputStream = null;
|
||||
|
||||
try {
|
||||
File tmpLocalTarget = new File(localFile.getAbsolutePath() +
|
||||
AbstractInboundRemoteFileSystemSynchronizingMessageSource.INCOMPLETE_EXTENSION);
|
||||
|
||||
fileOutputStream = new FileOutputStream(tmpLocalTarget);
|
||||
|
||||
String remoteFqPath = this.remotePath + "/" + entry.getFilename();
|
||||
in = sftpSession.getChannel().get(remoteFqPath);
|
||||
IOUtils.copy(in, fileOutputStream);
|
||||
|
||||
if (tmpLocalTarget.renameTo(localFile)) {
|
||||
// last step
|
||||
this.acknowledge(sftpSession, entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(fileOutputStream);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void syncRemoteToLocalFileSystem() throws Exception {
|
||||
SftpSession session = null;
|
||||
|
||||
try {
|
||||
session = clientPool.getSession();
|
||||
session.start();
|
||||
|
||||
ChannelSftp channelSftp = session.getChannel();
|
||||
Collection<ChannelSftp.LsEntry> beforeFilter = channelSftp.ls(remotePath);
|
||||
ChannelSftp.LsEntry[] entries = (beforeFilter == null) ? new ChannelSftp.LsEntry[0] : beforeFilter.toArray(new ChannelSftp.LsEntry[beforeFilter.size()]);
|
||||
Collection<ChannelSftp.LsEntry> files = this.filter.filterEntries(entries);
|
||||
|
||||
for (ChannelSftp.LsEntry lsEntry : files) {
|
||||
if ((lsEntry != null) && !lsEntry.getAttrs().isDir() && !lsEntry.getAttrs().isLink()) {
|
||||
copyFromRemoteToLocalDirectory(session, lsEntry, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("couldn't synchronize remote to local directory", e);
|
||||
} finally {
|
||||
if ((session != null) && (clientPool != null)) {
|
||||
clientPool.release(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trigger getTrigger() {
|
||||
return new PeriodicTrigger(10 * 1000);
|
||||
}
|
||||
|
||||
class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<ChannelSftp.LsEntry> {
|
||||
public void acknowledge(Object useful, ChannelSftp.LsEntry msg)
|
||||
throws Exception {
|
||||
SftpSession sftpSession = (SftpSession) useful;
|
||||
|
||||
String remoteFqPath = remotePath + "/" + msg.getFilename();
|
||||
|
||||
sftpSession.getChannel().rm(remoteFqPath);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("deleted " + msg.getFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.springframework.integration.sftp.impl;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
import org.springframework.integration.sftp.SftpSession;
|
||||
import org.springframework.integration.sftp.SftpSessionPool;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* a {@link org.springframework.integration.core.MessageSource} implementation for SFTP
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpInboundRemoteFileSystemSynchronizingMessageSource extends AbstractInboundRemoteFileSystemSynchronizingMessageSource<ChannelSftp.LsEntry, SftpInboundRemoteFileSystemSynchronizer> {
|
||||
/**
|
||||
* the pool of sessions
|
||||
*/
|
||||
private volatile SftpSessionPool clientPool;
|
||||
|
||||
|
||||
/**
|
||||
* the remote path on teh server
|
||||
*/
|
||||
private volatile String remotePath;
|
||||
|
||||
public void setClientPool(SftpSessionPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
public void setRemotePath(String remotePath) {
|
||||
this.remotePath = remotePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
this.synchronizer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() {
|
||||
this.synchronizer.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* there be dragons this way ... This method will check to ensure that the remote directory exists. If the directory
|
||||
* doesnt exist, and autoCreatePath is 'true,' then this method makes a few reasonably sane attempts
|
||||
* to create it. Otherwise, it fails fast.
|
||||
*
|
||||
* @param remotePath the path on the remote SSH / SFTP server to create.
|
||||
* @return whether or not the directory is there (regardless of whether we created it in this method or it already
|
||||
* existed.)
|
||||
*/
|
||||
private boolean checkThatRemotePathExists(String remotePath) {
|
||||
SftpSession session = null;
|
||||
ChannelSftp channelSftp = null;
|
||||
|
||||
try {
|
||||
session = this.clientPool.getSession();
|
||||
Assert.state(session != null, "session as returned from the pool should not be null. " + "If it is, it is most likely an error in the pool implementation. ");
|
||||
session.start();
|
||||
channelSftp = session.getChannel();
|
||||
|
||||
SftpATTRS attrs = channelSftp.stat(remotePath);
|
||||
assert (attrs != null) && attrs.isDir() : "attrs can't be null, and should indicate that it's a directory!";
|
||||
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
if (this.autoCreateDirectories && (this.clientPool != null) && (session != null)) {
|
||||
try {
|
||||
if (channelSftp != null) {
|
||||
channelSftp.mkdir(remotePath);
|
||||
|
||||
if (channelSftp.stat(remotePath).isDir()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if ((clientPool != null) && (session != null)) {
|
||||
clientPool.release(session);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
|
||||
this.checkThatRemotePathExists(this.remotePath);
|
||||
this.synchronizer.setClientPool(this.clientPool);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package org.springframework.integration.sftp.impl;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
import org.springframework.integration.sftp.QueuedSftpSessionPool;
|
||||
import org.springframework.integration.sftp.SftpEntryNamer;
|
||||
import org.springframework.integration.sftp.SftpSessionFactory;
|
||||
import org.springframework.integration.sftp.config.SftpSessionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* a factory bean to hide the fairly complex configuration possibilities for an SFTP endpoint
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends AbstractFactoryBean<SftpInboundRemoteFileSystemSynchronizingMessageSource> implements ResourceLoaderAware {
|
||||
/**
|
||||
* injected by the container
|
||||
*/
|
||||
private volatile ResourceLoader resourceLoader;
|
||||
private volatile Resource localDirectoryResource;
|
||||
private volatile String localDirectoryPath;
|
||||
private volatile String autoCreateDirectories;
|
||||
private volatile String autoDeleteRemoteFilesOnSync;
|
||||
private volatile String filenamePattern;
|
||||
private volatile EntryListFilter<ChannelSftp.LsEntry> filter;
|
||||
private int port = 22;
|
||||
private String host;
|
||||
private String keyFile;
|
||||
private String keyFilePassword;
|
||||
private String password;
|
||||
private String remoteDirectory;
|
||||
private String username;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocalDirectoryResource(Resource localDirectoryResource) {
|
||||
this.localDirectoryResource = localDirectoryResource;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocalDirectoryPath(String localDirectoryPath) {
|
||||
this.localDirectoryPath = localDirectoryPath;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoCreateDirectories(String autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) {
|
||||
this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilter(EntryListFilter<ChannelSftp.LsEntry> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setKeyFile(String keyFile) {
|
||||
this.keyFile = keyFile;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setKeyFilePassword(String keyFilePassword) {
|
||||
this.keyFilePassword = keyFilePassword;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SftpInboundRemoteFileSystemSynchronizingMessageSource createInstance()
|
||||
throws Exception {
|
||||
boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories);
|
||||
boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync);
|
||||
|
||||
SftpInboundRemoteFileSystemSynchronizingMessageSource sftpMsgSrc = new SftpInboundRemoteFileSystemSynchronizingMessageSource();
|
||||
sftpMsgSrc.setAutoCreateDirectories(autoCreatDirs);
|
||||
|
||||
// local directories
|
||||
if ((this.localDirectoryResource == null) || !StringUtils.hasText(this.localDirectoryPath)) {
|
||||
File tmp = SystemUtils.getJavaIoTmpDir();
|
||||
File sftpTmp = new File(tmp, "sftpInbound");
|
||||
this.localDirectoryPath = "file://" + sftpTmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
this.localDirectoryResource = this.fromText(localDirectoryPath);
|
||||
|
||||
// remote predicates
|
||||
SftpEntryNamer sftpEntryNamer = new SftpEntryNamer();
|
||||
CompositeEntryListFilter<ChannelSftp.LsEntry> compositeFtpFileListFilter = new CompositeEntryListFilter<ChannelSftp.LsEntry>();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingEntryListFilter<ChannelSftp.LsEntry> ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter<ChannelSftp.LsEntry>(sftpEntryNamer, filenamePattern);
|
||||
compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
this.filter = compositeFtpFileListFilter;
|
||||
|
||||
// pools
|
||||
SftpSessionFactory sessionFactory = SftpSessionUtils.buildSftpSessionFactory(this.host, this.password, this.username, this.keyFile, this.keyFilePassword, this.port);
|
||||
|
||||
QueuedSftpSessionPool pool = new QueuedSftpSessionPool(15, sessionFactory);
|
||||
pool.afterPropertiesSet();
|
||||
|
||||
SftpInboundRemoteFileSystemSynchronizer sftpSync = new SftpInboundRemoteFileSystemSynchronizer();
|
||||
sftpSync.setClientPool(pool);
|
||||
sftpSync.setLocalDirectory(this.localDirectoryResource);
|
||||
sftpSync.setShouldDeleteSourceFile(ackRemoteDir);
|
||||
sftpSync.setFilter(compositeFtpFileListFilter);
|
||||
sftpSync.setBeanFactory(this.getBeanFactory());
|
||||
sftpSync.setRemotePath(this.remoteDirectory);
|
||||
sftpSync.afterPropertiesSet(); // todo is this correct ?
|
||||
sftpSync.start(); //todo
|
||||
|
||||
sftpMsgSrc.setRemotePredicate(compositeFtpFileListFilter);
|
||||
sftpMsgSrc.setSynchronizer(sftpSync);
|
||||
sftpMsgSrc.setClientPool(pool);
|
||||
sftpMsgSrc.setRemotePath(this.remoteDirectory);
|
||||
sftpMsgSrc.setLocalDirectory(this.localDirectoryResource);
|
||||
sftpMsgSrc.setBeanFactory(this.getBeanFactory());
|
||||
sftpMsgSrc.setAutoStartup(true);
|
||||
sftpMsgSrc.afterPropertiesSet();
|
||||
sftpMsgSrc.start();
|
||||
|
||||
return sftpMsgSrc;
|
||||
}
|
||||
|
||||
private Resource fromText(String path) {
|
||||
ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader);
|
||||
resourceEditor.setAsText(path);
|
||||
|
||||
return (Resource) resourceEditor.getValue();
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* This tests the API, more than the end-to-end XML to Java approach.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class TestSftpReceipt {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TestSftpReceipt.class.getName());
|
||||
private SftpSessionFactory sftpSessionFactory;
|
||||
private String host;
|
||||
private String password;
|
||||
private String user;
|
||||
private String privateKeyPath;
|
||||
private String privateKeyPassword;
|
||||
private int port;
|
||||
|
||||
@Before
|
||||
public void before() throws Throwable {
|
||||
this.sftpSessionFactory = buildSFTPSessionFactory(this.host, this.password, this.user, this.privateKeyPath, this.privateKeyPassword, this.port);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceive() throws Throwable {
|
||||
String localMount = SystemUtils.getUserHome() + "/local_mount";
|
||||
String remoteMount = "remote_mount";
|
||||
|
||||
// local path
|
||||
File local = new File(localMount); // obviously this is just for test. Do what you need to do in your own
|
||||
|
||||
// we are testing, after all
|
||||
if (local.exists() && (local.list().length > 0)) {
|
||||
for (File f : local.listFiles()) {
|
||||
if (!f.delete()) {
|
||||
logger.fine("couldn't delete " + f.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resource localDirectory = new FileSystemResource(local);
|
||||
|
||||
// pool
|
||||
QueuedSftpSessionPool queuedSFTPSessionPool = new QueuedSftpSessionPool(sftpSessionFactory);
|
||||
queuedSFTPSessionPool.afterPropertiesSet();
|
||||
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.setPoolSize(10);
|
||||
taskScheduler.setErrorHandler(new ErrorHandler() {
|
||||
public void handleError(Throwable t) {
|
||||
System.out.println("Error occurred: " + ExceptionUtils.getFullStackTrace(t));
|
||||
}
|
||||
});
|
||||
|
||||
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
taskScheduler.initialize();
|
||||
|
||||
// synchronizer
|
||||
final SftpInboundSynchronizer sftpInboundSynchronizer = new SftpInboundSynchronizer();
|
||||
sftpInboundSynchronizer.setLocalDirectory(localDirectory);
|
||||
sftpInboundSynchronizer.setRemotePath(remoteMount);
|
||||
sftpInboundSynchronizer.setAutoCreatePath(true);
|
||||
sftpInboundSynchronizer.setPool(queuedSFTPSessionPool);
|
||||
sftpInboundSynchronizer.setShouldDeleteDownloadedRemoteFiles(false);
|
||||
sftpInboundSynchronizer.setTaskScheduler(taskScheduler);
|
||||
sftpInboundSynchronizer.afterPropertiesSet();
|
||||
sftpInboundSynchronizer.start();
|
||||
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(60 * 1000); // 1 minute
|
||||
|
||||
sftpInboundSynchronizer.stop();
|
||||
} catch (InterruptedException e) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private SftpSessionFactory buildSFTPSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port)
|
||||
throws Throwable {
|
||||
SftpSessionFactory sftpSessionFactory = new SftpSessionFactory();
|
||||
sftpSessionFactory.setPassword(pw);
|
||||
sftpSessionFactory.setPort(port);
|
||||
sftpSessionFactory.setRemoteHost(host);
|
||||
sftpSessionFactory.setUser(usr);
|
||||
sftpSessionFactory.setPrivateKey(pvKey);
|
||||
sftpSessionFactory.setPrivateKeyPassphrase(pvKeyPass);
|
||||
sftpSessionFactory.afterPropertiesSet();
|
||||
|
||||
return sftpSessionFactory;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
|
||||
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp-2.0.xsd">
|
||||
|
||||
|
||||
<context:component-scan base-package="org.springframework.integration.sftp"/>
|
||||
<context:property-placeholder
|
||||
location="file://${user.home}/Desktop/sftp.properties"
|
||||
@@ -49,6 +50,8 @@
|
||||
channel="inboundFilesChannel"
|
||||
filename-pattern=".*?jpg"
|
||||
username="${sftp.username}"
|
||||
auto-create-directories="true"
|
||||
auto-delete-remote-files-on-sync="true"
|
||||
host="${sftp.host}">
|
||||
<poller>
|
||||
<interval-trigger interval="1000" time-unit="MILLISECONDS"/>
|
||||
|
||||
@@ -54,11 +54,11 @@
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="file:${input.directory}"
|
||||
p:filter-ref="compositeFilter"/>
|
||||
<bean id="compositeFilter" class="org.springframework.integration.file.CompositeFileListFilter">
|
||||
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.integration.file.AcceptOnceFileListFilter" />
|
||||
<bean class="org.springframework.integration.file.PatternMatchingFileListFilter">
|
||||
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
|
||||
<bean class="org.springframework.integration.file.filters.PatternMatchingFileListFilter">
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
@@ -225,4 +225,4 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user