Merge pull request #559 from ghillert/INT-2668
* INT-2668: INT-2668 - Improve the File Overwrite Handling
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@
|
||||
.DS_Store
|
||||
.gradle
|
||||
.idea
|
||||
.pmd
|
||||
.project
|
||||
.settings
|
||||
bin
|
||||
|
||||
@@ -25,7 +25,6 @@ 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;
|
||||
@@ -35,6 +34,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.util.DefaultLockRegistry;
|
||||
@@ -75,7 +75,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
|
||||
private volatile boolean temporaryFileSuffixSet = false;
|
||||
|
||||
private volatile boolean append = false;
|
||||
private volatile FileExistsMode fileExistsMode = FileExistsMode.REPLACE;
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@@ -142,21 +142,31 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
this.temporaryFileSuffixSet = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will set 'append' flag which will let his handler to append data to the
|
||||
* existing file rather then creating a new file for each Message.
|
||||
* If 'true' it will also create a real instance of the LockRegistry to ensure
|
||||
* that there is no collisions when multiple threads are writing to the same file.
|
||||
* Otherwise the LockRegistry is set to {@link PassThruLockRegistry} which has no effect.
|
||||
* Will set the {@link FileExistsMode} that specifies what will happen in
|
||||
* case the destination exists. For example {@link FileExistsMode#APPEND}
|
||||
* instructs this handler to append data to the existing file rather then
|
||||
* creating a new file for each {@link Message}.
|
||||
*
|
||||
* @param append
|
||||
* If set to {@link FileExistsMode#APPEND}, the adapter will also
|
||||
* create a real instance of the {@link LockRegistry} to ensure that there
|
||||
* is no collisions when multiple threads are writing to the same file.
|
||||
*
|
||||
* Otherwise the LockRegistry is set to {@link PassThruLockRegistry} which
|
||||
* has no effect.
|
||||
*
|
||||
* @param fileExistsMode Must not be null
|
||||
*/
|
||||
public void setAppend(boolean append) {
|
||||
this.append = append;
|
||||
if (this.append){
|
||||
public void setFileExistsMode(FileExistsMode fileExistsMode) {
|
||||
|
||||
Assert.notNull(fileExistsMode, "'fileExistsMode' must not be null.");
|
||||
this.fileExistsMode = fileExistsMode;
|
||||
|
||||
if (FileExistsMode.APPEND.equals(fileExistsMode)){
|
||||
this.lockRegistry = this.lockRegistry instanceof PassThruLockRegistry
|
||||
? new DefaultLockRegistry()
|
||||
: this.lockRegistry;
|
||||
: this.lockRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,8 +243,9 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
"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");;
|
||||
Assert.state(!(this.temporaryFileSuffixSet
|
||||
&& FileExistsMode.APPEND.equals(this.fileExistsMode)),
|
||||
"'temporaryFileSuffix' can not be set when appending to an existing file");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,25 +261,36 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
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);
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
resultFile = this.handleByteArrayMessage(
|
||||
(byte[]) payload, originalFileFromHeader, tempFile, resultFile);
|
||||
}
|
||||
else if (payload instanceof String) {
|
||||
resultFile = this.handleStringMessage(
|
||||
(String) payload, originalFileFromHeader, tempFile, resultFile);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"unsupported Message payload type [" + payload.getClass().getName() + "]");
|
||||
}
|
||||
if (FileExistsMode.FAIL.equals(this.fileExistsMode) && resultFile.exists()) {
|
||||
throw new MessageHandlingException(requestMessage,
|
||||
"The destination file already exists at '" + resultFile.getAbsolutePath() + "'.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);
|
||||
|
||||
final boolean ignore = FileExistsMode.IGNORE.equals(this.fileExistsMode) && resultFile.exists();
|
||||
|
||||
if (!ignore) {
|
||||
|
||||
try {
|
||||
if (payload instanceof File) {
|
||||
resultFile = this.handleFileMessage((File) payload, tempFile, resultFile);
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
resultFile = this.handleByteArrayMessage(
|
||||
(byte[]) payload, originalFileFromHeader, tempFile, resultFile);
|
||||
}
|
||||
else if (payload instanceof String) {
|
||||
resultFile = this.handleStringMessage(
|
||||
(String) payload, originalFileFromHeader, tempFile, resultFile);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"unsupported Message payload type [" + payload.getClass().getName() + "]");
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!this.expectReply) {
|
||||
@@ -301,9 +323,9 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
}
|
||||
|
||||
private File handleFileMessage(final File sourceFile, File tempFile, final File resultFile) throws IOException {
|
||||
if (this.append){
|
||||
if (FileExistsMode.APPEND.equals(this.fileExistsMode)){
|
||||
File fileToWriteTo = this.determineFileToWrite(resultFile, tempFile);
|
||||
final FileOutputStream fos = new FileOutputStream(fileToWriteTo, this.append);
|
||||
final FileOutputStream fos = new FileOutputStream(fileToWriteTo, true);
|
||||
final FileInputStream fis = new FileInputStream(sourceFile);
|
||||
WhileLockedProcessor whileLockedProcessor = new WhileLockedProcessor(this.lockRegistry, fileToWriteTo.getAbsolutePath()){
|
||||
@Override
|
||||
@@ -333,7 +355,10 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
|
||||
private File handleByteArrayMessage(final byte[] bytes, File originalFile, File tempFile, final File resultFile) throws IOException {
|
||||
File fileToWriteTo = this.determineFileToWrite(resultFile, tempFile);
|
||||
final FileOutputStream fos = new FileOutputStream(fileToWriteTo, this.append);
|
||||
|
||||
final boolean append = FileExistsMode.APPEND.equals(this.fileExistsMode);
|
||||
|
||||
final FileOutputStream fos = new FileOutputStream(fileToWriteTo, append);
|
||||
WhileLockedProcessor whileLockedProcessor = new WhileLockedProcessor(this.lockRegistry, fileToWriteTo.getAbsolutePath()){
|
||||
@Override
|
||||
protected void whileLocked() throws IOException {
|
||||
@@ -348,7 +373,10 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
|
||||
private File handleStringMessage(final String content, File originalFile, File tempFile, final File resultFile) throws IOException {
|
||||
File fileToWriteTo = this.determineFileToWrite(resultFile, tempFile);
|
||||
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileToWriteTo, this.append), this.charset);
|
||||
|
||||
final boolean append = FileExistsMode.APPEND.equals(this.fileExistsMode);
|
||||
|
||||
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileToWriteTo, append), this.charset);
|
||||
WhileLockedProcessor whileLockedProcessor = new WhileLockedProcessor(this.lockRegistry, fileToWriteTo.getAbsolutePath()){
|
||||
@Override
|
||||
protected void whileLocked() throws IOException {
|
||||
@@ -363,18 +391,27 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
}
|
||||
|
||||
private File determineFileToWrite(File resultFile, File tempFile){
|
||||
File fileToWriteTo = null;
|
||||
if (this.append){
|
||||
fileToWriteTo = resultFile;
|
||||
}
|
||||
else {
|
||||
fileToWriteTo = tempFile;
|
||||
|
||||
final File fileToWriteTo;
|
||||
|
||||
switch (this.fileExistsMode) {
|
||||
case APPEND:
|
||||
fileToWriteTo = resultFile;
|
||||
break;
|
||||
case FAIL:
|
||||
case IGNORE:
|
||||
case REPLACE:
|
||||
fileToWriteTo = tempFile;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported FileExistsMode "
|
||||
+ this.fileExistsMode);
|
||||
}
|
||||
return fileToWriteTo;
|
||||
}
|
||||
|
||||
private void cleanUpAfterCopy(File fileToWriteTo, File resultFile, File originalFile) throws IOException{
|
||||
if (!this.append){
|
||||
if (!FileExistsMode.APPEND.equals(this.fileExistsMode)) {
|
||||
this.renameTo(fileToWriteTo, resultFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ abstract class FileWritingMessageHandlerBeanDefinitionBuilder {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "temporary-file-suffix");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "append");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "mode", "fileExistsMode");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
|
||||
String remoteFileNameGenerator = element.getAttribute("filename-generator");
|
||||
String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression");
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.FileWritingMessageHandler;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
|
||||
/**
|
||||
* Factory bean used to create {@link FileWritingMessageHandler}s.
|
||||
@@ -55,12 +56,12 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
|
||||
|
||||
private volatile String temporaryFileSuffix;
|
||||
|
||||
private volatile boolean append;
|
||||
private volatile FileExistsMode fileExistsMode;
|
||||
|
||||
private volatile boolean expectReply = true;
|
||||
|
||||
public void setAppend(boolean append) {
|
||||
this.append = append;
|
||||
public void setFileExistsMode(String fileExistsModeAsString) {
|
||||
this.fileExistsMode = FileExistsMode.getForString(fileExistsModeAsString);
|
||||
}
|
||||
|
||||
public void setDirectory(File directory) {
|
||||
@@ -142,7 +143,11 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
|
||||
handler.setTemporaryFileSuffix(this.temporaryFileSuffix);
|
||||
}
|
||||
handler.setExpectReply(this.expectReply);
|
||||
handler.setAppend(this.append);
|
||||
|
||||
if (this.fileExistsMode != null) {
|
||||
handler.setFileExistsMode(this.fileExistsMode);
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* When writing file, this enumeration indicates what action shall be taken in
|
||||
* case the destination file already exists.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public enum FileExistsMode {
|
||||
|
||||
/**
|
||||
* Append data to any pre-existing files.
|
||||
*/
|
||||
APPEND,
|
||||
|
||||
/**
|
||||
* Raise an exception in case the file to be written already exists.
|
||||
*/
|
||||
FAIL,
|
||||
|
||||
/**
|
||||
* If the file already exists, do nothing.
|
||||
*/
|
||||
IGNORE,
|
||||
|
||||
/**
|
||||
* If the file already exists, replace it.
|
||||
*/
|
||||
REPLACE;
|
||||
|
||||
/**
|
||||
* For a given non-null and not-empty input string, this method returns the
|
||||
* corresponding {@link FileExistsMode}. If it cannot be determined, an
|
||||
* {@link IllegalStateException} is thrown.
|
||||
*
|
||||
* @param fileExistsModeAsString Must neither be null nor empty
|
||||
*/
|
||||
public static FileExistsMode getForString(String fileExistsModeAsString) {
|
||||
|
||||
Assert.hasText(fileExistsModeAsString, "'fileExistsModeAsString' must neither be null nor empty.");
|
||||
|
||||
final FileExistsMode[] fileExistsModeValues = FileExistsMode.values();
|
||||
|
||||
for (FileExistsMode fileExistsMode : fileExistsModeValues) {
|
||||
if (fileExistsModeAsString.equalsIgnoreCase(fileExistsMode.name())) {
|
||||
return fileExistsMode;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid fileExistsMode '" + fileExistsModeAsString
|
||||
+ "'. The (case-insensitive) supported values are: "
|
||||
+ StringUtils.arrayToCommaDelimitedString(fileExistsModeValues));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides various support classes used across Spring Integration File Components.
|
||||
*/
|
||||
package org.springframework.integration.file.support;
|
||||
@@ -351,18 +351,47 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="append" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
If set to 'true', the data will will be appended to the
|
||||
existing file if such file exists, otherwise the new file
|
||||
will be created as usual but once created the subsequent data
|
||||
will be appended to it. This attribute is mutualy exclusive
|
||||
with the 'temporary-file-suffix' since append is done to the
|
||||
actual file and not its temporary counterpart. This attribute
|
||||
defaults to 'false' if not set explicitly.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="mode">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This attribute defaults to 'REPLACE' if not set explicitly.
|
||||
|
||||
The following options are available:
|
||||
|
||||
APPEND:
|
||||
|
||||
If append is specified, the data will be appended
|
||||
to the existing file if such file exists, otherwise the
|
||||
new file will be created as usual but once created the
|
||||
subsequent data will be appended to it. This attribute
|
||||
is mutualy exclusive with the 'temporary-file-suffix'
|
||||
since append is done to the actual file and not its
|
||||
temporary counterpart.
|
||||
|
||||
If set to APPEND, the component will also create a real
|
||||
instance of the LockRegistry to ensure that there are no
|
||||
collisions when multiple threads are writing to the same
|
||||
file.
|
||||
|
||||
FAIL:
|
||||
|
||||
If the target file exists, a MessageHandlingException
|
||||
is thrown.
|
||||
|
||||
IGNORE:
|
||||
|
||||
If the target file exists, the message payload is silently
|
||||
ignored.
|
||||
|
||||
REPLACE:
|
||||
|
||||
This is the default behavior when writing files. If the
|
||||
target file already exists, it will be overwritten.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="mode xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="delete-source-files" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
@@ -521,4 +550,51 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="mode">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="REPLACE">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This is the default behavior when writing files. If the
|
||||
target file already exists, it will be overwritten.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="APPEND">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
If append is specified, the data will be appended
|
||||
to the existing file if such file exists, otherwise the
|
||||
new file will be created as usual but once created the
|
||||
subsequent data will be appended to it. This attribute
|
||||
is mutualy exclusive with the 'temporary-file-suffix'
|
||||
since append is done to the actual file and not its
|
||||
temporary counterpart.
|
||||
|
||||
If set to APPEND, the component will also create a real
|
||||
instance of the LockRegistry to ensure that there are no
|
||||
collisions when multiple threads are writing to the same
|
||||
file.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="FAIL">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
If the target file exists, a MessageHandlingException
|
||||
is thrown.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="IGNORE">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
If the target file exists, the message payload is silently
|
||||
ignored.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -43,24 +43,34 @@
|
||||
order="555"
|
||||
auto-startup="false"
|
||||
directory="${java.io.tmpdir}"/>
|
||||
|
||||
|
||||
<file:outbound-channel-adapter id="usageChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'"
|
||||
append="true"
|
||||
mode="APPEND"
|
||||
directory="test"/>
|
||||
|
||||
|
||||
<file:outbound-channel-adapter id="usageChannelWithFailMode"
|
||||
filename-generator-expression="'fileToAppend.txt'"
|
||||
mode="FAIL"
|
||||
directory="test"/>
|
||||
|
||||
<file:outbound-channel-adapter id="usageChannelWithIgnoreMode"
|
||||
filename-generator-expression="'fileToAppend.txt'"
|
||||
mode="IGNORE"
|
||||
directory="test"/>
|
||||
|
||||
<si:channel id="usageChannelConcurrent">
|
||||
<si:dispatcher task-executor="executor"/>
|
||||
</si:channel>
|
||||
</si:channel>
|
||||
<file:outbound-channel-adapter channel="usageChannelConcurrent"
|
||||
filename-generator-expression="'fileToAppendConcurrent.txt'"
|
||||
append="true"
|
||||
mode="APPEND"
|
||||
directory="test"/>
|
||||
|
||||
|
||||
<bean id="customFileNameGenerator" class="org.springframework.integration.file.config.CustomFileNameGenerator"/>
|
||||
|
||||
<context:property-placeholder/>
|
||||
|
||||
|
||||
<task:executor id="executor" pool-size="100"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -26,12 +26,14 @@ import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.Assert;
|
||||
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.MessageChannel;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
@@ -77,6 +79,12 @@ public class FileOutboundChannelAdapterParserTests {
|
||||
@Autowired
|
||||
MessageChannel usageChannel;
|
||||
|
||||
@Autowired
|
||||
MessageChannel usageChannelWithFailMode;
|
||||
|
||||
@Autowired
|
||||
MessageChannel usageChannelWithIgnoreMode;
|
||||
|
||||
@Autowired
|
||||
MessageChannel usageChannelConcurrent;
|
||||
|
||||
@@ -143,7 +151,7 @@ public class FileOutboundChannelAdapterParserTests {
|
||||
@Test
|
||||
public void adapterWithCharset() {
|
||||
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapterWithCharset);
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
FileWritingMessageHandler handler = (FileWritingMessageHandler)
|
||||
adapterAccessor.getPropertyValue("handler");
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
@@ -162,61 +170,107 @@ public class FileOutboundChannelAdapterParserTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adapterUsageWithAppend() throws Exception{
|
||||
@Test
|
||||
public void adapterUsageWithAppend() throws Exception{
|
||||
|
||||
String expectedFileContent = "Initial File Content:String content:byte[] content:File content";
|
||||
String expectedFileContent = "Initial File Content:String content:byte[] content:File content";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
usageChannel.send(new GenericMessage<String>("Initial File Content:"));
|
||||
usageChannel.send(new GenericMessage<String>("String content:"));
|
||||
usageChannel.send(new GenericMessage<byte[]>("byte[] content:".getBytes()));
|
||||
usageChannel.send(new GenericMessage<File>(new File("test/input.txt")));
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
usageChannel.send(new GenericMessage<String>("Initial File Content:"));
|
||||
usageChannel.send(new GenericMessage<String>("String content:"));
|
||||
usageChannel.send(new GenericMessage<byte[]>("byte[] content:".getBytes()));
|
||||
usageChannel.send(new GenericMessage<File>(new File("test/input.txt")));
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
}
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adapterUsageWithAppendConcurrent() throws Exception{
|
||||
@Test
|
||||
public void adapterUsageWithFailMode() throws Exception{
|
||||
|
||||
File testFile = new File("test/fileToAppendConcurrent.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
String expectedFileContent = "Initial File Content:String content:byte[] content:File content";
|
||||
|
||||
StringBuffer aBuffer = new StringBuffer();
|
||||
StringBuffer bBuffer = new StringBuffer();
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
usageChannelWithFailMode.send(new GenericMessage<String>("Initial File Content:"));
|
||||
|
||||
try {
|
||||
usageChannelWithFailMode.send(new GenericMessage<String>("String content:"));
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Was expecting an Exception to be thrown.");
|
||||
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adapterUsageWithIgnoreMode() throws Exception{
|
||||
|
||||
|
||||
String expectedFileContent = "Initial File Content:";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
usageChannelWithIgnoreMode.send(new GenericMessage<String>("Initial File Content:"));
|
||||
usageChannelWithIgnoreMode.send(new GenericMessage<String>("String content:"));
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adapterUsageWithAppendConcurrent() throws Exception{
|
||||
|
||||
File testFile = new File("test/fileToAppendConcurrent.txt");
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
StringBuffer aBuffer = new StringBuffer();
|
||||
StringBuffer bBuffer = new StringBuffer();
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
aBuffer.append("a");
|
||||
bBuffer.append("b");
|
||||
}
|
||||
String aString = aBuffer.toString();
|
||||
String bString = bBuffer.toString();
|
||||
String aString = aBuffer.toString();
|
||||
String bString = bBuffer.toString();
|
||||
|
||||
for (int i = 0; i < 1; i ++) {
|
||||
usageChannelConcurrent.send(new GenericMessage<String>(aString));
|
||||
usageChannelConcurrent.send(new GenericMessage<String>(bString));
|
||||
for (int i = 0; i < 1; i ++) {
|
||||
usageChannelConcurrent.send(new GenericMessage<String>(aString));
|
||||
usageChannelConcurrent.send(new GenericMessage<String>(bString));
|
||||
}
|
||||
|
||||
Thread.sleep(2000);
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
int beginningIndex = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
assertAllCharactersAreSame(actualFileContent.substring(beginningIndex, beginningIndex+99999));
|
||||
beginningIndex += 100000;
|
||||
Thread.sleep(2000);
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
int beginningIndex = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
assertAllCharactersAreSame(actualFileContent.substring(beginningIndex, beginningIndex+99999));
|
||||
beginningIndex += 100000;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAllCharactersAreSame(String substring){
|
||||
char[] characters = substring.toCharArray();
|
||||
char c = characters[0];
|
||||
for (char character : characters) {
|
||||
private void assertAllCharactersAreSame(String substring){
|
||||
char[] characters = substring.toCharArray();
|
||||
char c = characters[0];
|
||||
for (char character : characters) {
|
||||
assertEquals(c, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:si="http://www.springframework.org/schema/integration"
|
||||
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
|
||||
@@ -13,21 +13,38 @@
|
||||
http://www.springframework.org/schema/integration/file
|
||||
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
|
||||
|
||||
<outbound-gateway id="ordered"
|
||||
request-channel="someChannel"
|
||||
reply-timeout="777"
|
||||
directory="${java.io.tmpdir}"
|
||||
auto-startup="false"
|
||||
order="777"
|
||||
filename-generator-expression="'foo.txt'"/>
|
||||
<int-file:outbound-gateway id="ordered"
|
||||
request-channel="someChannel" reply-timeout="777" directory="${java.io.tmpdir}"
|
||||
auto-startup="false" order="777" 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'"/>
|
||||
<int-file:outbound-gateway id="gatewayWithDirectoryExpression"
|
||||
request-channel="someChannel" directory-expression="'build/foo'"
|
||||
auto-startup="false" order="777" filename-generator-expression="'foo.txt'" />
|
||||
|
||||
<int-file:outbound-gateway id="gatewayWithReplaceMode"
|
||||
request-channel="gatewayWithReplaceModeChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'" mode="REPLACE"
|
||||
directory="test" />
|
||||
|
||||
<int-file:outbound-gateway id="gatewayWithAppendMode"
|
||||
request-channel="gatewayWithAppendModeChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'" mode="APPEND"
|
||||
directory="test" />
|
||||
|
||||
<int-file:outbound-gateway id="gatewayWithFailMode"
|
||||
request-channel="gatewayWithFailModeChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'" mode="FAIL"
|
||||
directory="test" />
|
||||
|
||||
<int-file:outbound-gateway id="gatewayWithIgnoreMode"
|
||||
request-channel="gatewayWithIgnoreModeChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'" mode="IGNORE"
|
||||
directory="test" />
|
||||
|
||||
<int-file:outbound-gateway id="gatewayWithFailModeLowercase"
|
||||
request-channel="gatewayWithFailModeLowercaseChannel"
|
||||
filename-generator-expression="'fileToAppend.txt'" mode="fail"
|
||||
directory="test" />
|
||||
<context:property-placeholder />
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -18,17 +18,28 @@ package org.springframework.integration.file.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileWritingMessageHandler;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -44,6 +55,21 @@ public class FileOutboundGatewayParserTests {
|
||||
@Autowired
|
||||
private EventDrivenConsumer gatewayWithDirectoryExpression;
|
||||
|
||||
@Autowired
|
||||
MessageChannel gatewayWithIgnoreModeChannel;
|
||||
|
||||
@Autowired
|
||||
MessageChannel gatewayWithFailModeChannel;
|
||||
|
||||
@Autowired
|
||||
MessageChannel gatewayWithAppendModeChannel;
|
||||
|
||||
@Autowired
|
||||
MessageChannel gatewayWithReplaceModeChannel;
|
||||
|
||||
@Autowired
|
||||
MessageChannel gatewayWithFailModeLowercaseChannel;
|
||||
|
||||
@Test
|
||||
public void checkOrderedGateway() throws Exception {
|
||||
|
||||
@@ -70,4 +96,190 @@ public class FileOutboundGatewayParserTests {
|
||||
assertEquals("'build/foo'", TestUtils.getPropertyValue(handler, "destinationDirectoryExpression", Expression.class).getExpressionString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uses the Ignore Mode of the File OutboundGateway. When persisting
|
||||
* a payload using the File Outbound Gateway and the mode is set to IGNORE,
|
||||
* then the destination file will be created and written if it does not yet exist,
|
||||
* BUT if it exists it will not be overwritten. Instead the Message Payload will
|
||||
* be silently ignored. The reply message will contain the pre-existing destination
|
||||
* {@link File} as its payload.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void gatewayWithIgnoreMode() throws Exception{
|
||||
|
||||
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithIgnoreModeChannel);
|
||||
|
||||
final String expectedFileContent = "Initial File Content:";
|
||||
final File testFile = new File("test/fileToAppend.txt");
|
||||
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("Initial File Content:"));
|
||||
|
||||
Message<?> replyMessage = messagingTemplate.sendAndReceive(new GenericMessage<String>("String content:"));
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
assertTrue(replyMessage.getPayload() instanceof File);
|
||||
|
||||
File replyPayload = (File) replyMessage.getPayload();
|
||||
|
||||
assertEquals(expectedFileContent, new String(FileCopyUtils.copyToByteArray(replyPayload)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uses the Fail mode of the File Outbound Gateway. When persisting
|
||||
* a payload using the File Outbound Gateway and the mode is set to Fail,
|
||||
* then the destination {@link File} will be created and written if it does
|
||||
* not yet exist. BUT if the destination {@link File} already exists, a
|
||||
* {@link MessageHandlingException} will be thrown.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void gatewayWithFailMode() throws Exception{
|
||||
|
||||
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithFailModeChannel);
|
||||
|
||||
String expectedFileContent = "Initial File Content:";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("Initial File Content:"));
|
||||
|
||||
final String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
try {
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("String content:"));
|
||||
|
||||
} catch (MessageHandlingException e) {
|
||||
assertTrue(e.getMessage().startsWith("The destination file already exists at '"));
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Was expecting a MessageHandlingException to be thrown.");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test is exactly the same as {@link #gatewayWithFailMode()}. However, the
|
||||
* mode is provided in lower-case ensuring that the mode can be provided
|
||||
* in an case-insensitive fashion.
|
||||
*
|
||||
* Instead a {@link MessageHandlingException} will be thrown.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void gatewayWithFailModeLowercase() throws Exception{
|
||||
|
||||
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithFailModeLowercaseChannel);
|
||||
|
||||
String expectedFileContent = "Initial File Content:";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("Initial File Content:"));
|
||||
|
||||
final String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
try {
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("String content:"));
|
||||
|
||||
} catch (MessageHandlingException e) {
|
||||
assertTrue(e.getMessage().startsWith("The destination file already exists at '"));
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Was expecting a MessageHandlingException to be thrown.");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uses the Append Mode of the File Outbound Gateway. When persisting
|
||||
* a payload using the File Outbound Gateway and the mode is set to APPEND,
|
||||
* then the destination file will be created and written, if it does not yet
|
||||
* exist. BUT if it exists it will be appended to the existing file.
|
||||
*
|
||||
* The reply message will contain the concatenated destination
|
||||
* {@link File} as its payload.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void gatewayWithAppendMode() throws Exception{
|
||||
|
||||
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithAppendModeChannel);
|
||||
|
||||
String expectedFileContent = "Initial File Content:String content:";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("Initial File Content:"));
|
||||
Message<?> m = messagingTemplate.sendAndReceive(new GenericMessage<String>("String content:"));
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
assertTrue(m.getPayload() instanceof File);
|
||||
|
||||
File replyPayload = (File) m.getPayload();
|
||||
assertEquals(expectedFileContent, new String(FileCopyUtils.copyToByteArray(replyPayload)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uses the Replace Mode of the File OutboundGateway. When persisting
|
||||
* a payload using the File Outbound Gateway and the mode is set to REPLACE,
|
||||
* then the destination file will be created and written if it does not yet exist.
|
||||
* If the destination file exists, it will be replaced.
|
||||
*
|
||||
* The reply message will contain the concatenated destination
|
||||
* {@link File} as its payload.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void gatewayWithReplaceMode() throws Exception{
|
||||
|
||||
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithReplaceModeChannel);
|
||||
|
||||
String expectedFileContent = "String content:";
|
||||
|
||||
File testFile = new File("test/fileToAppend.txt");
|
||||
|
||||
if (testFile.exists()){
|
||||
testFile.delete();
|
||||
}
|
||||
|
||||
messagingTemplate.sendAndReceive(new GenericMessage<String>("Initial File Content:"));
|
||||
Message<?> m = messagingTemplate.sendAndReceive(new GenericMessage<String>("String content:"));
|
||||
|
||||
String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile));
|
||||
assertEquals(expectedFileContent, actualFileContent);
|
||||
|
||||
assertTrue(m.getPayload() instanceof File);
|
||||
|
||||
File replyPayload = (File) m.getPayload();
|
||||
assertEquals(expectedFileContent, new String(FileCopyUtils.copyToByteArray(replyPayload)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -247,17 +247,51 @@
|
||||
</para>
|
||||
</note>
|
||||
</section>
|
||||
<section id="file-writing-append">
|
||||
<title>Append to Files</title>
|
||||
<section id="file-writing-destination-exists">
|
||||
<title>Dealing with Existing Destination Files</title>
|
||||
<para>
|
||||
Since Spring Integration 2.2 you can append Message
|
||||
content to the existing file instead of creating a new File each
|
||||
time. To do so, set the <emphasis>append</emphasis> attribute
|
||||
to <code>true</code>. Note that this attribute is mutually exclusive
|
||||
with <emphasis>temporary-file-suffix</emphasis> attribute since
|
||||
when appending content to the existing file, the adapter no longer
|
||||
uses a temporary file. This attribute defaults to 'false' if not
|
||||
set explicitly.
|
||||
When writing files and the destination file already exists, the
|
||||
default behavior is to overwrite that target file. This behavior,
|
||||
though, can be changed by setting the <emphasis>mode</emphasis>
|
||||
attribute on the respective File Outbound components. The following
|
||||
options exist:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>REPLACE (Default)</listitem>
|
||||
<listitem>APPEND</listitem>
|
||||
<listitem>FAIL</listitem>
|
||||
<listitem>IGNORE</listitem>
|
||||
</itemizedlist>
|
||||
<note>
|
||||
The <emphasis>mode</emphasis> attribute and the options
|
||||
<emphasis>APPEND</emphasis>, <emphasis>FAIL</emphasis> and
|
||||
<emphasis>IGNORE</emphasis>, are available since
|
||||
<emphasis>Spring Integration 2.2</emphasis>.
|
||||
</note>
|
||||
<para><emphasis>REPLACE</emphasis></para>
|
||||
<para>
|
||||
If the target file already exists, it will be overwritten. If the
|
||||
<emphasis>mode</emphasis> attribute is not specified, then this
|
||||
is the default behavior when writing files.
|
||||
</para>
|
||||
<para><emphasis>APPEND</emphasis></para>
|
||||
<para>
|
||||
This mode allows you to append Message content to the existing
|
||||
file instead of creating a new file each time. Note that this
|
||||
attribute is mutually exclusive with <emphasis>temporary-file-suffix</emphasis>
|
||||
attribute since when appending content to the existing file, the
|
||||
adapter no longer uses a temporary file.
|
||||
</para>
|
||||
<para><emphasis>FAIL</emphasis></para>
|
||||
<para>
|
||||
If the target file exists, a
|
||||
<ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/MessageHandlingException.html">MessageHandlingException</ulink>
|
||||
is thrown.
|
||||
</para>
|
||||
<para><emphasis>IGNORE</emphasis></para>
|
||||
<para>
|
||||
If the target file exists, the message payload is silently
|
||||
ignored.
|
||||
</para>
|
||||
</section>
|
||||
<section id="file-outbound-channel-adapter">
|
||||
@@ -280,15 +314,33 @@
|
||||
<section id="file-writing-output-gateway">
|
||||
<title>Outbound Gateway</title>
|
||||
<para>
|
||||
In cases where you want to continue processing messages based on the written File you can use
|
||||
the <code>outbound-gateway</code> instead. It plays a very similar role as the
|
||||
<code>outbound-channel-adapter</code>. However after writing the File, it will also send it
|
||||
to the reply channel as the payload of a Message.
|
||||
In cases where you want to continue processing messages based on
|
||||
the written file, you can use the <code>outbound-gateway</code>
|
||||
instead. It plays a very similar role as the
|
||||
<code>outbound-channel-adapter</code>. However, after writing the
|
||||
file, it will also send it to the reply channel as the payload of
|
||||
a Message.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int-file:outbound-gateway id="mover" request-channel="moveInput"
|
||||
<programlisting language="xml"><![CDATA[<int-file:outbound-gateway id="mover" request-channel="moveInput"
|
||||
reply-channel="output"
|
||||
directory="${output.directory}"
|
||||
delete-source-files="true"/>]]></programlisting>
|
||||
mode="REPLACE" delete-source-files="true"/>]]></programlisting>
|
||||
<para>
|
||||
As mentioned earlier, you can also specify the <emphasis>mode</emphasis>
|
||||
attribute, which defines the behavior of how to deal with situations
|
||||
where the destination file already exists. Please see
|
||||
<xref linkend="file-writing-destination-exists"/> for further
|
||||
details. Generally, when using the
|
||||
<emphasis>File Outbound Gateway</emphasis>, the result file is
|
||||
returned as the Message payload on the reply channel.
|
||||
</para>
|
||||
<para>
|
||||
This also applies when specifying the <emphasis>IGNORE</emphasis>
|
||||
mode. In that case the pre-existing destination file is returned.
|
||||
If the payload of the request message was a file, you still have
|
||||
access to that original file through the Message Header
|
||||
<emphasis><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/file/FileHeaders.html">FileHeaders.ORIGINAL_FILE</ulink></emphasis>.
|
||||
</para>
|
||||
<note>
|
||||
The 'outbound-gateway' works well in cases where you want to first move a file and then send it
|
||||
through a processing pipeline. In such cases, you may connect the file namespace's
|
||||
@@ -302,8 +354,6 @@
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
>>>>>>> INT-2618 - Document directory-expression attribute
|
||||
|
||||
<section id="file-transforming">
|
||||
<title>File Transformers</title>
|
||||
<para>
|
||||
|
||||
@@ -79,6 +79,26 @@
|
||||
message as a source of parameters.
|
||||
</para>
|
||||
</section>
|
||||
<section id="2.2-file-adapter">
|
||||
<title>File Adapter - Improved File Overwrite/Append Handling</title>
|
||||
<para>
|
||||
When using the <emphasis>File Oubound Channel Adapter</emphasis>
|
||||
or the <emphasis>File Outbound Gateway</emphasis>, a new
|
||||
<emphasis>mode</emphasis> property was added. Prior to
|
||||
<emphasis>Spring Integration 2.2</emphasis>, target files were
|
||||
replaced when they existed. Now you can specify
|
||||
the following options:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>REPLACE (Default)</listitem>
|
||||
<listitem>APPEND</listitem>
|
||||
<listitem>FAIL</listitem>
|
||||
<listitem>IGNORE</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
For more information please see <xref linkend="file-writing-destination-exists"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="2.2-tx">
|
||||
<title>Transaction Synchronization</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user