INT-805 - Dynamically create directories

Dynamically create directories with file:outbound-channel-adapter

* add **directory-expression** attribute for File Outbound Channel Adapter and File Outbound Gateway
* add JUnit tests

For reference: https://jira.springsource.org/browse/INT-805

INT-805 Code Review

* Fixed Spelling
* Fixed Code Convention issues
* Add support for expressions that resolve to File
* Add more JUnit tests

INT-805 - Code Review Changes

* Combine destinationDirectoryExpression and destinationDirectory
* Fix Tests

INT-805 - Code Review

* Always apply *destination directory* validation for each message (including LiteralExpressions)

INT-805 - Code Review changes

INT-805 - Added 2 exclusions to .gitignore
This commit is contained in:
Gunnar Hillert
2012-06-13 23:33:28 -04:00
committed by Oleg Zhurakousky
parent d74dbd3b1f
commit 121018190f
16 changed files with 744 additions and 144 deletions

4
.gitignore vendored
View File

@@ -19,7 +19,9 @@ logs
nohup.out
out
si.java.hsp
spring-integration-file/test/fileToAppend.txt
spring-integration-file/test/fileToAppendConcurrent.txt
spring-integration-jms/activemq-data/
spring-integration-samples/loanshark/application.log*
target
vf.gf.dmn-*
vf.gf.dmn-*

View File

