GH-3026 Support chmod with FTP
Resolves https://github.com/spring-projects/spring-integration/issues/3026 * Fix exception messages; remove test TODOs; test works on Windows # Conflicts: # spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java # src/reference/asciidoc/whats-new.adoc
This commit is contained in:
committed by
Artem Bilan
parent
f7032c7127
commit
b04a8576fa
@@ -20,6 +20,7 @@ import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.ftp.outbound.FtpMessageHandler;
|
||||
@@ -56,6 +57,7 @@ public class FtpOutboundChannelAdapterParser extends RemoteFileOutboundChannelAd
|
||||
.getValue();
|
||||
templateDefinition.getPropertyValues() // NOSONAR never null
|
||||
.add("existsMode", FtpRemoteFileTemplate.ExistsMode.NLST);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "chmod", "chmodOctal");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayP
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "working-dir-expression",
|
||||
"workingDirExpressionString");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "chmod", "chmodOctal");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.ftp.gateway;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -31,7 +32,9 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.file.remote.AbstractFileInfo;
|
||||
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.MessageSessionCallback;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
@@ -296,4 +299,22 @@ public class FtpOutboundGateway extends AbstractRemoteFileOutboundGateway<FTPFil
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChmodCapable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doChmod(RemoteFileOperations<FTPFile> remoteFileOperations, final String path, final int chmod) {
|
||||
remoteFileOperations.executeWithClient((ClientCallbackWithoutResult<FTPClient>) client -> {
|
||||
String chModCommand = "chmod " + Integer.toOctalString(chmod) + " " + path;
|
||||
try {
|
||||
client.sendSiteCommand(chModCommand);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to execute '" + chModCommand + "'", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.integration.ftp.outbound;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
@@ -47,4 +52,22 @@ public class FtpMessageHandler extends FileTransferringMessageHandler<FTPFile> {
|
||||
super(remoteFileTemplate, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChmodCapable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doChmod(RemoteFileTemplate<FTPFile> remoteFileTemplate, final String path, final int chmod) {
|
||||
remoteFileTemplate.executeWithClient((ClientCallbackWithoutResult<FTPClient>) client -> {
|
||||
String chModCommand = "chmod " + Integer.toOctalString(chmod) + " " + path;
|
||||
try {
|
||||
client.sendSiteCommand(chModCommand);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to execute '" + chModCommand + "'", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="int-file:remoteOutboundAttributeGroup" />
|
||||
<xsd:attributeGroup ref="int-file:chmod" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -517,6 +518,7 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="int-file:remoteOutboundAttributeGroup" />
|
||||
<xsd:attributeGroup ref="int-file:chmod" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
auto-create-directory="true"
|
||||
filename-pattern="*.txt"
|
||||
expression="payload"
|
||||
chmod="600"
|
||||
remote-directory="ftpTarget"
|
||||
mput-filter="sortingFilter"
|
||||
reply-channel="output"/>
|
||||
@@ -141,6 +142,7 @@
|
||||
session-factory="ftpSessionFactory"
|
||||
channel="appending"
|
||||
mode="APPEND"
|
||||
chmod="600"
|
||||
use-temporary-file-name="false"
|
||||
remote-directory="ftpTarget"
|
||||
auto-create-directory="true"
|
||||
|
||||
@@ -34,6 +34,8 @@ import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -58,6 +60,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -395,7 +398,12 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3088MPutNotRecursive() {
|
||||
public void testInt3088MPutNotRecursive() throws IOException {
|
||||
Session<?> session = ftpSessionFactory.getSession();
|
||||
session.close();
|
||||
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
|
||||
FTPClient client = spy(TestUtils.getPropertyValue(session, "client", FTPClient.class));
|
||||
new DirectFieldAccessor(session).setPropertyValue("client", client);
|
||||
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
|
||||
@@ -409,6 +417,8 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
assertThat(
|
||||
out.getPayload().get(1),
|
||||
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt")));
|
||||
verify(client).sendSiteCommand("chmod 600 ftpTarget/localSource1.txt");
|
||||
verify(client).sendSiteCommand("chmod 600 ftpTarget/localSource1.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -454,7 +464,12 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3412FileMode() {
|
||||
public void testInt3412FileMode() throws IOException {
|
||||
Session<?> session = ftpSessionFactory.getSession();
|
||||
session.close();
|
||||
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
|
||||
FTPClient client = spy(TestUtils.getPropertyValue(session, "client", FTPClient.class));
|
||||
new DirectFieldAccessor(session).setPropertyValue("client", client);
|
||||
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory);
|
||||
assertFalse(template.exists("ftpTarget/appending.txt"));
|
||||
Message<String> m = MessageBuilder.withPayload("foo")
|
||||
@@ -474,7 +489,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause().getCause().getMessage(), containsString("The destination file already exists"));
|
||||
}
|
||||
|
||||
verify(client, times(2)).sendSiteCommand("chmod 600 ftpTarget/appending.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -158,7 +158,8 @@ public class SftpOutboundGateway extends AbstractRemoteFileOutboundGateway<LsEnt
|
||||
client.chmod(chmod, path);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new GeneralSftpException("Failed to execute chmod", e);
|
||||
throw new GeneralSftpException(
|
||||
"Failed to execute 'chmod " + Integer.toOctalString(chmod) + " " + path + "'", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ public class SftpMessageHandler extends FileTransferringMessageHandler<LsEntry>
|
||||
client.chmod(chmod, path);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new GeneralSftpException("Failed to execute chmod", e);
|
||||
throw new GeneralSftpException(
|
||||
"Failed to execute 'chmod " + Integer.toOctalString(chmod) + " " + path + "'", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -833,6 +833,7 @@ The following example shows how to configure an `outbound-channel-adapter`:
|
||||
temporary-remote-directory-expression="headers['temp_remote_dir']"
|
||||
filename-generator="fileNameGenerator"
|
||||
use-temporary-filename="true"
|
||||
chmod="600"
|
||||
mode="REPLACE"/>
|
||||
----
|
||||
====
|
||||
@@ -860,6 +861,11 @@ The modes are defined by the `FileExistsMode` enumeration, which includes the fo
|
||||
`IGNORE` and `FAIL` do not transfer the file.
|
||||
`FAIL` causes an exception to be thrown, while `IGNORE` silently ignores the transfer (although a `DEBUG` log entry is produced).
|
||||
|
||||
Version 5.2 introduced the `chmod` attribute, which you can use to change the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter using java, you can use `setChmodOctal("600")` or `setChmod(0600)`.
|
||||
Only applies if your FTP server supports the `SITE CHMOD` subcommand.
|
||||
|
||||
==== Avoiding Partially Written Files
|
||||
|
||||
One of the common problems that arises when dealing with file transfers is the possibility of processing a partial file.
|
||||
@@ -1185,6 +1191,11 @@ See the https://github.com/spring-projects/spring-integration/tree/master/spring
|
||||
|
||||
The message payload resulting from a `put` operation is a `String` that represents the full path of the file on the server after transfer.
|
||||
|
||||
Version 5.2 introduced the `chmod` attribute, which changes the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter using java, you can use `setChmod(0600)`.
|
||||
Only applies if your FTP server supports the `SITE CHMOD` subcommand.
|
||||
|
||||
Using the `mput` Command
|
||||
|
||||
The `mput` sends multiple files to the server and supports only one option:
|
||||
@@ -1204,6 +1215,11 @@ The message payload resulting from an `mget` operation is a `List<String>` objec
|
||||
|
||||
See also <<ftp-partial>>.
|
||||
|
||||
Version 5.2 introduced the `chmod` attribute, which lets you change the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter with Java, you can use `setChmodOctal("600")` or `setChmod(0600)`.
|
||||
Only applies if your FTP server supports the `SITE CHMOD` subcommand.
|
||||
|
||||
==== Using the `rm` Command
|
||||
|
||||
The `rm` command removes files.
|
||||
|
||||
@@ -855,6 +855,10 @@ The modes are defined by the `FileExistsMode` enumeration, which has the followi
|
||||
With `IGNORE` and `FAIL`, the file is not transferred.
|
||||
`FAIL` causes an exception to be thrown, while `IGNORE` silently ignores the transfer (although a `DEBUG` log entry is produced).
|
||||
|
||||
Version 4.3 introduced the `chmod` attribute, which you can use to change the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter using java, you can use `setChmodOctal("600")` or `setChmod(0600)`.
|
||||
|
||||
==== Avoiding Partially Written Files
|
||||
|
||||
One of the common problems when dealing with file transfers is the possibility of processing a partial file.
|
||||
@@ -869,10 +873,6 @@ However, there may be situations where you do not want to use this technique (fo
|
||||
For situations like this, you can disable this feature by setting `use-temporary-file-name` to `false` (the default is `true`).
|
||||
When this attribute is `false`, the file is written with its final name, and the consuming application needs some other mechanism to detect that the file is completely uploaded before accessing it.
|
||||
|
||||
Version 4.3 introduced the `chmod` attribute, which you can use to change the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter using java, you can use `setChmodOctal("600")` or `setChmodDecimal(384)`.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
|
||||
The following Spring Boot application shows an example of how to configure the outbound adapter with Java:
|
||||
@@ -1174,7 +1174,7 @@ See also <<sftp-partial>>.
|
||||
|
||||
Version 4.3 introduced the `chmod` attribute, which lets you change the remote file permissions after upload.
|
||||
You can use the conventional Unix octal format (for example, `600` allows read-write for the file owner only).
|
||||
When configuring the adapter with Java, you can use `setChmodOctal("600")` or `setChmodDecimal(384)`.
|
||||
When configuring the adapter with Java, you can use `setChmodOctal("600")` or `setChmod(0600)`.
|
||||
|
||||
Using the `rm` Command
|
||||
|
||||
|
||||
Reference in New Issue
Block a user