GH-2996: Add Resource for remote file transfer (#3231)

* GH-2996: Add Resource for remote file transfer

Fixes https://github.com/spring-projects/spring-integration/issues/2996

* Fix `RemoteFileTemplate.send()` to support a `Resource` payload
for remote file transferring content
* Code style clean up for `RemoteFileTemplate`
* Remove `volatile` for configuration properties for better performance
* Change a `charset` to the `Charset` for only once conversion from string
during configuration phase
* Fix (S)FTP tests for new functionality
* Change affected tests to JUnit 5
* Document a new feature; mention all the supported types and `FileExistsMode` constants

* * Fix language in `whats-new.adoc`
This commit is contained in:
Artem Bilan
2020-03-31 16:09:07 -04:00
committed by GitHub
parent 63f8907e88
commit 6d5690fd58
8 changed files with 171 additions and 122 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,15 +17,17 @@
package org.springframework.integration.sftp.config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
@@ -41,47 +43,54 @@ import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @author David Turanski
* @author Gunnar Hillert
* @author Artem Bilan
*/
@SpringJUnitConfig
@DirtiesContext
public class OutboundChannelAdapterParserTests {
private static volatile int adviceCalled;
@Autowired
ApplicationContext context;
@Test
public void testOutboundChannelAdapterWithId() {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapter");
assertThat(consumer instanceof EventDrivenConsumer).isTrue();
PublishSubscribeChannel channel = context.getBean("inputChannel", PublishSubscribeChannel.class);
EventDrivenConsumer consumer = this.context.getBean("sftpOutboundAdapter", EventDrivenConsumer.class);
PublishSubscribeChannel channel = this.context.getBean("inputChannel", PublishSubscribeChannel.class);
assertThat(TestUtils.getPropertyValue(consumer, "inputChannel")).isEqualTo(channel);
assertThat(((EventDrivenConsumer) consumer).getComponentName()).isEqualTo("sftpOutboundAdapter");
assertThat(consumer.getComponentName()).isEqualTo("sftpOutboundAdapter");
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler",
FileTransferringMessageHandler.class);
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.remoteFileSeparator");
String remoteFileSeparator =
TestUtils.getPropertyValue(handler, "remoteFileTemplate.remoteFileSeparator", String.class);
assertThat(remoteFileSeparator).isNotNull();
assertThat(remoteFileSeparator).isEqualTo(".");
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryFileSuffix", String.class))
.isEqualTo(".bar");
Expression remoteDirectoryExpression = (Expression) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.directoryExpressionProcessor.expression");
assertThat(remoteDirectoryExpression).isNotNull();
assertThat(remoteDirectoryExpression instanceof LiteralExpression).isTrue();
Expression remoteDirectoryExpression =
TestUtils.getPropertyValue(handler, "remoteFileTemplate.directoryExpressionProcessor.expression",
Expression.class);
assertThat(remoteDirectoryExpression)
.isNotNull()
.isInstanceOf(LiteralExpression.class);
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"))
.isNotNull();
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"))
.isEqualTo(context.getBean("fileNameGenerator"));
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset")).isEqualTo("UTF-8");
CachingSessionFactory<?> sessionFactory = TestUtils.getPropertyValue(handler,
"remoteFileTemplate.sessionFactory", CachingSessionFactory.class);
DefaultSftpSessionFactory clientFactory = TestUtils.getPropertyValue(sessionFactory, "sessionFactory",
DefaultSftpSessionFactory.class);
.isEqualTo(this.context.getBean("fileNameGenerator"));
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset")).isEqualTo(StandardCharsets.UTF_8);
CachingSessionFactory<?> sessionFactory =
TestUtils.getPropertyValue(handler, "remoteFileTemplate.sessionFactory", CachingSessionFactory.class);
DefaultSftpSessionFactory clientFactory =
TestUtils.getPropertyValue(sessionFactory, "sessionFactory", DefaultSftpSessionFactory.class);
assertThat(TestUtils.getPropertyValue(clientFactory, "host")).isEqualTo("localhost");
assertThat(TestUtils.getPropertyValue(clientFactory, "port")).isEqualTo(2222);
assertThat(TestUtils.getPropertyValue(handler, "order")).isEqualTo(23);
@@ -93,73 +102,68 @@ public class OutboundChannelAdapterParserTests {
"handlers");
Iterator<MessageHandler> iterator = handlers.iterator();
assertThat(iterator.next())
.isSameAs(TestUtils.getPropertyValue(context.getBean("sftpOutboundAdapterWithExpression"), "handler"));
.isSameAs(TestUtils.getPropertyValue(
this.context.getBean("sftpOutboundAdapterWithExpression"), "handler"));
assertThat(iterator.next()).isSameAs(handler);
assertThat(TestUtils.getPropertyValue(handler, "chmod")).isEqualTo(384);
context.close();
}
@Test
public void testOutboundChannelAdapterWithWithRemoteDirectoryAndFileExpression() {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapterWithExpression");
assertThat(consumer instanceof EventDrivenConsumer).isTrue();
assertThat(TestUtils.getPropertyValue(consumer, "inputChannel")).isEqualTo(context.getBean("inputChannel"));
assertThat(((EventDrivenConsumer) consumer).getComponentName()).isEqualTo("sftpOutboundAdapterWithExpression");
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
SpelExpression remoteDirectoryExpression = (SpelExpression) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.directoryExpressionProcessor.expression");
EventDrivenConsumer consumer =
this.context.getBean("sftpOutboundAdapterWithExpression", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumer, "inputChannel"))
.isEqualTo(this.context.getBean("inputChannel"));
assertThat(consumer.getComponentName()).isEqualTo("sftpOutboundAdapterWithExpression");
FileTransferringMessageHandler<?> handler = TestUtils
.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
SpelExpression remoteDirectoryExpression =
TestUtils.getPropertyValue(handler, "remoteFileTemplate.directoryExpressionProcessor.expression",
SpelExpression.class);
assertThat(remoteDirectoryExpression).isNotNull();
assertThat(remoteDirectoryExpression.getExpressionString()).isEqualTo("'foo' + '/' + 'bar'");
FileNameGenerator generator = (FileNameGenerator) TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator");
FileNameGenerator generator =
TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator", FileNameGenerator.class);
Expression fileNameGeneratorExpression = TestUtils.getPropertyValue(generator, "expression", Expression.class);
assertThat(fileNameGeneratorExpression).isNotNull();
assertThat(fileNameGeneratorExpression.getExpressionString()).isEqualTo("payload.getName() + '-foo'");
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset")).isEqualTo("UTF-8");
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"))
.isNull();
context.close();
}
@Test
public void testOutboundChannelAdapterWithNoTemporaryFileName() {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("sftpOutboundAdapterWithNoTemporaryFileName");
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
Object consumer = this.context.getBean("sftpOutboundAdapterWithNoTemporaryFileName");
FileTransferringMessageHandler<?> handler = TestUtils
.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
assertThat((Boolean) TestUtils.getPropertyValue(handler, "remoteFileTemplate.useTemporaryFileName")).isFalse();
context.close();
}
@Test
public void advised() {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass());
Object consumer = context.getBean("advised");
Object consumer = this.context.getBean("advised");
MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
handler.handleMessage(new GenericMessage<>("foo"));
assertThat(adviceCalled).isEqualTo(1);
context.close();
}
@Test
public void testFailWithRemoteDirAndExpression() {
try {
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail.xml", this.getClass())
.close();
fail("Exception expected");
}
catch (BeanDefinitionStoreException e) {
assertThat(e.getMessage()).contains("Only one of 'remote-directory'");
}
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail.xml",
getClass()))
.withMessageContaining("Only one of 'remote-directory'");
}
@Test(expected = BeanDefinitionStoreException.class)
@Test
public void testFailWithFileExpressionAndFileGenerator() {
new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml",
this.getClass()).close();
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext(
"OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml",
getClass()));
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@@ -171,4 +175,5 @@ public class OutboundChannelAdapterParserTests {
}
}
}