INT-3960: File Outbound - Preserve Timestamp

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

Polishing - PR Comments

Fix errors according Checkstyle report.
Wrap `warn()` to `isWarnEnable()`.
Polishing for `setPreserveTimestamp()` JavaDocs.
This commit is contained in:
Gary Russell
2016-03-23 12:48:45 -04:00
committed by Artem Bilan
parent ad0839da8b
commit 516846b750
6 changed files with 82 additions and 7 deletions

View File

@@ -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.
@@ -39,4 +39,6 @@ public abstract class FileHeaders {
public static final String RENAME_TO = PREFIX + "renameTo";
public static final String SET_MODIFIED = PREFIX + "setModified";
}

View File

@@ -152,6 +152,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
private volatile MessageFlushPredicate flushPredicate = new DefaultFlushPredicate();
private volatile boolean preserveTimestamp;
/**
* Constructor which sets the {@link #destinationDirectoryExpression} using
* a {@link LiteralExpression}.
@@ -327,6 +329,19 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
this.flushPredicate = flushPredicate;
}
/**
* Set to true to preserve the destination file timestamp. If true and
* the payload is a {@link File}, the payload's {@code lastModified} time will be
* transferred to the destination file. For other payloads, the
* {@link FileHeaders#SET_MODIFIED} header {@value FileHeaders#SET_MODIFIED}
* will be used if present and it's a {@link Number}.
* @param preserveTimestamp the preserveTimestamp to set.
* @since 4.3
*/
public void setPreserveTimestamp(boolean preserveTimestamp) {
this.preserveTimestamp = preserveTimestamp;
}
@Override
protected void doInit() {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
@@ -408,6 +423,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
if (!ignore) {
try {
Object timestamp = requestMessage.getHeaders().get(FileHeaders.SET_MODIFIED);
if (!resultFile.exists() &&
generatedFileName.replaceAll("/", Matcher.quoteReplacement(File.separator))
.contains(File.separator)) {
@@ -415,6 +431,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
}
if (payload instanceof File) {
resultFile = handleFileMessage((File) payload, tempFile, resultFile);
timestamp = ((File) payload).lastModified();
}
else if (payload instanceof InputStream) {
resultFile = handleInputStreamMessage((InputStream) payload, originalFileFromHeader, tempFile,
@@ -432,6 +449,17 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
throw new IllegalArgumentException(
"unsupported Message payload type [" + payload.getClass().getName() + "]");
}
if (this.preserveTimestamp) {
if (timestamp instanceof Number) {
resultFile.setLastModified(((Number) timestamp).longValue());
}
else {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Could not set lastModified, header " + FileHeaders.SET_MODIFIED
+ " must be a Number, not " + timestamp.getClass());
}
}
}
}
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);

View File

@@ -509,6 +509,17 @@ Only files matching this regular expression will be picked up by this adapter.
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="preserve-timestamp" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
Specify whether to preserve the modified timestamp from the source
file on the destination file after copying. Applies to 'java.io.File' payloads.
For other payload types, the optional 'file_setModified' header will be used,
if present (and a 'Number'), to set the lastModified time.
By default, the timestamp will NOT be preserved.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
</xsd:complexType>

View File

@@ -120,13 +120,18 @@ public class FileWritingMessageHandlerTests {
@Test
public void stringPayloadCopiedToNewFile() throws Exception {
Message<?> message = MessageBuilder.withPayload(SAMPLE_CONTENT).build();
long lastModified = 1234000L;
Message<?> message = MessageBuilder.withPayload(SAMPLE_CONTENT)
.setHeader(FileHeaders.SET_MODIFIED, lastModified)
.build();
QueueChannel output = new QueueChannel();
handler.setCharset(DEFAULT_ENCODING);
handler.setOutputChannel(output);
handler.setPreserveTimestamp(true);
handler.handleMessage(message);
Message<?> result = output.receive(0);
assertFileContentIsMatching(result);
assertLastModifiedIs(result, lastModified);
}
@Test
@@ -184,11 +189,15 @@ public class FileWritingMessageHandlerTests {
@Test
public void filePayloadCopiedToNewFile() throws Exception {
Message<?> message = MessageBuilder.withPayload(sourceFile).build();
long lastModified = 12345000L;
sourceFile.setLastModified(lastModified);
QueueChannel output = new QueueChannel();
handler.setOutputChannel(output);
handler.setPreserveTimestamp(true);
handler.handleMessage(message);
Message<?> result = output.receive(0);
assertFileContentIsMatching(result);
assertLastModifiedIs(result, lastModified);
}
@Test
@@ -512,10 +521,11 @@ public class FileWritingMessageHandlerTests {
}
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);
assertFileContentIs(messageToFile(result), expected);
}
void assertLastModifiedIs(Message<?> result, long expected) {
assertThat(messageToFile(result).lastModified(), is(expected));
}
void assertFileContentIs(File destFile, String expected) throws IOException {
@@ -525,4 +535,11 @@ public class FileWritingMessageHandlerTests {
assertThat(new String(destFileContent, DEFAULT_ENCODING), is(expected));
}
protected File messageToFile(Message<?> result) {
assertThat(result, is(notNullValue()));
assertThat(result.getPayload(), is(instanceOf(File.class)));
File destFile = (File) result.getPayload();
return destFile;
}
}