From 588ccdbb007294bd5f573e57ae3fa315549d7454 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 5 Jan 2016 17:49:26 -0500 Subject: [PATCH] INT-3726-4.2.x: Support path for generateFileName JIRA: https://jira.spring.io/browse/INT-3726 Backport for `4.2.x` --- .../file/FileWritingMessageHandler.java | 14 +++++++--- .../file/FileWritingMessageHandlerTests.java | 28 +++++++++++++++---- src/reference/asciidoc/file.adoc | 13 +++++---- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java index 51c3f8e959..181ac90e86 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import java.util.regex.Matcher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -259,6 +260,9 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand validateDestinationDirectory(directory, this.autoCreateDirectory); } + Assert.state(!(this.temporaryFileSuffixSet && FileExistsMode.APPEND.equals(this.fileExistsMode)), + "'temporaryFileSuffix' can not be set when appending to an existing file"); + if (!this.fileNameGeneratorSet && this.fileNameGenerator instanceof BeanFactoryAware) { ((BeanFactoryAware) this.fileNameGenerator).setBeanFactory(this.getBeanFactory()); } @@ -277,9 +281,6 @@ 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 - && FileExistsMode.APPEND.equals(this.fileExistsMode)), - "'temporaryFileSuffix' can not be set when appending to an existing file"); } @Override @@ -307,6 +308,11 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand if (!ignore) { try { + if (!resultFile.exists() && + generatedFileName.replaceAll("/", Matcher.quoteReplacement(File.separator)) + .contains(File.separator)) { + resultFile.getParentFile().mkdirs(); + } if (payload instanceof File) { resultFile = handleFileMessage((File) payload, tempFile, resultFile); } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileWritingMessageHandlerTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/FileWritingMessageHandlerTests.java index bcbb49fdcb..cf7ff673d3 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/FileWritingMessageHandlerTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileWritingMessageHandlerTests.java @@ -33,18 +33,19 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; + import org.springframework.beans.factory.BeanFactory; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.file.support.FileExistsMode; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.support.GenericMessage; @@ -57,6 +58,7 @@ import org.springframework.util.FileCopyUtils; * @author Gary Russell * @author Tony Falabella * @author Gunnar Hillert + * @author Artem Bilan */ public class FileWritingMessageHandlerTests { @@ -115,6 +117,21 @@ public class FileWritingMessageHandlerTests { assertFileContentIsMatching(result); } + @Test + public void testFileNameHeader() throws Exception { + Message message = MessageBuilder.withPayload(SAMPLE_CONTENT) + .setHeader(FileHeaders.FILENAME, "dir1" + File.separator + "dir2/test") + .build(); + QueueChannel output = new QueueChannel(); + handler.setCharset(DEFAULT_ENCODING); + handler.setOutputChannel(output); + handler.handleMessage(message); + Message result = output.receive(0); + assertFileContentIsMatching(result); + File destFile = (File) result.getPayload(); + assertThat(destFile.getAbsolutePath(), containsString(TestUtils.applySystemFileSeparator("/dir1/dir2/test"))); + } + @Test public void stringPayloadCopiedToNewFileWithNewLines() throws Exception { Message message = MessageBuilder.withPayload(SAMPLE_CONTENT).build(); @@ -196,7 +213,8 @@ public class FileWritingMessageHandlerTests { assertFileContentIs(result, SAMPLE_CONTENT + System.getProperty("line.separator")); } - @Test @Ignore // INT-3289 ignored because it won't fail on all OS + @Test + @Ignore("INT-3289: doesn't fail on all OS") public void testCreateDirFail() { File dir = new File("/foo"); FileWritingMessageHandler handler = new FileWritingMessageHandler(dir); @@ -404,18 +422,18 @@ public class FileWritingMessageHandlerTests { assertFileContentIs(outFile, "foo"); } - void assertFileContentIsMatching(Message result) throws IOException, UnsupportedEncodingException { + void assertFileContentIsMatching(Message result) throws IOException { assertFileContentIs(result, SAMPLE_CONTENT); } - void assertFileContentIs(Message result, String expected) throws IOException, UnsupportedEncodingException { + void assertFileContentIs(Message result, String expected) throws IOException { assertThat(result, is(notNullValue())); assertThat(result.getPayload(), is(instanceOf(File.class))); File destFile = (File) result.getPayload(); assertFileContentIs(destFile, expected); } - void assertFileContentIs(File destFile, String expected) throws IOException, UnsupportedEncodingException { + void assertFileContentIs(File destFile, String expected) throws IOException { assertNotSame(destFile, sourceFile); assertThat(destFile.exists(), is(true)); byte[] destFileContent = FileCopyUtils.copyToByteArray(destFile); diff --git a/src/reference/asciidoc/file.adoc b/src/reference/asciidoc/file.adoc index b17a0bfcc0..de7c7a00d1 100644 --- a/src/reference/asciidoc/file.adoc +++ b/src/reference/asciidoc/file.adoc @@ -459,17 +459,22 @@ Once setup, the `DefaultFileNameGenerator` will employ the following resolution When using the XML namespace support, both, the _File Outbound Channel Adapter_ and the _File Outbound Gateway_ support the following two mutually exclusive configuration attributes: -* `filename-generator` (a reference to a `FileNameGenerator`) implementation) +* `filename-generator` (a reference to a `FileNameGenerator` implementation) * `filename-generator-expression` (an expression evaluating to a `String`) - - While writing files, a temporary file suffix will be used (default: `.writing`). It is appended to the filename while the file is being written. To customize the suffix, you can set the _temporary-file-suffix_ attribute on both the _File Outbound Channel Adapter_ and the _File Outbound Gateway_. NOTE: When using the _APPEND_ file _mode_, the _temporary-file-suffix_ attribute is ignored, since the data is appended to the file directly. +Starting with _version 4.2.5_ the generated file name (as a result of `filename-generator`/`filename-generator-expression` +evaluation) can represent a _sub-path_ together with the target file name. +It is used as a second constructor argument for `File(File parent, String child)` as before, but in the past we didn't +created (`mkdirs()`) directories for _sub-path_ assuming only the _file name_. +This approach is useful for cases when we need to restore the file system tree according the source directory. +For example we unzipping the archive and want to save all file in the target directory at the same order. + [[file-writing-output-directory]] ==== Specifying the Output Directory @@ -478,8 +483,6 @@ Both, the _File Outbound Channel Adapter_ and the _File Outbound Gateway_ provid * _directory_ * _directory-expression_ - - NOTE: The _directory-expression_ attribute is available since Spring Integration 2.2. *Using the directory attribute*