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:
Gary Russell
2019-08-15 17:38:24 -04:00
committed by Artem Bilan
parent f7032c7127
commit b04a8576fa
11 changed files with 94 additions and 10 deletions

View File

@@ -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");
}
}

View File

@@ -71,6 +71,7 @@ public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayP
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "working-dir-expression",
"workingDirExpressionString");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "chmod", "chmodOctal");
}
}

View File

@@ -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);
}
});
}
}

View File

@@ -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);
}
});
}
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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