INT-3973: SFTP - Support chmod

JIRA: https://jira.spring.io/browse/INT-3973

Add `chmod` to outbound adapter and gateway (put methods).

Polishing - PR Comments

Fix Checkstyle vulnerabilities
This commit is contained in:
Gary Russell
2016-03-21 19:19:27 -04:00
committed by Artem Bilan
parent 2936e97ea3
commit ad0839da8b
22 changed files with 392 additions and 39 deletions

View File

@@ -33,8 +33,11 @@ import org.junit.rules.TemporaryFolder;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import com.jcraft.jsch.ChannelSftp.LsEntry;
/**
* @author Gary Russell
* @author Artem Bilan
@@ -170,14 +173,14 @@ public class TestSftpServer implements InitializingBean, DisposableBean {
}
}
public DefaultSftpSessionFactory getSessionFactory() {
public CachingSessionFactory<LsEntry> getSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost("localhost");
factory.setPort(this.server.getPort());
factory.setUser("foo");
factory.setPassword("foo");
factory.setAllowUnknownKeys(true);
return factory;
return new CachingSessionFactory<LsEntry>(factory);
}
}

View File

@@ -18,7 +18,9 @@ package org.springframework.integration.sftp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import com.jcraft.jsch.ChannelSftp.LsEntry;
/**
* @author Gary Russell
@@ -34,7 +36,7 @@ public class TestSftpServerConfig {
}
@Bean
public DefaultSftpSessionFactory sftpSessionFactory(TestSftpServer server) {
public CachingSessionFactory<LsEntry> sftpSessionFactory(TestSftpServer server) {
return sftpServer().getSessionFactory();
}

View File

@@ -16,9 +16,9 @@
<property name="port" value="2222"/>
<property name="user" value="oleg"/>
</bean>
<int:channel id="inputChannel"/>
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapterWithExpression"
session-factory="sftpSessionFactory"
channel="inputChannel"
@@ -26,8 +26,8 @@
remote-filename-generator="fileNameGenerator"
remote-directory-expression="'foo' + '/' + 'bar'"
remote-filename-generator-expression="payload.getName() + '-foo'"/>
<bean id="fileNameGenerator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.file.FileNameGenerator"/>
</bean>

View File

@@ -32,6 +32,7 @@
temporary-file-suffix=".bar"
remote-directory="foo/bar"
temporary-remote-directory="foo/baz"
chmod="600"
order="23"/>
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapterWithExpression"

View File

@@ -32,22 +32,22 @@ import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.messaging.Message;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Oleg Zhurakousky
@@ -61,26 +61,33 @@ public class OutboundChannelAdapterParserTests {
@Test
public void testOutboundChannelAdapterWithId(){
ApplicationContext context =
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapter");
assertTrue(consumer instanceof EventDrivenConsumer);
PublishSubscribeChannel channel = context.getBean("inputChannel", PublishSubscribeChannel.class);
assertEquals(channel, TestUtils.getPropertyValue(consumer, "inputChannel"));
assertEquals("sftpOutboundAdapter", ((EventDrivenConsumer)consumer).getComponentName());
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler, "remoteFileTemplate.remoteFileSeparator");
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler",
FileTransferringMessageHandler.class);
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.remoteFileSeparator");
assertNotNull(remoteFileSeparator);
assertEquals(".", remoteFileSeparator);
assertEquals(".bar", TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryFileSuffix", String.class));
Expression remoteDirectoryExpression = (Expression) TestUtils.getPropertyValue(handler, "remoteFileTemplate.directoryExpressionProcessor.expression");
assertEquals(".bar",
TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryFileSuffix", String.class));
Expression remoteDirectoryExpression = (Expression) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.directoryExpressionProcessor.expression");
assertNotNull(remoteDirectoryExpression);
assertTrue(remoteDirectoryExpression instanceof LiteralExpression);
assertNotNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"));
assertEquals(context.getBean("fileNameGenerator"), TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
assertEquals(context.getBean("fileNameGenerator"),
TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset"));
CachingSessionFactory<?> sessionFactory = TestUtils.getPropertyValue(handler, "remoteFileTemplate.sessionFactory", CachingSessionFactory.class);
DefaultSftpSessionFactory clientFactory = TestUtils.getPropertyValue(sessionFactory, "sessionFactory", DefaultSftpSessionFactory.class);
CachingSessionFactory<?> sessionFactory = TestUtils.getPropertyValue(handler,
"remoteFileTemplate.sessionFactory", CachingSessionFactory.class);
DefaultSftpSessionFactory clientFactory = TestUtils.getPropertyValue(sessionFactory, "sessionFactory",
DefaultSftpSessionFactory.class);
assertEquals("localhost", TestUtils.getPropertyValue(clientFactory, "host"));
assertEquals(2222, TestUtils.getPropertyValue(clientFactory, "port"));
assertEquals(23, TestUtils.getPropertyValue(handler, "order"));
@@ -91,13 +98,16 @@ public class OutboundChannelAdapterParserTests {
TestUtils.getPropertyValue(channel, "dispatcher"),
"handlers");
Iterator<MessageHandler> iterator = handlers.iterator();
assertSame(TestUtils.getPropertyValue(context.getBean("sftpOutboundAdapterWithExpression"), "handler"), iterator.next());
assertSame(TestUtils.getPropertyValue(context.getBean("sftpOutboundAdapterWithExpression"), "handler"),
iterator.next());
assertSame(handler, iterator.next());
assertEquals(384, TestUtils.getPropertyValue(handler, "chmod"));
context.close();
}
@Test
public void testOutboundChannelAdapterWithWithRemoteDirectoryAndFileExpression(){
ApplicationContext context =
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapterWithExpression");
assertTrue(consumer instanceof EventDrivenConsumer);
@@ -113,32 +123,35 @@ public class OutboundChannelAdapterParserTests {
assertEquals("payload.getName() + '-foo'", fileNameGeneratorExpression.getExpressionString());
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset"));
assertNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"));
context.close();
}
@Test
public void testOutboundChannelAdapterWithNoTemporaryFileName(){
ApplicationContext context =
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapterWithNoTemporaryFileName");
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
assertFalse((Boolean)TestUtils.getPropertyValue(handler,"remoteFileTemplate.useTemporaryFileName"));
context.close();
}
@Test
public void advised(){
ApplicationContext context =
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("advised");
MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
context.close();
}
@Test
public void testFailWithRemoteDirAndExpression(){
try {
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail.xml", this.getClass());
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail.xml", this.getClass())
.close();
fail("Exception expected");
}
catch (BeanDefinitionStoreException e) {
@@ -149,8 +162,8 @@ public class OutboundChannelAdapterParserTests {
@Test(expected=BeanDefinitionStoreException.class)
public void testFailWithFileExpressionAndFileGenerator(){
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml", this.getClass());
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml",
this.getClass()).close();
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {

View File

@@ -73,6 +73,7 @@
auto-create-directory="true"
filename-pattern="*.txt"
expression="payload"
chmod="600"
remote-directory="sftpTarget"
reply-channel="output"/>

View File

@@ -21,12 +21,13 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -44,14 +45,15 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.remote.MessageSessionCallback;
import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.TestSftpServer;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
@@ -114,7 +116,7 @@ public class SftpServerOutboundTests {
private DirectChannel inboundMPutRecursiveFiltered;
@Autowired
private DefaultSftpSessionFactory sessionFactory;
private SessionFactory<LsEntry> sessionFactory;
@Autowired
private DirectChannel appending;
@@ -160,8 +162,8 @@ public class SftpServerOutboundTests {
assertThat(localFile.getPath().replaceAll(java.util.regex.Matcher.quoteReplacement(File.separator), "/"),
Matchers.containsString(dir.toUpperCase()));
Session<?> session2 = this.sessionFactory.getSession();
assertSame(TestUtils.getPropertyValue(session, "jschSession"),
TestUtils.getPropertyValue(session2, "jschSession"));
assertSame(TestUtils.getPropertyValue(session, "targetSession.jschSession"),
TestUtils.getPropertyValue(session2, "targetSession.jschSession"));
}
@Test
@@ -327,7 +329,13 @@ public class SftpServerOutboundTests {
}
@Test
public void testInt3088MPutNotRecursive() {
public void testInt3088MPutNotRecursive() throws Exception {
Session<?> session = sessionFactory.getSession();
session.close();
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
ChannelSftp channel = spy(TestUtils.getPropertyValue(session, "channel", ChannelSftp.class));
new DirectFieldAccessor(session).setPropertyValue("channel", channel);
String dir = "sftpSource/";
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
while (output.receive(0) != null) { }
@@ -344,6 +352,8 @@ public class SftpServerOutboundTests {
assertThat(
out.getPayload().get(1),
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt")));
verify(channel).chmod(384, "sftpTarget/localSource1.txt"); // 384 = 600 octal
verify(channel).chmod(384, "sftpTarget/localSource2.txt");
}
@Test
@@ -419,6 +429,9 @@ public class SftpServerOutboundTests {
@Test
public void testStream() {
Session<?> session = spy(this.sessionFactory.getSession());
session.close();
String dir = "sftpSource/";
this.inboundGetStream.send(new GenericMessage<Object>(dir + "sftpSource1.txt"));
Message<?> result = this.output.receive(1000);
@@ -426,7 +439,7 @@ public class SftpServerOutboundTests {
assertEquals("source1", result.getPayload());
assertEquals("sftpSource/", result.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
assertEquals("sftpSource1.txt", result.getHeaders().get(FileHeaders.REMOTE_FILE));
assertFalse(((Session<?>) result.getHeaders().get(FileHeaders.REMOTE_SESSION)).isOpen());
verify(session).close();
}
@Test
@@ -449,6 +462,7 @@ public class SftpServerOutboundTests {
assertEquals(6, files[0].getAttrs().getSize());
}
@SuppressWarnings("unused")
private static final class TestMessageSessionCallback
implements MessageSessionCallback<LsEntry, Object> {

View File

@@ -33,6 +33,7 @@ import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.SessionCallbackWithoutResult;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.sftp.TestSftpServer;
import org.springframework.integration.sftp.TestSftpServerConfig;
@@ -60,7 +61,7 @@ public class SftpRemoteFileTemplateTests {
private TestSftpServer sftpServer;
@Autowired
private DefaultSftpSessionFactory sessionFactory;
private CachingSessionFactory<LsEntry> sessionFactory;
@Before
@After