@@ -255,6 +255,7 @@ project('spring-integration-file') {
'org.springframework.beans.*;version="[3.1.1, 4.0.0)"',
'org.springframework.expression.*;version="[3.1.1, 4.0.0)"',
'org.springframework.context;version="[3.1.1, 4.0.0)"',
'org.springframework.context.expression.*;version="[3.1.1, 4.0.0)"',
'org.springframework.core.*;version="[3.1.1, 4.0.0)"',
'org.springframework.scheduling.*;version="[3.1.1, 4.0.0)"',
'org.springframework.transaction.*;version="[3.1.1, 4.0.0)"',

View File

@@ -26,6 +26,12 @@ import java.nio.charset.Charset;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.core.MessageHandler;
@@ -61,6 +67,7 @@ import org.springframework.util.FileCopyUtils;
* @author Alex Peters
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gunnar Hillert
*/
public class FileWritingMessageHandler extends AbstractReplyProducingMessageHandler {
@@ -74,7 +81,9 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
private final File destinationDirectory;
private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
private final Expression destinationDirectoryExpression;
private volatile boolean autoCreateDirectory = true;
@@ -86,11 +95,28 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
private volatile LockRegistry lockRegistry = new PassThruLockRegistry();
/**
* Constructor which sets the {@link #destinationDirectoryExpression} using
* a {@link LiteralExpression}.
*
* @param destinationDirectory Must not be null
* @see #FileWritingMessageHandler(Expression)
*/
public FileWritingMessageHandler(File destinationDirectory) {
Assert.notNull(destinationDirectory, "Destination directory must not be null.");
this.destinationDirectory = destinationDirectory;
this.destinationDirectoryExpression = new LiteralExpression(destinationDirectory.getPath());
}
/**
* Constructor which sets the {@link #destinationDirectoryExpression}.
*
* @param destinationDirectoryExpression Must not be null
* @see #FileWritingMessageHandler(File)
*/
public FileWritingMessageHandler(Expression destinationDirectoryExpression) {
Assert.notNull(destinationDirectoryExpression, "Destination directory expression must not be null.");
this.destinationDirectoryExpression = destinationDirectoryExpression;
}
/**
* Specify whether to create the destination directory automatically if it
@@ -103,6 +129,13 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
this.autoCreateDirectory = autoCreateDirectory;
}
/**
* By default, every file that is in the process of being transferred will
* appear in the file system with an additional suffix, which by default is
* ".writing". This can be changed by setting this property.
*
* @param temporaryFileSuffix
*/
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
Assert.notNull(temporaryFileSuffix, "'temporaryFileSuffix' must not be null"); // empty string is OK
this.temporaryFileSuffix = temporaryFileSuffix;
@@ -171,16 +204,35 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
@Override
public final void onInit() {
if (!this.destinationDirectory.exists() && this.autoCreateDirectory) {
this.destinationDirectory.mkdirs();
this.evaluationContext.addPropertyAccessor(new MapAccessor());
final BeanFactory beanFactory = this.getBeanFactory();
if (beanFactory != null) {
this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
if (this.destinationDirectoryExpression instanceof LiteralExpression) {
final File directory = new File(this.destinationDirectoryExpression.getValue(
this.evaluationContext, null, String.class));
validateDestinationDirectory(directory, this.autoCreateDirectory);
}
}
private void validateDestinationDirectory(File destinationDirectory, boolean autoCreateDirectory) {
if (!destinationDirectory.exists() && autoCreateDirectory) {
destinationDirectory.mkdirs();
}
Assert.isTrue(destinationDirectory.exists(),
"Destination directory [" + destinationDirectory + "] does not exist.");
Assert.isTrue(this.destinationDirectory.isDirectory(),
"Destination path [" + this.destinationDirectory + "] does not point to a directory.");
Assert.isTrue(this.destinationDirectory.canWrite(),
"Destination directory [" + this.destinationDirectory + "] is not writable.");
Assert.isTrue(destinationDirectory.isDirectory(),
"Destination path [" + destinationDirectory + "] does not point to a directory.");
Assert.isTrue(destinationDirectory.canWrite(),
"Destination directory [" + destinationDirectory + "] is not writable.");
Assert.state(!(this.temporaryFileSuffixSet && this.append),
"'temporaryFileSuffix' can not be set when appending to an existing file");;
}
@@ -192,8 +244,12 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
Assert.notNull(payload, "message payload must not be null");
String generatedFileName = this.fileNameGenerator.generateFileName(requestMessage);
File originalFileFromHeader = this.retrieveOriginalFileFromHeader(requestMessage);
File tempFile = new File(this.destinationDirectory, generatedFileName + temporaryFileSuffix);
File resultFile = new File(this.destinationDirectory, generatedFileName);
final File destinationDirectoryToUse = evaluateDestinationDirectoryExpression(requestMessage);
File tempFile = new File(destinationDirectoryToUse, generatedFileName + temporaryFileSuffix);
File resultFile = new File(destinationDirectoryToUse, generatedFileName);
try {
if (payload instanceof File) {
resultFile = this.handleFileMessage((File) payload, tempFile, resultFile);
@@ -348,4 +404,38 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
}
}
}
private File evaluateDestinationDirectoryExpression(Message<?> message) {
final File destinationDirectory;
final Object destinationDirectoryToUse = this.destinationDirectoryExpression.getValue(
this.evaluationContext, message);
if (destinationDirectoryToUse == null) {
throw new IllegalStateException(String.format("The provided " +
"destinationDirectoryExpression (%s) must not resolve to null.",
this.destinationDirectoryExpression.getExpressionString()));
}
else if (destinationDirectoryToUse instanceof String) {
final String destinationDirectoryPath = (String) destinationDirectoryToUse;
Assert.hasText(destinationDirectoryPath, String.format(
"Unable to resolve destination directory name for the provided Expression '%s'.",
this.destinationDirectoryExpression.getExpressionString()));
destinationDirectory = new File(destinationDirectoryPath);
}
else if (destinationDirectoryToUse instanceof File) {
destinationDirectory = (File) destinationDirectoryToUse;
} else {
throw new IllegalStateException(String.format("The provided " +
"destinationDirectoryExpression (%s) must be of type " +
"java.io.File or be a String.", this.destinationDirectoryExpression.getExpressionString()));
}
validateDestinationDirectory(destinationDirectory, this.autoCreateDirectory);
return destinationDirectory;
}
}

View File

@@ -17,9 +17,9 @@
package org.springframework.integration.file.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.util.StringUtils;
@@ -31,19 +31,34 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
* @author Artem Bilan
* @author Gunnar Hillert
*
* @since 1.0.3
*/
abstract class FileWritingMessageHandlerBeanDefinitionBuilder {
static BeanDefinitionBuilder configure(Element element, boolean expectReply, ParserContext parserContext) {
String directory = element.getAttribute("directory");
if (!StringUtils.hasText(directory)) {
parserContext.getReaderContext().error("directory is required", element);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FileWritingMessageHandlerFactoryBean.class);
builder.addPropertyValue("directory", directory);
String directory = element.getAttribute("directory");
String directoryExpression = element.getAttribute("directory-expression");
if (!StringUtils.hasText(directory) && !StringUtils.hasText(directoryExpression)) {
parserContext.getReaderContext().error("directory or directory-expression is required", element);
}
else if (StringUtils.hasText(directory) && StringUtils.hasText(directoryExpression)) {
parserContext.getReaderContext().error("Either directory or directory-expression must be provided but not both", element);
}
if (StringUtils.hasText(directoryExpression)) {
BeanDefinitionBuilder expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
expressionBuilder.addConstructorArgValue(directoryExpression);
builder.addPropertyValue("directoryExpression", expressionBuilder.getBeanDefinition());
}
builder.addPropertyValue("expectReply", expectReply);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "temporary-file-suffix");

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.file.config;
import java.io.File;
import org.springframework.expression.Expression;
import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.FileWritingMessageHandler;
@@ -30,12 +31,16 @@ import org.springframework.integration.file.FileWritingMessageHandler;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Gunnar Hillert
*
* @since 1.0.3
*/
public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageHandlerFactoryBean<FileWritingMessageHandler>{
private volatile File directory;
private volatile Expression directoryExpression;
private volatile String charset;
private volatile FileNameGenerator fileNameGenerator;
@@ -66,6 +71,10 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
this.charset = charset;
}
public void setDirectoryExpression(Expression directoryExpression) {
this.directoryExpression = directoryExpression;
}
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
this.fileNameGenerator = fileNameGenerator;
}
@@ -96,7 +105,21 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
@Override
protected FileWritingMessageHandler createHandler() {
FileWritingMessageHandler handler = new FileWritingMessageHandler(this.directory);
final FileWritingMessageHandler handler;
if (this.directory != null && this.directoryExpression != null) {
throw new IllegalStateException("Cannot set both directory and directoryExpression");
}
else if (this.directory != null) {
handler = new FileWritingMessageHandler(this.directory);
}
else if (this.directoryExpression != null) {
handler = new FileWritingMessageHandler(this.directoryExpression);
} else {
throw new IllegalStateException("Either directory or directoryExpression must not be null");
}
if (this.charset != null) {
handler.setCharset(this.charset);
}

View File

@@ -240,12 +240,26 @@ Only files matching this regular expression will be picked up by this adapter.
<xsd:documentation><![CDATA[Identifies the underlying Spring bean definition (EventDrivenConsumer)]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="directory" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[Specifies the output directory, e.g.:
directory="file:/absolute/output" or directory="file:relative/output"]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[Specifies the output directory, e.g.:
directory="file:/absolute/output" or directory="file:relative/output"
Either this attribute or 'directory-expression' must be provided.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[Specifies the output directory using a SpEL expression.
This allows you to dynamically specify the output directory
on a per message basis. For example a message header or payload
property can be used for specifying the destination directory
at runtime.]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-generator" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<int:channel id="inputChannelSaveToBaseDir" />
<int:channel id="inputChannelSaveToBaseDirDeleteSource" />
<int:channel id="inputChannelSaveToSubDir" />
<int:channel id="inputChannelSaveToSubDirAutoCreateOff" />
<int:channel id="inputChannelSaveToSubDirWrongExpression" />
<int:channel id="inputChannelSaveToSubDirWithHeader" />
<int:channel id="inputChannelSaveToSubDirWithFile" />
<int:channel id="inputChannelSaveToSubDirEmptyStringExpression" />
<int-file:outbound-channel-adapter id="save-to-base-directory"
channel="inputChannelSaveToBaseDir" auto-create-directory="true"
filename-generator-expression="'foo.txt'" directory="target/base-directory" />
<int-file:outbound-channel-adapter id="delete-source-files"
channel="inputChannelSaveToBaseDirDeleteSource" auto-create-directory="true"
delete-source-files="true" filename-generator-expression="'foo.txt'"
directory="target/base-directory" />
<int-file:outbound-channel-adapter id="save-to-sub-directory-wrong-expression"
channel="inputChannelSaveToSubDirWrongExpression" auto-create-directory="true"
directory-expression="'target/base-directory/sub-directory/foo.txt'" />
<int-file:outbound-channel-adapter id="save-to-sub-directory-empty-string-expression"
channel="inputChannelSaveToSubDirEmptyStringExpression" auto-create-directory="true"
directory-expression="' '" />
<int-file:outbound-channel-adapter id="save-to-sub-directory"
channel="inputChannelSaveToSubDir" auto-create-directory="true"
directory-expression="'target/base-directory/sub-directory'"
filename-generator-expression="'foo.txt'" />
<int-file:outbound-channel-adapter id="save-to-sub-directory-with-header"
channel="inputChannelSaveToSubDirWithHeader" auto-create-directory="true"
directory-expression="headers['myFileLocation']"
filename-generator-expression="'foo.txt'" />
<int-file:outbound-channel-adapter id="save-to-sub-directory-auto-create-off"
channel="inputChannelSaveToSubDirAutoCreateOff" auto-create-directory="false"
directory-expression="'target/base-directory2/sub-directory2'"
filename-generator-expression="'foo.txt'" />
<int-file:outbound-channel-adapter id="save-to-sub-directory-with-file-expression"
channel="inputChannelSaveToSubDirWithFile" auto-create-directory="true"
directory-expression="headers['subDirectory']"
filename-generator-expression="'foo.txt'" />
</beans:beans>

View File

@@ -0,0 +1,218 @@
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.file;
import java.io.File;
import java.io.FileOutputStream;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileCopyUtils;
/**
* @author Gunnar Hillert
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundChannelAdapterIntegrationTests {
static final String DEFAULT_ENCODING = "UTF-8";
static final String SAMPLE_CONTENT = "HelloWorld\n<EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
static File workDir;
FileWritingMessageHandler handler;
@Autowired
MessageChannel inputChannelSaveToBaseDir;
@Autowired
MessageChannel inputChannelSaveToBaseDirDeleteSource;
@Autowired
MessageChannel inputChannelSaveToSubDir;
@Autowired
MessageChannel inputChannelSaveToSubDirWithFile;
@Autowired
MessageChannel inputChannelSaveToSubDirAutoCreateOff;
@Autowired
MessageChannel inputChannelSaveToSubDirWrongExpression;
@Autowired
MessageChannel inputChannelSaveToSubDirWithHeader;
@Autowired
MessageChannel inputChannelSaveToSubDirEmptyStringExpression;
Message<File> message;
File sourceFile;
@Before
public void setUp() throws Exception {
sourceFile = File.createTempFile("anyFile", ".txt");
sourceFile.deleteOnExit();
FileCopyUtils.copy(SAMPLE_CONTENT.getBytes(DEFAULT_ENCODING),
new FileOutputStream(sourceFile, false));
message = MessageBuilder.withPayload(sourceFile).build();
}
@After
public void tearDown() {
sourceFile.delete();
}
@Test
public void saveToBaseDir() throws Exception {
this.inputChannelSaveToBaseDir.send(message);
Assert.assertTrue(new File("target/base-directory/foo.txt").exists());
}
@Test
public void saveToBaseDirDeleteSourceFile() throws Exception {
Assert.assertTrue(sourceFile.exists());
this.inputChannelSaveToBaseDirDeleteSource.send(message);
Assert.assertTrue(new File("target/base-directory/foo.txt").exists());
Assert.assertFalse(sourceFile.exists());
}
@Test
public void saveToSubDir() throws Exception {
this.inputChannelSaveToSubDir.send(message);
Assert.assertTrue(new File("target/base-directory/sub-directory/foo.txt").exists());
}
@Test
public void saveToSubDirWithWrongExpression() throws Exception {
try {
this.inputChannelSaveToSubDirWrongExpression.send(message);
} catch (MessageHandlingException e) {
Assert.assertEquals("Destination path [target/base-directory/sub-directory/foo.txt] does not point to a directory.", e.getCause().getMessage());
return;
}
Assert.fail("Was expecting a MessageHandlingException to be thrown");
}
@Test
public void saveToSubDirWithEmptyStringExpression() throws Exception {
try {
this.inputChannelSaveToSubDirEmptyStringExpression.send(message);
} catch (MessageHandlingException e) {
Assert.assertEquals("Unable to resolve destination directory name for the provided Expression '' ''.", e.getCause().getMessage());
return;
}
Assert.fail("Was expecting a MessageHandlingException to be thrown");
}
@Test
public void saveToSubDir2() throws Exception {
final Message<File> message2 = MessageBuilder.fromMessage(message)
.setHeader("myFileLocation", "target/base-directory/headerdir")
.build();
this.inputChannelSaveToSubDirWithHeader.send(message2);
Assert.assertTrue(new File("target/base-directory/headerdir/foo.txt").exists());
}
@Test
public void saveToSubDirAutoCreateOff() throws Exception {
try {
this.inputChannelSaveToSubDirAutoCreateOff.send(message);
} catch (MessageHandlingException e) {
Assert.assertEquals("Destination directory [target/base-directory2/sub-directory2] does not exist.", e.getCause().getMessage());
return;
}
Assert.fail("Was expecting a MessageHandlingException to be thrown");
}
@Test
public void saveToSubWithFileExpression() throws Exception {
final File directory = new File("target/base-directory/sub-directory");
final Message<File> messageWithFileHeader = MessageBuilder.fromMessage(message)
.setHeader("subDirectory", directory)
.build();
this.inputChannelSaveToSubDirWithFile.send(messageWithFileHeader);
Assert.assertTrue(new File("target/base-directory/sub-directory/foo.txt").exists());
}
@Test
public void saveToSubWithFileExpressionNull() throws Exception {
final File directory = null;
final Message<File> messageWithFileHeader = MessageBuilder.fromMessage(message)
.setHeader("subDirectory", directory)
.build();
try {
this.inputChannelSaveToSubDirWithFile.send(messageWithFileHeader);
} catch (MessageHandlingException e) {
Assert.assertEquals("The provided destinationDirectoryExpression " +
"(headers['subDirectory']) must not resolve to null.",
e.getCause().getMessage());
return;
}
Assert.fail("Was expecting a MessageHandlingException to be thrown");
}
@Test
public void saveToSubWithFileExpressionUnsupportedObjectType() throws Exception {
final Integer unsupportedObject = Integer.valueOf(1234);
final Message<File> messageWithFileHeader = MessageBuilder.fromMessage(message)
.setHeader("subDirectory", unsupportedObject)
.build();
try {
this.inputChannelSaveToSubDirWithFile.send(messageWithFileHeader);
} catch (MessageHandlingException e) {
Assert.assertEquals("The provided destinationDirectoryExpression" +
" (headers['subDirectory']) must be of type " +
"java.io.File or be a String.",
e.getCause().getMessage());
return;
}
Assert.fail("Was expecting a MessageHandlingException to be thrown");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -22,6 +22,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -33,37 +34,45 @@ import static org.junit.Assert.assertEquals;
/**
* @author Iwein Fuld
* @author Mark Fisher
* @author Gunnar Hillert
*
* @since 1.0.3
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundAdaptersWithClasspathInPropertiesTests {
@Autowired
@Qualifier("adapter")
private EventDrivenConsumer adapter;
@Autowired
@Qualifier("adapter")
private EventDrivenConsumer adapter;
@Autowired
@Qualifier("gateway")
private EventDrivenConsumer gateway;
@Autowired
@Qualifier("gateway")
private EventDrivenConsumer gateway;
@Test
public void outboundChannelAdapter() throws Exception {
DirectFieldAccessor accessor = new DirectFieldAccessor(
new DirectFieldAccessor(adapter).getPropertyValue("handler"));
File expected = new ClassPathResource("").getFile();
File actual = (File) accessor.getPropertyValue("destinationDirectory");
assertEquals("'destinationDirectory' should be set", expected, actual);
}
@Test
public void outboundChannelAdapter() throws Exception {
DirectFieldAccessor accessor = new DirectFieldAccessor(
new DirectFieldAccessor(adapter).getPropertyValue("handler"));
File expected = new ClassPathResource("").getFile();
@Test
public void outboundGateway() throws Exception {
DirectFieldAccessor accessor = new DirectFieldAccessor(
new DirectFieldAccessor(gateway).getPropertyValue("handler"));
File expected = new ClassPathResource("").getFile();
File actual = (File) accessor.getPropertyValue("destinationDirectory");
assertEquals("'destinationDirectory' should be set", expected, actual);
}
Expression destinationDirectoryExpression = (Expression) accessor.getPropertyValue("destinationDirectoryExpression");
File actual = new File(destinationDirectoryExpression.getExpressionString());
assertEquals("'destinationDirectory' should be set", expected, actual);
}
@Test
public void outboundGateway() throws Exception {
DirectFieldAccessor accessor = new DirectFieldAccessor(
new DirectFieldAccessor(gateway).getPropertyValue("handler"));
File expected = new ClassPathResource("").getFile();
Expression destinationDirectoryExpression = (Expression) accessor.getPropertyValue("destinationDirectoryExpression");
File actual = new File(destinationDirectoryExpression.getExpressionString());
assertEquals("'destinationDirectory' should be set", expected, actual);
}
}

View File

@@ -24,6 +24,10 @@
filename-generator="customFileNameGenerator"
directory="${java.io.tmpdir}"/>
<file:outbound-channel-adapter id="adapterWithDirectoryExpression"
channel="testChannel"
directory-expression="'foo/bar'"/>
<file:outbound-channel-adapter id="adapterWithDeleteFlag"
channel="testChannel"
delete-source-files="true"

View File

@@ -23,6 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import org.junit.Test;
@@ -31,6 +32,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageChannel;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.FileWritingMessageHandler;
@@ -39,6 +41,7 @@ import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author Mark Fisher
@@ -46,95 +49,118 @@ import org.springframework.util.FileCopyUtils;
* @author Iwein Fuld
* @author Gary Russell
* @author Oleg Zhurakousky
* @author Gunnar Hillert
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundChannelAdapterParserTests {
@Autowired
EventDrivenConsumer simpleAdapter;
@Autowired
EventDrivenConsumer simpleAdapter;
@Autowired
EventDrivenConsumer adapterWithCustomNameGenerator;
@Autowired
EventDrivenConsumer adapterWithCustomNameGenerator;
@Autowired
EventDrivenConsumer adapterWithDeleteFlag;
@Autowired
EventDrivenConsumer adapterWithDeleteFlag;
@Autowired
EventDrivenConsumer adapterWithOrder;
@Autowired
EventDrivenConsumer adapterWithOrder;
@Autowired
EventDrivenConsumer adapterWithCharset;
@Autowired
EventDrivenConsumer adapterWithCharset;
@Autowired
MessageChannel usageChannel;
@Autowired
EventDrivenConsumer adapterWithDirectoryExpression;
@Autowired
MessageChannel usageChannelConcurrent;
@Autowired
MessageChannel usageChannel;
@Test
public void simpleAdapter() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(simpleAdapter);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
File expected = new File(System.getProperty("java.io.tmpdir"));
File actual = (File) handlerAccessor.getPropertyValue("destinationDirectory");
assertEquals(".foo", TestUtils.getPropertyValue(handler, "temporaryFileSuffix", String.class));
assertThat(actual, is(expected));
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
assertNotNull(fileNameGenerator);
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
assertNotNull(expression);
assertEquals("'foo.txt'", expression);
assertEquals(Boolean.FALSE, handlerAccessor.getPropertyValue("deleteSourceFiles"));
}
@Autowired
MessageChannel usageChannelConcurrent;
@Test
public void adapterWithCustomFileNameGenerator() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithCustomNameGenerator);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
File expected = new File(System.getProperty("java.io.tmpdir"));
File actual = (File) handlerAccessor.getPropertyValue("destinationDirectory");
assertEquals(expected, actual);
assertTrue(handlerAccessor.getPropertyValue("fileNameGenerator") instanceof CustomFileNameGenerator);
assertEquals(".writing", handlerAccessor.getPropertyValue("temporaryFileSuffix"));
}
@Test
public void simpleAdapter() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(simpleAdapter);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
File expected = new File(System.getProperty("java.io.tmpdir"));
@Test
public void adapterWithDeleteFlag() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithDeleteFlag);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(Boolean.TRUE, handlerAccessor.getPropertyValue("deleteSourceFiles"));
}
Expression destinationDirectoryExpression = (Expression)handlerAccessor.getPropertyValue("destinationDirectoryExpression");
File actual = new File(destinationDirectoryExpression.getExpressionString());
assertEquals(".foo", TestUtils.getPropertyValue(handler, "temporaryFileSuffix", String.class));
assertThat(actual, is(expected));
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
assertNotNull(fileNameGenerator);
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
assertNotNull(expression);
assertEquals("'foo.txt'", expression);
assertEquals(Boolean.FALSE, handlerAccessor.getPropertyValue("deleteSourceFiles"));
}
@Test
public void adapterWithOrder() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithOrder);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(555, handlerAccessor.getPropertyValue("order"));
}
@Test
public void adapterWithCustomFileNameGenerator() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithCustomNameGenerator);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
File expected = new File(System.getProperty("java.io.tmpdir"));
@Test
public void adapterWithAutoStartupFalse() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithOrder);
assertEquals(Boolean.FALSE, adapterAccessor.getPropertyValue("autoStartup"));
}
Expression destinationDirectoryExpression = (Expression)handlerAccessor.getPropertyValue("destinationDirectoryExpression");
File actual = new File(destinationDirectoryExpression.getExpressionString());
@Test
public void adapterWithCharset() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithCharset);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
}
assertEquals(expected, actual);
assertTrue(handlerAccessor.getPropertyValue("fileNameGenerator") instanceof CustomFileNameGenerator);
assertEquals(".writing", handlerAccessor.getPropertyValue("temporaryFileSuffix"));
}
@Test
public void adapterWithDeleteFlag() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithDeleteFlag);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(Boolean.TRUE, handlerAccessor.getPropertyValue("deleteSourceFiles"));
}
@Test
public void adapterWithOrder() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithOrder);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(555, handlerAccessor.getPropertyValue("order"));
}
@Test
public void adapterWithAutoStartupFalse() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithOrder);
assertEquals(Boolean.FALSE, adapterAccessor.getPropertyValue("autoStartup"));
}
@Test
public void adapterWithCharset() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithCharset);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
adapterAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
}
@Test
public void adapterWithDirectoryExpression() {
FileWritingMessageHandler handler = TestUtils.getPropertyValue(adapterWithDirectoryExpression, "handler", FileWritingMessageHandler.class);
Method m = ReflectionUtils.findMethod(FileWritingMessageHandler.class, "getTemporaryFileSuffix");
ReflectionUtils.makeAccessible(m);
assertEquals(".writing", ReflectionUtils.invokeMethod(m, handler));
String expectedExpressionString = "'foo/bar'";
String actualExpressionString = TestUtils.getPropertyValue(handler, "destinationDirectoryExpression", Expression.class).getExpressionString();
assertEquals(expectedExpressionString, actualExpressionString);
}
@Test
public void adapterUsageWithAppend() throws Exception{

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<si:channel id="testChannel"/>
<file:outbound-channel-adapter id="simpleAdapter"
channel="testChannel"
temporary-file-suffix=".foo"
filename-generator-expression="'foo.txt'"/>
<context:property-placeholder/>
</beans>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<si:channel id="testChannel"/>
<file:outbound-channel-adapter id="simpleAdapter"
channel="testChannel"
directory="${java.io.tmpdir}"
directory-expression="'foo'"
temporary-file-suffix=".foo"
filename-generator-expression="'foo.txt'"/>
<context:property-placeholder/>
</beans>

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.file.config;
import static junit.framework.Assert.fail;
import static junit.framework.Assert.assertEquals;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
*
* @author Gunnar Hillert
*
*/
public class FileOutboundChannelAdapterParserWithErrorsTests {
@Test
public void testSettingDirectoryAndDirectoryExpression() {
try {
new ClassPathXmlApplicationContext("FileOutboundChannelAdapterParserWithErrorsTests-context.xml", getClass());
} catch (BeanDefinitionParsingException e) {
assertEquals("Configuration problem: Either directory or " +
"directory-expression must be provided but not both\nOffending " +
"resource: class path " +
"resource [org/springframework/integration/file/config/FileOutboundChannelAdapterParserWithErrorsTests-context.xml]",
e.getMessage());
return;
}
fail("Expected a BeanDefinitionParsingException to be thrown");
}
@Test
public void testNotSettingBothDirectoryAndDirectoryExpression() {
try {
new ClassPathXmlApplicationContext("FileOutboundChannelAdapterParserWithErrors2Tests-context.xml", getClass());
} catch (BeanDefinitionParsingException e) {
assertEquals("Configuration problem: directory or directory-expression " +
"is required\nOffending resource: class path resource " +
"[org/springframework/integration/file/config/FileOutboundChannelAdapterParserWithErrors2Tests-context.xml]",
e.getMessage());
return;
}
fail("Expected a BeanDefinitionParsingException to be thrown");
}
}

View File

@@ -14,11 +14,18 @@
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<outbound-gateway id="ordered"
request-channel="someChannel"
request-channel="someChannel"
directory="${java.io.tmpdir}"
auto-startup="false"
order="777"
filename-generator-expression="'foo.txt'"/>
filename-generator-expression="'foo.txt'"/>
<outbound-gateway id="gatewayWithDirectoryExpression"
request-channel="someChannel"
directory-expression="'build/foo'"
auto-startup="false"
order="777"
filename-generator-expression="'foo.txt'"/>
<context:property-placeholder />

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@@ -16,45 +16,54 @@
package org.springframework.integration.file.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.FileWritingMessageHandler;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Mark Fisher
* @author Gunnar Hillert
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundGatewayParserTests {
@Autowired
private ApplicationContext context;
@Autowired
private EventDrivenConsumer ordered;
@Autowired
private EventDrivenConsumer gatewayWithDirectoryExpression;
@Test
public void checkOrderedGateway() throws Exception {
Object gateway = context.getBean("ordered");
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(gateway);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
gatewayAccessor.getPropertyValue("handler");
assertEquals(Boolean.FALSE, gatewayAccessor.getPropertyValue("autoStartup"));
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(777, handlerAccessor.getPropertyValue("order"));
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
assertNotNull(fileNameGenerator);
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
assertNotNull(expression);
assertEquals("'foo.txt'", expression);
}
@Test
public void checkOrderedGateway() throws Exception {
DirectFieldAccessor gatewayAccessor = new DirectFieldAccessor(ordered);
FileWritingMessageHandler handler = (FileWritingMessageHandler)
gatewayAccessor.getPropertyValue("handler");
assertEquals(Boolean.FALSE, gatewayAccessor.getPropertyValue("autoStartup"));
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(777, handlerAccessor.getPropertyValue("order"));
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
assertNotNull(fileNameGenerator);
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
assertNotNull(expression);
assertEquals("'foo.txt'", expression);
}
@Test
public void testOutboundGatewayWithDirectoryExpression() throws Exception {
FileWritingMessageHandler handler = TestUtils.getPropertyValue(gatewayWithDirectoryExpression, "handler", FileWritingMessageHandler.class);
assertEquals("'build/foo'", TestUtils.getPropertyValue(handler, "destinationDirectoryExpression", Expression.class).getExpressionString());
}
}