Skip to content

Commit

Permalink
INT-3494: Resolve dir for writing as a Resource (#3109)
Browse files Browse the repository at this point in the history
* INT-3494: Resolve dir for writing as a Resource

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

The expression for local directory can be resolved into a `Resource`
or resource location.
* Fix `ExpressionUtils.expressionToFile()` to support `Resource` and
also use `ResourceUtils.getFile(path)` when expression result is a string
* Modify tests to ensure that resource is resolved properly
* Upgrade affected tests to JUnit 5
* Mention an new functionality in docs

* * Improve Java doc for `ExpressionUtils.expressionToFile()`
* Finish the sentence in the `file.adoc`
  • Loading branch information
artembilan authored and garyrussell committed Nov 14, 2019
1 parent 069d873 commit 0f5bd4a
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 212 deletions.
Expand Up @@ -17,6 +17,8 @@
package org.springframework.integration.expression;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand All @@ -25,6 +27,7 @@
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
Expand All @@ -39,6 +42,7 @@
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;

/**
* Utility class with static methods for helping with evaluation of SpEL expressions.
Expand All @@ -53,7 +57,7 @@ public final class ExpressionUtils {

private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

private static final Log logger = LogFactory.getLog(ExpressionUtils.class);
private static final Log LOGGER = LogFactory.getLog(ExpressionUtils.class);

private ExpressionUtils() {
super();
Expand Down Expand Up @@ -84,7 +88,7 @@ public static SimpleEvaluationContext createSimpleEvaluationContext() {
*/
public static StandardEvaluationContext createStandardEvaluationContext(@Nullable BeanFactory beanFactory) {
if (beanFactory == null) {
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
LOGGER.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
}
return (StandardEvaluationContext) doCreateContext(beanFactory, false);
}
Expand All @@ -98,7 +102,7 @@ public static StandardEvaluationContext createStandardEvaluationContext(@Nullabl
*/
public static SimpleEvaluationContext createSimpleEvaluationContext(@Nullable BeanFactory beanFactory) {
if (beanFactory == null) {
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
LOGGER.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
}
return (SimpleEvaluationContext) doCreateContext(beanFactory, true);
}
Expand Down Expand Up @@ -161,36 +165,56 @@ private static EvaluationContext createEvaluationContext(@Nullable ConversionSer
* @param expression the expression.
* @param evaluationContext the evaluation context.
* @param message the message (if available).
* @param name the name of the result of the evaluation.
* @param propertyName the property name the expression is evaluated for.
* @return the File.
* @since 5.0
*/
public static File expressionToFile(Expression expression, EvaluationContext evaluationContext,
@Nullable Message<?> message, String name) {

File file;
Object value = message == null
? expression.getValue(evaluationContext)
: expression.getValue(evaluationContext, message);
if (value == null) {
throw new IllegalStateException(String.format("The provided %s expression (%s) must not evaluate to null.",
name, expression.getExpressionString()));
}
else if (value instanceof File) {
file = (File) value;
@Nullable Message<?> message, String propertyName) {

Object value =
message == null
? expression.getValue(evaluationContext)
: expression.getValue(evaluationContext, message);

Assert.state(value != null, () ->
String.format("The provided %s expression (%s) must not evaluate to null.",
propertyName, expression.getExpressionString()));

if (value instanceof File) {
return (File) value;
}
else if (value instanceof String) {
String path = (String) value;
Assert.hasText(path, String.format("Unable to resolve %s for the provided Expression '%s'.", name,
Assert.hasText(path, String.format("Unable to resolve %s for the provided Expression '%s'.", propertyName,
expression.getExpressionString()));
file = new File(path);
try {
return ResourceUtils.getFile(path);
}
catch (FileNotFoundException ex) {
throw new IllegalStateException(
String.format("Unable to resolve %s for the provided Expression '%s'.",
propertyName, expression.getExpressionString()),
ex);
}
}
else if (value instanceof Resource) {
try {
return ((Resource) value).getFile();
}
catch (IOException ex) {
throw new IllegalStateException(
String.format("Unable to resolve %s for the provided Expression '%s'.",
propertyName, expression.getExpressionString()),
ex);
}
}
else {
throw new IllegalStateException(String.format(
"The provided %s expression (%s) must evaluate to type java.io.File or String, not %s.", name,
"The provided %s expression (%s) must evaluate to type java.io.File, String " +
"or org.springframework.core.io.Resource, not %s.", propertyName,
expression.getExpressionString(), value.getClass().getName()));
}
return file;
}

/**
Expand Down
Expand Up @@ -18,7 +18,7 @@
<si:header-enricher>
<si:header name="#{T(org.springframework.integration.file.FileHeaders).FILENAME}" value="${test.file}"/>
</si:header-enricher>
<file:outbound-channel-adapter id="file-outbound-channel-adapter-within-chain" directory="${work.dir}"/>
<file:outbound-channel-adapter id="file-outbound-channel-adapter-within-chain" directory-expression="${work.dir}"/>
</si:chain>

<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
Expand Down
Expand Up @@ -22,69 +22,47 @@
import java.io.IOException;
import java.util.Properties;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.FileCopyUtils;

/**
* //INT-2275
*
* @author Artem Bilan
* @author Gary Russell
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
public class FileOutboundChannelAdapterInsideChainTests {

public static final String TEST_FILE_NAME = FileOutboundChannelAdapterInsideChainTests.class.getSimpleName();
static final String TEST_FILE_NAME = FileOutboundChannelAdapterInsideChainTests.class.getSimpleName();

public static final String WORK_DIR_NAME = System.getProperty("java.io.tmpdir") + "/" + FileOutboundChannelAdapterInsideChainTests.class.getSimpleName() + "Dir";
static final String SAMPLE_CONTENT = "test";

public static final String SAMPLE_CONTENT = "test";
@TempDir
static File WORK_DIR;

public static Properties placeholderProperties = new Properties();

static {
placeholderProperties.put("test.file", TEST_FILE_NAME);
placeholderProperties.put("work.dir", WORK_DIR_NAME);
}

@Autowired
private MessageChannel outboundChainChannel;

private static File workDir;

@BeforeClass
public static void setupClass() {
workDir = new File(WORK_DIR_NAME);
workDir.mkdir();
workDir.deleteOnExit();
}

@AfterClass
public static void cleanUp() {
if (workDir != null && workDir.exists()) {
for (File file : workDir.listFiles()) {
file.delete();
}
}
workDir.delete();
@BeforeAll
static void setupClass() {
placeholderProperties.put("test.file", TEST_FILE_NAME);
placeholderProperties.put("work.dir", "'file://" + WORK_DIR.getAbsolutePath() + '\'');
}

@Test //INT-2275
public void testFileOutboundChannelAdapterWithinChain() throws IOException {
@Test
void testFileOutboundChannelAdapterWithinChain() throws IOException {
Message<String> message = MessageBuilder.withPayload(SAMPLE_CONTENT).build();
outboundChainChannel.send(message);
File testFile = new File(workDir, TEST_FILE_NAME);
File testFile = new File(WORK_DIR, TEST_FILE_NAME);
assertThat(testFile.exists()).isTrue();
byte[] testFileContent = FileCopyUtils.copyToByteArray(testFile);
assertThat(SAMPLE_CONTENT).isEqualTo(new String(testFileContent));
Expand Down

0 comments on commit 0f5bd4a

Please sign in to comment.