INT-3129: FTP local-filename-generator-expression

* Add `local-filename-generator-expression` attribute to FTP and SFTP Outbound Gateway
* Refactoring for `(S)FtpOutboundGatewayParser`
* Add `local-filename-generator-expression` tests
* Add 'What's New' and attribute description

JIRA: https://jira.springsource.org/browse/INT-3129

Polishing

Doc Polishing

Assert attribute is only set for get/mget

Move tests to the get gateways
This commit is contained in:
Artem Bilan
2013-09-18 18:07:01 +03:00
committed by Gary Russell
parent bbf93e1385
commit 417c848c0b
13 changed files with 198 additions and 49 deletions

View File

@@ -19,6 +19,7 @@ import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.util.StringUtils;
@@ -55,7 +56,16 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "local-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-local-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "order");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "rename-expression"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply"); return builder;
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "rename-expression");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
String localFileGeneratorExpression = element.getAttribute("local-filename-generator-expression");
if (StringUtils.hasText(localFileGeneratorExpression)) {
BeanDefinitionBuilder localFileGeneratorExpressionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
localFileGeneratorExpressionBuilder.addConstructorArgValue(localFileGeneratorExpression);
builder.addPropertyValue("localFilenameGeneratorExpression", localFileGeneratorExpressionBuilder.getBeanDefinition());
}
return builder;
}
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
@@ -77,21 +87,21 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
}
else if (hasFileNamePattern) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
this.getSimplePatternFileListFilterClassname());
this.getSimplePatternFileListFilterClassName());
filterBuilder.addConstructorArgValue(fileNamePattern);
builder.addPropertyValue("filter", filterBuilder.getBeanDefinition());
}
else if (hasFileNameRegex) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
this.getRegexPatternFileListFilterClassname());
this.getRegexPatternFileListFilterClassName());
filterBuilder.addConstructorArgValue(fileNameRegex);
builder.addPropertyValue("filter", filterBuilder.getBeanDefinition());
}
}
protected abstract String getRegexPatternFileListFilterClassname();
protected abstract String getRegexPatternFileListFilterClassName();
protected abstract String getSimplePatternFileListFilterClassname();
protected abstract String getSimplePatternFileListFilterClassName();
protected abstract String getGatewayClassName();

View File

