From 25b5afa528b4485569c680d003c68f8c21100079 Mon Sep 17 00:00:00 2001 From: Josh Long Date: Fri, 20 Aug 2010 10:06:13 +0000 Subject: [PATCH] initial commit of my work to make building filesystem based adapters more consistent, cleaner. --- .../MessageCountReleaseStrategy.java | 104 ++--- .../dispatcher/OrderedAwareLinkedHashSet.java | 384 ++++++++--------- .../store/AbstractMessageGroupStore.java | 184 ++++---- .../store/MessageGroupCallback.java | 52 +-- .../store/MessageGroupStoreReaper.java | 200 ++++----- .../AnnotationAggregatorTests.java | 160 +++---- ...ggregatorReturningMessageTests-context.xml | 54 +-- .../AutoGeneratedChannelTests-context.xml | 6 +- ...hannelCapacityPlaceholderTests-context.xml | 6 +- .../DispatchingChannelParserTests-context.xml | 2 +- .../config/channelInterceptorParserTests.xml | 8 +- .../channel/config/channelParserTests.xml | 2 +- .../channel/config/channelWithoutId.xml | 6 +- .../config/priorityChannelParserTests.xml | 8 +- .../config/rendezvousChannelParserTests.xml | 6 +- .../channel/directChannelParserTests.xml | 6 +- ...torWithMessageStoreParserTests-context.xml | 46 +- .../ChannelAdapterParserTests-context.xml | 8 +- .../config/FilterParserTests-context.xml | 4 +- .../SelectorChainParserTests-context.xml | 4 +- .../TopLevelSelectorParserTests-context.xml | 4 +- .../config/aggregatorParserTests.xml | 146 +++---- ...notatedEndpointActivationTests-context.xml | 6 +- ...messageParameterAnnotatedEndpointTests.xml | 6 +- ...eActivatorAnnotationPostProcessorTests.xml | 6 +- .../simpleAnnotatedEndpointTests.xml | 6 +- .../typeConvertingEndpointTests.xml | 6 +- .../messageBusWithAsyncEventMulticaster.xml | 6 +- .../config/messageBusWithDefaults.xml | 6 +- .../config/messageBusWithErrorChannel.xml | 6 +- .../config/messageBusWithTaskScheduler.xml | 6 +- ...messageBusWithoutAsyncEventMulticaster.xml | 4 +- .../config/simpleEndpointTests.xml | 8 +- ...derEnricherMethodInvokingTests-context.xml | 6 +- .../xml/HeaderFilterParserTests-context.xml | 6 +- .../xml/NestedChainParserTests-context.xml | 6 +- ...ToStringTransformerParserTests-context.xml | 6 +- ...ializingTransformerParserTests-context.xml | 6 +- ...ializingTransformerParserTests-context.xml | 6 +- .../config/xml/defaultPollerWithId.xml | 6 +- .../config/xml/defaultPollerWithoutId.xml | 6 +- .../config/xml/gatewayParserTests.xml | 6 +- .../config/xml/multipleDefaultPollers.xml | 6 +- .../config/xml/pollerWithAdviceChain.xml | 6 +- .../config/xml/pollerWithReceiveTimeout.xml | 6 +- .../config/xml/topLevelPollerWithoutId.xml | 4 +- .../dispatcher/propagationMandatoryTests.xml | 8 +- .../propagationNotSupportedTests.xml | 8 +- .../dispatcher/propagationRequiredTests.xml | 8 +- .../propagationRequiresNewTests.xml | 8 +- .../dispatcher/propagationSupportsTests.xml | 8 +- .../dispatcher/transactionTests.xml | 8 +- .../pollingEndpointErrorHandlingTests.xml | 4 +- .../endpoint/returnAddressTests.xml | 8 +- .../integration/filter/filterContextTests.xml | 8 +- .../integration/gateway/gatewayAutowiring.xml | 2 +- .../gateway/gatewayWithRendezvousChannel.xml | 6 +- .../gateway/gatewayWithResponseCorrelator.xml | 6 +- .../router/config/routerParserTests.xml | 6 +- .../router/config/splitterAggregatorTests.xml | 6 +- .../router/config/splitterParserTests.xml | 6 +- .../store/MessageStoreReaperTests-context.xml | 52 +-- .../transformer/transformerContextTests.xml | 8 +- ...actInboundRemoteFileSystemSychronizer.java | 148 +++++++ ...eFileSystemSynchronizingMessageSource.java | 112 +++++ .../file/DefaultDirectoryScanner.java | 21 +- .../integration/file/DirectoryScanner.java | 7 +- .../file/FileReadingMessageSource.java | 62 +-- .../file/HeadDirectoryScanner.java | 10 +- .../config/FileListFilterFactoryBean.java | 166 ++++---- .../FileReadingMessageSourceFactoryBean.java | 10 +- .../file/entries/AbstractEntryListFilter.java | 20 +- .../entries/AcceptAllEntryListFilter.java | 32 ++ .../AcceptOnceEntryFileListFilter.java | 17 +- .../entries/CompositeEntryListFilter.java | 36 +- .../file/entries/EntryListFilter.java | 17 +- .../integration/file/entries/EntryNamer.java | 15 + .../file/entries/FileEntryNamer.java | 34 ++ .../PatternMatchingEntryListFilter.java | 24 +- .../SingleEntryAdaptingEntryListFilter.java | 46 ++ .../{ => filters}/AbstractFileListFilter.java | 2 +- .../AcceptOnceFileListFilter.java | 6 +- .../CompositeFileListFilter.java | 7 +- .../file/{ => filters}/FileListFilter.java | 4 +- .../PatternMatchingFileListFilter.java | 3 +- .../locking/AbstractFileLockerFilter.java | 11 +- .../AbstractEventDrivenFileMonitor.java | 261 ++++++++++++ .../DirectedEventDrivenFileMonitor.java | 15 + .../monitors/EventDrivenDirectoryMonitor.java | 21 + .../file/monitors/FileAdditionListener.java | 19 + .../MessageSendingFileAdditionListener.java | 47 ++ .../config/spring-integration-file-1.0.xsd | 2 +- .../config/spring-integration-file-2.0.xsd | 2 +- .../file/CompositeFileListFilterTests.java | 7 +- ...gMessageSourceIntegrationTests-context.xml | 65 ++- ...eReadingMessageSourceIntegrationTests.java | 12 +- .../FileToChannelIntegrationTests-context.xml | 66 +-- .../PatternMatchingFileListFilterTests.java | 2 + .../integration/file/TestFileListFilter.java | 12 +- ...boundChannelAdapterParserTests-context.xml | 10 +- .../FileInboundChannelAdapterParserTests.java | 9 +- ...dChannelAdapterWithPatternParserTests.java | 26 +- ...AdapterWithPreventDuplicatesFlagTests.java | 74 ++-- ...annelAdapterWithQueueSizeTests-context.xml | 5 +- .../FileListFilterFactoryBeanTests.java | 55 +-- ...alidPatternMatchingFileListFilterTests.xml | 2 +- .../locking/FileLockingNamespaceTests.java | 7 +- .../file/locking/NioFileLockerTests.java | 14 +- .../patternMatchingFileListFilterTests.xml | 2 +- spring-integration-file/template.mf | 2 + .../ftp/AbstractFtpFileListFilter.java | 33 -- .../ftp/CompositeFtpFileListFilter.java | 37 -- .../integration/ftp/FtpFileEntryNamer.java | 17 + .../integration/ftp/FtpFileListFilter.java | 14 - .../integration/ftp/FtpFileSource.java | 16 +- ...ean.java => FtpFileSourceFactoryBean.java} | 29 +- .../ftp/FtpInboundSynchronizer.java | 15 +- .../ftp/PatternMatchingFtpFileListFilter.java | 51 --- .../ftp/config/FtpNamespaceHandler.java | 13 +- ...tpInboundRemoteFileSystemSynchronizer.java | 126 ++++++ ...eFileSystemSynchronizingMessageSource.java | 38 ++ ...SynchronizingMessageSourceFactoryBean.java | 170 ++++++++ .../ftp/config/spring-integration-ftp-2.0.xsd | 2 +- .../ftp/InboundFtpFileServiceActivator.java | 1 + .../test/resources/inbound-ftp-context.xml | 2 +- .../jdbc/config/JdbcMessageHandlerParser.java | 144 +++---- .../src/main/sql/sqlserver.properties | 22 +- .../src/main/sql/sybase.properties | 24 +- .../jdbc/JdbcMessageStoreTests-context.xml | 52 +-- .../jdbc/config/defaultJdbcMessageStore.xml | 28 +- .../config/jdbcOperationsJdbcMessageStore.xml | 36 +- .../jdbc/config/soupedUpJdbcMessageStore.xml | 32 +- .../mailToStringTransformerParserTests.xml | 2 +- .../mailToStringTransformerWithinChain.xml | 2 +- .../SecuredChannelsParserTests-context.xml | 4 +- .../sftp/AbstractSftpFileListFilter.java | 4 +- .../sftp/CompositeFtpFileListFilter.java | 4 +- .../PatternMatchingSftpFileListFilter.java | 4 +- .../integration/sftp/SftpEntryNamer.java | 16 + .../integration/sftp/SftpFileListFilter.java | 2 +- .../sftp/SftpInboundSynchronizer.java | 2 +- .../integration/sftp/SftpMessageSource.java | 26 +- .../sftp/config/SftpNamespaceHandler.java | 5 +- ...tpInboundRemoteFileSystemSynchronizer.java | 141 ++++++ ...eFileSystemSynchronizingMessageSource.java | 101 +++++ ...SynchronizingMessageSourceFactoryBean.java | 195 +++++++++ .../integration/sftp/TestSftpReceipt.java | 4 +- .../src/test/resources/TestInboundSftp.xml | 3 + src/docbkx/file.xml | 8 +- src/main/resources/license.txt | 402 +++++++++--------- src/main/resources/notice.txt | 42 +- 151 files changed, 3351 insertions(+), 1767 deletions(-) create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java rename spring-integration-file/src/main/java/org/springframework/integration/file/{ => filters}/AbstractFileListFilter.java (96%) rename spring-integration-file/src/main/java/org/springframework/integration/file/{ => filters}/AcceptOnceFileListFilter.java (92%) rename spring-integration-file/src/main/java/org/springframework/integration/file/{ => filters}/CompositeFileListFilter.java (96%) rename spring-integration-file/src/main/java/org/springframework/integration/file/{ => filters}/FileListFilter.java (92%) rename spring-integration-file/src/main/java/org/springframework/integration/file/{ => filters}/PatternMatchingFileListFilter.java (95%) create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/monitors/AbstractEventDrivenFileMonitor.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/monitors/DirectedEventDrivenFileMonitor.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/monitors/EventDrivenDirectoryMonitor.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/monitors/FileAdditionListener.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/monitors/MessageSendingFileAdditionListener.java delete mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpFileListFilter.java delete mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/CompositeFtpFileListFilter.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java delete mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileListFilter.java rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{FtpMessageSourceFactoryBean.java => FtpFileSourceFactoryBean.java} (85%) delete mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/PatternMatchingFtpFileListFilter.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java create mode 100644 spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpEntryNamer.java create mode 100644 spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java create mode 100644 spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java create mode 100644 spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MessageCountReleaseStrategy.java b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MessageCountReleaseStrategy.java index 7f75aa063c..a195cdf713 100755 --- a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MessageCountReleaseStrategy.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MessageCountReleaseStrategy.java @@ -1,52 +1,52 @@ -/* - * 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.aggregator; - -import org.springframework.integration.store.MessageGroup; - -/** - * A {@link ReleaseStrategy} that releases only the first n messages, where n is a threshold. - * - * @author Dave Syer - * - */ -public class MessageCountReleaseStrategy implements ReleaseStrategy { - - private final int threshold; - - /** - * @param threshold the number of messages to accept before releasing - */ - public MessageCountReleaseStrategy(int threshold) { - super(); - this.threshold = threshold; - } - - /** - * Convenient constructor is only one message is required (threshold=1). - */ - public MessageCountReleaseStrategy() { - this(1); - } - - /** - * Release the group if it has more messages than the threshold and has not previously been released. Previous - * releases leave an imprint on the group in the form of marked messages. It is possible that more messages than the - * threshold could be released, but only if multiple consumers receive messages from the same group concurrently. - */ - public boolean canRelease(MessageGroup group) { - return group.size() >= threshold && group.getMarked().size() == 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.aggregator; + +import org.springframework.integration.store.MessageGroup; + +/** + * A {@link ReleaseStrategy} that releases only the first n messages, where n is a threshold. + * + * @author Dave Syer + * + */ +public class MessageCountReleaseStrategy implements ReleaseStrategy { + + private final int threshold; + + /** + * @param threshold the number of messages to accept before releasing + */ + public MessageCountReleaseStrategy(int threshold) { + super(); + this.threshold = threshold; + } + + /** + * Convenient constructor is only one message is required (threshold=1). + */ + public MessageCountReleaseStrategy() { + this(1); + } + + /** + * Release the group if it has more messages than the threshold and has not previously been released. Previous + * releases leave an imprint on the group in the form of marked messages. It is possible that more messages than the + * threshold could be released, but only if multiple consumers receive messages from the same group concurrently. + */ + public boolean canRelease(MessageGroup group) { + return group.size() >= threshold && group.getMarked().size() == 0; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java index 5e289c3a50..cd76ac8134 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java @@ -1,192 +1,192 @@ -/* - * Copyright 2002-2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.integration.dispatcher; - -import java.util.Collection; -import java.util.Comparator; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; - -import org.springframework.core.OrderComparator; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * Special Set that maintains the following semantics: - * All elements that are un-ordered (do not implement {@link Ordered} interface or annotated - * {@link Order} annotation) will be stored in the order in which they were added, maintaining the - * semantics of the {@link LinkedHashSet}. However, for all {@link Ordered} elements a - * {@link Comparator} (instantiated by default) for this implementation of {@link Set}, will be - * used. Those elements will have precedence over un-ordered elements. If elements have the same - * order but themselves do not equal to one another the more recent addition will be placed to the - * right of (appended next to) the existing element with the same order, thus preserving the order - * of the insertion and maintaining {@link LinkedHashSet} semantics for the un-ordered elements. - *

- * The class is package-protected and only intended for use by the AbstractDispatcher. It - * must enforce safe concurrent access for all usage by the dispatcher. - * - * @author Oleg Zhurakousky - * @author Mark Fisher - * @since 1.0.3 - */ -@SuppressWarnings({"unchecked", "serial"}) -class OrderedAwareLinkedHashSet extends LinkedHashSet { - - private final OrderComparator comparator = new OrderComparator(); - - private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); - - private final ReadLock readLock = rwl.readLock(); - - private final WriteLock writeLock = rwl.writeLock(); - - - /** - * Every time an Ordered element is added via this method this - * Set will be re-sorted, otherwise the element is simply added - * to the end. Added element must not be null. - */ - public boolean add(E o) { - Assert.notNull(o,"Can not add NULL object"); - writeLock.lock(); - try { - boolean present = false; - if (o instanceof Ordered){ - present = this.addOrderedElement((Ordered) o); - } - else { - present = super.add(o); - } - return present; - } - finally { - writeLock.unlock(); - } - } - - /** - * Adds all elements in this Collection. - */ - public boolean addAll(Collection c) { - Assert.notNull(c,"Can not merge with NULL set"); - writeLock.lock(); - try { - for (E object : c) { - this.add(object); - } - return true; - } - finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - public boolean remove(Object o) { - writeLock.lock(); - try { - return super.remove(o); - } - finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - public boolean removeAll(Collection c){ - if (CollectionUtils.isEmpty(c)){ - return false; - } - writeLock.lock(); - try { - return super.removeAll(c); - } - finally { - writeLock.unlock(); - } - } - - @Override - public T[] toArray(T[] a) { - readLock.lock(); - try { - return super.toArray(a); - } - finally { - readLock.unlock(); - } - } - - @Override - public String toString() { - readLock.lock(); - try { - return StringUtils.collectionToCommaDelimitedString(this); - } - finally { - readLock.unlock(); - } - } - - private boolean addOrderedElement(Ordered adding) { - boolean added = false; - E[] tempUnorderedElements = (E[]) this.toArray(); - if (super.contains(adding)) { - return false; - } - super.clear(); - - if (tempUnorderedElements.length == 0) { - added = super.add((E) adding); - } - else { - Set tempSet = new LinkedHashSet(); - for (E current : tempUnorderedElements) { - if (current instanceof Ordered) { - if (this.comparator.compare(adding, current) < 0) { - added = super.add((E) adding); - super.add(current); - } - else { - super.add(current); - } - } - else { - tempSet.add(current); - } - } - if (!added) { - added = super.add((E) adding); - } - for (Object object : tempSet) { - super.add((E) object); - } - } - return added; - } - -} +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.dispatcher; + +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.springframework.core.OrderComparator; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/** + * Special Set that maintains the following semantics: + * All elements that are un-ordered (do not implement {@link Ordered} interface or annotated + * {@link Order} annotation) will be stored in the order in which they were added, maintaining the + * semantics of the {@link LinkedHashSet}. However, for all {@link Ordered} elements a + * {@link Comparator} (instantiated by default) for this implementation of {@link Set}, will be + * used. Those elements will have precedence over un-ordered elements. If elements have the same + * order but themselves do not equal to one another the more recent addition will be placed to the + * right of (appended next to) the existing element with the same order, thus preserving the order + * of the insertion and maintaining {@link LinkedHashSet} semantics for the un-ordered elements. + *

+ * The class is package-protected and only intended for use by the AbstractDispatcher. It + * must enforce safe concurrent access for all usage by the dispatcher. + * + * @author Oleg Zhurakousky + * @author Mark Fisher + * @since 1.0.3 + */ +@SuppressWarnings({"unchecked", "serial"}) +class OrderedAwareLinkedHashSet extends LinkedHashSet { + + private final OrderComparator comparator = new OrderComparator(); + + private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + + private final ReadLock readLock = rwl.readLock(); + + private final WriteLock writeLock = rwl.writeLock(); + + + /** + * Every time an Ordered element is added via this method this + * Set will be re-sorted, otherwise the element is simply added + * to the end. Added element must not be null. + */ + public boolean add(E o) { + Assert.notNull(o,"Can not add NULL object"); + writeLock.lock(); + try { + boolean present = false; + if (o instanceof Ordered){ + present = this.addOrderedElement((Ordered) o); + } + else { + present = super.add(o); + } + return present; + } + finally { + writeLock.unlock(); + } + } + + /** + * Adds all elements in this Collection. + */ + public boolean addAll(Collection c) { + Assert.notNull(c,"Can not merge with NULL set"); + writeLock.lock(); + try { + for (E object : c) { + this.add(object); + } + return true; + } + finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + public boolean remove(Object o) { + writeLock.lock(); + try { + return super.remove(o); + } + finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + public boolean removeAll(Collection c){ + if (CollectionUtils.isEmpty(c)){ + return false; + } + writeLock.lock(); + try { + return super.removeAll(c); + } + finally { + writeLock.unlock(); + } + } + + @Override + public T[] toArray(T[] a) { + readLock.lock(); + try { + return super.toArray(a); + } + finally { + readLock.unlock(); + } + } + + @Override + public String toString() { + readLock.lock(); + try { + return StringUtils.collectionToCommaDelimitedString(this); + } + finally { + readLock.unlock(); + } + } + + private boolean addOrderedElement(Ordered adding) { + boolean added = false; + E[] tempUnorderedElements = (E[]) this.toArray(); + if (super.contains(adding)) { + return false; + } + super.clear(); + + if (tempUnorderedElements.length == 0) { + added = super.add((E) adding); + } + else { + Set tempSet = new LinkedHashSet(); + for (E current : tempUnorderedElements) { + if (current instanceof Ordered) { + if (this.comparator.compare(adding, current) < 0) { + added = super.add((E) adding); + super.add(current); + } + else { + super.add(current); + } + } + else { + tempSet.add(current); + } + } + if (!added) { + added = super.add((E) adding); + } + for (Object object : tempSet) { + super.add((E) object); + } + } + return added; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java index c307cb8ff8..5733373851 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java @@ -1,93 +1,93 @@ -/* - * 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.store; - -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashSet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * @author Dave Syer - * - * @since 2.0 - * - */ -public abstract class AbstractMessageGroupStore implements MessageGroupStore, Iterable { - - protected final Log logger = LogFactory.getLog(getClass()); - - private Collection expiryCallbacks = new LinkedHashSet(); - - /** - * - */ - public AbstractMessageGroupStore() { - super(); - } - - /** - * Convenient injection point for expiry callbacks in the message store. Each of the callbacks provided will simply - * be registered with the store using {@link #registerMessageGroupExpiryCallback(MessageGroupCallback)}. - * - * @param expiryCallbacks the expiry callbacks to add - */ - public void setExpiryCallbacks(Collection expiryCallbacks) { - for (MessageGroupCallback callback : expiryCallbacks) { - registerMessageGroupExpiryCallback(callback); - } - } - - public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) { - expiryCallbacks.add(callback); - } - - public int expireMessageGroups(long timeout) { - int count = 0; - long threshold = System.currentTimeMillis() - timeout; - for (MessageGroup group : this) { - if (group.getTimestamp() < threshold) { - count++; - expire(group); - } - } - return count; - } - - public abstract Iterator iterator(); - - private void expire(MessageGroup group) { - - RuntimeException exception = null; - - for (MessageGroupCallback callback : expiryCallbacks) { - try { - callback.execute(this, group); - } catch (RuntimeException e) { - if (exception == null) { - exception = e; - } - logger.error("Exception in expiry callback", e); - } - } - - if (exception != null) { - throw exception; - } - - } - +/* + * 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.store; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Dave Syer + * + * @since 2.0 + * + */ +public abstract class AbstractMessageGroupStore implements MessageGroupStore, Iterable { + + protected final Log logger = LogFactory.getLog(getClass()); + + private Collection expiryCallbacks = new LinkedHashSet(); + + /** + * + */ + public AbstractMessageGroupStore() { + super(); + } + + /** + * Convenient injection point for expiry callbacks in the message store. Each of the callbacks provided will simply + * be registered with the store using {@link #registerMessageGroupExpiryCallback(MessageGroupCallback)}. + * + * @param expiryCallbacks the expiry callbacks to add + */ + public void setExpiryCallbacks(Collection expiryCallbacks) { + for (MessageGroupCallback callback : expiryCallbacks) { + registerMessageGroupExpiryCallback(callback); + } + } + + public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) { + expiryCallbacks.add(callback); + } + + public int expireMessageGroups(long timeout) { + int count = 0; + long threshold = System.currentTimeMillis() - timeout; + for (MessageGroup group : this) { + if (group.getTimestamp() < threshold) { + count++; + expire(group); + } + } + return count; + } + + public abstract Iterator iterator(); + + private void expire(MessageGroup group) { + + RuntimeException exception = null; + + for (MessageGroupCallback callback : expiryCallbacks) { + try { + callback.execute(this, group); + } catch (RuntimeException e) { + if (exception == null) { + exception = e; + } + logger.error("Exception in expiry callback", e); + } + } + + if (exception != null) { + throw exception; + } + + } + } \ No newline at end of file diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupCallback.java b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupCallback.java index 02cfc158d2..672dfd714d 100755 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupCallback.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupCallback.java @@ -1,26 +1,26 @@ -/* - * 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.store; - -/** - * @author Dave Syer - * - * @since 2.0 - * - */ -public interface MessageGroupCallback { - - void execute(MessageGroupStore messageGroupStore, MessageGroup group); - -} +/* + * 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.store; + +/** + * @author Dave Syer + * + * @since 2.0 + * + */ +public interface MessageGroupCallback { + + void execute(MessageGroupStore messageGroupStore, MessageGroup group); + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java index 51a613903a..4ee8c7ad0d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java @@ -1,100 +1,100 @@ -/* - * 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.store; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.util.Assert; - -/** - * Convenient configurable component to allow explicit timed expiry of {@link MessageGroup} instances in a - * {@link MessageGroupStore}. This component provides a no-args {@link #run()} method that is useful for remote or timed - * execution and a {@link #destroy()} method that can optionally be called on shutdown. - * - * @author Dave Syer - * - */ -public class MessageGroupStoreReaper implements Runnable, DisposableBean, InitializingBean { - - private static Log logger = LogFactory.getLog(MessageGroupStoreReaper.class); - - private MessageGroupStore messageGroupStore; - private boolean expireOnDestroy = false; - private long timeout = -1; - - public MessageGroupStoreReaper(MessageGroupStore messageGroupStore) { - this.messageGroupStore = messageGroupStore; - } - - public MessageGroupStoreReaper() { - } - - /** - * Flag to indicate that the stores should be expired when this component is destroyed (i.e. usuually when its - * enclosing {@link ApplicationContext} is closed). - * - * @param expireOnDestroy the flag value to set - */ - public void setExpireOnDestroy(boolean expireOnDestroy) { - this.expireOnDestroy = expireOnDestroy; - } - - /** - * Timeout in milliseconds (default -1). If negative then no groups ever time out. If greater than zero then all - * groups older than that value are expired when this component is {@link #run()}. - * - * @param timeout the timeout to set - */ - public void setTimeout(long timeout) { - this.timeout = timeout; - } - - /** - * A message group store to expire according the the other configurations. - * - * @param messageGroupStore the {@link MessageGroupStore} to set - */ - public void setMessageGroupStore(MessageGroupStore messageGroupStore) { - this.messageGroupStore = messageGroupStore; - } - - public void afterPropertiesSet() throws Exception { - Assert.state(messageGroupStore != null, "A MessageGroupStore must be provided"); - } - - public void destroy() throws Exception { - if (expireOnDestroy) { - logger.info("Expiring all messages from message group store: " + messageGroupStore); - messageGroupStore.expireMessageGroups(0); - } - } - - /** - * Expire all message groups older than the {@link #setTimeout(long) timeout} provided. Normally this method would - * be executed by a scheduled task. - */ - public void run() { - if (timeout >= 0) { - if (logger.isDebugEnabled()) { - logger.debug("Expiring all messages older than timeout=" + timeout + " from message group store: " - + messageGroupStore); - } - messageGroupStore.expireMessageGroups(timeout); - } - } - -} +/* + * 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.store; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.util.Assert; + +/** + * Convenient configurable component to allow explicit timed expiry of {@link MessageGroup} instances in a + * {@link MessageGroupStore}. This component provides a no-args {@link #run()} method that is useful for remote or timed + * execution and a {@link #destroy()} method that can optionally be called on shutdown. + * + * @author Dave Syer + * + */ +public class MessageGroupStoreReaper implements Runnable, DisposableBean, InitializingBean { + + private static Log logger = LogFactory.getLog(MessageGroupStoreReaper.class); + + private MessageGroupStore messageGroupStore; + private boolean expireOnDestroy = false; + private long timeout = -1; + + public MessageGroupStoreReaper(MessageGroupStore messageGroupStore) { + this.messageGroupStore = messageGroupStore; + } + + public MessageGroupStoreReaper() { + } + + /** + * Flag to indicate that the stores should be expired when this component is destroyed (i.e. usuually when its + * enclosing {@link ApplicationContext} is closed). + * + * @param expireOnDestroy the flag value to set + */ + public void setExpireOnDestroy(boolean expireOnDestroy) { + this.expireOnDestroy = expireOnDestroy; + } + + /** + * Timeout in milliseconds (default -1). If negative then no groups ever time out. If greater than zero then all + * groups older than that value are expired when this component is {@link #run()}. + * + * @param timeout the timeout to set + */ + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + /** + * A message group store to expire according the the other configurations. + * + * @param messageGroupStore the {@link MessageGroupStore} to set + */ + public void setMessageGroupStore(MessageGroupStore messageGroupStore) { + this.messageGroupStore = messageGroupStore; + } + + public void afterPropertiesSet() throws Exception { + Assert.state(messageGroupStore != null, "A MessageGroupStore must be provided"); + } + + public void destroy() throws Exception { + if (expireOnDestroy) { + logger.info("Expiring all messages from message group store: " + messageGroupStore); + messageGroupStore.expireMessageGroups(0); + } + } + + /** + * Expire all message groups older than the {@link #setTimeout(long) timeout} provided. Normally this method would + * be executed by a scheduled task. + */ + public void run() { + if (timeout >= 0) { + if (logger.isDebugEnabled()) { + logger.debug("Expiring all messages older than timeout=" + timeout + " from message group store: " + + messageGroupStore); + } + messageGroupStore.expireMessageGroups(timeout); + } + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/AnnotationAggregatorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/AnnotationAggregatorTests.java index 522abd3a49..5bd0ae304a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/AnnotationAggregatorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/AnnotationAggregatorTests.java @@ -1,80 +1,80 @@ -/* - * 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.aggregator.integration; - -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.Message; -import org.springframework.integration.annotation.Aggregator; -import org.springframework.integration.annotation.CorrelationStrategy; -import org.springframework.integration.annotation.ReleaseStrategy; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.core.MessageBuilder; -import org.springframework.integration.core.PollableChannel; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Mark Fisher - */ -@ContextConfiguration -@RunWith(SpringJUnit4ClassRunner.class) -public class AnnotationAggregatorTests { - - @Autowired - DirectChannel input; - - @Autowired - PollableChannel output; - - - @Test - public void testAggregationWithAnnotationStrategies() { - input.send(MessageBuilder.withPayload("a").build()); - input.send(MessageBuilder.withPayload("b").build()); - @SuppressWarnings("unchecked") - Message result = (Message) output.receive(); - String payload = result.getPayload(); - assertTrue("Wrong payload: "+payload, payload.contains("Payload=a")); - assertTrue("Wrong payload: "+payload, payload.contains("Payload=b")); - } - - @SuppressWarnings("unused") - private static class TestAggregator { - - @Aggregator - public Message aggregate(final List> messages) { - return MessageBuilder.withPayload(messages.toString()).build(); - } - - @ReleaseStrategy - public boolean release(final List> messages) { - return messages.size()>1; - } - - @CorrelationStrategy - public Object getKey(Message message) { - return "1"; - } - } - -} +/* + * 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.aggregator.integration; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.Aggregator; +import org.springframework.integration.annotation.CorrelationStrategy; +import org.springframework.integration.annotation.ReleaseStrategy; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageBuilder; +import org.springframework.integration.core.PollableChannel; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class AnnotationAggregatorTests { + + @Autowired + DirectChannel input; + + @Autowired + PollableChannel output; + + + @Test + public void testAggregationWithAnnotationStrategies() { + input.send(MessageBuilder.withPayload("a").build()); + input.send(MessageBuilder.withPayload("b").build()); + @SuppressWarnings("unchecked") + Message result = (Message) output.receive(); + String payload = result.getPayload(); + assertTrue("Wrong payload: "+payload, payload.contains("Payload=a")); + assertTrue("Wrong payload: "+payload, payload.contains("Payload=b")); + } + + @SuppressWarnings("unused") + private static class TestAggregator { + + @Aggregator + public Message aggregate(final List> messages) { + return MessageBuilder.withPayload(messages.toString()).build(); + } + + @ReleaseStrategy + public boolean release(final List> messages) { + return messages.size()>1; + } + + @CorrelationStrategy + public Object getKey(Message message) { + return "1"; + } + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/MethodInvokingAggregatorReturningMessageTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/MethodInvokingAggregatorReturningMessageTests-context.xml index 9c0452da3d..82de024d28 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/MethodInvokingAggregatorReturningMessageTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/aggregator/integration/MethodInvokingAggregatorReturningMessageTests-context.xml @@ -1,28 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/AutoGeneratedChannelTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/AutoGeneratedChannelTests-context.xml index 21194ef98c..6d0def4d9e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/AutoGeneratedChannelTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/AutoGeneratedChannelTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/DispatchingChannelParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/DispatchingChannelParserTests-context.xml index c3144d15cf..63c66021bb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/DispatchingChannelParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/DispatchingChannelParserTests-context.xml @@ -7,7 +7,7 @@ http://www.springframework.org/schema/integration/spring-integration.xsd"> - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelInterceptorParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelInterceptorParserTests.xml index 5ff14b01b0..b2230679d5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelInterceptorParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelInterceptorParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -26,5 +26,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelParserTests.xml index 03ae820db5..70c80bd907 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelParserTests.xml @@ -11,7 +11,7 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelWithoutId.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelWithoutId.xml index b3c0852e30..c04607fd46 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelWithoutId.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/channelWithoutId.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/priorityChannelParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/priorityChannelParserTests.xml index fd8d30ab19..9952f4c162 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/priorityChannelParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/priorityChannelParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -21,5 +21,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/rendezvousChannelParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/rendezvousChannelParserTests.xml index 835833d9a4..c2c31f3bba 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/config/rendezvousChannelParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/config/rendezvousChannelParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/directChannelParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/directChannelParserTests.xml index be034c0e12..b32bfb1b30 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/directChannelParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/directChannelParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml index 2e3a2eb1ed..8a2781a4e0 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml @@ -1,23 +1,23 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/ChannelAdapterParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/ChannelAdapterParserTests-context.xml index 9510341478..db85a343eb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/ChannelAdapterParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/ChannelAdapterParserTests-context.xml @@ -1,12 +1,12 @@ - - + @@ -24,5 +24,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml index 79fa610dfc..b2978426dd 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/AnnotatedEndpointActivationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/AnnotatedEndpointActivationTests-context.xml index 054b17cf1e..5eecbc6a13 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/AnnotatedEndpointActivationTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/AnnotatedEndpointActivationTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/messageParameterAnnotatedEndpointTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/messageParameterAnnotatedEndpointTests.xml index da477ddc25..0c0bb408d0 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/messageParameterAnnotatedEndpointTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/messageParameterAnnotatedEndpointTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/serviceActivatorAnnotationPostProcessorTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/serviceActivatorAnnotationPostProcessorTests.xml index 5ccb3386bc..1e9f702254 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/serviceActivatorAnnotationPostProcessorTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/serviceActivatorAnnotationPostProcessorTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/simpleAnnotatedEndpointTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/simpleAnnotatedEndpointTests.xml index 85660a3fad..96984dccc9 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/simpleAnnotatedEndpointTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/simpleAnnotatedEndpointTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/typeConvertingEndpointTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/typeConvertingEndpointTests.xml index 40cc548e3a..16f9abd373 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/typeConvertingEndpointTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/typeConvertingEndpointTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAsyncEventMulticaster.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAsyncEventMulticaster.xml index 8857f305e9..81b670ae7f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAsyncEventMulticaster.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAsyncEventMulticaster.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml index a5cd9c1a0c..4db2000b82 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannel.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannel.xml index 72f8efaa33..a942fc52e0 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannel.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannel.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithTaskScheduler.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithTaskScheduler.xml index 6c8b8c717c..0088cca99d 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithTaskScheduler.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithTaskScheduler.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithoutAsyncEventMulticaster.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithoutAsyncEventMulticaster.xml index f4c10f8738..4db2000b82 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithoutAsyncEventMulticaster.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithoutAsyncEventMulticaster.xml @@ -1,7 +1,7 @@ - - - + @@ -20,5 +20,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml index a86fed97cf..0ba0aa1e18 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd">

diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderFilterParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderFilterParserTests-context.xml index 1c3b26f6e1..ea47fddb25 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderFilterParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderFilterParserTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests-context.xml index 4584cee9be..ac98046813 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToStringTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToStringTransformerParserTests-context.xml index a3d6c75617..20e1f1646e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToStringTransformerParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToStringTransformerParserTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadDeserializingTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadDeserializingTransformerParserTests-context.xml index 8d53ce85ad..fa96090e1b 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadDeserializingTransformerParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadDeserializingTransformerParserTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadSerializingTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadSerializingTransformerParserTests-context.xml index 128f6ea1f5..57c67b7d97 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadSerializingTransformerParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/PayloadSerializingTransformerParserTests-context.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithId.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithId.xml index 0fa35c7858..1525366a5a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithId.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithId.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithoutId.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithoutId.xml index 8b06e0f03c..7d55d492ca 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithoutId.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/defaultPollerWithoutId.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/gatewayParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/gatewayParserTests.xml index c6b64624ae..9deaafe3b3 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/gatewayParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/gatewayParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/multipleDefaultPollers.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/multipleDefaultPollers.xml index 9273edb8c1..e258177cd7 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/multipleDefaultPollers.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/multipleDefaultPollers.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithAdviceChain.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithAdviceChain.xml index 7d6dc1680d..4428d7226c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithAdviceChain.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithAdviceChain.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithReceiveTimeout.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithReceiveTimeout.xml index b8ee2498f3..864b812e57 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithReceiveTimeout.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/pollerWithReceiveTimeout.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/topLevelPollerWithoutId.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/topLevelPollerWithoutId.xml index 18ff48f018..946dc8528c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/topLevelPollerWithoutId.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/topLevelPollerWithoutId.xml @@ -1,7 +1,7 @@ - - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -33,5 +33,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationNotSupportedTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationNotSupportedTests.xml index 5700856390..72c88a03cf 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationNotSupportedTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationNotSupportedTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -29,5 +29,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiredTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiredTests.xml index 6a334d6e45..937ab4083d 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiredTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiredTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -28,6 +28,6 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiresNewTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiresNewTests.xml index f42ad90785..4bd68226fb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiresNewTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationRequiresNewTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -29,5 +29,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationSupportsTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationSupportsTests.xml index f3bb30d005..43f93f3cab 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationSupportsTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/propagationSupportsTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -29,5 +29,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/transactionTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/transactionTests.xml index 845e32d3eb..f5a03ea67b 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/transactionTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/transactionTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -42,5 +42,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/pollingEndpointErrorHandlingTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/pollingEndpointErrorHandlingTests.xml index f49f51b4e1..546ea72356 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/pollingEndpointErrorHandlingTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/pollingEndpointErrorHandlingTests.xml @@ -5,7 +5,7 @@ xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> + http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> @@ -15,6 +15,6 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/returnAddressTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/returnAddressTests.xml index db55873a1d..f15ac7515f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/returnAddressTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/returnAddressTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> @@ -30,5 +30,5 @@ - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/filter/filterContextTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/filter/filterContextTests.xml index bd3edc2350..7f6f573463 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/filter/filterContextTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/filter/filterContextTests.xml @@ -1,12 +1,12 @@ - - + @@ -19,5 +19,5 @@ output-channel="output"/> - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayAutowiring.xml b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayAutowiring.xml index b67629a3c8..e14c1f6844 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayAutowiring.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayAutowiring.xml @@ -7,7 +7,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithRendezvousChannel.xml b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithRendezvousChannel.xml index f607a73934..596456ad47 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithRendezvousChannel.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithRendezvousChannel.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithResponseCorrelator.xml b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithResponseCorrelator.xml index ada93a204f..086a88cd50 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithResponseCorrelator.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/gatewayWithResponseCorrelator.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/config/routerParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/router/config/routerParserTests.xml index 869fef041b..3f640ea451 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/config/routerParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/config/routerParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterAggregatorTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterAggregatorTests.xml index 3950d94cc1..7941af2805 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterAggregatorTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterAggregatorTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterParserTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterParserTests.xml index 190a41014b..c03a7044f3 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterParserTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/config/splitterParserTests.xml @@ -1,11 +1,11 @@ - + http://www.springframework.org/schema/integration/spring-integration.xsd"> diff --git a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml index 8f1299d6d0..a244141c78 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml index 5679865023..39a13fba35 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml @@ -1,12 +1,12 @@ - - + @@ -16,5 +16,5 @@ - + diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java new file mode 100644 index 0000000000..2f4ce15ab4 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java @@ -0,0 +1,148 @@ +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; + + +/** + * This handles a lot of the common ground in our approach for synchronizing a remote file system locally + * + * @author Josh Long + */ +public abstract class AbstractInboundRemoteFileSystemSychronizer extends AbstractEndpoint { + /** + * Should we delete the source file? + * For an FTP server, for example, this would delete the original FTPFile instance + *

+ * At the moment I can simply see this triggering the setting + */ + 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 remote file system view! + */ + protected volatile EntryListFilter filter = new AcceptAllEntryListFilter(); + + /** + * 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 entryAcknowledgmentStrategy; + + /** + * Obviously thread safe - simply provides a NOOP impl so we don't have to keep dancing around NPE's + */ + private EntryAcknowledgmentStrategy noOpEntryAcknowledgmentStrategy = new EntryAcknowledgmentStrategy() { + public void acknowledge(Object o, T msg) { + } + }; + + public void setEntryAcknowledgmentStrategy(EntryAcknowledgmentStrategy entryAcknowledgmentStrategy) { + this.entryAcknowledgmentStrategy = entryAcknowledgmentStrategy; + } + + public void setShouldDeleteSourceFile(boolean shouldDeleteSourceFile) { + this.shouldDeleteSourceFile = shouldDeleteSourceFile; + } + + public void setLocalDirectory(Resource localDirectory) { + this.localDirectory = localDirectory; + } + + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } + + /** + * @param usefulContextOrClientData + * @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 + */ + 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 + */ + 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 + * + * @author Josh Long + * @param the entry type (file, sftp, ftp, ...) + */ + public static interface EntryAcknowledgmentStrategy { + /** + * 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) ; + } + } + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java new file mode 100644 index 0000000000..235b69ecbd --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java @@ -0,0 +1,112 @@ +package org.springframework.integration.file; + +import org.springframework.core.io.FileSystemResource; +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 org.springframework.util.Assert; + +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 + * + * @author Josh Long + */ +public abstract class AbstractInboundRemoteFileSystemSynchronizingMessageSource> extends AbstractEndpoint implements MessageSource { + /** + * 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 syncing up the remote FS + */ + 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 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 remotePredicate) { + this.remotePredicate = remotePredicate; + } + + private EntryListFilter buildFilter() { + FileEntryNamer fileEntryNamer = new FileEntryNamer(); + Pattern completePattern = Pattern.compile("^.*(?(new AcceptOnceEntryFileListFilter(), new PatternMatchingEntryListFilter(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 receive() { + return this.fileSource.receive(); + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java index 27580bbbfa..aff9f362ec 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java @@ -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 filter = new AcceptOnceEntryFileListFilter(); private FileLocker locker; public final List 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 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); } /** diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java index a194998c61..bcd3d69396 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java @@ -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 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 filter); /** diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java index 85d71be275..d20b346079 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java @@ -13,33 +13,38 @@ * 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; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.InitializingBean; + import org.springframework.integration.Message; 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.filters.FileListFilter}. By default, an {@link org.springframework.integration.file.filters.AcceptOnceFileListFilter} is used. It ensures files are * picked up only once from the directory. *

* 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.filters.AcceptOnceFileListFilter} 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.filters.AcceptOnceFileListFilter} would allow for + * this. See {@link org.springframework.integration.file.filters.CompositeFileListFilter} for a way to do this. *

* 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 +56,11 @@ import java.util.concurrent.PriorityBlockingQueue; * @author Iwein Fuld * @author Mark Fisher */ -public class FileReadingMessageSource implements MessageSource, - InitializingBean { - +public class FileReadingMessageSource implements MessageSource, 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 +68,6 @@ public class FileReadingMessageSource implements MessageSource, * There is no locking around the queue, so there is also no iteration. */ private final Queue toBeReceived; - private boolean scanEachPoll = false; /** @@ -92,7 +89,7 @@ public class FileReadingMessageSource implements MessageSource, */ 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,8 +105,7 @@ public class FileReadingMessageSource implements MessageSource, * @param receptionOrderComparator the comparator to be used to order the files in the internal queue */ public FileReadingMessageSource(Comparator receptionOrderComparator) { - toBeReceived = new PriorityBlockingQueue(DEFAULT_INTERNAL_QUEUE_CAPACITY, - receptionOrderComparator); + toBeReceived = new PriorityBlockingQueue(DEFAULT_INTERNAL_QUEUE_CAPACITY, receptionOrderComparator); } /** @@ -137,13 +133,13 @@ public class FileReadingMessageSource implements MessageSource, } /** - * 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.filters.FileListFilter}. By default a {@link org.springframework.integration.file.filters.AcceptOnceFileListFilter} with no bounds is used. In most + * cases a customized {@link org.springframework.integration.file.filters.FileListFilter} will be needed to deal with modification and duplication concerns. If + * multiple filters are required a {@link org.springframework.integration.file.filters.CompositeFileListFilter} can be used to group them together. *

* The supplied filter must be thread safe.. */ - public void setFilter(FileListFilter filter) { + public void setFilter(EntryListFilter filter) { Assert.notNull(filter, "'filter' must not be null"); this.scanner.setFilter(filter); } @@ -175,43 +171,50 @@ public class FileReadingMessageSource implements MessageSource, 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 receive() throws MessagingException { Message 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 filteredFiles = scanner.listFiles(directory); Set freshFiles = new HashSet(filteredFiles); + if (!freshFiles.isEmpty()) { toBeReceived.addAll(freshFiles); + if (logger.isDebugEnabled()) { logger.debug("Added to queue: " + freshFiles); } @@ -225,6 +228,7 @@ public class FileReadingMessageSource implements MessageSource, if (logger.isWarnEnabled()) { logger.warn("Failed to send: " + failedMessage); } + toBeReceived.offer(failedMessage.getPayload()); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java index 7f761687cd..6a3c291b24 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java @@ -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 maxNumberOfFiles 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 { private final int maxNumberOfFiles; public HeadFilter(int maxNumberOfFiles) { this.maxNumberOfFiles = maxNumberOfFiles; } - public List filterFiles(File[] files) { + public List filterEntries(File[] files) { return Arrays.asList(files).subList(0, Math.min(files.length, maxNumberOfFiles)); } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java index fa98a9bc0c..f88ff7a6ff 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java @@ -13,110 +13,114 @@ * 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 { +public class FileListFilterFactoryBean implements FactoryBean> { + private volatile EntryListFilter fileListFilter; + private volatile EntryListFilter filterReference; + private volatile Pattern filenamePattern; + private volatile Boolean preventDuplicates; + private final Object monitor = new Object(); + private volatile Collection> filterReferences; + private FileEntryNamer fileNamer = new FileEntryNamer(); - private volatile FileListFilter fileListFilter; + public void setFilterReferences(Collection> filterReferences) { + this.filterReferences = filterReferences; + } - private volatile FileListFilter filterReference; + public void setFilterReference(EntryListFilter 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 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 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 patternFilter = new PatternMatchingEntryListFilter(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(); + } else { // preventDuplicates is either TRUE or NULL + flf = new AcceptOnceEntryFileListFilter(); + } - 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 flfc = new CompositeEntryListFilter(); + for (EntryListFilter ff : filterReferences) + flfc.addFilter(ff); + + flf = flfc; + } + if( flf== null)flf =new CompositeEntryListFilter(); + + this.fileListFilter = flf; + } + + private CompositeEntryListFilter createCompositeWithAcceptOnceFilter(EntryListFilter otherFilter) { + CompositeEntryListFilter compositeFilter = new CompositeEntryListFilter(); + compositeFilter.addFilter(new AcceptOnceEntryFileListFilter(), otherFilter); + + return compositeFilter; + } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java index 09d5cfab4f..aa0c41b79e 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java @@ -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 filter; private volatile AbstractFileLockerFilter locker; @@ -69,7 +69,7 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean filter) { if (filter instanceof AbstractFileLockerFilter && this.locker == null) { this.setLocker((AbstractFileLockerFilter) filter); } @@ -133,7 +133,7 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean(this.filter, this.locker)); this.source.setLocker(locker); } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AbstractEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AbstractEntryListFilter.java index 3f00a62660..87239b84b2 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AbstractEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AbstractEntryListFilter.java @@ -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 implements EntryListFilter { - 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 implements InitializingBean, EntryListFilter { + public abstract boolean accept(T t); public List filterEntries(T[] entries) { List accepted = new ArrayList(); @@ -37,4 +47,8 @@ public abstract class AbstractEntryListFilter implements EntryListFilter { return accepted; } + + public void afterPropertiesSet() throws Exception { + // its all you! + } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java new file mode 100644 index 0000000000..87cbe8d847 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java @@ -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 + */ +public class AcceptAllEntryListFilter extends AbstractEntryListFilter { + @Override + public boolean accept(T t) { + return true; + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java index 789c8c00a5..cc37ee7197 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java @@ -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}. + *

+ * This implementation is thread safe. + * + * @author Iwein Fuld + * @since 1.0.0 + */ public class AcceptOnceEntryFileListFilter extends AbstractEntryListFilter { private final Queue 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 extends AbstractEntryListFilter this.seen = new LinkedBlockingQueue(); } - protected boolean accept(T pathname) { + public boolean accept(T pathname) { synchronized (this.monitor) { if (seen.contains(pathname)) { return false; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java index 82ecdc00e8..9dd9d8933e 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java @@ -13,32 +13,37 @@ * 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 implements EntryListFilter { - private final Set fileFilters; + private final Set> fileFilters; - public CompositeEntryListFilter(EntryListFilter... fileFilters) { - this.fileFilters = new LinkedHashSet(Arrays.asList(fileFilters)); + public CompositeEntryListFilter(EntryListFilter... fileFilters) { + this.fileFilters = new LinkedHashSet>(Arrays.asList(fileFilters)); } - public CompositeEntryListFilter(Collection fileFilters) { - this.fileFilters = new LinkedHashSet(fileFilters); + public CompositeEntryListFilter(Collection> fileFilters) { + this.fileFilters = new LinkedHashSet>(fileFilters); } @SuppressWarnings("unchecked") public List filterEntries(T[] entries) { Assert.notNull(entries, "'files' should not be null"); - List leftOver = Arrays.asList(entries); - for (EntryListFilter fileFilter : this.fileFilters) { - T[] ts =(T[]) leftOver.toArray(); + + List leftOver = Arrays.asList(entries); + + for (EntryListFilter fileFilter : this.fileFilters) { + T[] ts = (T[]) leftOver.toArray(); leftOver = fileFilter.filterEntries(ts); } + return leftOver; } @@ -47,7 +52,7 @@ public class CompositeEntryListFilter implements EntryListFilter { * @return this CompositeFileFilter instance with the added filters * @see #addFilters(Collection) */ - public CompositeEntryListFilter addFilter(EntryListFilter... filters) { + public CompositeEntryListFilter addFilter(EntryListFilter... filters) { return addFilters(Arrays.asList(filters)); } @@ -59,7 +64,16 @@ public class CompositeEntryListFilter implements EntryListFilter { * @param filtersToAdd a list of filters to add * @return this CompositeEntryListFilter instance with the added filters */ - public CompositeEntryListFilter addFilters(Collection filtersToAdd) { + public CompositeEntryListFilter addFilters(Collection> filtersToAdd) { + for (EntryListFilter elf : filtersToAdd) + if (elf instanceof InitializingBean) { + try { + ((InitializingBean) elf).afterPropertiesSet(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + this.fileFilters.addAll(filtersToAdd); return this; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java index 8ab82e15cc..076fca83a8 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java @@ -18,6 +18,19 @@ package org.springframework.integration.file.entries; import java.util.List; -public interface EntryListFilter { - List filterEntries(T [] entries ); +/** + * Strategy interface for filtering a group of entries / files. + *

+ * {@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}. + *

+ * This implementation is thread safe. + * + * @author Iwein Fuld + * @author Josh Long + * @since 1.0.0 + */ +public interface EntryListFilter { + List filterEntries(T[] entries); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java index 1bc181cb42..fde16e3a34 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java @@ -15,6 +15,21 @@ */ package org.springframework.integration.file.entries; +/** + * Responsible for coercing a String identification out of the {@link T} entry. + * @param the type of entry (there's an implementation for FTP, SFTP, and plain-old java.io.Files) + * + * @author Josh Long + */ public interface EntryNamer { + + /** + * 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); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java new file mode 100644 index 0000000000..23de52ac76 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java @@ -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 { + public String nameOf(File entry) { + return (entry != null) ? entry.getName() : null; + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java index 1d9d604b06..1e29c1d2cb 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java @@ -24,16 +24,29 @@ import java.util.regex.Pattern; /** - * experimental + * experimental + *

+ * 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 the type of entry */ -public abstract class PatternMatchingEntryListFilter extends AbstractEntryListFilter implements InitializingBean { +public class PatternMatchingEntryListFilter extends AbstractEntryListFilter implements InitializingBean { private Pattern pattern; private String patternExpression; private EntryNamer entryNamer; + public PatternMatchingEntryListFilter(EntryNamer en, String p) { + this.entryNamer = en; + this.patternExpression = p; + } + + public PatternMatchingEntryListFilter(EntryNamer 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 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(); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java new file mode 100644 index 0000000000..0842b6e027 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java @@ -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 one argument instea of an array + * + * @author Josh Long + */ +public class SingleEntryAdaptingEntryListFilter extends AbstractEntryListFilter { + + /** + * the {@link org.springframework.integration.file.entries.EntryListFilter} that you'd like to delegate to + */ + private volatile EntryListFilter entryFilter; + + public SingleEntryAdaptingEntryListFilter(EntryListFilter 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; + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/AbstractFileListFilter.java similarity index 96% rename from spring-integration-file/src/main/java/org/springframework/integration/file/AbstractFileListFilter.java rename to spring-integration-file/src/main/java/org/springframework/integration/file/filters/AbstractFileListFilter.java index 23fcda4766..2d05371b8d 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/AbstractFileListFilter.java @@ -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; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AcceptOnceFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/AcceptOnceFileListFilter.java similarity index 92% rename from spring-integration-file/src/main/java/org/springframework/integration/file/AcceptOnceFileListFilter.java rename to spring-integration-file/src/main/java/org/springframework/integration/file/filters/AcceptOnceFileListFilter.java index 576d168726..1abf68d4d1 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/AcceptOnceFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/AcceptOnceFileListFilter.java @@ -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.Queue; @@ -23,13 +23,13 @@ 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}. + * {@link org.springframework.integration.file.FileReadingMessageSource}. *

* This implementation is thread safe. * * @author Iwein Fuld * @since 1.0.0 - */ + */ @Deprecated public class AcceptOnceFileListFilter extends AbstractFileListFilter { private final Queue seen; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/CompositeFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/CompositeFileListFilter.java similarity index 96% rename from spring-integration-file/src/main/java/org/springframework/integration/file/CompositeFileListFilter.java rename to spring-integration-file/src/main/java/org/springframework/integration/file/filters/CompositeFileListFilter.java index a1ea42ee71..b1e876145a 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/CompositeFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/CompositeFileListFilter.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.springframework.integration.file.filters; + +import java.util.Set; -package org.springframework.integration.file; import org.springframework.util.Assert; @@ -22,13 +24,14 @@ 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. * * @author Iwein Fuld * @author Mark Fisher - */ + */ @Deprecated public class CompositeFileListFilter implements FileListFilter { private final Set fileFilters; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/FileListFilter.java similarity index 92% rename from spring-integration-file/src/main/java/org/springframework/integration/file/FileListFilter.java rename to spring-integration-file/src/main/java/org/springframework/integration/file/filters/FileListFilter.java index 615f57b129..3ed8bc7a47 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/FileListFilter.java @@ -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.List; @@ -23,7 +23,7 @@ import java.util.List; * Strategy interface for filtering a group of files. * * @author Iwein Fuld - */ + */ @Deprecated public interface FileListFilter { /** diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/PatternMatchingFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/PatternMatchingFileListFilter.java similarity index 95% rename from spring-integration-file/src/main/java/org/springframework/integration/file/PatternMatchingFileListFilter.java rename to spring-integration-file/src/main/java/org/springframework/integration/file/filters/PatternMatchingFileListFilter.java index 3dac0f7629..786477c454 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/PatternMatchingFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/PatternMatchingFileListFilter.java @@ -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; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/locking/AbstractFileLockerFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/locking/AbstractFileLockerFilter.java index 7804468f75..71fff4aac8 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/locking/AbstractFileLockerFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/locking/AbstractFileLockerFilter.java @@ -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 implements FileLocker { - protected final boolean accept(File file) { - return isLockable(file); + @Override + public boolean accept(File file) { + return this.isLockable(file); } -} \ No newline at end of file +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/AbstractEventDrivenFileMonitor.java b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/AbstractEventDrivenFileMonitor.java new file mode 100644 index 0000000000..4d47ca5186 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/AbstractEventDrivenFileMonitor.java @@ -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. + *

+ * In the meantime, this provides us with a base class for building event driven file adapters quickly. The two cases I see are: + *

+ *

    + *
  1. + *
  2. + *
+ * + * @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 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 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 filter) { + this.filter = new SingleEntryAdaptingEntryListFilter(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(this.maxQueueSize); + + if (this.executor == null) { + this.executor = new SimpleAsyncTaskExecutor(); + } + + if (this.filter == null) { + this.filter = new SingleEntryAdaptingEntryListFilter(new AcceptAllEntryListFilter()); + } + + 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 acceptOnceEntryFileListFilter=new AcceptOnceEntryFileListFilter() ; + acceptOnceEntryFileListFilter.afterPropertiesSet(); + PatternMatchingEntryListFilter patternMatchingEntryListFilter= + new PatternMatchingEntryListFilter(new FileEntryNamer(), ".*?jpg"); + patternMatchingEntryListFilter.afterPropertiesSet(); + + Collection> l=Arrays.asList( acceptOnceEntryFileListFilter, patternMatchingEntryListFilter); + CompositeEntryListFilter compositeEntryListFilter = new CompositeEntryListFilter(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) { + // + } + } + } + }); + } +} +*/ diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/DirectedEventDrivenFileMonitor.java b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/DirectedEventDrivenFileMonitor.java new file mode 100644 index 0000000000..c116762b43 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/DirectedEventDrivenFileMonitor.java @@ -0,0 +1,15 @@ +package org.springframework.integration.file.monitors; + +import java.io.File; + + +/** + * simply takes a hint and publishes an event as appropriate + * + * @author Josh Long + */ +public class DirectedEventDrivenFileMonitor extends AbstractEventDrivenFileMonitor { + public void directlyNotifyOfNewFile(File file) { + this.publishNewFileReceived(file); + } +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/EventDrivenDirectoryMonitor.java b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/EventDrivenDirectoryMonitor.java new file mode 100644 index 0000000000..718486a8f7 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/EventDrivenDirectoryMonitor.java @@ -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; +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/FileAdditionListener.java b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/FileAdditionListener.java new file mode 100644 index 0000000000..f32860346a --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/FileAdditionListener.java @@ -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); +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/MessageSendingFileAdditionListener.java b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/MessageSendingFileAdditionListener.java new file mode 100644 index 0000000000..3ae9b649a7 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/monitors/MessageSendingFileAdditionListener.java @@ -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 fileMsg = MessageBuilder.withPayload(f).build(); + this.messagingTemplate.send(fileMsg); + } +} diff --git a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-1.0.xsd b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-1.0.xsd index c2c89f3aa6..3753122ea1 100644 --- a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-1.0.xsd +++ b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-1.0.xsd @@ -53,7 +53,7 @@ - + diff --git a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.0.xsd b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.0.xsd index cf960a43d2..f00a15167c 100644 --- a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.0.xsd +++ b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.0.xsd @@ -56,7 +56,7 @@ - + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java index e3de58346b..ae44c96b44 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java @@ -26,13 +26,15 @@ import java.util.Arrays; import java.util.List; import org.junit.Test; +import org.springframework.integration.file.filters.CompositeFileListFilter; +import org.springframework.integration.file.filters.FileListFilter; /** * @author Iwein Fuld */ public class CompositeFileListFilterTests { - private FileListFilter fileFilterMock1 = mock(FileListFilter.class); + private FileListFilter fileFilterMock1 = mock( FileListFilter.class); private FileListFilter fileFilterMock2 = mock(FileListFilter.class); @@ -51,7 +53,8 @@ public class CompositeFileListFilterTests { @Test public void forwardedToAddedFilters() throws Exception { - CompositeFileListFilter compositeFileFilter = new CompositeFileListFilter().addFilter(fileFilterMock1, fileFilterMock2); + CompositeFileListFilter compositeFileFilter = new CompositeFileListFilter(); + compositeFileFilter.addFilter(fileFilterMock1, fileFilterMock2); List returnedFiles = Arrays.asList(new File[] { fileMock }); when(fileFilterMock1.filterFiles(isA(File[].class))).thenReturn(returnedFiles); when(fileFilterMock2.filterFiles(isA(File[].class))).thenReturn(returnedFiles); diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests-context.xml index f6c205b022..f0336e1452 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests-context.xml @@ -1,28 +1,49 @@ + 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"> - - + + - - - - - - - - - - - - + - + + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests.java index 44ae3e5845..76fcdc620f 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceIntegrationTests.java @@ -80,7 +80,8 @@ public class FileReadingMessageSourceIntegrationTests { @Test public void getFiles() throws Exception { Message received1 = pollableFileSource.receive(); - assertNotNull("This should return the first message", received1); + System.out.println( "getfiels() round 1"); + assertNotNull("This should return the first message", received1); pollableFileSource.onSend(received1); Message received2 = pollableFileSource.receive(); assertNotNull(received2); @@ -154,12 +155,13 @@ public class FileReadingMessageSourceIntegrationTests { /** * 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 + * @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 todo, final CountDownLatch start) { + 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++) { @@ -174,7 +176,7 @@ public class FileReadingMessageSourceIntegrationTests { catch (InterruptedException e) { Thread.currentThread().interrupt(); } - todo.run(); + runnable.run(); done.countDown(); } }).start(); diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileToChannelIntegrationTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/FileToChannelIntegrationTests-context.xml index a342db3147..0462c2fb0f 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/FileToChannelIntegrationTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileToChannelIntegrationTests-context.xml @@ -1,46 +1,48 @@ + 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"> - - + + - + - - - + + + - - - + + + - - - - - - - - - - - - - + + - + - \ No newline at end of file + + + + + + +
+ + + + + + + + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/PatternMatchingFileListFilterTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/PatternMatchingFileListFilterTests.java index c86e12b9d5..8970b8a484 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/PatternMatchingFileListFilterTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/PatternMatchingFileListFilterTests.java @@ -27,6 +27,8 @@ import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.file.filters.FileListFilter; +import org.springframework.integration.file.filters.PatternMatchingFileListFilter; /** * @author Mark Fisher diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/TestFileListFilter.java b/spring-integration-file/src/test/java/org/springframework/integration/file/TestFileListFilter.java index 88d4ad9d24..4b3b1bb6b8 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/TestFileListFilter.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/TestFileListFilter.java @@ -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 filterFiles(File[] files) { - return Arrays.asList(files); - } - +public class TestFileListFilter implements EntryListFilter { + public List filterEntries(File[] entries) { + return Arrays.asList(entries); + } } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests-context.xml index 1bf152ea65..242960f054 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests-context.xml @@ -20,15 +20,19 @@ - + + + + -
\ No newline at end of file +
diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java index 764b47afb2..bfa36f7f77 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java @@ -32,7 +32,9 @@ 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.entries.AcceptOnceEntryFileListFilter; +import org.springframework.integration.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.filters.CompositeFileListFilter; import org.springframework.integration.file.DefaultDirectoryScanner; import org.springframework.integration.file.FileReadingMessageSource; import org.springframework.test.context.ContextConfiguration; @@ -77,8 +79,9 @@ public class FileInboundChannelAdapterParserTests { 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); + Object filter = scannerAccessor.getPropertyValue("filter"); + assertTrue("'filter' should be set", + filter instanceof AcceptOnceEntryFileListFilter); } @Test diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java index 5bdc482781..dfefdf467d 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java @@ -34,11 +34,15 @@ 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.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.integration.file.filters.AcceptOnceFileListFilter; +import org.springframework.integration.file.filters.*; import org.springframework.integration.file.FileReadingMessageSource; -import org.springframework.integration.file.PatternMatchingFileListFilter; +import org.springframework.integration.file.filters.PatternMatchingFileListFilter; +import org.springframework.integration.file.filters.FileListFilter; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -88,7 +92,7 @@ public class FileInboundChannelAdapterWithPatternParserTests { @Test public void compositeFilterType() { DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner")); - assertTrue(scannerAccessor.getPropertyValue("filter") instanceof CompositeFileListFilter); + assertTrue(scannerAccessor.getPropertyValue("filter") instanceof CompositeEntryListFilter); } @Test @@ -106,11 +110,11 @@ public class FileInboundChannelAdapterWithPatternParserTests { public void acceptOnceFilter() { DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner")); - Set filters = (Set) new DirectFieldAccessor( + Set> filters = (Set>) new DirectFieldAccessor( scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters"); boolean hasAcceptOnceFilter = false; - for (FileListFilter filter : filters) { - if (filter instanceof AcceptOnceFileListFilter) { + for (EntryListFilter filter : filters) { + if (filter instanceof AcceptOnceEntryFileListFilter) { hasAcceptOnceFilter = true; } } @@ -122,11 +126,11 @@ public class FileInboundChannelAdapterWithPatternParserTests { public void patternFilter() { DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner")); - Set filters = (Set) new DirectFieldAccessor( + Set filters = (Set) new DirectFieldAccessor( scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters"); Pattern pattern = null; - for (FileListFilter filter : filters) { - if (filter instanceof PatternMatchingFileListFilter) { + for (EntryListFilter filter : filters) { + if (filter instanceof PatternMatchingEntryListFilter) { pattern = (Pattern) new DirectFieldAccessor(filter).getPropertyValue("pattern"); } } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java index d8b2b677c6..87d96d8922 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java @@ -23,6 +23,14 @@ 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.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.integration.file.filters.AcceptOnceFileListFilter; +import org.springframework.integration.file.filters.CompositeFileListFilter; +import org.springframework.integration.file.filters.FileListFilter; +import org.springframework.integration.file.filters.PatternMatchingFileListFilter; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -50,95 +58,95 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { @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 iterator = filters.iterator(); - assertTrue(iterator.next() instanceof AcceptOnceFileListFilter); - assertTrue(iterator.next() instanceof PatternMatchingFileListFilter); + 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 iterator = filters.iterator(); - assertTrue(iterator.next() instanceof AcceptOnceFileListFilter); - assertTrue(iterator.next() instanceof PatternMatchingFileListFilter); + 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 filter = this.extractFilter("patternAndFalse"); + assertFalse(filter instanceof CompositeEntryListFilter); + assertTrue(filter instanceof PatternMatchingEntryListFilter); } @Test public void defaultAndNull() throws Exception { - FileListFilter filter = this.extractFilter("defaultAndNull"); + EntryListFilter 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 result = filter.filterFiles(files); - assertEquals(1, result.size()); + List result = filter.filterEntries(files); + assertEquals(1 , result.size()); } @Test 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 result = filter.filterFiles(files); + List result = filter.filterEntries(files); assertEquals(1, result.size()); } @Test 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 result = filter.filterFiles(files); + List result = filter.filterEntries(files); assertEquals(3, result.size()); } - private FileListFilter extractFilter(String beanName) { - return (FileListFilter) new DirectFieldAccessor( + private EntryListFilter extractFilter(String beanName) { + return (EntryListFilter) new DirectFieldAccessor( new DirectFieldAccessor( new DirectFieldAccessor(context.getBean(beanName)) .getPropertyValue("source")) diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithQueueSizeTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithQueueSizeTests-context.xml index 62aa45c0e5..333cf41e1b 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithQueueSizeTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithQueueSizeTests-context.xml @@ -35,8 +35,9 @@ + - + @@ -47,4 +48,4 @@ -
\ No newline at end of file +
diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java index be9424c178..23ed447554 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java @@ -18,7 +18,8 @@ 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 org.springframework.integration.file.filters.*; import java.io.File; import java.util.Collection; @@ -45,7 +46,7 @@ public class FileListFilterFactoryBeanTests { FileListFilterFactoryBean factory = new FileListFilterFactoryBean(); TestFilter testFilter = new TestFilter(); factory.setFilterReference(testFilter); - FileListFilter result = factory.getObject(); + EntryListFilter result = factory.getObject(); assertFalse(result instanceof CompositeFileListFilter); assertSame(testFilter, result); } @@ -57,10 +58,10 @@ public class FileListFilterFactoryBeanTests { TestFilter testFilter = new TestFilter(); factory.setFilterReference(testFilter); factory.setPreventDuplicates(Boolean.TRUE); - FileListFilter result = factory.getObject(); - assertTrue(result instanceof CompositeFileListFilter); + EntryListFilter result = factory.getObject(); + assertTrue(result instanceof CompositeEntryListFilter); Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); - assertTrue(filters.iterator().next() instanceof AcceptOnceFileListFilter); + assertTrue(filters.iterator().next() instanceof AcceptOnceEntryFileListFilter); assertTrue(filters.contains(testFilter)); } @@ -70,8 +71,8 @@ public class FileListFilterFactoryBeanTests { TestFilter testFilter = new TestFilter(); factory.setFilterReference(testFilter); factory.setPreventDuplicates(Boolean.FALSE); - FileListFilter result = factory.getObject(); - assertFalse(result instanceof CompositeFileListFilter); + EntryListFilter result = factory.getObject(); + assertFalse(result instanceof CompositeEntryListFilter); assertSame(testFilter, result); } @@ -80,12 +81,12 @@ public class FileListFilterFactoryBeanTests { public void filenamePatternAndPreventDuplicatesNull() throws Exception { FileListFilterFactoryBean factory = new FileListFilterFactoryBean(); factory.setFilenamePattern(Pattern.compile("foo")); - FileListFilter result = factory.getObject(); - assertTrue(result instanceof CompositeFileListFilter); + EntryListFilter result = factory.getObject(); + assertTrue(result instanceof CompositeEntryListFilter); Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); - Iterator iterator = filters.iterator(); - assertTrue(iterator.next() instanceof AcceptOnceFileListFilter); - assertTrue(iterator.next() instanceof PatternMatchingFileListFilter); + Iterator iterator = filters.iterator(); + assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); + assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter); } @Test @@ -94,12 +95,12 @@ public class FileListFilterFactoryBeanTests { FileListFilterFactoryBean factory = new FileListFilterFactoryBean(); factory.setFilenamePattern(Pattern.compile("foo")); factory.setPreventDuplicates(Boolean.TRUE); - FileListFilter result = factory.getObject(); - assertTrue(result instanceof CompositeFileListFilter); + EntryListFilter result = factory.getObject(); + assertTrue(result instanceof CompositeEntryListFilter); Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); - Iterator iterator = filters.iterator(); - assertTrue(iterator.next() instanceof AcceptOnceFileListFilter); - assertTrue(iterator.next() instanceof PatternMatchingFileListFilter); + Iterator iterator = filters.iterator(); + assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); + assertTrue(iterator.next() instanceof PatternMatchingEntryListFilter); } @Test @@ -107,18 +108,18 @@ public class FileListFilterFactoryBeanTests { 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); + EntryListFilter result = factory.getObject(); + assertFalse(result instanceof CompositeEntryListFilter);; +// 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 { + @Override + public boolean accept(File file) { + return true; + } + } } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/invalidPatternMatchingFileListFilterTests.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/invalidPatternMatchingFileListFilterTests.xml index 3401588dcd..b1499bf518 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/invalidPatternMatchingFileListFilterTests.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/invalidPatternMatchingFileListFilterTests.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/locking/FileLockingNamespaceTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/locking/FileLockingNamespaceTests.java index 28f74dd6bd..5d43e31b22 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/locking/FileLockingNamespaceTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/locking/FileLockingNamespaceTests.java @@ -22,7 +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.entries.CompositeEntryListFilter; +import org.springframework.integration.file.filters.CompositeFileListFilter; import org.springframework.integration.file.FileReadingMessageSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -64,7 +65,7 @@ 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) { @@ -74,7 +75,7 @@ public class FileLockingNamespaceTests { @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 { diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/locking/NioFileLockerTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/locking/NioFileLockerTests.java index 46b427da68..1e38e3ff9b 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/locking/NioFileLockerTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/locking/NioFileLockerTests.java @@ -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 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)new ArrayList())); + assertThat(filter2.filterEntries(workdir.listFiles()), is((List)new ArrayList())); } -} \ No newline at end of file +} diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/patternMatchingFileListFilterTests.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/patternMatchingFileListFilterTests.xml index ddd5f8f965..bc8239e3c2 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/patternMatchingFileListFilterTests.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/patternMatchingFileListFilterTests.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + diff --git a/spring-integration-file/template.mf b/spring-integration-file/template.mf index 8a4ea705bb..cc7dd1ef9b 100644 --- a/spring-integration-file/template.mf +++ b/spring-integration-file/template.mf @@ -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" diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpFileListFilter.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpFileListFilter.java deleted file mode 100644 index 7eb08590c5..0000000000 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpFileListFilter.java +++ /dev/null @@ -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 filterFiles(FTPFile[] files) { - List accepted = new ArrayList(); - - if (files != null) { - for (FTPFile f : files) { - if (this.accept(f)) { - accepted.add(f); - } - } - } - - return accepted; - } -} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/CompositeFtpFileListFilter.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/CompositeFtpFileListFilter.java deleted file mode 100644 index acb844fdc7..0000000000 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/CompositeFtpFileListFilter.java +++ /dev/null @@ -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 filters; - - public CompositeFtpFileListFilter(FtpFileListFilter... ftpFileListFilter) { - this.filters = new LinkedHashSet(Arrays.asList(ftpFileListFilter)); - } - - public CompositeFtpFileListFilter(Collection ftpFileListFilter) { - this.filters = new LinkedHashSet(ftpFileListFilter); - } - public void addFilter( FtpFileListFilter ftpFileListFilter ) { - this.filters.add(ftpFileListFilter); - } - public List filterFiles(FTPFile[] files) { - Assert.notNull(files, "files[] can't be null!"); - - List leftOver = Arrays.asList(files); - - for (FtpFileListFilter ff : this.filters) - leftOver = ff.filterFiles(leftOver.toArray(new FTPFile[leftOver.size()])); - - return leftOver; - } -} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java new file mode 100644 index 0000000000..e511efac4d --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java @@ -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 { + public String nameOf(FTPFile entry) { + return entry.getName(); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileListFilter.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileListFilter.java deleted file mode 100644 index bb92725a99..0000000000 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileListFilter.java +++ /dev/null @@ -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 filterFiles (FTPFile [] files); -} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSource.java index 44cb3cb13b..da4b44e693 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSource.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSource.java @@ -20,10 +20,8 @@ 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.integration.file.entries.*; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; @@ -37,10 +35,11 @@ import java.util.regex.Pattern; * * @author Iwein Fuld */ +@Deprecated public class FtpFileSource implements MessageSource, InitializingBean, Lifecycle { private FileReadingMessageSource fileSource; private FtpInboundSynchronizer synchronizer; - + private EntryNamer fileEntryName = new FileEntryNamer(); public FtpFileSource() { this(new FileReadingMessageSource(), new FtpInboundSynchronizer()); } @@ -48,9 +47,12 @@ public class FtpFileSource implements MessageSource, InitializingBean, Lif public FtpFileSource(FileReadingMessageSource fileSource, FtpInboundSynchronizer synchronizer) { this.fileSource = fileSource; this.synchronizer = synchronizer; - Pattern completePattern = Pattern.compile("^.*(? f =new CompositeEntryListFilter(new AcceptOnceEntryFileListFilter(), + new PatternMatchingEntryListFilter(fileEntryName,completePattern)); + + fileSource.setFilter(f); } public void setFileSource(FileReadingMessageSource fileSource) { @@ -63,10 +65,10 @@ public class FtpFileSource implements MessageSource, InitializingBean, Lif public void setLocalWorkingDirectory(Resource localWorkingDirectory) { this.synchronizer.setLocalDirectory(localWorkingDirectory); - try { this.fileSource.setDirectory(localWorkingDirectory.getFile()); } catch (IOException e) { + // oops } } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSourceFactoryBean.java similarity index 85% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpMessageSourceFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSourceFactoryBean.java index c13ace9d46..cb6200c741 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileSourceFactoryBean.java @@ -2,6 +2,7 @@ package org.springframework.integration.ftp; import org.apache.commons.lang.SystemUtils; import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.context.ApplicationContext; @@ -11,6 +12,9 @@ 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.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.Assert; @@ -19,15 +23,15 @@ 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} + * Makes it easier to assemble the moving pieces involved in standing up an {@link org.springframework.integration.ftp.FtpFileSource} * * @author Josh Long */ -public class FtpMessageSourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware, ApplicationContextAware { +@Deprecated +public class FtpFileSourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware, ApplicationContextAware { private int port; private boolean autoCreateDirectories; private String filenamePattern; @@ -47,7 +51,7 @@ public class FtpMessageSourceFactoryBean extends AbstractFactoryBean filter; public void setFilenamePattern(String filenamePattern) { this.filenamePattern = filenamePattern; @@ -98,10 +102,11 @@ public class FtpMessageSourceFactoryBean extends AbstractFactoryBean filter) { this.filter = filter; } + private FtpFileEntryNamer ftpFileEntryNamer =new FtpFileEntryNamer(); @Override protected FtpFileSource createInstance() throws Exception { // setup local dir @@ -120,17 +125,15 @@ public class FtpMessageSourceFactoryBean extends AbstractFactoryBean compositeFtpFileListFilter = new CompositeEntryListFilter() ; + if( StringUtils.hasText( this.filenamePattern)){ + PatternMatchingEntryListFilter ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(ftpFileEntryNamer,filenamePattern) ; + compositeFtpFileListFilter.addFilter( ftpFilePatternMatchingEntryListFilter); - 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); - if (this.filter != null) { - compositeFtpFileListFilter.addFilter(this.filter); - } this.ftpInboundSynchronizer.setFilter(compositeFtpFileListFilter); diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundSynchronizer.java index 36c33731d2..86abdead21 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundSynchronizer.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundSynchronizer.java @@ -23,6 +23,8 @@ 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.integration.file.entries.AcceptAllEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.PeriodicTrigger; @@ -44,6 +46,7 @@ import java.util.concurrent.ScheduledFuture; * * @author Iwein Fuld */ +@Deprecated public class FtpInboundSynchronizer implements InitializingBean, Lifecycle { private static final Log logger = LogFactory.getLog(FtpInboundSynchronizer.class); static final String INCOMPLETE_EXTENSION = ".INCOMPLETE"; @@ -54,14 +57,10 @@ public class FtpInboundSynchronizer implements InitializingBean, Lifecycle { private volatile Resource localDirectory; private boolean running = false; private ScheduledFuture scheduledFuture; - private FtpFileListFilter filter; - private FtpFileListFilter acceptAllFtpFileListFilter = new FtpFileListFilter() { - public List filterFiles(FTPFile[] files) { - return Arrays.asList(files); - } - }; + private EntryListFilter filter; + private EntryListFilter acceptAllFtpFileListFilter = new AcceptAllEntryListFilter(); - public void setFilter(FtpFileListFilter filter) { + public void setFilter(EntryListFilter filter) { this.filter = filter; } @@ -94,7 +93,7 @@ public class FtpInboundSynchronizer implements InitializingBean, Lifecycle { 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 fileList = this.filter.filterFiles(client.listFiles()); + Collection fileList = this.filter.filterEntries(client.listFiles()); try { for (FTPFile ftpFile : fileList) { diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/PatternMatchingFtpFileListFilter.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/PatternMatchingFtpFileListFilter.java deleted file mode 100644 index feab1f1d0f..0000000000 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/PatternMatchingFtpFileListFilter.java +++ /dev/null @@ -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"); - } -} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java index d72823110a..9ca93523b0 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java @@ -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()); } } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java new file mode 100644 index 0000000000..301d2fa0ac --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java @@ -0,0 +1,126 @@ +package org.springframework.integration.ftp.impl; + +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.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; +import java.util.logging.Logger; + + +/** + * An FTP-adapter implementation of {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer} + * + * @author Josh Long + */ +public class FtpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer { + 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 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 { + 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()); + } + } + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java new file mode 100644 index 0000000000..42505532e8 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -0,0 +1,38 @@ +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 { + 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); + + + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java new file mode 100644 index 0000000000..3dba5340ca --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -0,0 +1,170 @@ +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.AbstractInboundRemoteFileSystemSychronizer; +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 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 filter; + private volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE; + private volatile String autoDeleteRemoteFilesOnSync; + + 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 compositeFtpFileListFilter = new CompositeEntryListFilter(); + + if (StringUtils.hasText(this.filenamePattern)) { + PatternMatchingEntryListFilter ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(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); + + if (compositeFtpFileListFilter != null) { + 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; + } + + public void setPort(String port) { + this.port = port; + } + + public void setAutoCreateDirectories(String autoCreateDirectories) { + this.autoCreateDirectories = autoCreateDirectories; + } + + public void setFilenamePattern(String filenamePattern) { + this.filenamePattern = filenamePattern; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setHost(String host) { + this.host = host; + } + + public void setRemoteDirectory(String remoteDirectory) { + this.remoteDirectory = remoteDirectory; + } + + public void setLocalWorkingDirectory(String localWorkingDirectory) { + this.localWorkingDirectory = localWorkingDirectory; + } + + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } + + public void setClientMode(int clientMode) { + this.clientMode = clientMode; + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } +} diff --git a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd index cf4dbc9fb2..391b4d7522 100644 --- a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd +++ b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd @@ -138,7 +138,7 @@ - + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java index 75ad88642e..48544076e1 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java @@ -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(); } } diff --git a/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml b/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml index 355348baaf..aa937e6a32 100644 --- a/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml +++ b/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml @@ -20,7 +20,7 @@ filename-pattern=".*?jpg" > - + diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java index ecd81ba4db..aea695afac 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java @@ -1,72 +1,72 @@ -/* - * 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.jdbc.config; - -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; -import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -/** - * @author Dave Syer - * @since 2.0 - * - */ -public class JdbcMessageHandlerParser extends AbstractOutboundChannelAdapterParser { - - protected boolean shouldGenerateId() { - return false; - } - - protected boolean shouldGenerateIdAsFallback() { - return true; - } - - @Override - protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - Object source = parserContext.extractSource(element); - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.jdbc.JdbcMessageHandler"); - String dataSourceRef = element.getAttribute("data-source"); - String jdbcOperationsRef = element.getAttribute("jdbc-operations"); - boolean refToDataSourceSet = StringUtils.hasText(dataSourceRef); - boolean refToJdbcOperationsSet = StringUtils.hasText(jdbcOperationsRef); - if ((refToDataSourceSet && refToJdbcOperationsSet) - || (!refToDataSourceSet && !refToJdbcOperationsSet)) { - parserContext.getReaderContext().error( - "Exactly one of the attributes data-source or " - + "simple-jdbc-operations should be set for the JDBC outbound-channel-adapter", source); - } - String query = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "query", parserContext); - if (!StringUtils.hasText(query)) { - throw new BeanCreationException("The query attrbitue is required"); - } - if (!StringUtils.hasText(query)) { - throw new BeanCreationException("The query attrbitue is required"); - } - if (refToDataSourceSet) { - builder.addConstructorArgReference(dataSourceRef); - } else { - builder.addConstructorArgReference(jdbcOperationsRef); - } - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "sql-parameter-source-factory"); - builder.addConstructorArgValue(query); - return builder.getBeanDefinition(); - } - -} +/* + * 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.jdbc.config; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * @author Dave Syer + * @since 2.0 + * + */ +public class JdbcMessageHandlerParser extends AbstractOutboundChannelAdapterParser { + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + Object source = parserContext.extractSource(element); + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition("org.springframework.integration.jdbc.JdbcMessageHandler"); + String dataSourceRef = element.getAttribute("data-source"); + String jdbcOperationsRef = element.getAttribute("jdbc-operations"); + boolean refToDataSourceSet = StringUtils.hasText(dataSourceRef); + boolean refToJdbcOperationsSet = StringUtils.hasText(jdbcOperationsRef); + if ((refToDataSourceSet && refToJdbcOperationsSet) + || (!refToDataSourceSet && !refToJdbcOperationsSet)) { + parserContext.getReaderContext().error( + "Exactly one of the attributes data-source or " + + "simple-jdbc-operations should be set for the JDBC outbound-channel-adapter", source); + } + String query = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "query", parserContext); + if (!StringUtils.hasText(query)) { + throw new BeanCreationException("The query attrbitue is required"); + } + if (!StringUtils.hasText(query)) { + throw new BeanCreationException("The query attrbitue is required"); + } + if (refToDataSourceSet) { + builder.addConstructorArgReference(dataSourceRef); + } else { + builder.addConstructorArgReference(jdbcOperationsRef); + } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "sql-parameter-source-factory"); + builder.addConstructorArgValue(query); + return builder.getBeanDefinition(); + } + +} diff --git a/spring-integration-jdbc/src/main/sql/sqlserver.properties b/spring-integration-jdbc/src/main/sql/sqlserver.properties index 60b6aa24e2..3796c21341 100644 --- a/spring-integration-jdbc/src/main/sql/sqlserver.properties +++ b/spring-integration-jdbc/src/main/sql/sqlserver.properties @@ -1,11 +1,11 @@ -# SQL language oddities -BIGINT = BIGINT -IDENTITY = -GENERATED = -DOUBLE = DOUBLE PRECISION -BLOB = IMAGE -CLOB = TEXT -TIMESTAMP = DATETIME -VARCHAR = VARCHAR -# for generating drop statements... -SEQUENCE = TABLE +# SQL language oddities +BIGINT = BIGINT +IDENTITY = +GENERATED = +DOUBLE = DOUBLE PRECISION +BLOB = IMAGE +CLOB = TEXT +TIMESTAMP = DATETIME +VARCHAR = VARCHAR +# for generating drop statements... +SEQUENCE = TABLE diff --git a/spring-integration-jdbc/src/main/sql/sybase.properties b/spring-integration-jdbc/src/main/sql/sybase.properties index 78adc66a1c..f9dc6e9a8f 100644 --- a/spring-integration-jdbc/src/main/sql/sybase.properties +++ b/spring-integration-jdbc/src/main/sql/sybase.properties @@ -1,12 +1,12 @@ -# SQL language oddities -BIGINT = BIGINT -IDENTITY = -GENERATED = -DOUBLE = DOUBLE PRECISION -BLOB = IMAGE -CLOB = TEXT -TIMESTAMP = DATETIME -VARCHAR = VARCHAR -NULL = NULL -# for generating drop statements... -SEQUENCE = TABLE +# SQL language oddities +BIGINT = BIGINT +IDENTITY = +GENERATED = +DOUBLE = DOUBLE PRECISION +BLOB = IMAGE +CLOB = TEXT +TIMESTAMP = DATETIME +VARCHAR = VARCHAR +NULL = NULL +# for generating drop statements... +SEQUENCE = TABLE diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests-context.xml index a11f031e2e..7f33a0fd3d 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests-context.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests-context.xml @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/defaultJdbcMessageStore.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/defaultJdbcMessageStore.xml index 705a2846ae..73f7dd023f 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/defaultJdbcMessageStore.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/defaultJdbcMessageStore.xml @@ -1,14 +1,14 @@ - - - - - - - - + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/jdbcOperationsJdbcMessageStore.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/jdbcOperationsJdbcMessageStore.xml index 494e36d2f7..b2185fb792 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/jdbcOperationsJdbcMessageStore.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/jdbcOperationsJdbcMessageStore.xml @@ -1,18 +1,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/soupedUpJdbcMessageStore.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/soupedUpJdbcMessageStore.xml index 6ba45f2f1d..4adf642f40 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/soupedUpJdbcMessageStore.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/soupedUpJdbcMessageStore.xml @@ -1,16 +1,16 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerParserTests.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerParserTests.xml index c2d8cfc91a..57e2ecef47 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerParserTests.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerParserTests.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/mail - http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> + http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerWithinChain.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerWithinChain.xml index eb0b38c755..a0e991a064 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerWithinChain.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailToStringTransformerWithinChain.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/mail - http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> + http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> diff --git a/spring-integration-security/src/test/java/org/springframework/integration/security/config/SecuredChannelsParserTests-context.xml b/spring-integration-security/src/test/java/org/springframework/integration/security/config/SecuredChannelsParserTests-context.xml index c861948b3f..1d7615ab08 100644 --- a/spring-integration-security/src/test/java/org/springframework/integration/security/config/SecuredChannelsParserTests-context.xml +++ b/spring-integration-security/src/test/java/org/springframework/integration/security/config/SecuredChannelsParserTests-context.xml @@ -14,8 +14,8 @@ http://www.springframework.org/schema/integration/security http://www.springframework.org/schema/integration/security/spring-integration-security.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context.xsd"> - + http://www.springframework.org/schema/context/spring-context.xsd"> + diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/AbstractSftpFileListFilter.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/AbstractSftpFileListFilter.java index cc5d1b8691..c50333ae39 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/AbstractSftpFileListFilter.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/AbstractSftpFileListFilter.java @@ -22,10 +22,10 @@ import java.util.List; /** - * Convenience implementation patterned off {@link org.springframework.integration.file.FileListFilter} + * Convenience implementation patterned off {@link org.springframework.integration.file.filters.FileListFilter} * * @author Josh Long - */ + */ @Deprecated public abstract class AbstractSftpFileListFilter implements SftpFileListFilter { abstract public boolean accept(ChannelSftp.LsEntry lsEntry); diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/CompositeFtpFileListFilter.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/CompositeFtpFileListFilter.java index 4d2d0b54a3..f3c26e5edd 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/CompositeFtpFileListFilter.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/CompositeFtpFileListFilter.java @@ -22,10 +22,10 @@ import java.util.*; /** - * Patterned very much on the {@link org.springframework.integration.file.CompositeFileListFilter} + * Patterned very much on the {@link org.springframework.integration.file.filters.CompositeFileListFilter} * * @author Josh Long - */ + */ @Deprecated public class CompositeFtpFileListFilter implements SftpFileListFilter { private Set filters; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/PatternMatchingSftpFileListFilter.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/PatternMatchingSftpFileListFilter.java index c79f1d9a0c..b9c21f5d15 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/PatternMatchingSftpFileListFilter.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/PatternMatchingSftpFileListFilter.java @@ -28,10 +28,10 @@ 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}. + * Patterned very much like {@link org.springframework.integration.file.filters.PatternMatchingFileListFilter}. * * @author Josh Long - */ + */ @Deprecated public class PatternMatchingSftpFileListFilter extends AbstractSftpFileListFilter implements InitializingBean { private Log logger = LogFactory.getLog(getClass()); diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpEntryNamer.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpEntryNamer.java new file mode 100644 index 0000000000..404c93510b --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpEntryNamer.java @@ -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{ + + public String nameOf(ChannelSftp.LsEntry entry) { + return entry.getFilename() ; + } +} diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpFileListFilter.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpFileListFilter.java index 98a14fc56d..f5bc8925d2 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpFileListFilter.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpFileListFilter.java @@ -24,7 +24,7 @@ import java.util.List; * and returns the balance. These are then sync'd to the local directory. * * @author Josh Long - */ + */ @Deprecated public interface SftpFileListFilter { List filterFiles (ChannelSftp.LsEntry [] files); } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpInboundSynchronizer.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpInboundSynchronizer.java index fe04a7e3ff..676eab8389 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpInboundSynchronizer.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpInboundSynchronizer.java @@ -44,7 +44,7 @@ import java.util.concurrent.ScheduledFuture; * * @author Josh Long * @author Mario Gray - */ + */ @Deprecated public class SftpInboundSynchronizer implements InitializingBean { private static final long DEFAULT_REFRESH_RATE = 10 * 1000; // 10 seconds diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpMessageSource.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpMessageSource.java index 3d418ba1e6..33d66dec17 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpMessageSource.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpMessageSource.java @@ -13,23 +13,28 @@ * 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.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.integration.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; + import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; import java.io.File; import java.io.IOException; + import java.util.regex.Pattern; @@ -46,13 +51,21 @@ public class SftpMessageSource implements MessageSource, InitializingBean, private SftpInboundSynchronizer synchronizer; private TaskScheduler taskScheduler; private Trigger trigger; + private SftpEntryNamer lsEntryEntryNamer = new SftpEntryNamer(); public SftpMessageSource(FileReadingMessageSource fileSource, SftpInboundSynchronizer synchronizer) { this.fileReadingMessageSource = fileSource; this.synchronizer = synchronizer; Pattern completePattern = Pattern.compile("^.*(? filePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(lsEntryEntryNamer, completePattern); + PatternMatchingEntryListFilter lsEntryPatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(this.lsEntryEntryNamer, completePattern); + + CompositeEntryListFilter fileCompositeEntryListFilter = new CompositeEntryListFilter(filePatternMatchingEntryListFilter, + lsEntryPatternMatchingEntryListFilter); + // todo this.fileReadingMessageSource.setFilter( fileCompositeEntryListFilter); + + //fileReadingMessageSource.setFilter( (completePattern))); } public void afterPropertiesSet() throws Exception { @@ -94,11 +107,12 @@ public class SftpMessageSource implements MessageSource, InitializingBean, public void setLocalDirectory(final Resource localDirectory) { this.localDirectory = localDirectory; + try { this.fileReadingMessageSource.setDirectory(localDirectory.getFile()); } catch (IOException e) { - } + this.synchronizer.setLocalDirectory(localDirectory); } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java index 8d3e3b83e0..ec2cc9aada 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java @@ -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"); diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java new file mode 100644 index 0000000000..b323e703ac --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java @@ -0,0 +1,141 @@ +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 { + /** + * the path on te 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"); + + 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 beforeFilter = channelSftp.ls(remotePath); + ChannelSftp.LsEntry[] entries = (beforeFilter == null) ? new ChannelSftp.LsEntry[0] : beforeFilter.toArray(new ChannelSftp.LsEntry[beforeFilter.size()]); + Collection 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 { + 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()); + } + } + } +} diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java new file mode 100644 index 0000000000..f45177b690 --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -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 { + /** + * 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); + } +} diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java new file mode 100644 index 0000000000..98fa75ea7e --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -0,0 +1,195 @@ +package org.springframework.integration.sftp.impl; + +import com.jcraft.jsch.ChannelSftp; + +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.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; +import org.springframework.integration.sftp.*; +import org.springframework.integration.sftp.config.SftpSessionUtils; + +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import org.springframework.util.ErrorHandler; +import org.springframework.util.StringUtils; + +import java.io.File; + +import java.util.Map; + + +/** + * a factory bean to hide the fairly complex configuration possibilities for an SFTP endpoint + * + * @author Josh Long + */ +public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends + AbstractFactoryBean 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 filter; + private int port = 22; + + public void setLocalDirectoryResource(Resource localDirectoryResource) { + this.localDirectoryResource = localDirectoryResource; + } + + public void setLocalDirectoryPath(String localDirectoryPath) { + this.localDirectoryPath = localDirectoryPath; + } + + public void setAutoCreateDirectories(String autoCreateDirectories) { + this.autoCreateDirectories = autoCreateDirectories; + } + + public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) { + this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync; + } + + public void setFilenamePattern(String filenamePattern) { + this.filenamePattern = filenamePattern; + } + + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } + + public void setPort(int port) { + this.port = port; + } + + public void setHost(String host) { + this.host = host; + } + + public void setKeyFile(String keyFile) { + this.keyFile = keyFile; + } + + public void setKeyFilePassword(String keyFilePassword) { + this.keyFilePassword = keyFilePassword; + } + + public void setLocalWorkingDirectory(String localWorkingDirectory) { + this.localWorkingDirectory = localWorkingDirectory; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setRemoteDirectory(String remoteDirectory) { + this.remoteDirectory = remoteDirectory; + } + + public void setUsername(String username) { + this.username = username; + } + + private String host; + private String keyFile; + private String keyFilePassword; + private String localWorkingDirectory; + private String password; + private String remoteDirectory; + private String 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 compositeFtpFileListFilter = new CompositeEntryListFilter(); + + if (StringUtils.hasText(this.filenamePattern)) { + PatternMatchingEntryListFilter ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(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.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(); + } + +} diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpReceipt.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpReceipt.java index 325651015e..71242c1c3a 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpReceipt.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpReceipt.java @@ -19,7 +19,7 @@ import java.util.logging.Logger; * @author Josh Long */ public class TestSftpReceipt { - +/* private static final Logger logger = Logger.getLogger(TestSftpReceipt.class.getName()); private SftpSessionFactory sftpSessionFactory; private String host; @@ -104,5 +104,5 @@ public class TestSftpReceipt { sftpSessionFactory.afterPropertiesSet(); return sftpSessionFactory; - } + }*/ } diff --git a/spring-integration-sftp/src/test/resources/TestInboundSftp.xml b/spring-integration-sftp/src/test/resources/TestInboundSftp.xml index 6ef7076255..aa6efb21db 100644 --- a/spring-integration-sftp/src/test/resources/TestInboundSftp.xml +++ b/spring-integration-sftp/src/test/resources/TestInboundSftp.xml @@ -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"> + diff --git a/src/docbkx/file.xml b/src/docbkx/file.xml index f5a57ff030..e4b6aa6662 100644 --- a/src/docbkx/file.xml +++ b/src/docbkx/file.xml @@ -54,11 +54,11 @@ class="org.springframework.integration.file.FileReadingMessageSource" p:inputDirectory="file:${input.directory}" p:filter-ref="compositeFilter"/> - + - - + + @@ -225,4 +225,4 @@ - \ No newline at end of file + diff --git a/src/main/resources/license.txt b/src/main/resources/license.txt index 29f81d812f..261eeb9e9f 100644 --- a/src/main/resources/license.txt +++ b/src/main/resources/license.txt @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 6394b12401..f62045a212 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,21 +1,21 @@ - ======================================================================== - == NOTICE file corresponding to section 4 d of the Apache License, == - == Version 2.0, in this case for the Spring Integration distribution. == - ======================================================================== - - This product includes software developed by - the Apache Software Foundation (http://www.apache.org). - - The end-user documentation included with a redistribution, if any, - must include the following acknowledgement: - - "This product includes software developed by the Spring Framework - Project (http://www.springframework.org)." - - Alternatively, this acknowledgement may appear in the software itself, - if and wherever such third-party acknowledgements normally appear. - - The names "Spring", "Spring Framework", and "Spring Integration" must - not be used to endorse or promote products derived from this software - without prior written permission. For written permission, please contact - enquiries@springsource.com. + ======================================================================== + == NOTICE file corresponding to section 4 d of the Apache License, == + == Version 2.0, in this case for the Spring Integration distribution. == + ======================================================================== + + This product includes software developed by + the Apache Software Foundation (http://www.apache.org). + + The end-user documentation included with a redistribution, if any, + must include the following acknowledgement: + + "This product includes software developed by the Spring Framework + Project (http://www.springframework.org)." + + Alternatively, this acknowledgement may appear in the software itself, + if and wherever such third-party acknowledgements normally appear. + + The names "Spring", "Spring Framework", and "Spring Integration" must + not be used to endorse or promote products derived from this software + without prior written permission. For written permission, please contact + enquiries@springsource.com.