Merge remote-tracking branch 'upstream/master' into 4.0.0-WIP
Conflicts: spring-integration-core/src/main/java/org/springframework/integration/gateway/MessagingGatewaySupport.java spring-integration-core/src/main/java/org/springframework/integration/support/channel/BeanFactoryChannelResolver.java spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorParserTests.java spring-integration-core/src/test/java/org/springframework/integration/config/annotation/AggregatorAnnotationTests.java spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests.java spring-integration-file/src/main/java/org/springframework/integration/file/DefaultFileNameGenerator.java spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java spring-integration-ftp/src/test/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizerTests.java spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/HelloWorldInterceptor.java spring-integration-jpa/src/test/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayIntegrationTests.java spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/store/MongoDbMessageGroupStoreTests.java spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/store/MongoDbMessageStoreTests.java spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests.java spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java spring-integration-sftp/src/test/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizerTests.java spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java Resolved.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -18,8 +18,11 @@ package org.springframework.integration.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.util.AbstractExpressionEvaluator;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -34,13 +37,17 @@ import org.springframework.util.StringUtils;
|
||||
* associated with the header if no expression has been provided), it checks if
|
||||
* the Message payload is a File instance, and if so, it uses the same name.
|
||||
* Finally, it falls back to the Message ID and adds the suffix '.msg'.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class DefaultFileNameGenerator extends AbstractExpressionEvaluator implements FileNameGenerator {
|
||||
|
||||
private volatile String expression = "headers['" + FileHeaders.FILENAME + "']";
|
||||
private static final String DEFAULT_EXPRESSION = "headers['" + FileHeaders.FILENAME + "']";
|
||||
|
||||
private final static ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
private volatile Expression expression = parser.parseExpression(DEFAULT_EXPRESSION);
|
||||
|
||||
/**
|
||||
* Specify an expression to be evaluated against the Message
|
||||
@@ -48,7 +55,7 @@ public class DefaultFileNameGenerator extends AbstractExpressionEvaluator implem
|
||||
*/
|
||||
public void setExpression(String expression) {
|
||||
Assert.hasText(expression, "expression must not be empty");
|
||||
this.expression = expression;
|
||||
this.expression = parser.parseExpression(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,9 +64,10 @@ public class DefaultFileNameGenerator extends AbstractExpressionEvaluator implem
|
||||
*/
|
||||
public void setHeaderName(String headerName) {
|
||||
Assert.notNull(headerName, "'headerName' must not be null");
|
||||
this.expression = "headers['" + headerName + "']";
|
||||
this.expression = parser.parseExpression("headers['" + headerName + "']");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateFileName(Message<?> message) {
|
||||
Object filenameProperty = this.evaluateExpression(this.expression, message);
|
||||
if (filenameProperty instanceof String && StringUtils.hasText((String) filenameProperty)) {
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.filters.RegexPatternFileListFilter;
|
||||
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -42,18 +44,20 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, false);
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getGatewayClassName());
|
||||
|
||||
builder.addConstructorArgReference(element.getAttribute("session-factory"));
|
||||
builder.addConstructorArgValue(templateDefinition);
|
||||
|
||||
builder.addConstructorArgValue(element.getAttribute("command"));
|
||||
builder.addConstructorArgValue(element.getAttribute(EXPRESSION_ATTRIBUTE));
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "command-options", "options");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "temporary-file-suffix");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
|
||||
this.configureFilter(builder, element, parserContext);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "remote-file-separator");
|
||||
this.configureFilter(builder, element, parserContext, "filter", "filename", "filter");
|
||||
this.configureFilter(builder, element, parserContext, "mput-filter", "mput", "mputFilter");
|
||||
|
||||
BeanDefinition localDirExpressionDef = IntegrationNamespaceUtils
|
||||
.createExpressionDefinitionFromValueOrExpression("local-directory", "local-directory-expression",
|
||||
@@ -74,10 +78,11 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
String filter = element.getAttribute("filter");
|
||||
String fileNamePattern = element.getAttribute("filename-pattern");
|
||||
String fileNameRegex = element.getAttribute("filename-regex");
|
||||
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext,
|
||||
String filterAttribute, String patternPrefix, String propertyName) {
|
||||
String filter = element.getAttribute(filterAttribute);
|
||||
String fileNamePattern = element.getAttribute(patternPrefix + "-pattern");
|
||||
String fileNameRegex = element.getAttribute(patternPrefix + "-regex");
|
||||
boolean hasFilter = StringUtils.hasText(filter);
|
||||
boolean hasFileNamePattern = StringUtils.hasText(fileNamePattern);
|
||||
boolean hasFileNameRegex = StringUtils.hasText(fileNameRegex);
|
||||
@@ -85,23 +90,27 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
|
||||
count += hasFileNamePattern ? 1 : 0;
|
||||
count += hasFileNameRegex ? 1 : 0;
|
||||
if (count > 1) {
|
||||
parserContext.getReaderContext().error("at most one of 'filename-pattern', " +
|
||||
"'filename-regex', or 'filter' is allowed on remote file inbound adapter", element);
|
||||
parserContext.getReaderContext().error("at most one of '" + patternPrefix + "-pattern', " +
|
||||
"'" + patternPrefix + "-regex', or '" + filterAttribute + "' is allowed on a remote file outbound gateway", element);
|
||||
}
|
||||
else if (hasFilter) {
|
||||
builder.addPropertyReference("filter", filter);
|
||||
builder.addPropertyReference(propertyName, filter);
|
||||
}
|
||||
else if (hasFileNamePattern) {
|
||||
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
this.getSimplePatternFileListFilterClassName());
|
||||
"filter".equals(filterAttribute) ?
|
||||
this.getSimplePatternFileListFilterClassName() :
|
||||
SimplePatternFileListFilter.class.getName());
|
||||
filterBuilder.addConstructorArgValue(fileNamePattern);
|
||||
builder.addPropertyValue("filter", filterBuilder.getBeanDefinition());
|
||||
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
|
||||
}
|
||||
else if (hasFileNameRegex) {
|
||||
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
this.getRegexPatternFileListFilterClassName());
|
||||
"filter".equals(filterAttribute) ?
|
||||
this.getRegexPatternFileListFilterClassName() :
|
||||
RegexPatternFileListFilter.class.getName());
|
||||
filterBuilder.addConstructorArgValue(fileNameRegex);
|
||||
builder.addPropertyValue("filter", filterBuilder.getBeanDefinition());
|
||||
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2013 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.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public final class FileParserUtils {
|
||||
|
||||
private FileParserUtils() {
|
||||
}
|
||||
|
||||
public static BeanDefinition parseRemoteFileTemplate(Element element, ParserContext parserContext,
|
||||
boolean atLeastOneRemoteDirectoryAttributeRequired) {
|
||||
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.genericBeanDefinition(RemoteFileTemplate.class);
|
||||
|
||||
templateBuilder.addConstructorArgReference(element.getAttribute("session-factory"));
|
||||
// configure MessageHandler properties
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(templateBuilder, element, "temporary-file-suffix");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(templateBuilder, element, "use-temporary-file-name");
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(templateBuilder, element, "auto-create-directory");
|
||||
|
||||
BeanDefinition expressionDef =
|
||||
IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("remote-directory",
|
||||
"remote-directory-expression", parserContext, element, atLeastOneRemoteDirectoryAttributeRequired);
|
||||
if (expressionDef != null) {
|
||||
templateBuilder.addPropertyValue("remoteDirectoryExpression", expressionDef);
|
||||
}
|
||||
expressionDef = IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("temporary-remote-directory",
|
||||
"temporary-remote-directory-expression", parserContext, element, false);
|
||||
if (expressionDef != null) {
|
||||
templateBuilder.addPropertyValue("temporaryRemoteDirectoryExpression", expressionDef);
|
||||
}
|
||||
|
||||
// configure remote FileNameGenerator
|
||||
String remoteFileNameGenerator = element.getAttribute("remote-filename-generator");
|
||||
String remoteFileNameGeneratorExpression = element.getAttribute("remote-filename-generator-expression");
|
||||
boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator);
|
||||
boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression);
|
||||
if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) {
|
||||
if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) {
|
||||
parserContext.getReaderContext().error(
|
||||
"at most one of 'remote-filename-generator-expression' or 'remote-filename-generator' "
|
||||
+ "is allowed on a remote file outbound adapter", element);
|
||||
}
|
||||
if (hasRemoteFileNameGenerator) {
|
||||
templateBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DefaultFileNameGenerator.class);
|
||||
fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression);
|
||||
templateBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(templateBuilder, element, "charset");
|
||||
templateBuilder.addPropertyValue("remoteFileSeparator", element.getAttribute("remote-file-separator"));
|
||||
return templateBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,19 +18,12 @@ package org.springframework.integration.file.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -45,71 +38,10 @@ public class RemoteFileOutboundChannelAdapterParser extends AbstractOutboundChan
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder handlerBuilder = BeanDefinitionBuilder.genericBeanDefinition(FileTransferringMessageHandler.class);
|
||||
|
||||
handlerBuilder.addConstructorArgReference(element.getAttribute("session-factory"));
|
||||
// configure MessageHandler properties
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, true);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "temporary-file-suffix");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "use-temporary-file-name");
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "auto-create-directory");
|
||||
|
||||
this.configureRemoteDirectories(element, handlerBuilder);
|
||||
|
||||
// configure remote FileNameGenerator
|
||||
String remoteFileNameGenerator = element.getAttribute("remote-filename-generator");
|
||||
String remoteFileNameGeneratorExpression = element.getAttribute("remote-filename-generator-expression");
|
||||
boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator);
|
||||
boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression);
|
||||
if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) {
|
||||
if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) {
|
||||
throw new BeanDefinitionStoreException("at most one of 'remote-filename-generator-expression' or 'remote-filename-generator' " +
|
||||
"is allowed on a remote file outbound adapter");
|
||||
}
|
||||
if (hasRemoteFileNameGenerator) {
|
||||
handlerBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultFileNameGenerator.class);
|
||||
fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression);
|
||||
handlerBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "charset");
|
||||
handlerBuilder.addPropertyValue("remoteFileSeparator", element.getAttribute("remote-file-separator"));
|
||||
handlerBuilder.addConstructorArgValue(templateDefinition);
|
||||
return handlerBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void configureRemoteDirectories(Element element, BeanDefinitionBuilder handlerBuilder){
|
||||
this.doConfigureRemoteDirectory(element, handlerBuilder, "remote-directory", "remote-directory-expression", "remoteDirectoryExpression", true);
|
||||
this.doConfigureRemoteDirectory(element, handlerBuilder, "temporary-remote-directory", "temporary-remote-directory-expression", "temporaryRemoteDirectoryExpression", false);
|
||||
}
|
||||
|
||||
private void doConfigureRemoteDirectory(Element element, BeanDefinitionBuilder handlerBuilder,
|
||||
String directoryAttribute, String directoryExpressionAttribute,
|
||||
String directoryExpressionPropertyName, boolean atLeastOneRequired){
|
||||
String remoteDirectory = element.getAttribute(directoryAttribute);
|
||||
String remoteDirectoryExpression = element.getAttribute(directoryExpressionAttribute);
|
||||
boolean hasRemoteDirectory = StringUtils.hasText(remoteDirectory);
|
||||
boolean hasRemoteDirectoryExpression = StringUtils.hasText(remoteDirectoryExpression);
|
||||
if (atLeastOneRequired){
|
||||
if (!(hasRemoteDirectory ^ hasRemoteDirectoryExpression)) {
|
||||
throw new BeanDefinitionStoreException("exactly one of '" + directoryAttribute + "' or '" + directoryExpressionAttribute + "' " +
|
||||
"is required on a remote file outbound adapter");
|
||||
}
|
||||
}
|
||||
|
||||
BeanDefinition remoteDirectoryExpressionDefinition = null;
|
||||
if (hasRemoteDirectory) {
|
||||
remoteDirectoryExpressionDefinition = new RootBeanDefinition(LiteralExpression.class);
|
||||
remoteDirectoryExpressionDefinition.getConstructorArgumentValues().addGenericArgumentValue(remoteDirectory);
|
||||
}
|
||||
else if (hasRemoteDirectoryExpression) {
|
||||
remoteDirectoryExpressionDefinition = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
remoteDirectoryExpressionDefinition.getConstructorArgumentValues().addGenericArgumentValue(remoteDirectoryExpression);
|
||||
}
|
||||
if (remoteDirectoryExpressionDefinition != null){
|
||||
handlerBuilder.addPropertyValue(directoryExpressionPropertyName, remoteDirectoryExpressionDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Stores "seen" files in a MetadataStore to survive application restarts.
|
||||
* The default key is 'prefix' plus the absolute file name; value is the timestamp of the file.
|
||||
* Files are deemed as already 'seen' if they exist in the store and have the
|
||||
* same modified time as the current file.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> {
|
||||
|
||||
protected final MetadataStore store;
|
||||
|
||||
protected final String prefix;
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
public AbstractPersistentAcceptOnceFileListFilter(MetadataStore store, String prefix) {
|
||||
Assert.notNull(store, "'store' cannot be null");
|
||||
Assert.notNull(prefix, "'prefix' cannot be null");
|
||||
this.store = store;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean accept(F file) {
|
||||
String key = buildKey(file);
|
||||
synchronized(monitor) {
|
||||
String value = store.get(key);
|
||||
if (value != null && isEqual(file, value)) {
|
||||
return false;
|
||||
}
|
||||
store.put(key, value(file));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value stored for the key is the last modified date.
|
||||
* @param file The file.
|
||||
* @return The value to store for the file.
|
||||
*/
|
||||
private String value(F file) {
|
||||
return Long.toString(this.modified(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you wish to use something other than the
|
||||
* modified timestamp to determine equality.
|
||||
* @param file The file.
|
||||
* @param value The current value for the key in the store.
|
||||
* @return true if equal.
|
||||
*/
|
||||
protected boolean isEqual(F file, String value) {
|
||||
return Long.valueOf(value).longValue() == this.modified(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default key is the {@link #prefix} plus the full filename.
|
||||
* @param file The file.
|
||||
* @return The key.
|
||||
*/
|
||||
protected String buildKey(F file) {
|
||||
return this.prefix + this.fileName(file);
|
||||
}
|
||||
|
||||
protected abstract long modified(F file);
|
||||
|
||||
protected abstract String fileName(F file);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class FileSystemPersistentAcceptOnceFileListFilter extends AbstractPersistentAcceptOnceFileListFilter<File> {
|
||||
|
||||
public FileSystemPersistentAcceptOnceFileListFilter(MetadataStore store, String prefix) {
|
||||
super(store, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long modified(File file) {
|
||||
return file.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String fileName(File file) {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013 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.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Callback for stream-based file retrieval using a RemoteFileOperations.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public interface InputStreamCallback {
|
||||
|
||||
/**
|
||||
* Called with the InputStream for the remote file. The caller will
|
||||
* take care of closing the stream and finalizing the file retrieval operation after
|
||||
* this method exits.
|
||||
*
|
||||
* @param stream The InputStream.
|
||||
* @throws IOException
|
||||
*/
|
||||
void doWithInputStream(InputStream stream) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013 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.remote;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Strategy for performing operations on remote files.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public interface RemoteFileOperations<F> {
|
||||
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
* @throws Exception
|
||||
*/
|
||||
String send(Message<?> message);
|
||||
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message.
|
||||
* The subDirectory is appended to the remote directory evaluated from
|
||||
* the message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param subDirectory The sub directory.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
String send(Message<?> message, String subDirectory);
|
||||
/**
|
||||
* Retrieve a remote file as an InputStream, based on information in a message.
|
||||
*
|
||||
* @param callback the callback.
|
||||
* @return true if the operation was successful.
|
||||
*/
|
||||
boolean get(Message<?> message, InputStreamCallback callback);
|
||||
|
||||
/**
|
||||
* Remove a remote file.
|
||||
*
|
||||
* @param path The full path to the file.
|
||||
* @return true when successful
|
||||
*/
|
||||
boolean remove(String path);
|
||||
|
||||
/**
|
||||
* Rename a remote file, creating directories if needed.
|
||||
*
|
||||
* @param fromPath The current path.
|
||||
* @param toPath The new path.
|
||||
*/
|
||||
void rename(String fromPath, String toPath);
|
||||
|
||||
/**
|
||||
* Execute the callback's doInSession method after obtaining a session.
|
||||
* Reliably closes the session when the method exits.
|
||||
*
|
||||
* @param callback the SessionCallback.
|
||||
* @return The result of the callback method.
|
||||
*/
|
||||
<T> T execute(SessionCallback<F, T> callback);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright 2013 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.remote;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
* @author Mark Fisher
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, InitializingBean, BeanFactoryAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
/**
|
||||
* the {@link SessionFactory} for acquiring remote file Sessions.
|
||||
*/
|
||||
private final SessionFactory<F> sessionFactory;
|
||||
|
||||
private volatile String temporaryFileSuffix =".writing";
|
||||
|
||||
private volatile boolean autoCreateDirectory = false;
|
||||
|
||||
private volatile boolean useTemporaryFileName = true;
|
||||
|
||||
private volatile ExpressionEvaluatingMessageProcessor<String> directoryExpressionProcessor;
|
||||
|
||||
private volatile ExpressionEvaluatingMessageProcessor<String> temporaryDirectoryExpressionProcessor;
|
||||
|
||||
private volatile ExpressionEvaluatingMessageProcessor<String> fileNameProcessor;
|
||||
|
||||
private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
|
||||
|
||||
private volatile boolean fileNameGeneratorSet;
|
||||
|
||||
private volatile String charset = "UTF-8";
|
||||
|
||||
private volatile String remoteFileSeparator = "/";
|
||||
|
||||
private volatile boolean hasExplicitlySetSuffix;
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
public RemoteFileTemplate(SessionFactory<F> sessionFactory) {
|
||||
Assert.notNull(sessionFactory, "sessionFactory must not be null");
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectory(boolean autoCreateDirectory) {
|
||||
this.autoCreateDirectory = autoCreateDirectory;
|
||||
}
|
||||
|
||||
public void setRemoteFileSeparator(String remoteFileSeparator) {
|
||||
Assert.notNull(remoteFileSeparator, "'remoteFileSeparator' must not be null");
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
}
|
||||
|
||||
public final String getRemoteFileSeparator() {
|
||||
return remoteFileSeparator;
|
||||
}
|
||||
|
||||
public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) {
|
||||
Assert.notNull(remoteDirectoryExpression, "remoteDirectoryExpression must not be null");
|
||||
this.directoryExpressionProcessor = new ExpressionEvaluatingMessageProcessor<String>(remoteDirectoryExpression, String.class);
|
||||
}
|
||||
|
||||
public void setTemporaryRemoteDirectoryExpression(Expression temporaryRemoteDirectoryExpression) {
|
||||
Assert.notNull(temporaryRemoteDirectoryExpression, "temporaryRemoteDirectoryExpression must not be null");
|
||||
this.temporaryDirectoryExpressionProcessor = new ExpressionEvaluatingMessageProcessor<String>(temporaryRemoteDirectoryExpression, String.class);
|
||||
}
|
||||
|
||||
public void setFileNameExpression(Expression fileNameExpression) {
|
||||
Assert.notNull(fileNameExpression, "fileNameExpression must not be null");
|
||||
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(fileNameExpression, String.class);
|
||||
}
|
||||
|
||||
public String getTemporaryFileSuffix() {
|
||||
return this.temporaryFileSuffix;
|
||||
}
|
||||
|
||||
public boolean isUseTemporaryFileName() {
|
||||
return useTemporaryFileName;
|
||||
}
|
||||
|
||||
public void setUseTemporaryFileName(boolean useTemporaryFileName) {
|
||||
this.useTemporaryFileName = useTemporaryFileName;
|
||||
}
|
||||
|
||||
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
|
||||
this.fileNameGenerator = (fileNameGenerator != null) ? fileNameGenerator : new DefaultFileNameGenerator();
|
||||
this.fileNameGeneratorSet = fileNameGenerator != null;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
|
||||
Assert.notNull(temporaryFileSuffix, "'temporaryFileSuffix' must not be null");
|
||||
this.hasExplicitlySetSuffix = true;
|
||||
this.temporaryFileSuffix = temporaryFileSuffix;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
BeanFactory beanFactory = this.beanFactory;
|
||||
if (beanFactory != null) {
|
||||
if (this.directoryExpressionProcessor != null) {
|
||||
this.directoryExpressionProcessor.setBeanFactory(beanFactory);
|
||||
}
|
||||
if (this.temporaryDirectoryExpressionProcessor != null) {
|
||||
this.temporaryDirectoryExpressionProcessor.setBeanFactory(beanFactory);
|
||||
}
|
||||
if (!this.fileNameGeneratorSet && this.fileNameGenerator instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.fileNameGenerator).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (this.fileNameProcessor != null) {
|
||||
this.fileNameProcessor.setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
if (this.autoCreateDirectory){
|
||||
Assert.hasText(this.remoteFileSeparator, "'remoteFileSeparator' must not be empty when 'autoCreateDirectory' is set to 'true'");
|
||||
}
|
||||
if (hasExplicitlySetSuffix && !useTemporaryFileName){
|
||||
this.logger.warn("Since 'use-temporary-file-name' is set to 'false' the value of 'temporary-file-suffix' has no effect");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(final Message<?> message) {
|
||||
return this.send(message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(final Message<?> message, final String subDirectory) {
|
||||
Assert.notNull(this.directoryExpressionProcessor, "'remoteDirectoryExpression' is required");
|
||||
final StreamHolder inputStreamHolder = this.payloadToInputStream(message);
|
||||
if (inputStreamHolder != null) {
|
||||
return this.execute(new SessionCallback<F, String>() {
|
||||
|
||||
@Override
|
||||
public String doInSession(Session<F> session) throws IOException {
|
||||
String fileName = inputStreamHolder.getName();
|
||||
try {
|
||||
String remoteDirectory = RemoteFileTemplate.this.directoryExpressionProcessor
|
||||
.processMessage(message);
|
||||
remoteDirectory = RemoteFileTemplate.this.normalizeDirectoryPath(remoteDirectory);
|
||||
if (StringUtils.hasText(subDirectory)) {
|
||||
if (subDirectory.startsWith(RemoteFileTemplate.this.remoteFileSeparator)) {
|
||||
remoteDirectory += subDirectory.substring(1);
|
||||
}
|
||||
else {
|
||||
remoteDirectory += RemoteFileTemplate.this.normalizeDirectoryPath(subDirectory);
|
||||
}
|
||||
}
|
||||
String temporaryRemoteDirectory = remoteDirectory;
|
||||
if (RemoteFileTemplate.this.temporaryDirectoryExpressionProcessor != null) {
|
||||
temporaryRemoteDirectory = RemoteFileTemplate.this.temporaryDirectoryExpressionProcessor
|
||||
.processMessage(message);
|
||||
}
|
||||
fileName = RemoteFileTemplate.this.fileNameGenerator.generateFileName(message);
|
||||
RemoteFileTemplate.this.sendFileToRemoteDirectory(inputStreamHolder.getStream(),
|
||||
temporaryRemoteDirectory, remoteDirectory, fileName, session);
|
||||
return remoteDirectory + fileName;
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new MessageDeliveryException(message, "File [" + inputStreamHolder.getName()
|
||||
+ "] not found in local working directory; it was moved or deleted unexpectedly.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageDeliveryException(message, "Failed to transfer file ["
|
||||
+ inputStreamHolder.getName() + " -> " + fileName
|
||||
+ "] from local directory to remote directory.", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message, "Error handling message for file ["
|
||||
+ inputStreamHolder.getName() + " -> " + fileName + "]", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// A null holder means a File payload that does not exist.
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("File " + message.getPayload() + " does not exist");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(final String path) {
|
||||
return this.execute(new SessionCallback<F, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInSession(Session<F> session) throws IOException {
|
||||
return session.remove(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(final String fromPath, final String toPath) {
|
||||
Assert.hasText(fromPath, "Old filename cannot be null or empty");
|
||||
Assert.hasText(toPath, "New filename cannot be null or empty");
|
||||
|
||||
this.execute(new SessionCallbackWithoutResult<F>() {
|
||||
|
||||
@Override
|
||||
public void doInSessionWithoutResult(Session<F> session) throws IOException {
|
||||
int lastSeparator = toPath.lastIndexOf(RemoteFileTemplate.this.remoteFileSeparator);
|
||||
if (lastSeparator > 0) {
|
||||
String remoteFileDirectory = toPath.substring(0, lastSeparator + 1);
|
||||
RemoteFileUtils.makeDirectories(remoteFileDirectory, session,
|
||||
RemoteFileTemplate.this.remoteFileSeparator, RemoteFileTemplate.this.logger);
|
||||
}
|
||||
session.rename(fromPath, toPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean get(final Message<?> message, final InputStreamCallback callback) {
|
||||
Assert.notNull(this.fileNameProcessor, "'fileNameProcessor' needed to use get");
|
||||
return this.execute(new SessionCallback<F, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInSession(Session<F> session) throws IOException {
|
||||
final String remotePath = RemoteFileTemplate.this.fileNameProcessor.processMessage(message);
|
||||
InputStream inputStream = session.readRaw(remotePath);
|
||||
callback.doWithInputStream(inputStream);
|
||||
inputStream.close();
|
||||
return session.finalizeRaw();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T execute(SessionCallback<F, T> callback) {
|
||||
Session<F> session = null;
|
||||
try {
|
||||
session = this.sessionFactory.getSession();
|
||||
Assert.notNull(session, "failed to acquire a Session");
|
||||
return callback.doInSession(session);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessagingException("Failed to execute on session", e);
|
||||
}
|
||||
finally {
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("failed to close Session", ignored);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StreamHolder payloadToInputStream(Message<?> message) throws MessageDeliveryException {
|
||||
try {
|
||||
Object payload = message.getPayload();
|
||||
InputStream dataInputStream = null;
|
||||
String name = null;
|
||||
if (payload instanceof File) {
|
||||
File inputFile = (File) payload;
|
||||
if (inputFile.exists()) {
|
||||
dataInputStream = new BufferedInputStream(new FileInputStream(inputFile));
|
||||
name = inputFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
else if (payload instanceof byte[] || payload instanceof String) {
|
||||
byte[] bytes = null;
|
||||
if (payload instanceof String) {
|
||||
bytes = ((String) payload).getBytes(this.charset);
|
||||
name = "String payload";
|
||||
}
|
||||
else {
|
||||
bytes = (byte[]) payload;
|
||||
name = "byte[] payload";
|
||||
}
|
||||
dataInputStream = new ByteArrayInputStream(bytes);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported payload type. The only supported payloads are " +
|
||||
"java.io.File, java.lang.String, and byte[]");
|
||||
}
|
||||
if (dataInputStream == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return new StreamHolder(dataInputStream, name);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message, "Failed to create sendable file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendFileToRemoteDirectory(InputStream inputStream, String temporaryRemoteDirectory,
|
||||
String remoteDirectory, String fileName, Session<F> session) throws IOException {
|
||||
|
||||
remoteDirectory = this.normalizeDirectoryPath(remoteDirectory);
|
||||
temporaryRemoteDirectory = this.normalizeDirectoryPath(temporaryRemoteDirectory);
|
||||
|
||||
String remoteFilePath = remoteDirectory + fileName;
|
||||
String tempRemoteFilePath = temporaryRemoteDirectory + fileName;
|
||||
// write remote file first with temporary file extension if enabled
|
||||
|
||||
String tempFilePath = tempRemoteFilePath + (useTemporaryFileName ? this.temporaryFileSuffix : "");
|
||||
|
||||
if (this.autoCreateDirectory) {
|
||||
try {
|
||||
RemoteFileUtils.makeDirectories(remoteDirectory, session, this.remoteFileSeparator, this.logger);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// Revert to old FTP behavior if recursive mkdir fails, for backwards compatibility
|
||||
session.mkdir(remoteDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
session.write(inputStream, tempFilePath);
|
||||
// then rename it to its final name if necessary
|
||||
if (useTemporaryFileName){
|
||||
session.rename(tempFilePath, remoteFilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Failed to write to '" + tempFilePath + "' while uploading the file", e);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeDirectoryPath(String directoryPath){
|
||||
if (!StringUtils.hasText(directoryPath)) {
|
||||
directoryPath = "";
|
||||
}
|
||||
else if (!directoryPath.endsWith(this.remoteFileSeparator)) {
|
||||
directoryPath += this.remoteFileSeparator;
|
||||
}
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
private class StreamHolder {
|
||||
|
||||
private final InputStream stream;
|
||||
|
||||
private final String name;
|
||||
|
||||
private StreamHolder(InputStream stream, String name) {
|
||||
this.stream = stream;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public InputStream getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2013 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.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
/**
|
||||
* Callback invoked by {@code RemoteFileOperations.execute()} - allows multiple operations
|
||||
* on a session.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public interface SessionCallback<F, T> {
|
||||
|
||||
/**
|
||||
* Called within the context of a session.
|
||||
* Perform some operation(s) on the session. The caller will take
|
||||
* care of closing the session after this method exits.
|
||||
*
|
||||
* @param session The session.
|
||||
* @return The result of type T.
|
||||
* @throws IOException
|
||||
*/
|
||||
T doInSession(Session<F> session) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013 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.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
/**
|
||||
* Simple convenience implementation of {@link SessionCallback} for cases where
|
||||
* no result is returned.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public abstract class SessionCallbackWithoutResult<F> implements SessionCallback<F, Object> {
|
||||
|
||||
@Override
|
||||
public Object doInSession(Session<F> session) throws IOException {
|
||||
this.doInSessionWithoutResult(session);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called within the context of a session.
|
||||
* Perform some operation(s) on the session. The caller will take
|
||||
* care of closing the session after this method exits.
|
||||
*
|
||||
* @param session The session.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract void doInSessionWithoutResult(Session<F> session) throws IOException;
|
||||
|
||||
}
|
||||
@@ -38,7 +38,8 @@ import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.filters.FileListFilter;
|
||||
import org.springframework.integration.file.remote.AbstractFileInfo;
|
||||
import org.springframework.integration.file.remote.RemoteFileUtils;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
@@ -59,7 +60,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
protected final SessionFactory<F> sessionFactory;
|
||||
private final RemoteFileTemplate<F> remoteFileTemplate;
|
||||
|
||||
protected final Command command;
|
||||
|
||||
@@ -91,7 +92,17 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
/**
|
||||
* Move (rename) a remote file.
|
||||
*/
|
||||
MV("mv");
|
||||
MV("mv"),
|
||||
|
||||
/**
|
||||
* Put a local file to the remote system.
|
||||
*/
|
||||
PUT("put"),
|
||||
|
||||
/**
|
||||
* Put multiple local files to the remote system.
|
||||
*/
|
||||
MPUT("mput");
|
||||
|
||||
private String command;
|
||||
|
||||
@@ -187,25 +198,27 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
protected volatile Set<Option> options = new HashSet<Option>();
|
||||
|
||||
private volatile String remoteFileSeparator = "/";
|
||||
|
||||
private volatile Expression localDirectoryExpression;
|
||||
|
||||
private volatile boolean autoCreateLocalDirectory = true;
|
||||
|
||||
private volatile String temporaryFileSuffix = ".writing";
|
||||
|
||||
/**
|
||||
* An {@link FileListFilter} that runs against the <em>remote</em> file system view.
|
||||
* A {@link FileListFilter} that runs against the <em>remote</em> file system view.
|
||||
*/
|
||||
private volatile FileListFilter<F> filter;
|
||||
|
||||
/**
|
||||
* A {@link FileListFilter} that runs against the <em>local</em> file system view when
|
||||
* using MPUT.
|
||||
*/
|
||||
private volatile FileListFilter<File> mputFilter;
|
||||
|
||||
private volatile Expression localFilenameGeneratorExpression;
|
||||
|
||||
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, String command,
|
||||
String expression) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
Assert.notNull(sessionFactory, "'sessionFactory' cannot be null");
|
||||
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
|
||||
this.command = Command.toCommand(command);
|
||||
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
|
||||
new SpelExpressionParser().parseExpression(expression));
|
||||
@@ -213,12 +226,30 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, Command command,
|
||||
String expression) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
Assert.notNull(sessionFactory, "'sessionFactory' cannot be null");
|
||||
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
|
||||
this.command = command;
|
||||
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
|
||||
new SpelExpressionParser().parseExpression(expression));
|
||||
}
|
||||
|
||||
public AbstractRemoteFileOutboundGateway(RemoteFileTemplate<F> remoteFileTemplate, String command,
|
||||
String expression) {
|
||||
Assert.notNull(remoteFileTemplate, "'remoteFileTemplate' cannot be null");
|
||||
this.remoteFileTemplate = remoteFileTemplate;
|
||||
this.command = Command.toCommand(command);
|
||||
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
|
||||
new SpelExpressionParser().parseExpression(expression));
|
||||
}
|
||||
|
||||
public AbstractRemoteFileOutboundGateway(RemoteFileTemplate<F> remoteFileTemplate, Command command,
|
||||
String expression) {
|
||||
Assert.notNull(remoteFileTemplate, "'remoteFileTemplate' cannot be null");
|
||||
this.remoteFileTemplate = remoteFileTemplate;
|
||||
this.command = command;
|
||||
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
|
||||
new SpelExpressionParser().parseExpression(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options the options to set
|
||||
@@ -237,7 +268,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
* @param remoteFileSeparator the remoteFileSeparator to set
|
||||
*/
|
||||
public void setRemoteFileSeparator(String remoteFileSeparator) {
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
this.remoteFileTemplate.setRemoteFileSeparator(remoteFileSeparator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,7 +295,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
* @param temporaryFileSuffix the temporaryFileSuffix to set
|
||||
*/
|
||||
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
|
||||
this.temporaryFileSuffix = temporaryFileSuffix;
|
||||
this.remoteFileTemplate.setTemporaryFileSuffix(temporaryFileSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,6 +305,13 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filter the filter to set
|
||||
*/
|
||||
public void setMputFilter(FileListFilter<File> filter) {
|
||||
this.mputFilter = filter;
|
||||
}
|
||||
|
||||
public void setRenameExpression(String expression) {
|
||||
Assert.notNull(expression, "'expression' cannot be null");
|
||||
this.renameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
|
||||
@@ -330,88 +368,105 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
if (this.getBeanFactory() != null) {
|
||||
this.fileNameProcessor.setBeanFactory(this.getBeanFactory());
|
||||
this.renameProcessor.setBeanFactory(this.getBeanFactory());
|
||||
this.remoteFileTemplate.setBeanFactory(this.getBeanFactory());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Session<F> session = this.sessionFactory.getSession();
|
||||
try {
|
||||
switch (this.command) {
|
||||
case LS:
|
||||
return doLs(requestMessage, session);
|
||||
case GET:
|
||||
return doGet(requestMessage, session);
|
||||
case MGET:
|
||||
return doMget(requestMessage, session);
|
||||
case RM:
|
||||
return doRm(requestMessage, session);
|
||||
case MV:
|
||||
return doMv(requestMessage, session);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessagingException(requestMessage, e);
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
switch (this.command) {
|
||||
case LS:
|
||||
return doLs(requestMessage);
|
||||
case GET:
|
||||
return doGet(requestMessage);
|
||||
case MGET:
|
||||
return doMget(requestMessage);
|
||||
case RM:
|
||||
return doRm(requestMessage);
|
||||
case MV:
|
||||
return doMv(requestMessage);
|
||||
case PUT:
|
||||
return doPut(requestMessage);
|
||||
case MPUT:
|
||||
return doMput(requestMessage);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object doLs(Message<?> requestMessage, Session<F> session) throws IOException {
|
||||
private Object doLs(Message<?> requestMessage) {
|
||||
String dir = this.fileNameProcessor.processMessage(requestMessage);
|
||||
if (!dir.endsWith(this.remoteFileSeparator)) {
|
||||
dir += this.remoteFileSeparator;
|
||||
if (!dir.endsWith(this.remoteFileTemplate.getRemoteFileSeparator())) {
|
||||
dir += this.remoteFileTemplate.getRemoteFileSeparator();
|
||||
}
|
||||
List<?> payload = ls(session, dir);
|
||||
final String fullDir = dir;
|
||||
List<?> payload = this.remoteFileTemplate.execute(new SessionCallback<F, List<?>>() {
|
||||
|
||||
@Override
|
||||
public List<?> doInSession(Session<F> session) throws IOException {
|
||||
return AbstractRemoteFileOutboundGateway.this.ls(session, fullDir);
|
||||
}
|
||||
});
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, dir)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Object doGet(Message<?> requestMessage, Session<F> session) throws IOException {
|
||||
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
File payload = this.get(requestMessage, session, remoteDir, remoteFilePath, remoteFilename, true);
|
||||
private Object doGet(final Message<?> requestMessage) {
|
||||
final String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
final String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
final String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
File payload = this.remoteFileTemplate.execute(new SessionCallback<F, File>() {
|
||||
|
||||
@Override
|
||||
public File doInSession(Session<F> session) throws IOException {
|
||||
return AbstractRemoteFileOutboundGateway.this.get(requestMessage, session, remoteDir, remoteFilePath,
|
||||
remoteFilename, true);
|
||||
|
||||
}
|
||||
});
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Object doMget(Message<?> requestMessage, Session<F> session) throws IOException {
|
||||
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
List<File> payload = this.mGet(requestMessage, session, remoteDir, remoteFilename);
|
||||
private Object doMget(final Message<?> requestMessage) {
|
||||
final String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
final String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
final String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
List<File> payload = this.remoteFileTemplate.execute(new SessionCallback<F, List<File>>() {
|
||||
|
||||
@Override
|
||||
public List<File> doInSession(Session<F> session) throws IOException {
|
||||
return AbstractRemoteFileOutboundGateway.this.mGet(requestMessage, session, remoteDir, remoteFilename);
|
||||
}
|
||||
});
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Object doRm(Message<?> requestMessage, Session<F> session) throws IOException {
|
||||
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
private Object doRm(Message<?> requestMessage) {
|
||||
final String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
boolean payload = this.rm(session, remoteFilePath);
|
||||
boolean payload = this.remoteFileTemplate.remove(remoteFilePath);
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Object doMv(Message<?> requestMessage, Session<F> session) throws IOException {
|
||||
private Object doMv(Message<?> requestMessage) {
|
||||
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
|
||||
String remoteFilename = this.getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = this.getRemoteDirectory(remoteFilePath, remoteFilename);
|
||||
String remoteFileNewPath = this.renameProcessor.processMessage(requestMessage);
|
||||
Assert.hasLength(remoteFileNewPath, "New filename cannot be empty");
|
||||
|
||||
this.mv(session, remoteFilePath, remoteFileNewPath);
|
||||
this.remoteFileTemplate.rename(remoteFilePath, remoteFileNewPath);
|
||||
return MessageBuilder.withPayload(Boolean.TRUE)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
@@ -419,6 +474,66 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
.build();
|
||||
}
|
||||
|
||||
private String doPut(Message<?> requestMessage) {
|
||||
return this.doPut(requestMessage, null);
|
||||
}
|
||||
|
||||
private String doPut(Message<?> requestMessage, String subDirectory) {
|
||||
String path = this.remoteFileTemplate.send(requestMessage, subDirectory);
|
||||
if (path == null) {
|
||||
throw new MessagingException(requestMessage, "No local file found for " + requestMessage);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private Object doMput(Message<?> requestMessage) {
|
||||
File file = null;
|
||||
if (requestMessage.getPayload() instanceof File) {
|
||||
file = (File) requestMessage.getPayload();
|
||||
}
|
||||
else if (requestMessage.getPayload() instanceof String) {
|
||||
file = new File((String) requestMessage.getPayload());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Only File or String payloads allowed for 'mput'");
|
||||
}
|
||||
if (!file.isDirectory()) {
|
||||
return this.doPut(requestMessage);
|
||||
}
|
||||
else {
|
||||
List<String> replies = this.putLocalDirectory(requestMessage, file, null);
|
||||
return replies;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> putLocalDirectory(Message<?> requestMessage, File file, String subDirectory) {
|
||||
File[] files = file.listFiles();
|
||||
List<File> filteredFiles = this.filterMputFiles(files);
|
||||
List<String> replies = new ArrayList<String>();
|
||||
for (File filteredFile : filteredFiles) {
|
||||
if (!filteredFile.isDirectory()) {
|
||||
String path = this.doPut(MessageBuilder.withPayload(filteredFile)
|
||||
.copyHeaders(requestMessage.getHeaders())
|
||||
.build(), subDirectory);
|
||||
if (path == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("File " + filteredFile.getAbsolutePath() + " removed before transfer; ignoring");
|
||||
}
|
||||
}
|
||||
else {
|
||||
replies.add(path);
|
||||
}
|
||||
}
|
||||
else if (this.options.contains(Option.RECURSIVE)){
|
||||
String newSubDirectory = (StringUtils.hasText(subDirectory) ?
|
||||
subDirectory + this.remoteFileTemplate.getRemoteFileSeparator() : "")
|
||||
+ filteredFile.getName();
|
||||
replies.addAll(this.putLocalDirectory(requestMessage, filteredFile, newSubDirectory));
|
||||
}
|
||||
}
|
||||
return replies;
|
||||
}
|
||||
|
||||
protected List<?> ls(Session<F> session, String dir) throws IOException {
|
||||
List<F> lsFiles = listFilesInRemoteDir(session, dir, "");
|
||||
if (!this.options.contains(Option.LINKS)) {
|
||||
@@ -467,7 +582,8 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
}
|
||||
}
|
||||
if (recursion && this.isDirectory(file) && !(".".equals(fileName)) && !("..".equals(fileName))) {
|
||||
lsFiles.addAll(listFilesInRemoteDir(session, directory, subDirectory + fileName + this.remoteFileSeparator));
|
||||
lsFiles.addAll(listFilesInRemoteDir(session, directory, subDirectory + fileName
|
||||
+ this.remoteFileTemplate.getRemoteFileSeparator()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,6 +595,13 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
return (this.filter != null) ? this.filter.filterFiles(files) : Arrays.asList(files);
|
||||
}
|
||||
|
||||
protected final List<File> filterMputFiles(File[] files) {
|
||||
if (files == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return (this.mputFilter != null) ? this.mputFilter.filterFiles(files) : Arrays.asList(files);
|
||||
}
|
||||
|
||||
protected void purgeLinks(List<F> lsFiles) {
|
||||
Iterator<F> iterator = lsFiles.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
@@ -520,7 +643,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
}
|
||||
File localFile = new File(this.generateLocalDirectory(message, remoteDir), this.generateLocalFileName(message, remoteFilename));
|
||||
if (!localFile.exists()) {
|
||||
String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix;
|
||||
String tempFileName = localFile.getAbsolutePath() + this.remoteFileTemplate.getTemporaryFileSuffix();
|
||||
File tempFile = new File(tempFileName);
|
||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
|
||||
try {
|
||||
@@ -583,12 +706,13 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
+ " with pattern " + remoteFilename);
|
||||
}
|
||||
List<File> files = new ArrayList<File>();
|
||||
String remoteFileSeparator = this.remoteFileTemplate.getRemoteFileSeparator();
|
||||
for (String fileName : fileNames) {
|
||||
File file;
|
||||
if (fileName.contains(this.remoteFileSeparator) &&
|
||||
if (fileName.contains(remoteFileSeparator) &&
|
||||
fileName.startsWith(remoteDirectory)) { // the server returned the full path
|
||||
file = this.get(message, session, remoteDirectory, fileName,
|
||||
fileName.substring(fileName.lastIndexOf(this.remoteFileSeparator)), false);
|
||||
fileName.substring(fileName.lastIndexOf(remoteFileSeparator)), false);
|
||||
}
|
||||
else {
|
||||
file = this.get(message, session, remoteDirectory,
|
||||
@@ -626,21 +750,22 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
private String getRemoteDirectory(String remoteFilePath, String remoteFilename) {
|
||||
String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf(remoteFilename));
|
||||
if (remoteDir.length() == 0) {
|
||||
remoteDir = this.remoteFileSeparator;
|
||||
remoteDir = this.remoteFileTemplate.getRemoteFileSeparator();
|
||||
}
|
||||
return remoteDir;
|
||||
}
|
||||
|
||||
private String generateFullPath(String remoteDirectory, String remoteFilename) {
|
||||
String path;
|
||||
if (this.remoteFileSeparator.equals(remoteDirectory)) {
|
||||
String remoteFileSeparator = this.remoteFileTemplate.getRemoteFileSeparator();
|
||||
if (remoteFileSeparator.equals(remoteDirectory)) {
|
||||
path = remoteFilename;
|
||||
}
|
||||
else if (remoteDirectory.endsWith(this.remoteFileSeparator)) {
|
||||
else if (remoteDirectory.endsWith(remoteFileSeparator)) {
|
||||
path = remoteDirectory + remoteFilename;
|
||||
}
|
||||
else {
|
||||
path = remoteDirectory + this.remoteFileSeparator + remoteFilename;
|
||||
path = remoteDirectory + remoteFileSeparator + remoteFilename;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@@ -650,7 +775,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
*/
|
||||
protected String getRemoteFilename(String remoteFilePath) {
|
||||
String remoteFileName;
|
||||
int index = remoteFilePath.lastIndexOf(this.remoteFileSeparator);
|
||||
int index = remoteFilePath.lastIndexOf(this.remoteFileTemplate.getRemoteFileSeparator());
|
||||
if (index < 0) {
|
||||
remoteFileName = remoteFilePath;
|
||||
}
|
||||
@@ -660,20 +785,6 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
return remoteFileName;
|
||||
}
|
||||
|
||||
protected boolean rm(Session<?> session, String remoteFilePath)
|
||||
throws IOException {
|
||||
return session.remove(remoteFilePath);
|
||||
}
|
||||
|
||||
protected void mv(Session<?> session, String remoteFilePath, String remoteFileNewPath) throws IOException {
|
||||
int lastSeparator = remoteFileNewPath.lastIndexOf(this.remoteFileSeparator);
|
||||
if (lastSeparator > 0) {
|
||||
String remoteFileDirectory = remoteFileNewPath.substring(0, lastSeparator + 1);
|
||||
RemoteFileUtils.makeDirectories(remoteFileDirectory, session, this.remoteFileSeparator, this.logger);
|
||||
}
|
||||
session.rename(remoteFilePath, remoteFileNewPath);
|
||||
}
|
||||
|
||||
private File generateLocalDirectory(Message<?> message, String remoteDirectory) {
|
||||
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
evaluationContext.setVariable("remoteDirectory", remoteDirectory);
|
||||
@@ -707,4 +818,5 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
abstract protected List<AbstractFileInfo<F>> asFileInfoList(Collection<F> files);
|
||||
|
||||
abstract protected F enhanceNameWithSubDirectory(F file, String directory);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,29 +16,15 @@
|
||||
|
||||
package org.springframework.integration.file.remote.handler;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.RemoteFileUtils;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.messaging.MessageHandler} implementation that transfers files to a remote server.
|
||||
@@ -53,56 +39,37 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
|
||||
|
||||
private volatile String temporaryFileSuffix =".writing";
|
||||
|
||||
private final SessionFactory<F> sessionFactory;
|
||||
|
||||
private volatile boolean autoCreateDirectory = false;
|
||||
|
||||
private volatile boolean useTemporaryFileName = true;
|
||||
|
||||
private volatile ExpressionEvaluatingMessageProcessor<String> directoryExpressionProcessor;
|
||||
|
||||
private volatile ExpressionEvaluatingMessageProcessor<String> temporaryDirectoryExpressionProcessor;
|
||||
|
||||
private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
|
||||
|
||||
private volatile boolean fileNameGeneratorSet;
|
||||
|
||||
private volatile String charset = "UTF-8";
|
||||
|
||||
private volatile String remoteFileSeparator = "/";
|
||||
|
||||
private volatile boolean hasExplicitlySetSuffix;
|
||||
|
||||
private final RemoteFileTemplate<F> remoteFileTemplate;
|
||||
|
||||
public FileTransferringMessageHandler(SessionFactory<F> sessionFactory) {
|
||||
Assert.notNull(sessionFactory, "sessionFactory must not be null");
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
|
||||
}
|
||||
|
||||
public FileTransferringMessageHandler(RemoteFileTemplate<F> remoteFileTemplate) {
|
||||
Assert.notNull(remoteFileTemplate, "remoteFileTemplate must not be null");
|
||||
this.remoteFileTemplate = remoteFileTemplate;
|
||||
}
|
||||
|
||||
|
||||
public void setAutoCreateDirectory(boolean autoCreateDirectory) {
|
||||
this.autoCreateDirectory = autoCreateDirectory;
|
||||
this.remoteFileTemplate.setAutoCreateDirectory(autoCreateDirectory);
|
||||
}
|
||||
|
||||
public void setRemoteFileSeparator(String remoteFileSeparator) {
|
||||
Assert.notNull(remoteFileSeparator, "'remoteFileSeparator' must not be null");
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
this.remoteFileTemplate.setRemoteFileSeparator(remoteFileSeparator);
|
||||
}
|
||||
|
||||
public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) {
|
||||
Assert.notNull(remoteDirectoryExpression, "remoteDirectoryExpression must not be null");
|
||||
this.directoryExpressionProcessor = new ExpressionEvaluatingMessageProcessor<String>(remoteDirectoryExpression, String.class);
|
||||
this.remoteFileTemplate.setRemoteDirectoryExpression(remoteDirectoryExpression);
|
||||
}
|
||||
|
||||
public void setTemporaryRemoteDirectoryExpression(Expression temporaryRemoteDirectoryExpression) {
|
||||
Assert.notNull(temporaryRemoteDirectoryExpression, "temporaryRemoteDirectoryExpression must not be null");
|
||||
this.temporaryDirectoryExpressionProcessor = new ExpressionEvaluatingMessageProcessor<String>(temporaryRemoteDirectoryExpression, String.class);
|
||||
this.remoteFileTemplate.setTemporaryRemoteDirectoryExpression(temporaryRemoteDirectoryExpression);
|
||||
}
|
||||
|
||||
protected String getTemporaryFileSuffix() {
|
||||
return this.temporaryFileSuffix;
|
||||
return this.remoteFileTemplate.getTemporaryFileSuffix();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,198 +80,36 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
|
||||
}
|
||||
|
||||
protected boolean isUseTemporaryFileName() {
|
||||
return useTemporaryFileName;
|
||||
return this.remoteFileTemplate.isUseTemporaryFileName();
|
||||
}
|
||||
|
||||
|
||||
public void setUseTemporaryFileName(boolean useTemporaryFileName) {
|
||||
this.useTemporaryFileName = useTemporaryFileName;
|
||||
this.remoteFileTemplate.setUseTemporaryFileName(useTemporaryFileName);
|
||||
}
|
||||
|
||||
|
||||
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
|
||||
this.fileNameGenerator = (fileNameGenerator != null) ? fileNameGenerator : new DefaultFileNameGenerator();
|
||||
this.fileNameGeneratorSet = fileNameGenerator != null;
|
||||
this.remoteFileTemplate.setFileNameGenerator(fileNameGenerator);
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
this.remoteFileTemplate.setCharset(charset);
|
||||
}
|
||||
|
||||
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
|
||||
Assert.notNull(temporaryFileSuffix, "'temporaryFileSuffix' must not be null");
|
||||
this.hasExplicitlySetSuffix = true;
|
||||
this.temporaryFileSuffix = temporaryFileSuffix;
|
||||
this.remoteFileTemplate.setTemporaryFileSuffix(temporaryFileSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.directoryExpressionProcessor, "remoteDirectoryExpression is required");
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
if (beanFactory != null) {
|
||||
this.directoryExpressionProcessor.setBeanFactory(beanFactory);
|
||||
if (this.temporaryDirectoryExpressionProcessor != null) {
|
||||
this.temporaryDirectoryExpressionProcessor.setBeanFactory(beanFactory);
|
||||
}
|
||||
if (!this.fileNameGeneratorSet && this.fileNameGenerator instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.fileNameGenerator).setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
if (this.autoCreateDirectory){
|
||||
Assert.hasText(this.remoteFileSeparator, "'remoteFileSeparator' must not be empty when 'autoCreateDirectory' is set to 'true'");
|
||||
}
|
||||
if (hasExplicitlySetSuffix && !useTemporaryFileName){
|
||||
this.logger.warn("Since 'use-temporary-file-name' is set to 'false' the value of 'temporary-file-suffix' has no effect");
|
||||
}
|
||||
this.remoteFileTemplate.setBeanFactory(this.getBeanFactory());
|
||||
this.remoteFileTemplate.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
StreamHolder inputStreamHolder = this.payloadToInputStream(message);
|
||||
if (inputStreamHolder != null) {
|
||||
Session<F> session = this.sessionFactory.getSession();
|
||||
String fileName = inputStreamHolder.getName();
|
||||
try {
|
||||
String remoteDirectory = this.directoryExpressionProcessor.processMessage(message);
|
||||
String temporaryRemoteDirectory = remoteDirectory;
|
||||
if (this.temporaryDirectoryExpressionProcessor != null){
|
||||
temporaryRemoteDirectory = this.temporaryDirectoryExpressionProcessor.processMessage(message);
|
||||
}
|
||||
fileName = this.fileNameGenerator.generateFileName(message);
|
||||
this.sendFileToRemoteDirectory(inputStreamHolder.getStream(), temporaryRemoteDirectory, remoteDirectory, fileName, session);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"File [" + inputStreamHolder.getName() + "] not found in local working directory; it was moved or deleted unexpectedly.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Failed to transfer file [" + inputStreamHolder.getName() + " -> " + fileName + "] from local directory to remote directory.", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Error handling message for file [" + inputStreamHolder.getName() + " -> " + fileName + "]", e);
|
||||
}
|
||||
finally {
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A null holder means a File payload that does not exist.
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("File " + message.getPayload() + " does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StreamHolder payloadToInputStream(Message<?> message) throws MessageDeliveryException {
|
||||
try {
|
||||
Object payload = message.getPayload();
|
||||
InputStream dataInputStream = null;
|
||||
String name = null;
|
||||
if (payload instanceof File) {
|
||||
File inputFile = (File) payload;
|
||||
if (inputFile.exists()) {
|
||||
dataInputStream = new BufferedInputStream(new FileInputStream(inputFile));
|
||||
name = inputFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
else if (payload instanceof byte[] || payload instanceof String) {
|
||||
byte[] bytes = null;
|
||||
if (payload instanceof String) {
|
||||
bytes = ((String) payload).getBytes(this.charset);
|
||||
name = "String payload";
|
||||
}
|
||||
else {
|
||||
bytes = (byte[]) payload;
|
||||
name = "byte[] payload";
|
||||
}
|
||||
dataInputStream = new ByteArrayInputStream(bytes);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported payload type. The only supported payloads are " +
|
||||
"java.io.File, java.lang.String, and byte[]");
|
||||
}
|
||||
if (dataInputStream == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return new StreamHolder(dataInputStream, name);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message, "Failed to create sendable file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendFileToRemoteDirectory(InputStream inputStream, String temporaryRemoteDirectory,
|
||||
String remoteDirectory, String fileName, Session<F> session) throws FileNotFoundException, IOException {
|
||||
|
||||
remoteDirectory = this.normalizeDirectoryPath(remoteDirectory);
|
||||
temporaryRemoteDirectory = this.normalizeDirectoryPath(temporaryRemoteDirectory);
|
||||
|
||||
String remoteFilePath = remoteDirectory + fileName;
|
||||
String tempRemoteFilePath = temporaryRemoteDirectory + fileName;
|
||||
// write remote file first with temporary file extension if enabled
|
||||
|
||||
String tempFilePath = tempRemoteFilePath + (useTemporaryFileName ? this.temporaryFileSuffix : "");
|
||||
|
||||
if (this.autoCreateDirectory) {
|
||||
try {
|
||||
RemoteFileUtils.makeDirectories(remoteDirectory, session, this.remoteFileSeparator, this.logger);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// Revert to old FTP behavior if recursive mkdir fails, for backwards compatibility
|
||||
session.mkdir(remoteDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
session.write(inputStream, tempFilePath);
|
||||
// then rename it to its final name if necessary
|
||||
if (useTemporaryFileName){
|
||||
session.rename(tempFilePath, remoteFilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Failed to write to '" + tempFilePath + "' while uploading the file", e);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeDirectoryPath(String directoryPath){
|
||||
if (!StringUtils.hasText(directoryPath)) {
|
||||
directoryPath = "";
|
||||
}
|
||||
else if (!directoryPath.endsWith(this.remoteFileSeparator)) {
|
||||
directoryPath += this.remoteFileSeparator;
|
||||
}
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
private class StreamHolder {
|
||||
|
||||
private final InputStream stream;
|
||||
|
||||
private final String name;
|
||||
|
||||
private StreamHolder(InputStream stream, String name) {
|
||||
this.stream = stream;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public InputStream getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
this.remoteFileTemplate.send(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.expression.IntegrationEvaluationContextAware;
|
||||
import org.springframework.integration.file.filters.FileListFilter;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
@@ -59,6 +61,8 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final RemoteFileTemplate<F> remoteFileTemplate;
|
||||
|
||||
private volatile EvaluationContext evaluationContext;
|
||||
|
||||
private volatile String remoteFileSeparator = "/";
|
||||
@@ -75,11 +79,6 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
*/
|
||||
private volatile String remoteDirectory;
|
||||
|
||||
/**
|
||||
* the {@link SessionFactory} for acquiring remote file Sessions.
|
||||
*/
|
||||
private final SessionFactory<F> sessionFactory;
|
||||
|
||||
/**
|
||||
* An {@link FileListFilter} that runs against the <em>remote</em> file system view.
|
||||
*/
|
||||
@@ -102,7 +101,7 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
*/
|
||||
public AbstractInboundFileSynchronizer(SessionFactory<F> sessionFactory) {
|
||||
Assert.notNull(sessionFactory, "sessionFactory must not be null");
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +143,7 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void afterPropertiesSet() {
|
||||
Assert.notNull(this.remoteDirectory, "remoteDirectory must not be null");
|
||||
Assert.notNull(this.evaluationContext, "evaluationContext must not be null");
|
||||
@@ -157,36 +157,37 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
return temporaryFileSuffix;
|
||||
}
|
||||
|
||||
public void synchronizeToLocalDirectory(File localDirectory) {
|
||||
Session<F> session = null;
|
||||
@Override
|
||||
public void synchronizeToLocalDirectory(final File localDirectory) {
|
||||
try {
|
||||
session = this.sessionFactory.getSession();
|
||||
Assert.notNull(session, "failed to acquire a Session");
|
||||
F[] files = session.list(this.remoteDirectory);
|
||||
if (!ObjectUtils.isEmpty(files)) {
|
||||
Collection<F> filteredFiles = this.filterFiles(files);
|
||||
for (F file : filteredFiles) {
|
||||
if (file != null) {
|
||||
this.copyFileToLocalDirectory(this.remoteDirectory, file, localDirectory, session);
|
||||
int transferred = this.remoteFileTemplate.execute(new SessionCallback<F, Integer>() {
|
||||
|
||||
@Override
|
||||
public Integer doInSession(Session<F> session) throws IOException {
|
||||
F[] files = session.list(AbstractInboundFileSynchronizer.this.remoteDirectory);
|
||||
if (!ObjectUtils.isEmpty(files)) {
|
||||
Collection<F> filteredFiles = AbstractInboundFileSynchronizer.this.filterFiles(files);
|
||||
for (F file : filteredFiles) {
|
||||
if (file != null) {
|
||||
AbstractInboundFileSynchronizer.this.copyFileToLocalDirectory(
|
||||
AbstractInboundFileSynchronizer.this.remoteDirectory, file, localDirectory,
|
||||
session);
|
||||
}
|
||||
}
|
||||
return filteredFiles.size();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(transferred + " files transferred");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Problem occurred while synchronizing remote to local directory", e);
|
||||
}
|
||||
finally {
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("failed to close Session", ignored);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFileToLocalDirectory(String remoteDirectoryPath, F remoteFile, File localDirectory, Session<F> session) throws IOException {
|
||||
|
||||
@@ -645,7 +645,79 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
<xsd:enumeration value="rm"/>
|
||||
<xsd:enumeration value="mget"/>
|
||||
<xsd:enumeration value="mv"/>
|
||||
<xsd:enumeration value="put"/>
|
||||
<xsd:enumeration value="mput"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:attributeGroup name="remoteOutboundAttributeGroup">
|
||||
<xsd:attribute name="remote-directory-expression"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to provide a SpEL expression which
|
||||
will compute the directory
|
||||
path where the files will be transferred to
|
||||
(e.g., "headers.['remote_dir'] +
|
||||
'/myTransfers'");
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="temporary-remote-directory-expression"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to provide a SpEL expression which
|
||||
will compute the temporary directory
|
||||
path where files will be transferred to before they are moved to the remote-directory
|
||||
(e.g., "headers.['remote_dir'] +
|
||||
'/temp/myTransfers'");
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="auto-create-directory" type="xsd:string"
|
||||
default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether to automatically create the
|
||||
remote target directory if
|
||||
it doesn't exist.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="remote-filename-generator" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to specify a reference to
|
||||
[org.springframework.integration.file.FileNameGenerator] bean.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.integration.file.FileNameGenerator" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="remote-filename-generator-expression"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to provide SpEL expression which
|
||||
will compute file name of
|
||||
the remote file (e.g., assuming payload
|
||||
is java.io.File
|
||||
"payload.getName() + '.transfered'");
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="use-temporary-file-name" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to suppress using a temporary file name while writing the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -122,6 +122,17 @@ public class DefaultFileNameGeneratorTests {
|
||||
assertEquals("bar", filename);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExpressionTakesPrecedenceOverFilePayload() {
|
||||
DefaultFileNameGenerator generator = new DefaultFileNameGenerator();
|
||||
generator.setBeanFactory(mock(BeanFactory.class));
|
||||
generator.setExpression("'foobar'");
|
||||
File payload = new File("/some/path/ignore");
|
||||
Message<?> message = MessageBuilder.withPayload(payload).build();
|
||||
String filename = generator.generateFileName(message);
|
||||
assertEquals("foobar", filename);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customHeaderNameTakesPrecedenceOverDefault() {
|
||||
DefaultFileNameGenerator generator = new DefaultFileNameGenerator();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p" 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.xsd">
|
||||
|
||||
<!-- under test -->
|
||||
<bean id="pollableFileSource" class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:directory="file:${java.io.tmpdir}/FileReadingMessageSourcePersistentFilterIntegrationTests"
|
||||
p:filter-ref="persistentFilter"/>
|
||||
|
||||
<!-- persistent filter -->
|
||||
<bean id="persistentFilter" class="org.springframework.integration.file.filters.FileSystemPersistentAcceptOnceFileListFilter">
|
||||
<constructor-arg ref="ppms" />
|
||||
<constructor-arg value="frmsPersistTest" />
|
||||
</bean>
|
||||
|
||||
<bean id="ppms" class="org.springframework.integration.metadata.PropertiesPersistingMetadataStore">
|
||||
<property name="baseDirectory"
|
||||
value="#{T(System).getProperty('java.io.tmpdir') + T(java.io.File).separator + 'FileReadingMessageSourcePersistentFilterIntegrationTests.meta'}"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.file;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Iwein Fuld
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class FileReadingMessageSourcePersistentFilterIntegrationTests {
|
||||
|
||||
AbstractApplicationContext context;
|
||||
|
||||
FileReadingMessageSource pollableFileSource;
|
||||
|
||||
private static File inputDir;
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() throws Throwable {
|
||||
if(inputDir.exists()) {
|
||||
inputDir.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setupInputDir() {
|
||||
inputDir = new File(System.getProperty("java.io.tmpdir") + "/"
|
||||
+ FileReadingMessageSourcePersistentFilterIntegrationTests.class.getSimpleName());
|
||||
inputDir.mkdir();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void generateTestFiles() throws Exception {
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
File.createTempFile("test", null, inputDir).setLastModified(System.currentTimeMillis() - 1000);
|
||||
this.loadContextAndGetMessageSource();
|
||||
}
|
||||
|
||||
private void loadContextAndGetMessageSource() {
|
||||
this.context = new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-context.xml",
|
||||
this.getClass());
|
||||
this.pollableFileSource = context.getBean(FileReadingMessageSource.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanoutInputDir() throws Exception {
|
||||
File[] listFiles = inputDir.listFiles();
|
||||
for (int i = 0; i < listFiles.length; i++) {
|
||||
listFiles[i].delete();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void removeInputDir() throws Exception {
|
||||
inputDir.delete();
|
||||
File persistDir = new File(System.getProperty("java.io.tmpdir") + "/"
|
||||
+ FileReadingMessageSourcePersistentFilterIntegrationTests.class.getSimpleName()
|
||||
+ ".meta");
|
||||
File persist = new File(persistDir, "metadata-store.properties");
|
||||
persist.delete();
|
||||
persistDir.delete();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void configured() throws Exception {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(pollableFileSource);
|
||||
assertEquals(inputDir, accessor.getPropertyValue("directory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFiles() throws Exception {
|
||||
Message<File> received1 = pollableFileSource.receive();
|
||||
System.out.println("receive files round 1");
|
||||
assertNotNull("This should return the first message", received1);
|
||||
pollableFileSource.onSend(received1);
|
||||
Message<File> received2 = pollableFileSource.receive();
|
||||
assertNotNull(received2);
|
||||
pollableFileSource.onSend(received2);
|
||||
Message<File> received3 = pollableFileSource.receive();
|
||||
assertNotNull(received3);
|
||||
pollableFileSource.onSend(received3);
|
||||
assertNotSame(received1 + " == " + received2, received1.getPayload(), received2.getPayload());
|
||||
assertNotSame(received1 + " == " + received3, received1.getPayload(), received3.getPayload());
|
||||
assertNotSame(received2 + " == " + received3, received2.getPayload(), received3.getPayload());
|
||||
this.context.destroy();
|
||||
|
||||
this.loadContextAndGetMessageSource();
|
||||
Message<File> received4 = pollableFileSource.receive();
|
||||
assertNull(received4);
|
||||
this.context.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -106,9 +106,9 @@ public class FileOutboundChannelAdapterParserTests {
|
||||
assertThat(actual, is(expected));
|
||||
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
|
||||
assertNotNull(fileNameGenerator);
|
||||
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
|
||||
Expression expression = TestUtils.getPropertyValue(fileNameGenerator, "expression", Expression.class);
|
||||
assertNotNull(expression);
|
||||
assertEquals("'foo.txt'", expression);
|
||||
assertEquals("'foo.txt'", expression.getExpressionString());
|
||||
assertEquals(Boolean.FALSE, handlerAccessor.getPropertyValue("deleteSourceFiles"));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -93,9 +94,9 @@ public class FileOutboundGatewayParserTests {
|
||||
assertEquals(Boolean.TRUE, handlerAccessor.getPropertyValue("requiresReply"));
|
||||
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
|
||||
assertNotNull(fileNameGenerator);
|
||||
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
|
||||
Expression expression = TestUtils.getPropertyValue(fileNameGenerator, "expression", Expression.class);
|
||||
assertNotNull(expression);
|
||||
assertEquals("'foo.txt'", expression);
|
||||
assertEquals("'foo.txt'", expression.getExpressionString());
|
||||
|
||||
Long sendTimeout = TestUtils.getPropertyValue(handler, "messagingTemplate.sendTimeout", Long.class);
|
||||
assertEquals(Long.valueOf(777), sendTimeout);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.filters;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class PersistentAcceptOnceFileListFilterTests {
|
||||
|
||||
@Test
|
||||
public void testFileSystem() throws Exception {
|
||||
MetadataStore store = new SimpleMetadataStore();
|
||||
FileSystemPersistentAcceptOnceFileListFilter filter = new FileSystemPersistentAcceptOnceFileListFilter(store, "foo:");
|
||||
File file = File.createTempFile("foo", ".txt");
|
||||
assertTrue(filter.filterFiles(new File[] {file}).size() == 1);
|
||||
assertTrue(filter.filterFiles(new File[] {file}).size() == 0);
|
||||
file.setLastModified(27L);
|
||||
assertTrue(filter.filterFiles(new File[] {file}).size() == 1);
|
||||
assertTrue(filter.filterFiles(new File[] {file}).size() == 0);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,10 +15,15 @@
|
||||
*/
|
||||
package org.springframework.integration.file.remote.gateway;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -36,20 +41,25 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.filters.AbstractSimplePatternFileListFilter;
|
||||
import org.springframework.integration.file.remote.AbstractFileInfo;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
|
||||
/**
|
||||
@@ -61,6 +71,9 @@ public class RemoteFileOutboundGatewayTests {
|
||||
|
||||
private final String tmpDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
@Rule
|
||||
public final TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testBad() throws Exception {
|
||||
@@ -987,6 +1000,120 @@ public class RemoteFileOutboundGatewayTests {
|
||||
out.getHeaders().get(FileHeaders.REMOTE_FILE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPut() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<TestLsEntry> sessionFactory = mock(SessionFactory.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Session<TestLsEntry> session = mock(Session.class);
|
||||
RemoteFileTemplate<TestLsEntry> template = new RemoteFileTemplate<TestLsEntry>(sessionFactory);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
template.setBeanFactory(mock(BeanFactory.class));
|
||||
template.afterPropertiesSet();
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway(template, "put", null);
|
||||
FileTransferringMessageHandler<TestLsEntry> handler = new FileTransferringMessageHandler<TestLsEntry>(sessionFactory);
|
||||
handler.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
handler.setBeanFactory(mock(BeanFactory.class));
|
||||
handler.afterPropertiesSet();
|
||||
gw.afterPropertiesSet();
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
final AtomicReference<String> written = new AtomicReference<String>();
|
||||
doAnswer(new Answer<Object>() {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
written.set((String) invocation.getArguments()[1]);
|
||||
return null;
|
||||
}
|
||||
}).when(session).write(any(InputStream.class), anyString());
|
||||
Message<String> requestMessage = MessageBuilder.withPayload("hello")
|
||||
.setHeader(FileHeaders.FILENAME, "bar.txt")
|
||||
.build();
|
||||
String path = (String) gw.handleRequestMessage(requestMessage);
|
||||
assertEquals("foo/bar.txt", path);
|
||||
verify(session).rename("foo/bar.txt.writing", "foo/bar.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMput() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<TestLsEntry> sessionFactory = mock(SessionFactory.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Session<TestLsEntry> session = mock(Session.class);
|
||||
RemoteFileTemplate<TestLsEntry> template = new RemoteFileTemplate<TestLsEntry>(sessionFactory);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
template.setBeanFactory(mock(BeanFactory.class));
|
||||
template.afterPropertiesSet();
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway(template, "mput", null);
|
||||
gw.afterPropertiesSet();
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
final AtomicReference<String> written = new AtomicReference<String>();
|
||||
doAnswer(new Answer<Object>() {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
written.set((String) invocation.getArguments()[1]);
|
||||
return null;
|
||||
}
|
||||
}).when(session).write(any(InputStream.class), anyString());
|
||||
tempFolder.newFile("baz.txt");
|
||||
tempFolder.newFile("qux.txt");
|
||||
Message<File> requestMessage = MessageBuilder.withPayload(tempFolder.getRoot())
|
||||
.build();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> out = (List<String>) gw.handleRequestMessage(requestMessage);
|
||||
assertEquals(2, out.size());
|
||||
assertThat(out.get(0),
|
||||
not(equalTo(out.get(1))));
|
||||
assertThat(out.get(0), anyOf(
|
||||
equalTo("foo/baz.txt"), equalTo("foo/qux.txt")));
|
||||
assertThat(out.get(1), anyOf(
|
||||
equalTo("foo/baz.txt"), equalTo("foo/qux.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMputRecursive() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<TestLsEntry> sessionFactory = mock(SessionFactory.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Session<TestLsEntry> session = mock(Session.class);
|
||||
RemoteFileTemplate<TestLsEntry> template = new RemoteFileTemplate<TestLsEntry>(sessionFactory);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
template.setBeanFactory(mock(BeanFactory.class));
|
||||
template.afterPropertiesSet();
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway(template, "mput", null);
|
||||
gw.setOptions("-R");
|
||||
gw.afterPropertiesSet();
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
final AtomicReference<String> written = new AtomicReference<String>();
|
||||
doAnswer(new Answer<Object>() {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
written.set((String) invocation.getArguments()[1]);
|
||||
return null;
|
||||
}
|
||||
}).when(session).write(any(InputStream.class), anyString());
|
||||
tempFolder.newFile("baz.txt");
|
||||
tempFolder.newFile("qux.txt");
|
||||
File dir1 = tempFolder.newFolder();
|
||||
File file3 = File.createTempFile("foo", ".txt", dir1);
|
||||
|
||||
Message<File> requestMessage = MessageBuilder.withPayload(tempFolder.getRoot())
|
||||
.build();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> out = (List<String>) gw.handleRequestMessage(requestMessage);
|
||||
assertEquals(3, out.size());
|
||||
assertThat(out.get(0),
|
||||
not(equalTo(out.get(1))));
|
||||
assertThat(out.get(0), anyOf(
|
||||
equalTo("foo/baz.txt"), equalTo("foo/qux.txt"), equalTo("foo/" + dir1.getName() + "/" + file3.getName())));
|
||||
assertThat(out.get(1), anyOf(
|
||||
equalTo("foo/baz.txt"), equalTo("foo/qux.txt"), equalTo("foo/" + dir1.getName() + "/" + file3.getName())));
|
||||
assertThat(out.get(2), anyOf(
|
||||
equalTo("foo/baz.txt"), equalTo("foo/qux.txt"), equalTo("foo/" + dir1.getName() + "/" + file3.getName())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestRemoteFileOutboundGateway extends AbstractRemoteFileOutboundGateway<TestLsEntry> {
|
||||
@@ -998,6 +1125,13 @@ class TestRemoteFileOutboundGateway extends AbstractRemoteFileOutboundGateway<Te
|
||||
this.setBeanFactory(mock(BeanFactory.class));
|
||||
}
|
||||
|
||||
public TestRemoteFileOutboundGateway(RemoteFileTemplate<TestLsEntry> remoteFileTemplate, String command,
|
||||
String expression) {
|
||||
super(remoteFileTemplate, command, expression);
|
||||
this.setBeanFactory(mock(BeanFactory.class));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isDirectory(TestLsEntry file) {
|
||||
return file.isDirectory();
|
||||
|
||||
Reference in New Issue
Block a user