@@ -29,9 +29,12 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
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;
@@ -49,6 +52,7 @@ import org.springframework.util.StringUtils;
* Base class for Outbound Gateways that perform remote file operations.
*
* @author Gary Russell
* @author Artem Bilan
* @since 2.1
*/
public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReplyProducingMessageHandler {
@@ -178,6 +182,8 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
private volatile FileListFilter<F> filter;
private volatile Expression localFilenameGeneratorExpression;
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, String command,
String expression) {
this.sessionFactory = sessionFactory;
@@ -249,6 +255,12 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
new SpelExpressionParser().parseExpression(expression));
}
public void setLocalFilenameGeneratorExpression(Expression localFilenameGeneratorExpression) {
Assert.notNull(localFilenameGeneratorExpression, "'localFilenameGeneratorExpression' must not be null");
this.localFilenameGeneratorExpression = localFilenameGeneratorExpression;
}
@Override
protected void onInit() {
super.onInit();
@@ -334,7 +346,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
if (remoteDir.length() == 0) {
remoteDir = this.remoteFileSeparator;
}
File payload = get(session, remoteFilePath, remoteFilename, true);
File payload = get(requestMessage, session, remoteFilePath, remoteFilename, true);
return MessageBuilder.withPayload(payload)
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
@@ -348,7 +360,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
if (remoteDir.length() == 0) {
remoteDir = this.remoteFileSeparator;
}
List<File> payload = mGet(session, remoteDir, remoteFilename);
List<File> payload = mGet(requestMessage, session, remoteDir, remoteFilename);
return MessageBuilder.withPayload(payload)
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
@@ -454,11 +466,13 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
/**
* Copy a remote file to the configured local directory.
*
* @param message
* @param session
* @param remoteFilePath
* @throws IOException
*/
protected File get(Session<F> session, String remoteFilePath, String remoteFilename, boolean lsFirst)
protected File get(Message<?> message, Session<F> session, String remoteFilePath, String remoteFilename, boolean lsFirst)
throws IOException {
F[] files = null;
if (lsFirst) {
@@ -467,7 +481,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
throw new MessagingException(remoteFilePath + " is not a file");
}
}
File localFile = new File(this.localDirectory, remoteFilename);
File localFile = new File(this.localDirectory, this.generateLocalFileName(message, remoteFilename));
if (!localFile.exists()) {
String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix;
File tempFile = new File(tempFileName);
@@ -488,6 +502,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
fileOutputStream.close();
}
catch (Exception ignored2) {
//Ignore it
}
}
if (!tempFile.renameTo(localFile)) {
@@ -503,8 +518,8 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
}
}
protected List<File> mGet(Session<F> session, String remoteDirectory,
String remoteFilename) throws IOException {
protected List<File> mGet(Message<?> message, Session<F> session, String remoteDirectory,
String remoteFilename) throws IOException {
String path = generateFullPath(remoteDirectory, remoteFilename);
String[] fileNames = session.listNames(path);
if (fileNames == null) {
@@ -519,11 +534,11 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
File file;
if (fileName.contains(this.remoteFileSeparator) &&
fileName.startsWith(remoteDirectory)) { // the server returned the full path
file = this.get(session, fileName,
file = this.get(message, session, fileName,
fileName.substring(fileName.lastIndexOf(this.remoteFileSeparator)), false);
}
else {
file = this.get(session, generateFullPath(remoteDirectory, fileName), fileName, false);
file = this.get(message, session, generateFullPath(remoteDirectory, fileName), fileName, false);
}
files.add(file);
}
@@ -573,6 +588,15 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
session.rename(remoteFilePath, remoteFileNewPath);
}
private String generateLocalFileName(Message<?> message, String remoteFileName){
if (this.localFilenameGeneratorExpression != null){
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
evaluationContext.setVariable("remoteFileName", remoteFileName);
return this.localFilenameGeneratorExpression.getValue(evaluationContext, message, String.class);
}
return remoteFileName;
}
abstract protected boolean isDirectory(F file);
abstract protected boolean isLink(F file);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -16,28 +16,30 @@
package org.springframework.integration.ftp.config;
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter;
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
/**
* @author Gary Russell
* @author Artem Bilan
* @since 2.1
*
*/
public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayParser {
private static final String BASE_PACKAGE = "org.springframework.integration.ftp";
public String getGatewayClassName() {
return BASE_PACKAGE + ".gateway.FtpOutboundGateway";
return FtpOutboundGateway.class.getName();
}
@Override
protected String getSimplePatternFileListFilterClassname() {
return BASE_PACKAGE + ".filters.FtpSimplePatternFileListFilter";
protected String getSimplePatternFileListFilterClassName() {
return FtpSimplePatternFileListFilter.class.getName();
}
@Override
protected String getRegexPatternFileListFilterClassname() {
return BASE_PACKAGE + ".filters.FtpRegexPatternFileListFilter";
protected String getRegexPatternFileListFilterClassName() {
return FtpRegexPatternFileListFilter.class.getName();
}
}

View File

@@ -394,6 +394,21 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filename-generator-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a SpEL expression to
generate the file name of
the local (transferred) file. The root
object of the SpEL
evaluation is the request Message, but the name of the original
remote file is also provided as the 'remoteFileName' variable.
For example, a valid expression would be:
"#remoteFileName.toUpperCase() + headers.foo".
Only used with 'get' and 'mget' commands.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -31,6 +31,10 @@
order="1"
/>
<bean id="fooString" class="java.lang.String">
<constructor-arg value="foo" />
</bean>
<int-ftp:outbound-gateway id="gateway2"
local-directory="local-test-dir"
session-factory="csf"
@@ -44,6 +48,7 @@
expression="payload"
order="2"
requires-reply="false"
local-filename-generator-expression="#remoteFileName.toUpperCase() + '.a' + @fooString"
>
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.ftp.config.FtpOutboundGatewayParserTests$FooAdvice" />

View File

@@ -21,10 +21,13 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.endpoint.AbstractEndpoint;
@@ -39,6 +42,7 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ReflectionUtils;
/**
* @author Gary Russell
@@ -77,8 +81,9 @@ public class FtpOutboundGatewayParserTests {
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
Set<Option> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains(Option.NAME_ONLY));
assertTrue(options.contains(Option.NOSORT));
@@ -88,7 +93,7 @@ public class FtpOutboundGatewayParserTests {
}
@Test
public void testGateway2() {
public void testGateway2() throws Exception {
FtpOutboundGateway gateway = TestUtils.getPropertyValue(gateway2,
"handler", FtpOutboundGateway.class);
assertEquals("X", TestUtils.getPropertyValue(gateway, "remoteFileSeparator"));
@@ -104,6 +109,21 @@ public class FtpOutboundGatewayParserTests {
gateway.handleMessage(new GenericMessage<String>("foo"));
assertFalse(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
assertEquals(1, adviceCalled);
//INT-3129
assertNotNull(TestUtils.getPropertyValue(gateway, "localFilenameGeneratorExpression"));
final AtomicReference<Method> genMethod = new AtomicReference<Method>();
ReflectionUtils.doWithMethods(FtpOutboundGateway.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if ("generateLocalFileName".equals(method.getName())) {
method.setAccessible(true);
genMethod.set(method);
}
}
});
assertEquals("FOO.afoo", genMethod.get().invoke(gateway, new GenericMessage<String>(""), "foo"));
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -16,28 +16,30 @@
package org.springframework.integration.sftp.config;
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter;
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
/**
* @author Gary Russell
* @author Artem Bilan
* @since 2.1
*
*/
public class SftpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayParser {
private static final String BASE_PACKAGE = "org.springframework.integration.sftp";
public String getGatewayClassName() {
return BASE_PACKAGE + ".gateway.SftpOutboundGateway";
return SftpOutboundGateway.class.getName();
}
@Override
protected String getSimplePatternFileListFilterClassname() {
return BASE_PACKAGE + ".filters.SftpSimplePatternFileListFilter";
protected String getSimplePatternFileListFilterClassName() {
return SftpSimplePatternFileListFilter.class.getName();
}
@Override
protected String getRegexPatternFileListFilterClassname() {
return BASE_PACKAGE + ".filters.SftpRegexPatternFileListFilter";
protected String getRegexPatternFileListFilterClassName() {
return SftpRegexPatternFileListFilter.class.getName();
}
}

View File

@@ -393,6 +393,21 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filename-generator-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a SpEL expression to
generate the file name of
the local (transferred) file. The root
object of the SpEL
evaluation is the request Message, but the name of the original
remote file is also provided as the 'remoteFileName' variable.
For example, a valid expression would be:
"#remoteFileName.toUpperCase() + headers.foo".
Only used with 'get' and 'mget' commands.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -31,6 +31,10 @@
order="1"
/>
<bean id="fooString" class="java.lang.String">
<constructor-arg value="foo" />
</bean>
<int-sftp:outbound-gateway id="gateway2"
local-directory="local-test-dir"
session-factory="csf"
@@ -44,6 +48,7 @@
expression="payload"
order="2"
requires-reply="false"
local-filename-generator-expression="#remoteFileName.toUpperCase() + '.a' + @fooString"
/>
<int-sftp:outbound-gateway id="gateway3"

View File

@@ -21,10 +21,13 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.endpoint.AbstractEndpoint;
@@ -37,6 +40,7 @@ import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ReflectionUtils;
/**
* @author Gary Russell
@@ -77,7 +81,7 @@ public class SftpOutboundGatewayParserTests {
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
Set<Option> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains(Option.NAME_ONLY));
assertTrue(options.contains(Option.NOSORT));
@@ -86,7 +90,7 @@ public class SftpOutboundGatewayParserTests {
}
@Test
public void testGateway2() {
public void testGateway2() throws Exception {
SftpOutboundGateway gateway = TestUtils.getPropertyValue(gateway2,
"handler", SftpOutboundGateway.class);
assertEquals("X", TestUtils.getPropertyValue(gateway, "remoteFileSeparator"));
@@ -100,6 +104,21 @@ public class SftpOutboundGatewayParserTests {
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains(Option.PRESERVE_TIMESTAMP));
//INT-3129
assertNotNull(TestUtils.getPropertyValue(gateway, "localFilenameGeneratorExpression"));
final AtomicReference<Method> genMethod = new AtomicReference<Method>();
ReflectionUtils.doWithMethods(SftpOutboundGateway.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if ("generateLocalFileName".equals(method.getName())) {
method.setAccessible(true);
genMethod.set(method);
}
}
});
assertEquals("FOO.afoo", genMethod.get().invoke(gateway, new GenericMessage<String>(""), "foo"));
}
@Test

View File

@@ -341,7 +341,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
<listitem>mv (move/rename file)</listitem>
</itemizedlist>
</para>
<para><emphasis>ls</emphasis></para>
<para><emphasis role="bold">ls</emphasis></para>
<para>
ls lists remote file(s) and supports the following options:
<itemizedlist>
@@ -366,7 +366,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
The remote directory that the <emphasis>ls</emphasis> command acted on is provided
in the <classname>file_remoteDirectory</classname> header.
</para>
<para><emphasis>get</emphasis></para>
<para><emphasis role="bold">get</emphasis></para>
<para>
<emphasis>get</emphasis> retrieves a remote file and supports the following option:
<itemizedlist>
@@ -381,7 +381,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>mget</emphasis></para>
<para><emphasis role="bold">mget</emphasis></para>
<para>
<emphasis>mget</emphasis> retrieves multiple remote files based on a pattern and supports the following option:
<itemizedlist>
@@ -399,7 +399,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
for the filenames is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>rm</emphasis></para>
<para><emphasis role="bold">rm</emphasis></para>
<para>
The <emphasis>rm</emphasis> command has no options.
</para>
@@ -409,7 +409,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>mv</emphasis></para>
<para><emphasis role="bold">mv</emphasis></para>
<para>
The <emphasis>mv</emphasis> command has no options.
</para>
@@ -425,9 +425,20 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
the <code>file_renameTo</code> header.
</para>
<para>
For all commands, the PATH that the command acts on is provided by the 'expression'
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
retrieve all files, or 'somedirectory/*' etc.
<emphasis role="bold">Additional Information</emphasis>
</para>
<para>
The <emphasis>get</emphasis> and <emphasis>mget</emphasis> commands support
the <emphasis>local-filename-generator-expression</emphasis> attribute. It
defines a SpEL expression to generate the name of local file(s) during the transfer.
The root object of the evaluation context is the request Message but, in addition, the <code>remoteFileName</code>
variable is also available, which is particularly useful for <emphasis>mget</emphasis>, for
example: <code>local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo"</code>
</para>
<para>
For all commands, the PATH that the command acts on is provided by the 'expression'
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
retrieve all files, or 'somedirectory/*' etc.
</para>
</para>
<para>

View File

@@ -377,7 +377,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
<listitem>mv (move/rename file)</listitem>
</itemizedlist>
</para>
<para><emphasis>ls</emphasis></para>
<para><emphasis role="bold">ls</emphasis></para>
<para>
ls lists remote file(s) and supports the following options:
<itemizedlist>
@@ -402,7 +402,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
The remote directory that the <emphasis>ls</emphasis> command acted on is provided
in the <classname>file_remoteDirectory</classname> header.
</para>
<para><emphasis>get</emphasis></para>
<para><emphasis role="bold">get</emphasis></para>
<para>
<emphasis>get</emphasis> retrieves a remote file and supports the following option:
<itemizedlist>
@@ -417,7 +417,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>mget</emphasis></para>
<para><emphasis role="bold">mget</emphasis></para>
<para>
<emphasis>mget</emphasis> retrieves multiple remote files based on a pattern and supports the following option:
<itemizedlist>
@@ -435,7 +435,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
for the filenames is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>rm</emphasis></para>
<para><emphasis role="bold">rm</emphasis></para>
<para>
The <emphasis>rm</emphasis> command has no options.
</para>
@@ -445,7 +445,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
provided in the <classname>file_remoteFile</classname> header.
</para>
<para><emphasis>mv</emphasis></para>
<para><emphasis role="bold">mv</emphasis></para>
<para>
The <emphasis>mv</emphasis> command has no options.
</para>
@@ -461,9 +461,20 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
the <code>file_renameTo</code> header.
</para>
<para>
For all commands, the PATH that the command acts on is provided by the 'expression'
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
retrieve all files, or 'somedirectory/*' etc.
<emphasis role="bold">Additional Information</emphasis>
</para>
<para>
The <emphasis>get</emphasis> and <emphasis>mget</emphasis> commands support
the <emphasis>local-filename-generator-expression</emphasis> attribute. It
defines a SpEL expression to generate the name of local file(s) during the transfer.
The root object of the evaluation context is the request Message but, in addition, the <code>remoteFileName</code>
variable is also available, which is particularly useful for <emphasis>mget</emphasis>, for
example: <code>local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo"</code>
</para>
<para>
For all commands, the PATH that the command acts on is provided by the 'expression'
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
retrieve all files, or 'somedirectory/*' etc.
</para>
</para>
<para>

View File

@@ -175,6 +175,10 @@
to be maintained across JVM executions, a custom filter that retains state, perhaps on
the file system, can now be configured.
</para>
<para>
For more information, see
<xref linkend="ftp-inbound"/> and <xref linkend="sftp-inbound"/>.
</para>
</section>
<section id="3.0-xFTP-gw">
<title>FTP, SFTP and FTPS Gateways</title>
@@ -182,6 +186,12 @@
The gateways now support the <code>mv</code> command, enabling the renaming of remote
files.
</para>
<para>
The <code>local-filename-generator-expression</code> attribute is now supported,
enabling the naming of local files during transfer. By default, the same
name as the remote file is used. For more information, see
<xref linkend="ftp-outbound-gateway"/> and <xref linkend="sftp-outbound-gateway"/>.
</para>
</section>
<section id="3.0-jdbc-mysql-v5_6_4">
<title>JDBC Message Store Improvements</title>