Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

INT-2981 Add Support for 'mv' to (S)FTP Outbound Gateways #779

Closed
wants to merge 6 commits into from

2 participants

Gary Russell Gunnar Hillert
Gary Russell
Owner

I took the opportunity to do a little refatoring (mainly change commands and options to enums, and change a big if/else block to a switch statement).

You may find it easier to review each commit individually.

...remote/gateway/AbstractRemoteFileOutboundGateway.java
((6 lines not shown))
- public static final String COMMAND_LS = "ls";
+ public static enum Command {
Gunnar Hillert Owner

As the class already has 500+ lines of code, wouldn't it be more advisable to move the 2 enums Command and Option into their own classes? Possibly also adding some JavaDoc?

Gary Russell Owner

I prefer the encapsulation but we could add javadoc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...tegration/sftp/config/spring-integration-sftp-3.0.xsd
@@ -243,7 +243,7 @@
<xsd:attribute name="command" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
- sftp command - ls, get or rm
+ sftp command - ls, get, rm, mget or mv.
Gunnar Hillert Owner

Technically, I would love seeing the type to be an enumeration - In order to provide a slightly better user experience for STS users, could you do:

<xsd:simpleType>
    <xsd:union memberTypes=remoteCommand xsd:string"/>
</xsd:simpleType>

<xsd:simpleType name="remoteCommand">
    <xsd:restriction base="xsd:token">
        <xsd:enumeration value="ls"/>
        <xsd:enumeration value="get"/>
        <xsd:enumeration value="rm"/>
        <xsd:enumeration value="mget"/>
        <xsd:enumeration value="mv"/>
    </xsd:restriction>
</xsd:simpleType>
Gary Russell Owner

But then you can't use property placeholders. Maybe that's unlikely in this case, but...

Gunnar Hillert Owner

I think you can - any String is still allowed (xsd:string). It just provides some additional hint to STS to provide code completion.

Gary Russell Owner

Sorry - missed the union

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Gary Russell
Owner

Pushed - added javadocs to enums; polished schemas to use an enumerated type.

Gunnar Hillert
Owner

Merged.

Gunnar Hillert ghillert closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 15, 2013
  1. Gary Russell
  2. Gary Russell
  3. Gary Russell
  4. Gary Russell
  5. Gary Russell
  6. Gary Russell

    INT-2981 Polishing - PR Comments

    garyrussell authored
    Add javadocs to enums.
    
    Polish schemas to use an enumerated type for available
    gateway commands.
This page is out of date. Refresh to see the latest.
Showing with 562 additions and 163 deletions.
  1. +3 −1 spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java
  2. +3 −3 ...file/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java
  3. +76 −0 spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileUtils.java
  4. +228 −94 ...le/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java
  5. +3 −35 ...-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java
  6. +10 −0 ...gration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-3.0.xsd
  7. +100 −9 ...-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java
  8. +17 −3 ...ntegration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-3.0.xsd
  9. +11 −0 ...tion-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml
  10. +22 −7 ...integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java
  11. +17 −3 ...gration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-3.0.xsd
  12. +10 −0 ...n-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml
  13. +21 −6 ...egration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java
  14. +17 −1 src/reference/docbook/ftp.xml
  15. +17 −1 src/reference/docbook/sftp.xml
  16. +7 −0 src/reference/docbook/whats-new.xml
4 spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -35,4 +35,6 @@
public static final String REMOTE_FILE = PREFIX + "remoteFile";
+ public static final String RENAME_TO = PREFIX + "renameTo";
+
}
6 ...e/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -15,14 +15,13 @@
*/
package org.springframework.integration.file.config;
-import org.w3c.dom.Element;
-
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.file.remote.session.SessionFactoryFactoryBean;
import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
/**
* @author Gary Russell
@@ -60,6 +59,7 @@ protected BeanDefinitionBuilder parseHandler(Element element, ParserContext pars
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");
return builder;
}
76 spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileUtils.java
View
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ * 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.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.springframework.integration.file.remote.session.Session;
+
+/**
+ * Utility methods for supporting remote file operations.
+ * @author Gary Russell
+ * @since 3.0
+ *
+ */
+public class RemoteFileUtils {
+
+ private RemoteFileUtils() {}
+
+ /**
+ * Recursively create remote directories.
+ * @param <F> The session type.
+ * @param path The directory path.
+ * @param session The session.
+ * @throws IOException
+ */
+ public static <F> void makeDirectories(String path, Session<F> session, String remoteFileSeparator, Log logger)
+ throws IOException {
+
+ if (!session.exists(path)){
+
+ int nextSeparatorIndex = path.lastIndexOf(remoteFileSeparator);
+
+ if (nextSeparatorIndex > -1){
+ List<String> pathsToCreate = new LinkedList<String>();
+ while (nextSeparatorIndex > -1){
+ String pathSegment = path.substring(0, nextSeparatorIndex);
+ if (pathSegment.length() == 0 || session.exists(pathSegment)) {
+ // no more paths to create
+ break;
+ }
+ else {
+ pathsToCreate.add(0, pathSegment);
+ nextSeparatorIndex = pathSegment.lastIndexOf(remoteFileSeparator);
+ }
+ }
+
+ for (String pathToCreate : pathsToCreate) {
+ if (logger.isDebugEnabled()){
+ logger.debug("Creating '" + pathToCreate + "'");
+ }
+ session.mkdir(pathToCreate);
+ }
+ }
+ else {
+ session.mkdir(path);
+ }
+ }
+ }
+
+}
322 ...src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -35,6 +35,7 @@
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.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
@@ -54,36 +55,114 @@
protected final SessionFactory<F> sessionFactory;
- protected final String command;
+ protected final Command command;
- public static final String COMMAND_LS = "ls";
-
- public static final String COMMAND_GET = "get";
-
- public static final String COMMAND_RM = "rm";
-
- public static final String COMMAND_MGET = "mget";
-
- public static final String OPTION_NAME_ONLY = "-1";
-
- public static final String OPTION_ALL = "-a";
+ /**
+ * Enumeration of commands supported by the gateways.
+ */
+ public static enum Command {
+ /**
+ * List remote files.
+ */
+ LS("ls"),
+ /**
+ * Retrieve a remote file.
+ */
+ GET("get"),
+ /**
+ * Remove a remote file (path - including wildcards).
+ */
+ RM("rm"),
+ /**
+ * Retrieve multiple files matching a wildcard path.
+ */
+ MGET("mget"),
+ /**
+ * Move (rename) a remote file.
+ */
+ MV("mv");
+
+ private String command;
+
+ private Command(String command) {
+ this.command = command;
+ }
- public static final String OPTION_NOSORT = "-f";
+ public String getCommand() {
+ return this.command;
+ }
- public static final String OPTION_SUBDIRS = "-dirs";
+ public static Command toCommand(String cmd) {
+ for (Command command : values()) {
+ if (command.getCommand().equals(cmd)) {
+ return command;
+ }
+ }
+ throw new IllegalArgumentException("No Command with value '" + cmd + "'");
+ }
+ }
- public static final String OPTION_LINKS = "-links";
+ /**
+ * Enumeration of options supported by various commands.
+ *
+ */
+ public static enum Option {
+ /**
+ * Don't return full file information; just the name (ls).
+ */
+ NAME_ONLY("-1"),
+ /**
+ * Include directories {@code .} and {@code ..} in the results (ls).
+ */
+ ALL("-a"),
+ /**
+ * Do not sort the results (ls with NAME_ONLY).
+ */
+ NOSORT("-f"),
+ /**
+ * Include directories in the results (ls).
+ */
+ SUBDIRS("-dirs"),
+ /**
+ * Include links in the results (ls).
+ */
+ LINKS("-links"),
+ /**
+ * Preserve the server timestamp (get, mget).
+ */
+ PRESERVE_TIMESTAMP("-P"),
+ /**
+ * Throw an exception if no files returned (mget).
+ */
+ EXCEPTION_WHEN_EMPTY("-x");
+
+ private String option;
+
+ private Option(String option) {
+ this.option = option;
+ }
- public static final String OPTION_PRESERVE_TIMESTAMP = "-P";
+ public String getOption() {
+ return this.option;
+ }
- public static final String OPTION_EXCEPTION_WHEN_EMPTY = "-x";
+ public static Option toOption(String opt) {
+ for (Option option : values()) {
+ if (option.getOption().equals(opt)) {
+ return option;
+ }
+ }
+ throw new IllegalArgumentException("No option with value '" + opt + "'");
+ }
+ }
- private final Set<String> supportedCommands = new HashSet<String>(Arrays.asList(
- COMMAND_LS, COMMAND_GET, COMMAND_RM, COMMAND_MGET));
+ private final ExpressionEvaluatingMessageProcessor<String> fileNameProcessor;
- private final ExpressionEvaluatingMessageProcessor<String> processor;
+ private volatile ExpressionEvaluatingMessageProcessor<String> renameProcessor =
+ new ExpressionEvaluatingMessageProcessor<String>(
+ new SpelExpressionParser().parseExpression("headers." + FileHeaders.RENAME_TO));
- protected volatile Set<String> options = new HashSet<String>();
+ protected volatile Set<Option> options = new HashSet<Option>();
private volatile String remoteFileSeparator = "/";
@@ -102,8 +181,16 @@
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, String command,
String expression) {
this.sessionFactory = sessionFactory;
+ this.command = Command.toCommand(command);
+ this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
+ new SpelExpressionParser().parseExpression(expression));
+ }
+
+ public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, Command command,
+ String expression) {
+ this.sessionFactory = sessionFactory;
this.command = command;
- this.processor = new ExpressionEvaluatingMessageProcessor<String>(
+ this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression(expression));
}
@@ -114,7 +201,10 @@ public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, Strin
public void setOptions(String options) {
String[] opts = options.split("\\s");
for (String opt : opts) {
- this.options.add(opt.trim());
+ String trimmedOpt = opt.trim();
+ if (StringUtils.hasLength(trimmedOpt)) {
+ this.options.add(Option.toOption(trimmedOpt));
+ }
}
}
@@ -153,21 +243,22 @@ public void setFilter(FileListFilter<F> filter) {
this.filter = filter;
}
+ public void setRenameExpression(String expression) {
+ Assert.notNull(expression, "'expression' cannot be null");
+ this.renameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
+ new SpelExpressionParser().parseExpression(expression));
+ }
+
@Override
protected void onInit() {
super.onInit();
Assert.notNull(this.command, "command must not be null");
- Assert.isTrue(
- this.supportedCommands.contains(this.command),
- "command must be one of "
- + StringUtils
- .collectionToCommaDelimitedString(this.supportedCommands));
- if (COMMAND_RM.equals(this.command) || COMMAND_MGET.equals(this.command) ||
- COMMAND_GET.equals(this.command)) {
+ if (Command.RM.equals(this.command) || Command.MGET.equals(this.command) ||
+ Command.GET.equals(this.command)) {
Assert.isNull(this.filter, "Filters are not supported with the rm, get, and mget commands");
}
- if (COMMAND_GET.equals(this.command)
- || COMMAND_MGET.equals(this.command)) {
+ if (Command.GET.equals(this.command)
+ || Command.MGET.equals(this.command)) {
Assert.notNull(this.localDirectory, "localDirectory must not be null");
try {
if (!this.localDirectory.exists()) {
@@ -193,7 +284,7 @@ protected void onInit() {
}
}
if (this.getBeanFactory() != null) {
- this.processor.setBeanFactory(this.getBeanFactory());
+ this.fileNameProcessor.setBeanFactory(this.getBeanFactory());
}
}
@@ -201,65 +292,99 @@ protected void onInit() {
protected Object handleRequestMessage(Message<?> requestMessage) {
Session<F> session = this.sessionFactory.getSession();
try {
- if (COMMAND_LS.equals(this.command)) {
- String dir = this.processor.processMessage(requestMessage);
- if (!dir.endsWith(this.remoteFileSeparator)) {
- dir += this.remoteFileSeparator;
- }
- List<?> payload = ls(session, dir);
- return MessageBuilder.withPayload(payload)
- .setHeader(FileHeaders.REMOTE_DIRECTORY, dir)
- .build();
- }
- else if (COMMAND_GET.equals(this.command)) {
- String remoteFilePath = this.processor.processMessage(requestMessage);
- String remoteFilename = getRemoteFilename(remoteFilePath);
- String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
- if (remoteDir.length() == 0) {
- remoteDir = this.remoteFileSeparator;
- }
- File payload = get(session, remoteFilePath, remoteFilename, true);
- return MessageBuilder.withPayload(payload)
- .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
- .setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
- .build();
- }
- else if (COMMAND_MGET.equals(this.command)) {
- String remoteFilePath = this.processor.processMessage(requestMessage);
- String remoteFilename = getRemoteFilename(remoteFilePath);
- String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
- if (remoteDir.length() == 0) {
- remoteDir = this.remoteFileSeparator;
- }
- List<File> payload = mGet(session, remoteDir, remoteFilename);
- return MessageBuilder.withPayload(payload)
- .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
- .setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
- .build();
- }
- else if (COMMAND_RM.equals(this.command)) {
- String remoteFilePath = this.processor.processMessage(requestMessage);
- String remoteFilename = getRemoteFilename(remoteFilePath);
- String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
- if (remoteDir.length() == 0) {
- remoteDir = this.remoteFileSeparator;
- }
- boolean payload = rm(session, remoteFilePath);
- return MessageBuilder.withPayload(payload)
- .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
- .setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
- .build();
- }
- else {
+ 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) {
+ }
+ catch (IOException e) {
throw new MessagingException(requestMessage, e);
- } finally {
+ }
+ finally {
session.close();
}
}
+ private Object doLs(Message<?> requestMessage, Session<F> session) throws IOException {
+ String dir = this.fileNameProcessor.processMessage(requestMessage);
+ if (!dir.endsWith(this.remoteFileSeparator)) {
+ dir += this.remoteFileSeparator;
+ }
+ List<?> payload = ls(session, dir);
+ 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 = getRemoteFilename(remoteFilePath);
+ String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
+ if (remoteDir.length() == 0) {
+ remoteDir = this.remoteFileSeparator;
+ }
+ File payload = get(session, 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 = getRemoteFilename(remoteFilePath);
+ String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
+ if (remoteDir.length() == 0) {
+ remoteDir = this.remoteFileSeparator;
+ }
+ List<File> payload = mGet(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);
+ String remoteFilename = getRemoteFilename(remoteFilePath);
+ String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
+ if (remoteDir.length() == 0) {
+ remoteDir = this.remoteFileSeparator;
+ }
+ boolean payload = rm(session, 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 {
+ String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
+ String remoteFilename = getRemoteFilename(remoteFilePath);
+ String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
+ String remoteFileNewPath = this.renameProcessor.processMessage(requestMessage);
+ Assert.hasLength(remoteFileNewPath, "New filename cannot be empty");
+ if (remoteDir.length() == 0) {
+ remoteDir = this.remoteFileSeparator;
+ }
+ mv(session, remoteFilePath, remoteFileNewPath);
+ return MessageBuilder.withPayload(Boolean.TRUE)
+ .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
+ .setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
+ .setHeader(FileHeaders.RENAME_TO, remoteFileNewPath)
+ .build();
+ }
+
protected List<?> ls(Session<F> session, String dir) throws IOException {
List<F> lsFiles = new ArrayList<F>();
F[] files = session.list(dir);
@@ -267,7 +392,7 @@ else if (COMMAND_RM.equals(this.command)) {
Collection<F> filteredFiles = this.filterFiles(files);
for (F file : filteredFiles) {
if (file != null) {
- if (this.options.contains(OPTION_SUBDIRS) || !isDirectory(file)) {
+ if (this.options.contains(Option.SUBDIRS) || !isDirectory(file)) {
lsFiles.add(file);
}
}
@@ -276,18 +401,18 @@ else if (COMMAND_RM.equals(this.command)) {
else {
return lsFiles;
}
- if (!this.options.contains(OPTION_LINKS)) {
+ if (!this.options.contains(Option.LINKS)) {
purgeLinks(lsFiles);
}
- if (!this.options.contains(OPTION_ALL)) {
+ if (!this.options.contains(Option.ALL)) {
purgeDots(lsFiles);
}
- if (this.options.contains(OPTION_NAME_ONLY)) {
+ if (this.options.contains(Option.NAME_ONLY)) {
List<String> results = new ArrayList<String>();
for (F file : lsFiles) {
results.add(getFilename(file));
}
- if (!this.options.contains(OPTION_NOSORT)) {
+ if (!this.options.contains(Option.NOSORT)) {
Collections.sort(results);
}
return results;
@@ -297,7 +422,7 @@ else if (COMMAND_RM.equals(this.command)) {
for (AbstractFileInfo<F> file : canonicalFiles) {
file.setRemoteDirectory(dir);
}
- if (!this.options.contains(OPTION_NOSORT)) {
+ if (!this.options.contains(Option.NOSORT)) {
Collections.sort(canonicalFiles);
}
return canonicalFiles;
@@ -367,7 +492,7 @@ protected File get(Session<F> session, String remoteFilePath, String remoteFilen
if (!tempFile.renameTo(localFile)) {
throw new MessagingException("Failed to rename local file");
}
- if (lsFirst && this.options.contains(OPTION_PRESERVE_TIMESTAMP)) {
+ if (lsFirst && this.options.contains(Option.PRESERVE_TIMESTAMP)) {
localFile.setLastModified(getModified(files[0]));
}
return localFile;
@@ -384,7 +509,7 @@ protected File get(Session<F> session, String remoteFilePath, String remoteFilen
if (fileNames == null) {
fileNames = new String[0];
}
- if (fileNames.length == 0 && this.options.contains(OPTION_EXCEPTION_WHEN_EMPTY)) {
+ if (fileNames.length == 0 && this.options.contains(Option.EXCEPTION_WHEN_EMPTY)) {
throw new MessagingException("No files found at " + remoteDirectory
+ " with pattern " + remoteFilename);
}
@@ -438,6 +563,15 @@ protected boolean rm(Session<?> session, String remoteFilePath)
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);
+ }
+
abstract protected boolean isDirectory(F file);
abstract protected boolean isLink(F file);
38 ...le/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -20,8 +20,6 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -31,6 +29,7 @@
import org.springframework.integration.MessagingException;
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.session.SessionFactory;
import org.springframework.integration.handler.AbstractMessageHandler;
@@ -246,7 +245,7 @@ private void sendFileToRemoteDirectory(File file, String temporaryRemoteDirector
if (this.autoCreateDirectory) {
try {
- this.makeDirectories(remoteDirectory, session);
+ RemoteFileUtils.makeDirectories(remoteDirectory, session, this.remoteFileSeparator, this.logger);
}
catch (IllegalStateException e) {
// Revert to old FTP behavior if recursive mkdir fails, for backwards compatibility
@@ -280,35 +279,4 @@ else if (!directoryPath.endsWith(this.remoteFileSeparator)) {
return directoryPath;
}
- private void makeDirectories(String path, Session<F> session) throws IOException {
- if (!session.exists(path)){
-
- int nextSeparatorIndex = path.lastIndexOf(this.remoteFileSeparator);
-
- if (nextSeparatorIndex > -1){
- List<String> pathsToCreate = new LinkedList<String>();
- while (nextSeparatorIndex > -1){
- String pathSegment = path.substring(0, nextSeparatorIndex);
- if (pathSegment.length() == 0 || session.exists(pathSegment)) {
- // no more paths to create
- break;
- }
- else {
- pathsToCreate.add(0, pathSegment);
- nextSeparatorIndex = pathSegment.lastIndexOf(this.remoteFileSeparator);
- }
- }
-
- for (String pathToCreate : pathsToCreate) {
- if (logger.isDebugEnabled()){
- logger.debug("Creating '" + pathToCreate + "'");
- }
- session.mkdir(pathToCreate);
- }
- }
- else {
- session.mkdir(path);
- }
- }
- }
}
10 ...tion-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-3.0.xsd
View
@@ -562,4 +562,14 @@ Only files matching this regular expression will be picked up by this adapter.
</xsd:restriction>
</xsd:simpleType>
+ <xsd:simpleType name="remoteGatewayCommand">
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="ls"/>
+ <xsd:enumeration value="get"/>
+ <xsd:enumeration value="rm"/>
+ <xsd:enumeration value="mget"/>
+ <xsd:enumeration value="mv"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
</xsd:schema>
109 ...le/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -19,6 +19,8 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,8 +34,11 @@
import java.util.Collection;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.file.FileHeaders;
@@ -42,6 +47,7 @@
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.message.GenericMessage;
+import org.springframework.integration.support.MessageBuilder;
/**
@@ -52,7 +58,7 @@
@SuppressWarnings("rawtypes")
public class RemoteFileOutboundGatewayTests {
- private String tmpDir = System.getProperty("java.io.tmpdir");
+ private final String tmpDir = System.getProperty("java.io.tmpdir");
@Test(expected=IllegalArgumentException.class)
@@ -292,6 +298,91 @@ public boolean exists(String path) throws IOException {
gw.handleRequestMessage(new GenericMessage<String>("testremote/*"));
}
+ @Test
+ public void testMove() throws Exception {
+ SessionFactory sessionFactory = mock(SessionFactory.class);
+ TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
+ (sessionFactory, "mv", "payload");
+ Session<?> session = mock(Session.class);
+ final AtomicReference<String> args = new AtomicReference<String>();
+ doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ args.set((String) arguments[0] + (String) arguments[1]);
+ return null;
+ }
+ }).when(session).rename(anyString(), anyString());
+ when (sessionFactory.getSession()).thenReturn(session);
+ Message<String> requestMessage = MessageBuilder.withPayload("foo")
+ .setHeader(FileHeaders.RENAME_TO, "bar")
+ .build();
+ Message<?> out = (Message<?>) gw.handleRequestMessage(requestMessage);
+ assertEquals("foo", out.getHeaders().get(FileHeaders.REMOTE_FILE));
+ assertEquals("foobar", args.get());
+ assertEquals(Boolean.TRUE, out.getPayload());
+ }
+
+ @Test
+ public void testMoveWithExpression() throws Exception {
+ SessionFactory sessionFactory = mock(SessionFactory.class);
+ TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
+ (sessionFactory, "mv", "payload");
+ gw.setRenameExpression("payload.substring(1)");
+ Session<?> session = mock(Session.class);
+ final AtomicReference<String> args = new AtomicReference<String>();
+ doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ args.set((String) arguments[0] + (String) arguments[1]);
+ return null;
+ }
+ }).when(session).rename(anyString(), anyString());
+ when (sessionFactory.getSession()).thenReturn(session);
+ Message<?> out = (Message<?>) gw.handleRequestMessage(new GenericMessage<String>("foo"));
+ assertEquals("oo", out.getHeaders().get(FileHeaders.RENAME_TO));
+ assertEquals("foo", out.getHeaders().get(FileHeaders.REMOTE_FILE));
+ assertEquals("foooo", args.get());
+ assertEquals(Boolean.TRUE, out.getPayload());
+ }
+
+ @Test
+ public void testMoveWithMkDirs() throws Exception {
+ SessionFactory sessionFactory = mock(SessionFactory.class);
+ TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
+ (sessionFactory, "mv", "payload");
+ gw.setRenameExpression("'foo/bar/baz'");
+ Session<?> session = mock(Session.class);
+ final AtomicReference<String> args = new AtomicReference<String>();
+ doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ args.set((String) arguments[0] + (String) arguments[1]);
+ return null;
+ }
+ }).when(session).rename(anyString(), anyString());
+ final List<String> madeDirs = new ArrayList<String>();
+ doAnswer(new Answer<Object>() {
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ madeDirs.add((String) invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(session).mkdir(anyString());
+ when (sessionFactory.getSession()).thenReturn(session);
+ Message<String> requestMessage = MessageBuilder.withPayload("foo")
+ .setHeader(FileHeaders.RENAME_TO, "bar")
+ .build();
+ Message<?> out = (Message<?>) gw.handleRequestMessage(requestMessage);
+ assertEquals("foo", out.getHeaders().get(FileHeaders.REMOTE_FILE));
+ assertEquals("foofoo/bar/baz", args.get());
+ assertEquals(Boolean.TRUE, out.getPayload());
+ assertEquals(2, madeDirs.size());
+ assertEquals("foo", madeDirs.get(0));
+ assertEquals("foo/bar", madeDirs.get(1));
+ }
+
/**
* @return
*/
@@ -650,7 +741,7 @@ public void testRm() throws Exception {
@SuppressWarnings({ "rawtypes", "unchecked" })
public TestRemoteFileOutboundGateway(SessionFactory sessionFactory,
String command, String expression) {
- super(sessionFactory, command, expression);
+ super(sessionFactory, Command.toCommand(command), expression);
}
@Override
@@ -683,12 +774,12 @@ protected long getModified(TestLsEntry file) {
class TestLsEntry extends AbstractFileInfo<TestLsEntry> {
- private String filename;
- private int size;
- private boolean dir;
- private boolean link;
- private long modified;
- private String permissions;
+ private final String filename;
+ private final int size;
+ private final boolean dir;
+ private final boolean link;
+ private final long modified;
+ private final String permissions;
public TestLsEntry(String filename, int size, boolean dir, boolean link,
long modified, String permissions) {
20 ...gration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-3.0.xsd
View
@@ -3,6 +3,7 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
+ xmlns:int-file="http://www.springframework.org/schema/integration/file"
targetNamespace="http://www.springframework.org/schema/integration/ftp"
elementFormDefault="qualified" attributeFormDefault="unqualified">
@@ -10,6 +11,8 @@
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-3.0.xsd" />
+ <xsd:import namespace="http://www.springframework.org/schema/integration/file"
+ schemaLocation="http://www.springframework.org/schema/integration/file/spring-integration-file-3.0.xsd" />
<xsd:element name="outbound-channel-adapter">
<xsd:annotation>
@@ -238,12 +241,15 @@
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
- <xsd:attribute name="command" use="required" type="xsd:string">
+ <xsd:attribute name="command" use="required">
<xsd:annotation>
<xsd:documentation>
- FTP command - ls, get or rm
+ FTP command.
</xsd:documentation>
</xsd:annotation>
+ <xsd:simpleType>
+ <xsd:union memberTypes="int-file:remoteGatewayCommand xsd:string"/>
+ </xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="command-options" type="xsd:string">
<xsd:annotation>
@@ -270,6 +276,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="rename-expression" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ SpEL expression representing the path for the
+ new filename when using the 'mv' command. Defaults
+ to "headers.['file_renameTo']".
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -507,5 +522,4 @@
</xsd:attribute>
</xsd:complexType>
-
</xsd:schema>
11 ...n-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml
View
@@ -47,6 +47,17 @@
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-gateway>
+ <int-ftp:outbound-gateway id="gateway3"
+ session-factory="sf"
+ request-channel="inbound1"
+ reply-channel="outbound"
+ auto-startup="false"
+ command="mv"
+ expression="payload"
+ rename-expression="'foo'"
+ order="1"
+ />
+
<int-ftp:outbound-gateway id="withBeanExpression"
local-directory="local-test-dir"
session-factory="sf"
29 ...egration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -28,6 +28,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.endpoint.AbstractEndpoint;
+import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Command;
+import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
@@ -57,6 +59,9 @@
AbstractEndpoint gateway2;
@Autowired
+ AbstractEndpoint gateway3;
+
+ @Autowired
AbstractEndpoint withBeanExpression;
private static volatile int adviceCalled;
@@ -71,11 +76,11 @@ public void testGateway1() {
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
- assertEquals("ls", TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
- assertTrue(options.contains("-1"));
- assertTrue(options.contains("-f"));
+ assertTrue(options.contains(Option.NAME_ONLY));
+ assertTrue(options.contains(Option.NOSORT));
Long sendTimeout = TestUtils.getPropertyValue(gateway, "messagingTemplate.sendTimeout", Long.class);
assertEquals(Long.valueOf(777), sendTimeout);
@@ -91,19 +96,29 @@ public void testGateway2() {
assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
- assertEquals("get", TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals(Command.GET, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
- assertTrue(options.contains("-P"));
+ assertTrue(options.contains(Option.PRESERVE_TIMESTAMP));
gateway.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
+ public void testGatewayMv() {
+ FtpOutboundGateway gateway = TestUtils.getPropertyValue(gateway3,
+ "handler", FtpOutboundGateway.class);
+ assertNotNull(TestUtils.getPropertyValue(gateway, "sessionFactory"));
+ assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
+ assertEquals(Command.MV, TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals("'foo'", TestUtils.getPropertyValue(gateway, "renameProcessor.expression.expression"));
+ }
+
+ @Test
public void testWithBeanExpression() {
FtpOutboundGateway gateway = TestUtils.getPropertyValue(withBeanExpression,
"handler", FtpOutboundGateway.class);
- ExpressionEvaluatingMessageProcessor<?> processor = TestUtils.getPropertyValue(gateway, "processor",
+ ExpressionEvaluatingMessageProcessor<?> processor = TestUtils.getPropertyValue(gateway, "fileNameProcessor",
ExpressionEvaluatingMessageProcessor.class);
assertNotNull(processor);
assertEquals("foo", processor.processMessage(MessageBuilder.withPayload("bar").build()));
20 ...tion-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-3.0.xsd
View
@@ -3,6 +3,7 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
+ xmlns:int-file="http://www.springframework.org/schema/integration/file"
targetNamespace="http://www.springframework.org/schema/integration/sftp"
elementFormDefault="qualified" attributeFormDefault="unqualified">
@@ -10,7 +11,8 @@
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-3.0.xsd" />
-
+ <xsd:import namespace="http://www.springframework.org/schema/integration/file"
+ schemaLocation="http://www.springframework.org/schema/integration/file/spring-integration-file-3.0.xsd" />
<xsd:element name="outbound-channel-adapter">
<xsd:annotation>
@@ -240,12 +242,15 @@
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
- <xsd:attribute name="command" use="required" type="xsd:string">
+ <xsd:attribute name="command" use="required">
<xsd:annotation>
<xsd:documentation>
- sftp command - ls, get or rm
+ sftp command.
</xsd:documentation>
</xsd:annotation>
+ <xsd:simpleType>
+ <xsd:union memberTypes="int-file:remoteGatewayCommand xsd:string"/>
+ </xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="command-options" type="xsd:string">
<xsd:annotation>
@@ -272,6 +277,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="rename-expression" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ SpEL expression representing the path for the
+ new filename when using the 'mv' command. Defaults
+ to "headers.['file_renameTo']".
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
10 ...ftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml
View
@@ -43,6 +43,16 @@
order="2"
/>
+ <int-sftp:outbound-gateway id="gateway3"
+ session-factory="sf"
+ request-channel="inbound1"
+ reply-channel="outbound"
+ command="mv"
+ expression="payload"
+ rename-expression="'foo'"
+ order="1"
+ />
+
<int-sftp:outbound-gateway id="advised"
local-directory="local-test-dir"
session-factory="sf"
27 ...ation-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -28,6 +28,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.endpoint.AbstractEndpoint;
+import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Command;
+import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
@@ -55,6 +57,9 @@
AbstractEndpoint gateway2;
@Autowired
+ AbstractEndpoint gateway3;
+
+ @Autowired
AbstractEndpoint advised;
private static volatile int adviceCalled;
@@ -69,11 +74,11 @@ public void testGateway1() {
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
- assertEquals("ls", TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
- assertTrue(options.contains("-1"));
- assertTrue(options.contains("-f"));
+ assertTrue(options.contains(Option.NAME_ONLY));
+ assertTrue(options.contains(Option.NOSORT));
Long sendTimeout = TestUtils.getPropertyValue(gateway, "messagingTemplate.sendTimeout", Long.class);
assertEquals(Long.valueOf(777), sendTimeout);
@@ -89,10 +94,20 @@ public void testGateway2() {
assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
- assertEquals("get", TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals(Command.GET, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
- assertTrue(options.contains("-P"));
+ assertTrue(options.contains(Option.PRESERVE_TIMESTAMP));
+ }
+
+ @Test
+ public void testGatewayMv() {
+ SftpOutboundGateway gateway = TestUtils.getPropertyValue(gateway3,
+ "handler", SftpOutboundGateway.class);
+ assertNotNull(TestUtils.getPropertyValue(gateway, "sessionFactory"));
+ assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
+ assertEquals(Command.MV, TestUtils.getPropertyValue(gateway, "command"));
+ assertEquals("'foo'", TestUtils.getPropertyValue(gateway, "renameProcessor.expression.expression"));
}
@Test
18 src/reference/docbook/ftp.xml
View
@@ -327,6 +327,7 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
<listitem>get (retrieve file)</listitem>
<listitem>mget (retrieve file(s))</listitem>
<listitem>rm (remove file(s))</listitem>
+ <listitem>mv (move/rename file)</listitem>
</itemizedlist>
</para>
<para><emphasis>ls</emphasis></para>
@@ -397,8 +398,23 @@ 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>
- In each case, the PATH that these commands act on is provided by the 'expression'
+ The <emphasis>mv</emphasis> command has no options.
+ </para>
+ <para>
+ The <emphasis>expression</emphasis> attribute defines the "from" path and the
+ <emphasis>rename-expression</emphasis> attribute defines the "to" path. By default, the
+ <emphasis>rename-expression</emphasis> is <code>headers['file_renameTo']</code>. This
+ expression must not evaluate to null, or an empty <code>String</code>. If necessary,
+ any remote directories needed will be created.
+ The payload of the result message is <code>Boolean.TRUE</code>.
+ The original remote directory is provided in the <code>file_remoteDirectory</code> header, and the filename is
+ provided in the <code>file_remoteFile</code> header. The new path is in
+ 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.
</para>
18 src/reference/docbook/sftp.xml
View
@@ -360,6 +360,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
<listitem>get (retrieve file)</listitem>
<listitem>mget (retrieve file(s))</listitem>
<listitem>rm (remove file(s))</listitem>
+ <listitem>mv (move/rename file)</listitem>
</itemizedlist>
</para>
<para><emphasis>ls</emphasis></para>
@@ -430,8 +431,23 @@ 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>
- In each case, the PATH that these commands act on is provided by the 'expression'
+ The <emphasis>mv</emphasis> command has no options.
+ </para>
+ <para>
+ The <emphasis>expression</emphasis> attribute defines the "from" path and the
+ <emphasis>rename-expression</emphasis> attribute defines the "to" path. By default, the
+ <emphasis>rename-expression</emphasis> is <code>headers['file_renameTo']</code>. This
+ expression must not evaluate to null, or an empty <code>String</code>. If necessary,
+ any remote directories needed will be created.
+ The payload of the result message is <code>Boolean.TRUE</code>.
+ The original remote directory is provided in the <code>file_remoteDirectory</code> header, and the filename is
+ provided in the <code>file_remoteFile</code> header. The new path is in
+ 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.
</para>
7 src/reference/docbook/whats-new.xml
View
@@ -78,6 +78,13 @@
URI-schemes supported by Spring Web Services. For more information see <xref linkend="outbound-uri"/>.
</para>
</section>
+ <section id="3.0-xFTP-gw">
+ <title>(S)FTP(S) Gateways</title>
+ <para>
+ The gateways now support the <code>mv</code> command, enabling the renaming of remote
+ files.
+ </para>
+ </section>
</section>
</chapter>
Something went wrong with that request. Please try again.