Skip to content

Commit

Permalink
GH-3647: Use remoteDirExpression in MV command (#3651)
Browse files Browse the repository at this point in the history
* GH-3647: Use remoteDirExpression in MV command

Fixes #3647

To simplify a source and renameTo remote file expressions, the `remoteDirectoryExpression`
is consulted now, when they are not full paths.
This is useful when we want just to rename a remote file in some dir

* * Add JavaDoc for `getDirectoryExpressionProcessor()`
* Fix language in docs
  • Loading branch information
artembilan committed Oct 21, 2021
1 parent e84bab6 commit 78366e7
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessagingException;
Expand Down Expand Up @@ -158,6 +160,15 @@ public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) {
new ExpressionEvaluatingMessageProcessor<>(remoteDirectoryExpression, String.class);
}

/**
* Return the processor for remote directory SpEL expression if any.
* @return the processor for remote directory SpEL expression.
*/
@Nullable
public MessageProcessor<String> getDirectoryExpressionProcessor() {
return this.directoryExpressionProcessor;
}

/**
* Set a temporary remote directory expression; used when transferring files to the remote
* system. After a successful transfer the file is renamed using the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.integration.support.MutableMessage;
import org.springframework.integration.support.PartialSuccessException;
import org.springframework.integration.support.utils.IntegrationUtils;
Expand Down Expand Up @@ -736,8 +738,45 @@ private Object doMv(Message<?> requestMessage) {
String remoteFilePath = obtainRemoteFilePath(requestMessage);
String remoteFilename = getRemoteFilename(remoteFilePath);
String remoteDir = getRemoteDirectory(remoteFilePath, remoteFilename);
if (remoteDir == null) {
MessageProcessor<String> directoryExpressionProcessor =
this.remoteFileTemplate.getDirectoryExpressionProcessor();
if (directoryExpressionProcessor != null) {
remoteDir = directoryExpressionProcessor.processMessage(requestMessage);
if (remoteDir != null) {
remoteFilePath = remoteDir + this.remoteFileTemplate.getRemoteFileSeparator() + remoteFilename;
}
}
}

String remoteFileNewPath = buildRemoteFileNewPath(requestMessage, remoteDir);
Assert.hasLength(remoteFileNewPath, "New filename cannot be empty");

return executeMv(requestMessage, remoteFilename, remoteDir, remoteFilePath, remoteFileNewPath);
}

private String obtainRemoteFilePath(Message<?> requestMessage) {
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
Assert.state(remoteFilePath != null,
() -> "The 'fileNameProcessor' evaluated to null 'remoteFilePath' from message: " + requestMessage);
return remoteFilePath;
}

private String buildRemoteFileNewPath(Message<?> requestMessage, @Nullable String remoteDir) {
String remoteFileNewPath = this.renameProcessor.processMessage(requestMessage);
Assert.hasLength(remoteFileNewPath, "New filename cannot be empty");
if (remoteDir != null) {
String remoteFilename = getRemoteFilename(remoteFileNewPath);
String dir = getRemoteDirectory(remoteFileNewPath, remoteFilename);
if (dir == null) {
remoteFileNewPath = remoteDir + this.remoteFileTemplate.getRemoteFileSeparator() + remoteFilename;
}
}
return remoteFileNewPath;
}

private AbstractIntegrationMessageBuilder<Boolean> executeMv(Message<?> requestMessage, String remoteFilename,
String remoteDir, String remoteFilePath, String remoteFileNewPath) {

return this.remoteFileTemplate.execute(session -> {
Boolean result = mv(requestMessage, session, remoteFilePath, remoteFileNewPath);
Expand All @@ -747,15 +786,7 @@ private Object doMv(Message<?> requestMessage) {
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
.setHeader(FileHeaders.RENAME_TO, remoteFileNewPath)
.setHeader(FileHeaders.REMOTE_HOST_PORT, session.getHostPort());
}
);
}

private String obtainRemoteFilePath(Message<?> requestMessage) {
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
Assert.state(remoteFilePath != null,
() -> "The 'fileNameProcessor' evaluated to null 'remoteFilePath' from message: " + requestMessage);
return remoteFilePath;
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2021 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.
Expand Down Expand Up @@ -79,7 +79,7 @@ public void testSftpInboundFlow() {
.regexFilter(".*\\.txt$")
.localFilenameExpression("#this.toUpperCase() + '.a'")
.localDirectory(getTargetLocalDirectory())
.remoteComparator(Comparator.naturalOrder()),
.remoteComparator(Comparator.naturalOrder()),
e -> e.id("sftpInboundAdapter").poller(Pollers.fixedDelay(100)))
.channel(out)
.get();
Expand All @@ -105,10 +105,10 @@ public void testSftpInboundFlow() {
public void testSftpInboundStreamFlow() throws Exception {
QueueChannel out = new QueueChannel();
StandardIntegrationFlow flow = IntegrationFlows.from(
Sftp.inboundStreamingAdapter(new SftpRemoteFileTemplate(sessionFactory()))
.remoteDirectory("sftpSource")
.regexFilter(".*\\.txt$"),
e -> e.id("sftpInboundAdapter").poller(Pollers.fixedDelay(100)))
Sftp.inboundStreamingAdapter(new SftpRemoteFileTemplate(sessionFactory()))
.remoteDirectory("sftpSource")
.regexFilter(".*\\.txt$"),
e -> e.id("sftpInboundAdapter").poller(Pollers.fixedDelay(100)))
.channel(out)
.get();
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
Expand Down Expand Up @@ -230,7 +230,7 @@ public void testSftpMgetFlow() {
QueueChannel out = new QueueChannel();
IntegrationFlow flow = f -> f
.handle(Sftp.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.MGET,
"payload")
"payload")
.options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
.regexFileNameFilter("(subSftpSource|.*1.txt)")
.localDirectoryExpression("'" + getTargetLocalDirectoryName() + "' + #remoteDirectory")
Expand Down Expand Up @@ -272,6 +272,31 @@ public void testSftpSessionCallback() {
registration.destroy();
}


@Test
public void testSftpMv() {
QueueChannel out = new QueueChannel();
IntegrationFlow flow = f -> f
.handle(Sftp.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.MV, "payload")
.renameExpression("payload.concat('.done')")
.remoteDirectoryExpression("'sftpSource'"))
.channel(out);
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
registration.getInputChannel().send(new GenericMessage<>("sftpSource2.txt"));
Message<?> receive = out.receive(10_000);
assertThat(receive)
.isNotNull()
.extracting(Message::getPayload)
.isEqualTo(Boolean.TRUE);

assertThat(receive.getHeaders())
.containsEntry(FileHeaders.REMOTE_FILE, "sftpSource2.txt")
.containsEntry(FileHeaders.REMOTE_DIRECTORY, "sftpSource")
.containsEntry(FileHeaders.RENAME_TO, "sftpSource/sftpSource2.txt.done");

registration.destroy();
}

@Configuration
@EnableIntegration
public static class ContextConfiguration {
Expand Down
6 changes: 5 additions & 1 deletion src/reference/asciidoc/ftp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,7 @@ The `file_remoteDirectory` header provides the remote directory, and the `file_r

==== Using the `mv` Command

The `mv` command moves files
The `mv` command moves files.

The `mv` command has no options.

Expand All @@ -1259,6 +1259,10 @@ The payload of the result message is `Boolean.TRUE`.
The `file_remoteDirectory` header provides the original remote directory, and `file_remoteFile` header provides the file name.
The new path is in the `file_renameTo` header.

Starting with version 5.5.6, the `remoteDirectoryExpression` can be used in the `mv` command for convenience.
If the "`from`" file is not a full file path, the result of `remoteDirectoryExpression` is used as the remote directory.
The same applies for the "`to`" file, for example, if the task is just to rename a remote file in some directory.

==== Additional Information about FTP Outbound Gateway Commands

The `get` and `mget` commands support the `local-filename-generator-expression` attribute.
Expand Down
4 changes: 4 additions & 0 deletions src/reference/asciidoc/sftp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,10 @@ The payload of the result message is `Boolean.TRUE`.
The the `file_remoteDirectory` header holds the original remote directory, and the `file_remoteFile` header holds the filename.
The `file_renameTo` header holds the new path.

Starting with version 5.5.6, the `remoteDirectoryExpression` can be used in the `mv` command for convenience.
If the "`from`" file is not a full file path, the result of `remoteDirectoryExpression` is used as the remote directory.
The same applies for the "`to`" file, for example, if the task is just to rename a remote file in some directory.

==== Additional Command Information

The `get` and `mget` commands support the `local-filename-generator-expression` attribute.
Expand Down
2 changes: 2 additions & 0 deletions src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ For this reason, the property is `false` by default; this may change in a future

The `FileInboundChannelAdapterSpec` has now a convenient `recursive(boolean)` option instead of requiring an explicit reference to the `RecursiveDirectoryScanner`.

The `remoteDirectoryExpression` can now be used in the `mv` command for convenience.

[[x5.5-mongodb]]
==== MongoDb Changes

Expand Down

0 comments on commit 78366e7

Please sign in to comment.