INT-3088 (S)FTP Outbound Gateway - PUT and MPUT
- Core support in file module - FTP Parser and Test https://jira.springsource.org/browse/INT-3088 INT-3088 Add SFTP Support for PUT, MPUT INT-3088 Polishing - PR Comments INT-3088 Docbook For PUT/MPUT
This commit is contained in:
committed by
Artem Bilan
parent
88de8117aa
commit
59d6f7dfc1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends Abst
|
||||
* 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
|
||||
* @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);
|
||||
|
||||
@@ -29,11 +29,24 @@ public interface RemoteFileOperations<F> {
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message.
|
||||
*
|
||||
* @param message The message
|
||||
* @param message The message.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
* @throws Exception
|
||||
*/
|
||||
void send(Message<?> message);
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -99,6 +99,10 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
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);
|
||||
@@ -173,18 +177,32 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final Message<?> message) {
|
||||
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) {
|
||||
this.execute(new SessionCallbackWithoutResult<F>() {
|
||||
return this.execute(new SessionCallback<F, String>() {
|
||||
|
||||
@Override
|
||||
public void doInSessionWithoutResult(Session<F> session) throws IOException {
|
||||
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
|
||||
@@ -193,6 +211,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
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()
|
||||
@@ -215,6 +234,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("File " + message.getPayload() + " does not exist");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.io.IOException;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
/**
|
||||
* Callback invoked by {@code RemoteFileOperations.execute()) - allows multiple operations
|
||||
* Callback invoked by {@code RemoteFileOperations.execute()} - allows multiple operations
|
||||
* on a session.
|
||||
*
|
||||
* @author Gary Russell
|
||||
|
||||
@@ -92,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;
|
||||
|
||||
@@ -188,19 +198,20 @@ 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;
|
||||
|
||||
@@ -222,6 +233,23 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
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
|
||||
@@ -240,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,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>(
|
||||
@@ -350,6 +385,10 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
return doRm(requestMessage);
|
||||
case MV:
|
||||
return doMv(requestMessage);
|
||||
case PUT:
|
||||
return doPut(requestMessage);
|
||||
case MPUT:
|
||||
return doMput(requestMessage);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -357,8 +396,8 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
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();
|
||||
}
|
||||
final String fullDir = dir;
|
||||
List<?> payload = this.remoteFileTemplate.execute(new SessionCallback<F, List<?>>() {
|
||||
@@ -435,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)) {
|
||||
@@ -483,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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,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()) {
|
||||
@@ -536,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 {
|
||||
@@ -599,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,
|
||||
@@ -642,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;
|
||||
}
|
||||
@@ -666,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;
|
||||
}
|
||||
@@ -709,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);
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
|
||||
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.remoteFileTemplate.setAutoCreateDirectory(autoCreateDirectory);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,16 +41,21 @@ 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.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
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.integration.message.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());
|
||||
File file1 = tempFolder.newFile("baz.txt");
|
||||
File file2 = 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