Some file module Sonar fixes (#2718)

* Some file module Sonar fixes

* * Fixes after testing

* * Some further Sonar fixes for file module

* * Further Sonar fixes for file module

* * More Sonar fixes for file module

* * Fix `RemoteFileTemplate` issues

* * Fix new sonar smells after previous one

* * Review feedback
This commit is contained in:
Artem Bilan
2019-01-25 14:37:57 -05:00
committed by Gary Russell
parent db239f8ba0
commit a609bd9398
17 changed files with 810 additions and 770 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -47,6 +47,7 @@ import org.springframework.integration.file.filters.DiscardAwareFileListFilter;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.filters.ResettableFileListFilter;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
@@ -103,15 +104,15 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
*/
private final Queue<File> toBeReceived;
private volatile File directory;
private File directory;
private volatile DirectoryScanner scanner = new DefaultDirectoryScanner();
private DirectoryScanner scanner = new DefaultDirectoryScanner();
private volatile boolean scannerExplicitlySet;
private boolean scannerExplicitlySet;
private volatile boolean autoCreateDirectory = true;
private boolean autoCreateDirectory = true;
private volatile boolean scanEachPoll = false;
private boolean scanEachPoll = false;
private FileListFilter<File> filter;
@@ -119,7 +120,7 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
private boolean useWatchService;
private WatchEventType[] watchEvents = new WatchEventType[] { WatchEventType.CREATE };
private WatchEventType[] watchEvents = { WatchEventType.CREATE };
/**
* Creates a FileReadingMessageSource with a naturally ordered queue of unbounded capacity.
@@ -132,7 +133,6 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
* Creates a FileReadingMessageSource with a bounded queue of the given
* capacity. This can be used to reduce the memory footprint of this
* component when reading from a large directory.
*
* @param internalQueueCapacity
* the size of the queue used to cache files to be received
* internally. This queue can be made larger to optimize the
@@ -151,26 +151,22 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
/**
* Creates a FileReadingMessageSource with a {@link PriorityBlockingQueue}
* ordered with the passed in {@link Comparator}
* <p>
* The size of the queue used should be large enough to hold all the files
* ordered with the passed in {@link Comparator}.
* <p> The size of the queue used should be large enough to hold all the files
* in the input directory in order to sort all of them, so restricting the
* size of the queue is mutually exclusive with ordering. No guarantees
* about file delivery order can be made under concurrent access.
* <p>
*
* @param receptionOrderComparator
* the comparator to be used to order the files in the internal
* queue
*/
public FileReadingMessageSource(Comparator<File> receptionOrderComparator) {
public FileReadingMessageSource(@Nullable Comparator<File> receptionOrderComparator) {
this.toBeReceived = new PriorityBlockingQueue<>(DEFAULT_INTERNAL_QUEUE_CAPACITY, receptionOrderComparator);
}
/**
* Specify the input directory.
*
* @param directory to monitor
*/
public void setDirectory(File directory) {
@@ -181,7 +177,6 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
/**
* Optionally specify a custom scanner, for example the
* {@link WatchServiceDirectoryScanner}
*
* @param scanner scanner implementation
*/
public void setScanner(DirectoryScanner scanner) {
@@ -207,7 +202,6 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
* <em>true</em>. If set to <em>false</em> and the
* source directory does not exist, an Exception will be thrown upon
* initialization.
*
* @param autoCreateDirectory
* should the directory to be monitored be created when this
* component starts up?
@@ -224,8 +218,7 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
* If multiple filters are required a
* {@link org.springframework.integration.file.filters.CompositeFileListFilter}
* can be used to group them together.
* <p>
* <b>The supplied filter must be thread safe.</b>.
* <p> <b>The supplied filter must be thread safe.</b>.
* @param filter a filter
*/
public void setFilter(FileListFilter<File> filter) {
@@ -256,7 +249,6 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
* will more likely be out of sync with the file system if this flag is set
* to <code>false</code>, but it will change more often (causing expensive
* reordering) if it is set to <code>true</code>.
*
* @param scanEachPoll
* whether or not the component should re-scan (as opposed to not
* rescanning until the entire backlog has been delivered)
@@ -304,15 +296,15 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
@Override
public void start() {
if (!this.running.getAndSet(true)) {
if (!this.directory.exists() && this.autoCreateDirectory) {
this.directory.mkdirs();
if (!this.directory.exists() && this.autoCreateDirectory && !this.directory.mkdirs()) {
throw new IllegalStateException("Cannot create directory or ita parents: " + this.directory);
}
Assert.isTrue(this.directory.exists(),
"Source directory [" + this.directory + "] does not exist.");
() -> "Source directory [" + this.directory + "] does not exist.");
Assert.isTrue(this.directory.isDirectory(),
"Source path [" + this.directory + "] does not point to a directory.");
() -> "Source path [" + this.directory + "] does not point to a directory.");
Assert.isTrue(this.directory.canRead(),
"Source directory [" + this.directory + "] is not readable.");
() -> "Source directory [" + this.directory + "] is not readable.");
if (this.scanner instanceof Lifecycle) {
((Lifecycle) this.scanner).start();
}
@@ -336,7 +328,7 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
Assert.notNull(this.directory, "'directory' must not be null");
Assert.state(!(this.scannerExplicitlySet && this.useWatchService),
"The 'scanner' and 'useWatchService' options are mutually exclusive: " + this.scanner);
() -> "The 'scanner' and 'useWatchService' options are mutually exclusive: " + this.scanner);
if (this.useWatchService) {
this.scanner = new WatchServiceDirectoryScanner();
@@ -345,8 +337,8 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
// Check that the filter and locker options are _NOT_ set if an external scanner has been set.
// The external scanner is responsible for the filter and locker options in that case.
Assert.state(!(this.scannerExplicitlySet && (this.filter != null || this.locker != null)),
"When using an external scanner the 'filter' and 'locker' options should not be used. Instead, set these options on the external DirectoryScanner: "
+ this.scanner);
() -> "When using an external scanner the 'filter' and 'locker' options should not be used. " +
"Instead, set these options on the external DirectoryScanner: " + this.scanner);
if (this.filter != null) {
this.scanner.setFilter(this.filter);
}
@@ -492,7 +484,7 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
files.addAll(filesFromEvents());
return files.toArray(new File[files.size()]);
return files.toArray(new File[0]);
}
private Set<File> filesFromEvents() {
@@ -504,57 +496,11 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE ||
event.kind() == StandardWatchEventKinds.ENTRY_MODIFY ||
event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
Path item = (Path) event.context();
File file = new File(parentDir, item.toFile().getName());
if (logger.isDebugEnabled()) {
logger.debug("Watch event [" + event.kind() + "] for file [" + file + "]");
}
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
if (getFilter() instanceof ResettableFileListFilter) {
((ResettableFileListFilter<File>) getFilter()).remove(file);
}
boolean fileRemoved = files.remove(file);
if (fileRemoved && logger.isDebugEnabled()) {
logger.debug("The file [" + file +
"] has been removed from the queue because of DELETE event.");
}
}
else {
if (file.exists()) {
if (file.isDirectory()) {
files.addAll(walkDirectory(file.toPath(), event.kind()));
}
else {
files.remove(file);
files.add(file);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("A file [" + file + "] for the event [" + event.kind() +
"] doesn't exist. Ignored.");
}
}
}
processFilesFromNormalEvent(files, parentDir, event);
}
else if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
if (logger.isDebugEnabled()) {
logger.debug("Watch event [" + StandardWatchEventKinds.OVERFLOW +
"] with context [" + event.context() + "]");
}
for (WatchKey watchKey : this.pathKeys.values()) {
watchKey.cancel();
}
this.pathKeys.clear();
if (event.context() != null && event.context() instanceof Path) {
files.addAll(walkDirectory((Path) event.context(), event.kind()));
}
else {
files.addAll(walkDirectory(FileReadingMessageSource.this.directory.toPath(), event.kind()));
}
processFilesFromOverflowEvent(files, event);
}
}
key.reset();
@@ -563,6 +509,61 @@ public class FileReadingMessageSource extends AbstractMessageSource<File>
return files;
}
private void processFilesFromNormalEvent(Set<File> files, File parentDir, WatchEvent<?> event) {
Path item = (Path) event.context();
File file = new File(parentDir, item.toFile().getName());
if (logger.isDebugEnabled()) {
logger.debug("Watch event [" + event.kind() + "] for file [" + file + "]");
}
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
if (getFilter() instanceof ResettableFileListFilter) {
((ResettableFileListFilter<File>) getFilter()).remove(file);
}
boolean fileRemoved = files.remove(file);
if (fileRemoved && logger.isDebugEnabled()) {
logger.debug("The file [" + file +
"] has been removed from the queue because of DELETE event.");
}
}
else {
if (file.exists()) {
if (file.isDirectory()) {
files.addAll(walkDirectory(file.toPath(), event.kind()));
}
else {
files.remove(file);
files.add(file);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("A file [" + file + "] for the event [" + event.kind() +
"] doesn't exist. Ignored.");
}
}
}
}
private void processFilesFromOverflowEvent(Set<File> files, WatchEvent<?> event) {
if (logger.isDebugEnabled()) {
logger.debug("Watch event [" + StandardWatchEventKinds.OVERFLOW +
"] with context [" + event.context() + "]");
}
for (WatchKey watchKey : this.pathKeys.values()) {
watchKey.cancel();
}
this.pathKeys.clear();
if (event.context() != null && event.context() instanceof Path) {
files.addAll(walkDirectory((Path) event.context(), event.kind()));
}
else {
files.addAll(walkDirectory(FileReadingMessageSource.this.directory.toPath(), event.kind()));
}
}
private Set<File> walkDirectory(Path directory, final WatchEvent.Kind<?> kind) {
final Set<File> walkedFiles = new LinkedHashSet<>();
try {

View File

@@ -25,6 +25,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
@@ -32,7 +33,6 @@ import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFilePermission;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
@@ -42,9 +42,7 @@ import java.util.concurrent.locks.Lock;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.stream.Collectors;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.Lifecycle;
@@ -111,13 +109,24 @@ import org.springframework.util.StringUtils;
public class FileWritingMessageHandler extends AbstractReplyProducingMessageHandler
implements Lifecycle, MessageTriggerAction {
private final Log logger = LogFactory.getLog(this.getClass());
private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final long DEFAULT_FLUSH_INTERVAL = 30000L;
private final Map<String, FileState> fileStates = new HashMap<String, FileState>();
private static final PosixFilePermission[] POSIX_FILE_PERMISSIONS =
{
PosixFilePermission.OTHERS_EXECUTE,
PosixFilePermission.OTHERS_WRITE,
PosixFilePermission.OTHERS_READ,
PosixFilePermission.GROUP_EXECUTE,
PosixFilePermission.GROUP_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OWNER_EXECUTE,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_READ
};
private final Map<String, FileState> fileStates = new HashMap<>();
private final Expression destinationDirectoryExpression;
@@ -332,8 +341,12 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
this.flushWhenIdle = flushWhenIdle;
}
/**
* Configure a {@link TaskScheduler} for flush operations.
* @param taskScheduler the {@link TaskScheduler} to use.
*/
@Override
public void setTaskScheduler(TaskScheduler taskScheduler) {
public void setTaskScheduler(TaskScheduler taskScheduler) { // NOSONAR
super.setTaskScheduler(taskScheduler);
}
@@ -370,7 +383,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
*/
public void setChmodOctal(String chmod) {
Assert.notNull(chmod, "'chmod' cannot be null");
setChmod(Integer.parseInt(chmod, 8));
setChmod(Integer.parseInt(chmod, 8)); // NOSONAR 8-bit
}
/**
@@ -382,53 +395,17 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
* @since 5.0
*/
public void setChmod(int chmod) {
Assert.isTrue(chmod >= 0 && chmod <= 0777, "'chmod' must be between 0 and 0777 (octal)");
Assert.isTrue(chmod >= 0 && chmod <= 0777, // NOSONAR permissions octal
"'chmod' must be between 0 and 0777 (octal)");
if (!FileUtils.IS_POSIX) {
this.logger.error("'chmod' setting ignored - the file system does not support Posix attributes");
return;
}
/*
* Bitset.valueOf(byte[]) takes a little-endian array of bytes to create a BitSet.
* Since we are interested in 9 bits, we construct an array with the low-order byte
* (bits 0-7) followed by the second order byte (bit 8).
* BitSet.stream() returns a stream of ints representing those bits that are set.
* We use that stream with a switch to create the set of PosixFilePermissions
* representing the bits that were set in the chmod value.
*/
BitSet bits = BitSet.valueOf(new byte[] { (byte) chmod, (byte) (chmod >> 8) });
final Set<PosixFilePermission> posixPermissions = new HashSet<>();
bits.stream().forEach(b -> {
switch (b) {
case 0:
posixPermissions.add(PosixFilePermission.OTHERS_EXECUTE);
break;
case 1:
posixPermissions.add(PosixFilePermission.OTHERS_WRITE);
break;
case 2:
posixPermissions.add(PosixFilePermission.OTHERS_READ);
break;
case 3:
posixPermissions.add(PosixFilePermission.GROUP_EXECUTE);
break;
case 4:
posixPermissions.add(PosixFilePermission.GROUP_WRITE);
break;
case 5:
posixPermissions.add(PosixFilePermission.GROUP_READ);
break;
case 6:
posixPermissions.add(PosixFilePermission.OWNER_EXECUTE);
break;
case 7:
posixPermissions.add(PosixFilePermission.OWNER_WRITE);
break;
case 8:
posixPermissions.add(PosixFilePermission.OWNER_READ);
break;
}
});
this.permissions = posixPermissions;
BitSet bits = BitSet.valueOf(new byte[] { (byte) chmod, (byte) (chmod >> 8) }); // NOSONAR
this.permissions = bits.stream()
.boxed()
.map((b) -> POSIX_FILE_PERMISSIONS[b])
.collect(Collectors.toSet());
}
/**
@@ -449,7 +426,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
if (this.destinationDirectoryExpression instanceof LiteralExpression) {
final File directory = ExpressionUtils.expressionToFile(this.destinationDirectoryExpression,
this.evaluationContext, null, "destinationDirectoryExpression");
this.evaluationContext, null, "destinationDirectoryExpression");
validateDestinationDirectory(directory, this.autoCreateDirectory);
}
@@ -469,7 +446,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
if (this.flushTask == null && FileExistsMode.APPEND_NO_FLUSH.equals(this.fileExistsMode)) {
TaskScheduler taskScheduler = getTaskScheduler();
Assert.state(taskScheduler != null, "'taskScheduler' is required for FileExistsMode.APPEND_NO_FLUSH");
this.flushTask = taskScheduler.scheduleAtFixedRate(new Flusher(), this.flushInterval / 3);
this.flushTask = taskScheduler.scheduleAtFixedRate(new Flusher(), this.flushInterval / 3); // NOSONAR
}
}
@@ -485,7 +462,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
flusher.run();
boolean needInterrupt = this.fileStates.size() > 0;
int n = 0;
while (n++ < 10 && this.fileStates.size() > 0) {
while (n++ < 10 && this.fileStates.size() > 0) { // NOSONAR
try {
Thread.sleep(1);
}
@@ -520,15 +497,13 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
() -> "Destination directory [" + destinationDirectory + "] is not writable.");
}
@Override
@Override // NOSONAR
protected Object handleRequestMessage(Message<?> requestMessage) {
Assert.notNull(requestMessage, "message must not be null");
Object payload = requestMessage.getPayload();
Assert.notNull(payload, "message payload must not be null");
String generatedFileName = this.fileNameGenerator.generateFileName(requestMessage);
File originalFileFromHeader = retrieveOriginalFileFromHeader(requestMessage);
final File destinationDirectoryToUse = evaluateDestinationDirectoryExpression(requestMessage);
File destinationDirectoryToUse = evaluateDestinationDirectoryExpression(requestMessage);
File tempFile = new File(destinationDirectoryToUse, generatedFileName + this.temporaryFileSuffix);
File resultFile = new File(destinationDirectoryToUse, generatedFileName);
@@ -543,7 +518,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
if (payload instanceof File) {
timestamp = ((File) payload).lastModified();
}
boolean ignore = (FileExistsMode.IGNORE.equals(this.fileExistsMode)
boolean ignore = (FileExistsMode.IGNORE.equals(this.fileExistsMode) // NOSONAR
&& (exists || (StringUtils.hasText(this.temporaryFileSuffix) && tempFile.exists())))
|| ((exists && FileExistsMode.REPLACE_IF_MODIFIED.equals(this.fileExistsMode))
&& (timestamp instanceof Number
@@ -556,56 +531,66 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
resultFile.getParentFile().mkdirs(); //NOSONAR - will fail on the writing below
}
if (payload instanceof File) {
resultFile = handleFileMessage((File) payload, tempFile, resultFile, requestMessage);
}
else if (payload instanceof InputStream) {
resultFile = handleInputStreamMessage((InputStream) payload, originalFileFromHeader, tempFile,
resultFile, requestMessage);
}
else if (payload instanceof byte[]) {
resultFile = this.handleByteArrayMessage(
(byte[]) payload, originalFileFromHeader, tempFile, resultFile, requestMessage);
}
else if (payload instanceof String) {
resultFile = this.handleStringMessage(
(String) payload, originalFileFromHeader, tempFile, resultFile, requestMessage);
}
else {
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 == null ? "null" : timestamp.getClass()));
}
}
}
resultFile = writeMessageToFile(requestMessage, originalFileFromHeader, tempFile, resultFile,
timestamp);
}
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);
}
}
if (!this.expectReply) {
return null;
}
if (resultFile != null) {
if (originalFileFromHeader == null && payload instanceof File) {
return this.getMessageBuilderFactory().withPayload(resultFile)
.setHeader(FileHeaders.ORIGINAL_FILE, payload);
}
if (resultFile != null && originalFileFromHeader == null && payload instanceof File) {
return getMessageBuilderFactory()
.withPayload(resultFile)
.setHeader(FileHeaders.ORIGINAL_FILE, payload);
}
return resultFile;
}
private File writeMessageToFile(Message<?> requestMessage, File originalFileFromHeader, File tempFile,
File resultFile, Object timestamp) throws IOException {
File fileToReturn = null;
Object payload = requestMessage.getPayload();
if (payload instanceof File) {
fileToReturn = handleFileMessage((File) payload, tempFile, resultFile, requestMessage);
}
else if (payload instanceof InputStream) {
fileToReturn = handleInputStreamMessage((InputStream) payload, originalFileFromHeader, tempFile,
resultFile, requestMessage);
}
else if (payload instanceof byte[]) {
fileToReturn = handleByteArrayMessage((byte[]) payload, originalFileFromHeader, tempFile, resultFile,
requestMessage);
}
else if (payload instanceof String) {
fileToReturn = handleStringMessage((String) payload, originalFileFromHeader, tempFile, resultFile,
requestMessage);
}
else {
throw new IllegalArgumentException(
"Unsupported Message payload type [" + payload.getClass().getName() + "]");
}
if (this.preserveTimestamp) {
if (timestamp instanceof Number) {
if (!fileToReturn.setLastModified(((Number) timestamp).longValue())) {
throw new IllegalStateException("Could not set last modified '" + timestamp
+ "' timestamp on file: " + fileToReturn);
}
}
else if (this.logger.isWarnEnabled()) {
this.logger.warn("Could not set lastModified, header " + FileHeaders.SET_MODIFIED
+ " must be a Number, not " + (timestamp == null ? "null" : timestamp.getClass()));
}
}
return fileToReturn;
}
/**
* Retrieves the File instance from the {@link FileHeaders#ORIGINAL_FILE}
* header if available. If the value is not a File instance or a String
@@ -654,39 +639,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
FileWritingMessageHandler.this.newFileCallback.accept(fileToWriteTo, requestMessage);
}
FileState state = getFileState(fileToWriteTo, false);
BufferedOutputStream bos = null;
try {
bos = state != null ? state.stream : createOutputStream(fileToWriteTo, true);
byte[] buffer = new byte[StreamUtils.BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = sourceFileInputStream.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
if (FileWritingMessageHandler.this.appendNewLine) {
bos.write(System.lineSeparator().getBytes());
}
}
finally {
try {
sourceFileInputStream.close();
}
catch (IOException ex) {
}
try {
if (state == null || FileWritingMessageHandler.this.flushTask == null) {
if (bos != null) {
bos.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
appendStreamToFile(fileToWriteTo, sourceFileInputStream);
}
};
@@ -696,38 +649,56 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
}
else {
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(tempFile), this.bufferSize);
try (InputStream inputStream = sourceFileInputStream;
OutputStream outputStream =
new BufferedOutputStream(new FileOutputStream(tempFile), this.bufferSize)) {
byte[] buffer = new byte[StreamUtils.BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = sourceFileInputStream.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
if (this.appendNewLine) {
bos.write(System.lineSeparator().getBytes());
}
bos.flush();
}
finally {
try {
sourceFileInputStream.close();
}
catch (IOException ex) {
}
try {
if (bos != null) {
bos.close();
}
}
catch (IOException ex) {
outputStream.write(System.lineSeparator().getBytes());
}
outputStream.flush();
}
cleanUpAfterCopy(tempFile, resultFile, originalFile);
return resultFile;
}
}
private void appendStreamToFile(File fileToWriteTo, InputStream sourceFileInputStream) throws IOException {
FileState state = getFileState(fileToWriteTo, false);
BufferedOutputStream bos = null;
try (InputStream inputStream = sourceFileInputStream) {
bos = state != null ? state.stream : createOutputStream(fileToWriteTo, true);
byte[] buffer = new byte[StreamUtils.BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
if (FileWritingMessageHandler.this.appendNewLine) {
bos.write(System.lineSeparator().getBytes());
}
}
finally {
try {
if (state == null || FileWritingMessageHandler.this.flushTask == null) {
if (bos != null) {
bos.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
}
private File handleByteArrayMessage(byte[] bytes, File originalFile, File tempFile, File resultFile,
Message<?> requestMessage) throws IOException {
@@ -745,38 +716,42 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
FileWritingMessageHandler.this.newFileCallback.accept(fileToWriteTo, requestMessage);
}
FileState state = getFileState(fileToWriteTo, false);
BufferedOutputStream bos = null;
try {
bos = state != null ? state.stream : createOutputStream(fileToWriteTo, append);
bos.write(bytes);
if (FileWritingMessageHandler.this.appendNewLine) {
bos.write(System.lineSeparator().getBytes());
}
}
finally {
try {
if (state == null || FileWritingMessageHandler.this.flushTask == null) {
if (bos != null) {
bos.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
writeBytesToFile(fileToWriteTo, append, bytes);
}
};
whileLockedProcessor.doWhileLocked();
this.cleanUpAfterCopy(fileToWriteTo, resultFile, originalFile);
cleanUpAfterCopy(fileToWriteTo, resultFile, originalFile);
return resultFile;
}
private void writeBytesToFile(File fileToWriteTo, boolean append, byte[] bytes) throws IOException {
FileState state = getFileState(fileToWriteTo, false);
BufferedOutputStream bos = null;
try {
bos = state != null ? state.stream : createOutputStream(fileToWriteTo, append);
bos.write(bytes);
if (this.appendNewLine) {
bos.write(System.lineSeparator().getBytes());
}
}
finally {
try {
if (state == null || this.flushTask == null) {
if (bos != null) {
bos.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
}
private File handleStringMessage(String content, File originalFile, File tempFile, File resultFile,
Message<?> requestMessage) throws IOException {
@@ -794,40 +769,43 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
FileWritingMessageHandler.this.newFileCallback.accept(fileToWriteTo, requestMessage);
}
FileState state = getFileState(fileToWriteTo, true);
BufferedWriter writer = null;
try {
writer = state != null ? state.writer : createWriter(fileToWriteTo, append);
writer.write(content);
if (FileWritingMessageHandler.this.appendNewLine) {
writer.newLine();
}
}
finally {
try {
if (state == null || FileWritingMessageHandler.this.flushTask == null) {
if (writer != null) {
writer.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
writeStringToFile(fileToWriteTo, append, content);
}
};
whileLockedProcessor.doWhileLocked();
this.cleanUpAfterCopy(fileToWriteTo, resultFile, originalFile);
cleanUpAfterCopy(fileToWriteTo, resultFile, originalFile);
return resultFile;
}
private void writeStringToFile(File fileToWriteTo, boolean append, String content) throws IOException {
FileState state = getFileState(fileToWriteTo, true);
BufferedWriter writer = null;
try {
writer = state != null ? state.writer : createWriter(fileToWriteTo, append);
writer.write(content);
if (FileWritingMessageHandler.this.appendNewLine) {
writer.newLine();
}
}
finally {
try {
if (state == null || FileWritingMessageHandler.this.flushTask == null) {
if (writer != null) {
writer.close();
}
clearState(fileToWriteTo, state);
}
else {
state.lastWrite = System.currentTimeMillis();
}
}
catch (IOException ex) {
}
}
}
private File determineFileToWrite(File resultFile, File tempFile) {
final File fileToWriteTo;
@@ -856,8 +834,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
this.renameTo(fileToWriteTo, resultFile);
}
if (this.deleteSourceFiles && originalFile != null) {
originalFile.delete();
if (this.deleteSourceFiles && originalFile != null && !originalFile.delete()) {
throw new IllegalStateException("Could not delete original file: " + originalFile);
}
setPermissions(resultFile);
@@ -909,7 +887,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
if (appendNoFlush) {
String absolutePath = fileToWriteTo.getAbsolutePath();
state = this.fileStates.get(absolutePath);
if (state != null && ((isString && state.stream != null) || (!isString && state.writer != null))) {
if (state != null // NOSONAR
&& ((isString && state.stream != null) || (!isString && state.writer != null))) {
state.close();
state = null;
this.fileStates.remove(absolutePath);
@@ -1101,6 +1080,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
this.lock.unlock();
}
}
}
private final class Flusher implements Runnable {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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.
@@ -46,7 +46,6 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, false,
getTemplateClass());
@@ -66,8 +65,9 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "command-options", "options");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
this.configureFilter(builder, element, parserContext, "filter", "filename", "filter");
this.configureFilter(builder, element, parserContext, "mput-filter", "mput", "mputFilter");
configureFilter(builder, element, parserContext, FileParserUtils.FILTER_ATTRIBUTE, "filename",
FileParserUtils.FILTER_ATTRIBUTE);
configureFilter(builder, element, parserContext, "mput-filter", "mput", "mputFilter");
BeanDefinition localDirExpressionDef = IntegrationNamespaceUtils
.createExpressionDefinitionFromValueOrExpression("local-directory", "local-directory-expression",
@@ -92,6 +92,7 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext,
String filterAttribute, String patternPrefix, String propertyName) {
String filter = element.getAttribute(filterAttribute);
String filterExpression = element.getAttribute(filterAttribute + "-expression");
String fileNamePattern = element.getAttribute(patternPrefix + "-pattern");
@@ -107,38 +108,57 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
if (count > 1) {
parserContext.getReaderContext()
.error("at most one of '" + patternPrefix + "-pattern', " +
"'" + patternPrefix + "-regex', '" + filterAttribute +
"' or '" + filterAttribute + "-expression' is allowed on a remote file outbound gateway",
"'" + patternPrefix + "-regex', '" + filterAttribute +
"' or '" + filterAttribute + "-expression' is allowed on a remote file outbound " +
"gateway",
element);
}
else if (hasFilter) {
builder.addPropertyReference(propertyName, filter);
}
else if (hasFilterExpression) {
BeanDefinition expressionFilterBeanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFileListFilter.class)
.addConstructorArgValue(filterExpression)
.getBeanDefinition();
builder.addPropertyValue(propertyName, expressionFilterBeanDefinition);
registerExpressionFilter(builder, propertyName, filterExpression);
}
else if (hasFileNamePattern) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"filter".equals(filterAttribute) ?
this.getSimplePatternFileListFilterClassName() :
SimplePatternFileListFilter.class.getName());
filterBuilder.addConstructorArgValue(fileNamePattern);
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
registerPatternFilter(builder, filterAttribute, propertyName, fileNamePattern);
}
else if (hasFileNameRegex) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"filter".equals(filterAttribute) ?
this.getRegexPatternFileListFilterClassName() :
RegexPatternFileListFilter.class.getName());
filterBuilder.addConstructorArgValue(fileNameRegex);
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
registerRegexFilter(builder, filterAttribute, propertyName, fileNameRegex);
}
}
private void registerRegexFilter(BeanDefinitionBuilder builder, String filterAttribute, String propertyName,
String fileNameRegex) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
FileParserUtils.FILTER_ATTRIBUTE.equals(filterAttribute) ?
getRegexPatternFileListFilterClassName() :
RegexPatternFileListFilter.class.getName());
filterBuilder.addConstructorArgValue(fileNameRegex);
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
}
private void registerPatternFilter(BeanDefinitionBuilder builder, String filterAttribute, String propertyName,
String fileNamePattern) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
FileParserUtils.FILTER_ATTRIBUTE.equals(filterAttribute) ?
getSimplePatternFileListFilterClassName() :
SimplePatternFileListFilter.class.getName());
filterBuilder.addConstructorArgValue(fileNamePattern);
builder.addPropertyValue(propertyName, filterBuilder.getBeanDefinition());
}
private void registerExpressionFilter(BeanDefinitionBuilder builder, String propertyName,
String filterExpression) {
BeanDefinition expressionFilterBeanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFileListFilter.class)
.addConstructorArgValue(filterExpression)
.getBeanDefinition();
builder.addPropertyValue(propertyName, expressionFilterBeanDefinition);
}
protected abstract String getRegexPatternFileListFilterClassName();
protected abstract String getSimplePatternFileListFilterClassName();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -56,7 +56,7 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
String filterBeanName = this.registerFilter(element, parserContext);
String lockerBeanName = registerLocker(element, parserContext);
if (filterBeanName != null) {
builder.addPropertyReference("filter", filterBeanName);
builder.addPropertyReference(FileParserUtils.FILTER_ATTRIBUTE, filterBeanName);
}
if (lockerBeanName != null) {
builder.addPropertyReference("locker", lockerBeanName);
@@ -83,14 +83,14 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
return lockerBeanName;
}
private String registerFilter(Element element, ParserContext parserContext) {
private String registerFilter(Element element, ParserContext parserContext) { // NOSONAR
String filenamePattern = element.getAttribute("filename-pattern");
String filenameRegex = element.getAttribute("filename-regex");
String preventDuplicates = element.getAttribute("prevent-duplicates");
String ignoreHidden = element.getAttribute("ignore-hidden");
String filter = element.getAttribute("filter");
String filter = element.getAttribute(FileParserUtils.FILTER_ATTRIBUTE);
String filterExpression = element.getAttribute("filter-expression");
if (!StringUtils.hasText(filter)
if (!StringUtils.hasText(filter) // NOSONAR
&& !StringUtils.hasText(filenamePattern)
&& !StringUtils.hasText(filenameRegex)
&& !StringUtils.hasText(preventDuplicates)
@@ -102,7 +102,7 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
BeanDefinitionBuilder.genericBeanDefinition(FileListFilterFactoryBean.class);
factoryBeanBuilder.setRole(BeanDefinition.ROLE_SUPPORT);
if (StringUtils.hasText(filter)) {
factoryBeanBuilder.addPropertyReference("filter", filter);
factoryBeanBuilder.addPropertyReference(FileParserUtils.FILTER_ATTRIBUTE, filter);
}
if (StringUtils.hasText(filterExpression)) {
if (StringUtils.hasText(filter)) {
@@ -113,7 +113,7 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFileListFilter.class)
.addConstructorArgValue(filterExpression)
.getBeanDefinition();
factoryBeanBuilder.addPropertyValue("filter", expressionFilterBeanDefinition);
factoryBeanBuilder.addPropertyValue(FileParserUtils.FILTER_ATTRIBUTE, expressionFilterBeanDefinition);
}
if (StringUtils.hasText(filenamePattern)) {
if (StringUtils.hasText(filter)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -44,6 +44,8 @@ import org.springframework.util.StringUtils;
*/
public final class FileParserUtils {
public static final String FILTER_ATTRIBUTE = "filter";
private FileParserUtils() {
}
@@ -97,10 +99,12 @@ public final class FileParserUtils {
return templateBuilder.getBeanDefinition();
}
static void configureFilter(BeanDefinitionBuilder synchronizerBuilder, Element element, ParserContext parserContext,
Class<? extends FileListFilter<?>> patternClass, Class<? extends FileListFilter<?>> regexClass,
static void configureFilter(BeanDefinitionBuilder synchronizerBuilder, Element element, // NOSONAR
ParserContext parserContext, Class<? extends FileListFilter<?>> patternClass,
Class<? extends FileListFilter<?>> regexClass,
Class<? extends AbstractPersistentAcceptOnceFileListFilter<?>> persistentAcceptOnceFileListFilterClass) {
String filter = element.getAttribute("filter");
String filter = element.getAttribute(FILTER_ATTRIBUTE);
String filterExpression = element.getAttribute("filter-expression");
String fileNamePattern = element.getAttribute("filename-pattern");
String fileNameRegex = element.getAttribute("filename-regex");
@@ -128,14 +132,14 @@ public final class FileParserUtils {
element);
}
if (hasFilter) {
synchronizerBuilder.addPropertyReference("filter", filter);
synchronizerBuilder.addPropertyReference(FILTER_ATTRIBUTE, filter);
}
else if (hasFilterExpression) {
BeanDefinition expressionFilterBeanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFileListFilter.class)
.addConstructorArgValue(filterExpression)
.getBeanDefinition();
synchronizerBuilder.addPropertyValue("filter", expressionFilterBeanDefinition);
synchronizerBuilder.addPropertyValue(FILTER_ATTRIBUTE, expressionFilterBeanDefinition);
}
else if (hasFileNamePattern) {
BeanDefinition patternFilter =
@@ -175,7 +179,7 @@ public final class FileParserUtils {
BeanDefinitionBuilder.genericBeanDefinition(CompositeFileListFilter.class)
.addConstructorArgValue(filters)
.getBeanDefinition();
synchronizerBuilder.addPropertyValue("filter", compositeFilterDefinition);
synchronizerBuilder.addPropertyValue(FILTER_ATTRIBUTE, compositeFilterDefinition);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -19,12 +19,7 @@ package org.springframework.integration.file.config;
import java.io.File;
import java.util.Comparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.integration.file.DirectoryScanner;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.filters.CompositeFileListFilter;
@@ -39,36 +34,29 @@ import org.springframework.integration.file.locking.AbstractFileLockerFilter;
*
* @since 1.0.3
*/
public class FileReadingMessageSourceFactoryBean implements FactoryBean<FileReadingMessageSource>,
BeanFactoryAware {
public class FileReadingMessageSourceFactoryBean extends AbstractFactoryBean<FileReadingMessageSource> {
private static Log logger = LogFactory.getLog(FileReadingMessageSourceFactoryBean.class);
private FileReadingMessageSource source;
private volatile FileReadingMessageSource source;
private File directory;
private volatile File directory;
private FileListFilter<File> filter;
private volatile FileListFilter<File> filter;
private AbstractFileLockerFilter locker;
private volatile AbstractFileLockerFilter locker;
private Comparator<File> comparator;
private volatile Comparator<File> comparator;
private volatile DirectoryScanner scanner;
private DirectoryScanner scanner;
private boolean useWatchService;
private FileReadingMessageSource.WatchEventType[] watchEvents;
private volatile Boolean scanEachPoll;
private Boolean scanEachPoll;
private volatile Boolean autoCreateDirectory;
private Boolean autoCreateDirectory;
private volatile Integer queueSize;
private volatile BeanFactory beanFactory;
private final Object initializationMonitor = new Object();
private Integer queueSize;
public void setDirectory(File directory) {
this.directory = directory;
@@ -114,87 +102,83 @@ public class FileReadingMessageSourceFactoryBean implements FactoryBean<FileRead
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
public Class<?> getObjectType() {
return FileReadingMessageSource.class;
}
@Override
public FileReadingMessageSource getObject() throws Exception {
protected FileReadingMessageSource createInstance() {
if (this.source == null) {
initSource();
}
return this.source;
}
@Override
public Class<?> getObjectType() {
return FileReadingMessageSource.class;
}
@Override
public boolean isSingleton() {
return true;
}
private void initSource() throws Exception {
synchronized (this.initializationMonitor) {
if (this.source != null) {
return;
private void initSource() { // NOSONAR
if (this.source != null) {
return;
}
boolean comparatorSet = this.comparator != null;
boolean queueSizeSet = this.queueSize != null;
if (comparatorSet) {
if (queueSizeSet) {
logger.warn("'comparator' and 'queueSize' are mutually exclusive. Ignoring 'queueSize'");
}
boolean comparatorSet = this.comparator != null;
boolean queueSizeSet = this.queueSize != null;
if (comparatorSet) {
if (queueSizeSet) {
logger.warn("'comparator' and 'queueSize' are mutually exclusive. Ignoring 'queueSize'");
}
this.source = new FileReadingMessageSource(this.comparator);
this.source = new FileReadingMessageSource(this.comparator);
}
else if (queueSizeSet) {
this.source = new FileReadingMessageSource(this.queueSize);
}
else {
this.source = new FileReadingMessageSource();
}
this.source.setDirectory(this.directory);
if (this.scanner != null) {
this.source.setScanner(this.scanner);
}
else {
this.source.setUseWatchService(this.useWatchService);
if (this.watchEvents != null) {
this.source.setWatchEvents(this.watchEvents);
}
else if (queueSizeSet) {
this.source = new FileReadingMessageSource(this.queueSize);
}
if (this.filter != null) {
if (this.locker == null) {
this.source.setFilter(this.filter);
}
else {
this.source = new FileReadingMessageSource();
}
this.source.setDirectory(this.directory);
if (this.scanner != null) {
this.source.setScanner(this.scanner);
}
else {
this.source.setUseWatchService(this.useWatchService);
if (this.watchEvents != null) {
this.source.setWatchEvents(this.watchEvents);
}
}
if (this.filter != null) {
if (this.locker == null) {
this.source.setFilter(this.filter);
}
else {
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(this.filter);
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(this.locker);
}
}
else if (this.locker != null) {
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(new FileListFilterFactoryBean().getObject());
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<>();
compositeFileListFilter.addFilter(this.filter);
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(this.locker);
}
if (this.scanEachPoll != null) {
this.source.setScanEachPoll(this.scanEachPoll);
}
else if (this.locker != null) {
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<>();
try {
compositeFileListFilter.addFilter(new FileListFilterFactoryBean().getObject());
}
if (this.autoCreateDirectory != null) {
this.source.setAutoCreateDirectory(this.autoCreateDirectory);
}
if (this.beanFactory != null) {
this.source.setBeanFactory(this.beanFactory);
catch (Exception e) {
throw new IllegalStateException(e);
}
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(this.locker);
}
if (this.scanEachPoll != null) {
this.source.setScanEachPoll(this.scanEachPoll);
}
if (this.autoCreateDirectory != null) {
this.source.setAutoCreateDirectory(this.autoCreateDirectory);
}
this.source.setBeanFactory(getBeanFactory());
try {
this.source.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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 java.io.IOException;
import java.util.List;
import org.springframework.integration.metadata.ConcurrentMetadataStore;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -31,19 +32,22 @@ import org.springframework.util.Assert;
* same modified time as the current file.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 3.0
*
*/
public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends AbstractFileListFilter<F>
implements ReversibleFileListFilter<F>, ResettableFileListFilter<F>, Closeable {
protected final ConcurrentMetadataStore store;
protected final ConcurrentMetadataStore store; // NOSONAR
protected final Flushable flushableStore;
@Nullable
protected final Flushable flushableStore; // NOSONAR
protected final String prefix;
protected final String prefix; // NOSONAR
protected volatile boolean flushOnUpdate;
protected volatile boolean flushOnUpdate; // NOSONAR
private final Object monitor = new Object();
@@ -136,7 +140,7 @@ public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends Abst
* @return The value to store for the file.
*/
private String value(F file) {
return Long.toString(this.modified(file));
return Long.toString(modified(file));
}
/**
@@ -147,7 +151,7 @@ public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends Abst
* @return true if equal.
*/
protected boolean isEqual(F file, String value) {
return Long.valueOf(value) == this.modified(file);
return Long.valueOf(value) == modified(file);
}
/**
@@ -156,7 +160,7 @@ public abstract class AbstractPersistentAcceptOnceFileListFilter<F> extends Abst
* @return The key.
*/
protected String buildKey(F file) {
return this.prefix + this.fileName(file);
return this.prefix + fileName(file);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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.
@@ -25,37 +25,47 @@ import org.springframework.util.Assert;
* Filters a listing of files by qualifying their 'name'
* against a regular expression (an instance of {@link java.util.regex.Pattern})
*
* @param <F> the type of file entry
*
* @author Iwein Fuld
* @author Josh Long
* @param <F> the type of file entry
* @author Artem Bilan
*
* @since 2.0
*/
public abstract class AbstractRegexPatternFileListFilter<F> extends AbstractDirectoryAwareFileListFilter<F>
implements InitializingBean {
implements InitializingBean { // TODO Remove in the next version
private volatile Pattern pattern;
private Pattern pattern;
public AbstractRegexPatternFileListFilter(String pattern) {
this.pattern = Pattern.compile(pattern);
this(Pattern.compile(pattern));
}
public AbstractRegexPatternFileListFilter(Pattern pattern) {
Assert.notNull(pattern, "'pattern' must not be null!");
this.pattern = pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public void setPattern(String pattern) {
this.pattern = Pattern.compile(pattern);
Assert.notNull(pattern, "'pattern' must not be null!");
setPattern(Pattern.compile(pattern));
}
public void setPattern(Pattern pattern) {
Assert.notNull(pattern, "'pattern' must not be null!");
this.pattern = pattern;
}
/**
* @deprecated since 5.1.3. Will be removed in the next 5.2 version.
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.pattern, "'pattern' must not be null!");
@Deprecated
public void afterPropertiesSet() {
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2019 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,8 @@ import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import org.springframework.lang.Nullable;
/**
* {@link FileListFilter} that passes files only one time. This can
* conveniently be used to prevent duplication of files, as is done in
@@ -32,11 +34,12 @@ import java.util.concurrent.LinkedBlockingQueue;
* @author Iwein Fuld
* @author Josh Long
* @author Gary Russell
* @since 1.0.0
* @author Artem Bilan
*/
public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> implements ReversibleFileListFilter<F>,
ResettableFileListFilter<F> {
@Nullable
private final Queue<F> seen;
private final Set<F> seenSet = new HashSet<F>();
@@ -48,7 +51,6 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
* Creates an AcceptOnceFileListFilter that is based on a bounded queue. If the queue overflows,
* files that fall out will be passed through this filter again if passed to the
* {@link #filterFiles(Object[])}
*
* @param maxCapacity the maximum number of Files to maintain in the 'seen' queue.
*/
public AcceptOnceFileListFilter(int maxCapacity) {
@@ -69,22 +71,16 @@ public class AcceptOnceFileListFilter<F> extends AbstractFileListFilter<F> imple
if (this.seenSet.contains(file)) {
return false;
}
if (this.seen != null) {
if (!this.seen.offer(file)) {
F removed = this.seen.poll();
this.seenSet.remove(removed);
this.seen.add(file);
}
if (this.seen != null && !this.seen.offer(file)) {
F removed = this.seen.poll();
this.seenSet.remove(removed);
this.seen.add(file);
}
this.seenSet.add(file);
return true;
}
}
/**
* {@inheritDoc}
* @since 4.0.4
*/
@Override
public void rollback(F file, List<F> files) {
synchronized (this.monitor) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -104,7 +104,7 @@ public class CompositeFileListFilter<F>
((InitializingBean) elf).afterPropertiesSet();
}
catch (Exception e) {
throw new RuntimeException(e);
throw new IllegalStateException(e);
}
}
}
@@ -127,7 +127,7 @@ public class CompositeFileListFilter<F>
@Override
public List<F> filterFiles(F[] files) {
Assert.notNull(files, "'files' should not be null");
List<F> results = new ArrayList<F>(Arrays.asList(files));
List<F> results = new ArrayList<>(Arrays.asList(files));
for (FileListFilter<F> fileFilter : this.fileFilters) {
List<F> currentResults = fileFilter.filterFiles(files);
results.retainAll(currentResults);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018 the original author or authors.
* Copyright 2018-2019 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.
@@ -18,6 +18,8 @@ package org.springframework.integration.file.filters;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
/**
* The {@link FileListFilter} modification which can accept a {@link Consumer}
* which can be called when filter discards the file.
@@ -28,6 +30,6 @@ import java.util.function.Consumer;
*/
public interface DiscardAwareFileListFilter<F> extends FileListFilter<F> {
void addDiscardCallback(Consumer<F> discardCallback);
void addDiscardCallback(@Nullable Consumer<F> discardCallback);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2018 the original author or authors.
* Copyright 2015-2019 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.
@@ -17,11 +17,14 @@
package org.springframework.integration.file.filters;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
/**
* The {@link FileListFilter} implementation to filter those files which
* {@link File#lastModified()} is less than the {@link #age} in comparison
@@ -40,10 +43,13 @@ import java.util.function.Consumer;
*/
public class LastModifiedFileListFilter implements DiscardAwareFileListFilter<File> {
private static final long ONE_SECOND = 1000;
private static final long DEFAULT_AGE = 60;
private volatile long age = DEFAULT_AGE;
@Nullable
private Consumer<File> discardCallback;
public LastModifiedFileListFilter() {
@@ -59,6 +65,30 @@ public class LastModifiedFileListFilter implements DiscardAwareFileListFilter<Fi
this.age = age;
}
/**
* Set the age that files have to be before being passed by this filter.
* If {@link File#lastModified()} plus age is greater than the current time, the file
* is filtered. The resolution is seconds.
* Defaults to 60 seconds.
* @param age the age
* @param unit the timeUnit.
*/
public void setAge(long age, TimeUnit unit) {
this.age = unit.toSeconds(age);
}
/**
* Set the age that files have to be before being passed by this filter.
* If {@link File#lastModified()} plus age is greater than the current time, the file
* is filtered. The resolution is seconds.
* Defaults to 60 seconds.
* @param age the age
* @since 5.1.3
*/
public void setAge(Duration age) {
setAge(age.getSeconds());
}
/**
* Set the age that files have to be before being passed by this filter.
* If {@link File#lastModified()} plus age is greater than the current time, the file
@@ -74,29 +104,17 @@ public class LastModifiedFileListFilter implements DiscardAwareFileListFilter<Fi
return this.age;
}
/**
* Set the age that files have to be before being passed by this filter.
* If {@link File#lastModified()} plus age is greater than the current time, the file
* is filtered. The resolution is seconds.
* Defaults to 60 seconds.
* @param age the age
* @param unit the timeUnit.
*/
public void setAge(long age, TimeUnit unit) {
this.age = unit.toSeconds(age);
}
@Override
public void addDiscardCallback(Consumer<File> discardCallback) {
public void addDiscardCallback(@Nullable Consumer<File> discardCallback) {
this.discardCallback = discardCallback;
}
@Override
public List<File> filterFiles(File[] files) {
List<File> list = new ArrayList<>();
long now = System.currentTimeMillis() / 1000;
long now = System.currentTimeMillis() / ONE_SECOND;
for (File file : files) {
if (file.lastModified() / 1000 + this.age <= now) {
if (file.lastModified() / ONE_SECOND + this.age <= now) {
list.add(file);
}
else if (this.discardCallback != null) {

View File

@@ -67,7 +67,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
/**
* the {@link SessionFactory} for acquiring remote file Sessions.
*/
protected final SessionFactory<F> sessionFactory;
protected final SessionFactory<F> sessionFactory; // NOSONAR
/*
* Not static as normal since we want this TL to be scoped within the template instance.
@@ -295,48 +295,11 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
final StreamHolder inputStreamHolder = payloadToInputStream(message);
if (inputStreamHolder != null) {
try {
return this.execute(session -> {
String fileName = inputStreamHolder.getName();
try {
String remoteDirectory = RemoteFileTemplate.this.directoryExpressionProcessor
.processMessage(message);
remoteDirectory = RemoteFileTemplate.this.normalizeDirectoryPath(remoteDirectory);
if (StringUtils.hasText(subDirectory)) {
if (subDirectory.startsWith(RemoteFileTemplate.this.remoteFileSeparator)) {
remoteDirectory += subDirectory.substring(1);
}
else {
remoteDirectory += RemoteFileTemplate.this.normalizeDirectoryPath(subDirectory);
}
}
String temporaryRemoteDirectory = remoteDirectory;
if (RemoteFileTemplate.this.temporaryDirectoryExpressionProcessor != null) {
temporaryRemoteDirectory = RemoteFileTemplate.this.temporaryDirectoryExpressionProcessor
.processMessage(message);
}
fileName = RemoteFileTemplate.this.fileNameGenerator.generateFileName(message);
RemoteFileTemplate.this.sendFileToRemoteDirectory(inputStreamHolder.getStream(),
temporaryRemoteDirectory, remoteDirectory, fileName, session, mode);
return remoteDirectory + fileName;
}
catch (FileNotFoundException e) {
throw new MessageDeliveryException(message, "File [" + inputStreamHolder.getName()
+ "] not found in local working directory; it was moved or deleted unexpectedly.", e);
}
catch (IOException e) {
throw new MessageDeliveryException(message, "Failed to transfer file ["
+ inputStreamHolder.getName() + " -> " + fileName
+ "] from local directory to remote directory.", e);
}
catch (Exception e) {
throw new MessageDeliveryException(message, "Error handling message for file ["
+ inputStreamHolder.getName() + " -> " + fileName + "]", e);
}
});
return execute(session -> doSend(message, subDirectory, mode, inputStreamHolder, session));
}
finally {
try {
inputStreamHolder.getStream().close();
inputStreamHolder.stream.close();
}
catch (IOException e) {
}
@@ -351,6 +314,45 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
}
}
private String doSend(Message<?> message, String subDirectory, FileExistsMode mode,
StreamHolder inputStreamHolder, Session<F> session) {
String fileName = inputStreamHolder.name;
try {
String remoteDirectory = this.directoryExpressionProcessor.processMessage(message);
remoteDirectory = normalizeDirectoryPath(remoteDirectory);
if (StringUtils.hasText(subDirectory)) {
if (subDirectory.startsWith(this.remoteFileSeparator)) {
remoteDirectory += subDirectory.substring(1);
}
else {
remoteDirectory += normalizeDirectoryPath(subDirectory);
}
}
String temporaryRemoteDirectory = remoteDirectory;
if (this.temporaryDirectoryExpressionProcessor != null) {
temporaryRemoteDirectory = this.temporaryDirectoryExpressionProcessor.processMessage(message);
}
fileName = this.fileNameGenerator.generateFileName(message);
sendFileToRemoteDirectory(inputStreamHolder.stream, temporaryRemoteDirectory, remoteDirectory, fileName,
session, mode);
return remoteDirectory + fileName;
}
catch (FileNotFoundException e) {
throw new MessageDeliveryException(message, "File [" + inputStreamHolder.name
+ "] not found in local working directory; it was moved or deleted unexpectedly.", e);
}
catch (IOException e) {
throw new MessageDeliveryException(message, "Failed to transfer file ["
+ inputStreamHolder.name + " -> " + fileName
+ "] from local directory to remote directory.", e);
}
catch (Exception e) {
throw new MessageDeliveryException(message, "Error handling message for file ["
+ inputStreamHolder.name + " -> " + fileName + "]", e);
}
}
@Override
public boolean exists(final String path) {
return execute(session -> session.exists(path));
@@ -391,17 +393,10 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
public boolean get(final String remotePath, final InputStreamCallback callback) {
Assert.notNull(remotePath, "'remotePath' cannot be null");
return execute(session -> {
InputStream inputStream = null;
try {
inputStream = session.readRaw(remotePath);
try (InputStream inputStream = session.readRaw(remotePath)) {
callback.doWithInputStream(inputStream);
return session.finalizeRaw();
}
finally {
if (inputStream != null) {
inputStream.close();
}
}
});
}
@@ -456,9 +451,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
session.close();
}
catch (Exception ignored) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("failed to close Session", ignored);
}
this.logger.debug("failed to close Session", ignored);
}
}
}
@@ -557,13 +550,13 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
}
}
try {
try (InputStream stream = inputStream) {
boolean rename = this.useTemporaryFileName;
if (FileExistsMode.REPLACE.equals(mode)) {
session.write(inputStream, tempFilePath);
session.write(stream, tempFilePath);
}
else if (FileExistsMode.APPEND.equals(mode)) {
session.append(inputStream, tempFilePath);
session.append(stream, tempFilePath);
}
else {
if (exists(remoteFilePath)) {
@@ -579,7 +572,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
rename = false;
}
else {
session.write(inputStream, tempFilePath);
session.write(stream, tempFilePath);
}
}
// then rename it to its final name if necessary
@@ -590,9 +583,6 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
catch (Exception e) {
throw new MessagingException("Failed to write to '" + tempFilePath + "' while uploading the file", e);
}
finally {
inputStream.close();
}
}
private String normalizeDirectoryPath(String directoryPath) {
@@ -613,19 +603,11 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
private final String name;
private StreamHolder(InputStream stream, String name) {
StreamHolder(InputStream stream, String name) {
this.stream = stream;
this.name = name;
}
public InputStream getStream() {
return this.stream;
}
public String getName() {
return this.name;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -34,12 +34,13 @@ import org.springframework.util.Assert;
* @author Oleg Zhurakousky
* @author David Turanski
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
protected final RemoteFileTemplate<F> remoteFileTemplate;
protected final RemoteFileTemplate<F> remoteFileTemplate; // NOSONAR
private final FileExistsMode mode;
@@ -175,7 +176,7 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
*/
public void setChmodOctal(String chmod) {
Assert.notNull(chmod, "'chmod' cannot be null");
setChmod(Integer.parseInt(chmod, 8));
setChmod(Integer.parseInt(chmod, 8)); // NOSONAR
}
/**
@@ -200,7 +201,7 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
}
@Override
protected void handleMessageInternal(Message<?> message) throws Exception {
protected void handleMessageInternal(Message<?> message) {
String path = this.remoteFileTemplate.send(message, this.mode);
if (this.chmod != null && isChmodCapable()) {
doChmod(this.remoteFileTemplate, path, this.chmod);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -48,6 +48,7 @@ import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -75,7 +76,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
protected static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
protected final Log logger = LogFactory.getLog(this.getClass());
protected final Log logger = LogFactory.getLog(this.getClass()); // NOSONAR
private final RemoteFileTemplate<F> remoteFileTemplate;
@@ -93,7 +94,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
/**
* the path on the remote mount as a String.
*/
private volatile Expression remoteDirectoryExpression;
private Expression remoteDirectoryExpression;
/**
* The current evaluation of the expression.
@@ -103,6 +104,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
/**
* An {@link FileListFilter} that runs against the <em>remote</em> file system view.
*/
@Nullable
private FileListFilter<F> filter;
/**
@@ -119,6 +121,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
private BeanFactory beanFactory;
@Nullable
private Comparator<F> comparator;
/**
@@ -131,7 +134,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
}
@Nullable
protected Comparator<F> getComparator() {
return this.comparator;
}
@@ -142,7 +145,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
* @param comparator the comparator.
* @since 5.1
*/
public void setComparator(Comparator<F> comparator) {
public void setComparator(@Nullable Comparator<F> comparator) {
this.comparator = comparator;
}
@@ -222,11 +225,11 @@ public abstract class AbstractInboundFileSynchronizer<F>
* Set the filter to be applied to the remote files before transferring.
* @param filter the file list filter.
*/
public void setFilter(FileListFilter<F> filter) {
public void setFilter(@Nullable FileListFilter<F> filter) {
doSetFilter(filter);
}
protected final void doSetFilter(FileListFilter<F> filter) {
protected final void doSetFilter(@Nullable FileListFilter<F> filter) {
this.filter = filter;
}
@@ -302,46 +305,8 @@ public abstract class AbstractInboundFileSynchronizer<F>
this.logger.trace("Synchronizing " + this.evaluatedRemoteDirectory + " to " + localDirectory);
}
try {
int transferred = this.remoteFileTemplate.execute(session -> {
F[] files = session.list(this.evaluatedRemoteDirectory);
if (!ObjectUtils.isEmpty(files)) {
files = FileUtils.purgeUnwantedElements(files, e -> !isFile(e), this.comparator);
}
if (!ObjectUtils.isEmpty(files)) {
List<F> filteredFiles = filterFiles(files);
if (maxFetchSize >= 0 && filteredFiles.size() > maxFetchSize) {
rollbackFromFileToListEnd(filteredFiles, filteredFiles.get(maxFetchSize));
List<F> newList = new ArrayList<>(maxFetchSize);
for (int i = 0; i < maxFetchSize; i++) {
newList.add(filteredFiles.get(i));
}
filteredFiles = newList;
}
int copied = filteredFiles.size();
for (F file : filteredFiles) {
try {
if (file != null && !copyFileToLocalDirectory(this.evaluatedRemoteDirectory, file,
localDirectory, session)) {
copied--;
}
}
catch (RuntimeException e1) {
rollbackFromFileToListEnd(filteredFiles, file);
throw e1;
}
catch (IOException e1) {
rollbackFromFileToListEnd(filteredFiles, file);
throw e1;
}
}
return copied;
}
else {
return 0;
}
});
int transferred = this.remoteFileTemplate.execute(session ->
transferFilesFromRemoteToLocal(localDirectory, maxFetchSize, session));
if (this.logger.isDebugEnabled()) {
this.logger.debug(transferred + " files transferred from '" + this.evaluatedRemoteDirectory + "'");
}
@@ -352,6 +317,45 @@ public abstract class AbstractInboundFileSynchronizer<F>
}
}
private Integer transferFilesFromRemoteToLocal(File localDirectory, int maxFetchSize, Session<F> session)
throws IOException {
F[] files = session.list(this.evaluatedRemoteDirectory);
if (!ObjectUtils.isEmpty(files)) {
files = FileUtils.purgeUnwantedElements(files, e -> !isFile(e), this.comparator);
}
if (!ObjectUtils.isEmpty(files)) {
List<F> filteredFiles = filterFiles(files);
if (maxFetchSize >= 0 && filteredFiles.size() > maxFetchSize) {
rollbackFromFileToListEnd(filteredFiles, filteredFiles.get(maxFetchSize));
List<F> newList = new ArrayList<>(maxFetchSize);
for (int i = 0; i < maxFetchSize; i++) {
newList.add(filteredFiles.get(i));
}
filteredFiles = newList;
}
int copied = filteredFiles.size();
for (F file : filteredFiles) {
try {
if (file != null && !copyFileToLocalDirectory(this.evaluatedRemoteDirectory, file,
localDirectory, session)) {
copied--;
}
}
catch (RuntimeException | IOException e1) {
rollbackFromFileToListEnd(filteredFiles, file);
throw e1;
}
}
return copied;
}
else {
return 0;
}
}
protected void rollbackFromFileToListEnd(List<F> filteredFiles, F file) {
if (this.filter instanceof ReversibleFileListFilter) {
((ReversibleFileListFilter<F>) this.filter)
@@ -359,8 +363,9 @@ public abstract class AbstractInboundFileSynchronizer<F>
}
}
protected boolean copyFileToLocalDirectory(String remoteDirectoryPath, F remoteFile, File localDirectory,
Session<F> session) throws IOException {
protected boolean copyFileToLocalDirectory(String remoteDirectoryPath, F remoteFile, // NOSONAR
File localDirectory, Session<F> session) throws IOException {
String remoteFileName = getFilename(remoteFile);
String localFileName = generateLocalFileName(remoteFileName);
String remoteFilePath = remoteDirectoryPath != null
@@ -398,47 +403,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
boolean renamed = false;
if (transfer) {
String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix;
File tempFile = new File(tempFileName);
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
try {
session.read(remoteFilePath, outputStream);
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
else {
throw new MessagingException("Failure occurred while copying '" + remoteFilePath
+ "' from the remote to the local directory", e);
}
}
finally {
try {
outputStream.close();
}
catch (Exception ignored2) {
}
}
renamed = tempFile.renameTo(localFile);
if (!renamed) {
if (localFile.delete()) {
renamed = tempFile.renameTo(localFile);
if (!renamed && this.logger.isInfoEnabled()) {
this.logger.info("Cannot rename '"
+ tempFileName
+ "' to local file '" + localFile + "' after deleting. " +
"The local file may be busy in some other process.");
}
}
else if (this.logger.isInfoEnabled()) {
this.logger.info("Cannot delete local file '" + localFile +
"'. The local file may be busy in some other process.");
}
}
renamed = copyRemoteContentToLocalFile(session, remoteFilePath, localFile);
}
if (renamed) {
@@ -448,8 +413,8 @@ public abstract class AbstractInboundFileSynchronizer<F>
this.logger.debug("deleted remote file: " + remoteFilePath);
}
}
if (this.preserveTimestamp) {
localFile.setLastModified(modified);
if (this.preserveTimestamp && !localFile.setLastModified(modified)) {
throw new IllegalStateException("Could not sent last modified on file: " + localFile);
}
return true;
}
@@ -469,6 +434,44 @@ public abstract class AbstractInboundFileSynchronizer<F>
return false;
}
private boolean copyRemoteContentToLocalFile(Session<F> session, String remoteFilePath, File localFile) {
boolean renamed;
String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix;
File tempFile = new File(tempFileName);
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(tempFile))) {
session.read(remoteFilePath, outputStream);
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
else {
throw new MessagingException("Failure occurred while copying '" + remoteFilePath
+ "' from the remote to the local directory", e);
}
}
renamed = tempFile.renameTo(localFile);
if (!renamed) {
if (localFile.delete()) {
renamed = tempFile.renameTo(localFile);
if (!renamed && this.logger.isInfoEnabled()) {
this.logger.info("Cannot rename '"
+ tempFileName
+ "' to local file '" + localFile + "' after deleting. " +
"The local file may be busy in some other process.");
}
}
else if (this.logger.isInfoEnabled()) {
this.logger.info("Cannot delete local file '" + localFile +
"'. The local file may be busy in some other process.");
}
}
return renamed;
}
private String generateLocalFileName(String remoteFileName) {
if (this.localFilenameGeneratorExpression != null) {
return this.localFilenameGeneratorExpression.getValue(this.evaluationContext, remoteFileName,

View File

@@ -75,7 +75,7 @@ import org.springframework.util.StringUtils;
*/
public class FileSplitter extends AbstractMessageSplitter {
private static final JsonObjectMapper<?, ?> objectMapper =
private static final JsonObjectMapper<?, ?> OBJECT_MAPPER =
JsonObjectMapperProvider.jsonAvailable() ? JsonObjectMapperProvider.newInstance() : null;
private final boolean returnIterator;
@@ -140,7 +140,7 @@ public class FileSplitter extends AbstractMessageSplitter {
if (markers) {
setApplySequence(false);
if (markersJson) {
Assert.notNull(objectMapper, "'markersJson' requires an object mapper");
Assert.notNull(OBJECT_MAPPER, "'markersJson' requires an object mapper");
}
}
this.markersJson = markersJson;
@@ -170,9 +170,9 @@ public class FileSplitter extends AbstractMessageSplitter {
protected Object splitMessage(final Message<?> message) {
Object payload = message.getPayload();
Reader reader = null;
Reader reader;
final String filePath;
String filePath;
if (payload instanceof String) {
try {
@@ -214,7 +214,39 @@ public class FileSplitter extends AbstractMessageSplitter {
return message;
}
final BufferedReader bufferedReader = new BufferedReader(reader) {
Iterator<Object> iterator = messageToFileIterator(message, reader, filePath);
if (this.returnIterator) {
return iterator;
}
else {
List<Object> lines = new ArrayList<>();
while (iterator.hasNext()) {
lines.add(iterator.next());
}
return lines;
}
}
private Iterator<Object> messageToFileIterator(Message<?> message, Reader reader, String filePath) {
BufferedReader bufferedReader = wrapToBufferedReader(message, reader);
String firstLineAsHeader = null;
if (this.firstLineHeaderName != null) {
try {
firstLineAsHeader = bufferedReader.readLine();
}
catch (IOException e) {
throw new MessageHandlingException(message, "IOException while reading first line", e);
}
}
return new FileIterator(message, bufferedReader, firstLineAsHeader, filePath);
}
private BufferedReader wrapToBufferedReader(Message<?> message, Reader reader) {
return new BufferedReader(reader) {
@Override
public void close() throws IOException {
@@ -230,145 +262,6 @@ public class FileSplitter extends AbstractMessageSplitter {
}
};
String firstLineAsHeader;
if (this.firstLineHeaderName != null) {
try {
firstLineAsHeader = bufferedReader.readLine();
}
catch (IOException e) {
throw new MessageHandlingException(message, "IOException while reading first line", e);
}
}
else {
firstLineAsHeader = null;
}
Iterator<Object> iterator = new CloseableIterator<Object>() {
boolean markers = FileSplitter.this.markers;
boolean sof = this.markers;
boolean eof;
boolean done;
String line;
long lineCount;
boolean hasNextCalled;
@Override
public boolean hasNext() {
this.hasNextCalled = true;
try {
if (!this.done && this.line == null) {
this.line = bufferedReader.readLine();
}
boolean ready = !this.done && this.line != null;
if (!ready) {
if (this.markers) {
this.eof = true;
if (this.sof) {
this.done = true;
}
}
bufferedReader.close();
}
return this.sof || ready || this.eof;
}
catch (IOException e) {
try {
this.done = true;
bufferedReader.close();
}
catch (IOException e1) {
// ignored
}
throw new MessageHandlingException(message, "IOException while iterating", e);
}
}
@Override
public Object next() {
if (!this.hasNextCalled) {
hasNext();
}
this.hasNextCalled = false;
if (this.sof) {
this.sof = false;
return markerToReturn(new FileMarker(filePath, Mark.START, 0));
}
if (this.eof) {
this.eof = false;
this.markers = false;
this.done = true;
return markerToReturn(new FileMarker(filePath, Mark.END, this.lineCount));
}
if (this.line != null) {
String line = this.line;
this.line = null;
this.lineCount++;
AbstractIntegrationMessageBuilder<String> messageBuilder =
getMessageBuilderFactory()
.withPayload(line);
if (firstLineAsHeader != null) {
messageBuilder.setHeader(FileSplitter.this.firstLineHeaderName, firstLineAsHeader);
}
return messageBuilder;
}
else {
this.done = true;
throw new NoSuchElementException(filePath + " has been consumed");
}
}
private AbstractIntegrationMessageBuilder<Object> markerToReturn(FileMarker fileMarker) {
Object payload;
if (FileSplitter.this.markersJson) {
try {
payload = objectMapper.toJson(fileMarker);
}
catch (Exception e) {
throw new MessageHandlingException(message, "Failed to convert marker to JSON", e);
}
}
else {
payload = fileMarker;
}
return getMessageBuilderFactory().withPayload(payload)
.setHeader(FileHeaders.MARKER, fileMarker.mark.name());
}
@Override
public void close() {
try {
this.done = true;
bufferedReader.close();
}
catch (IOException e) {
// ignored
}
}
};
if (this.returnIterator) {
return iterator;
}
else {
List<Object> lines = new ArrayList<Object>();
while (iterator.hasNext()) {
lines.add(iterator.next());
}
return lines;
}
}
@Override
@@ -407,6 +300,141 @@ public class FileSplitter extends AbstractMessageSplitter {
}
}
private class FileIterator implements CloseableIterator<Object> {
private final Message<?> message;
private final BufferedReader bufferedReader;
private final String firstLineAsHeader;
private final String filePath;
private boolean markers = FileSplitter.this.markers;
private boolean sof = this.markers;
private boolean eof;
private boolean done;
private String line;
private long lineCount;
private boolean hasNextCalled;
FileIterator(Message<?> message, BufferedReader bufferedReader, String firstLineAsHeader,
String filePath) {
this.message = message;
this.bufferedReader = bufferedReader;
this.firstLineAsHeader = firstLineAsHeader;
this.filePath = filePath;
}
@Override
public boolean hasNext() {
this.hasNextCalled = true;
try {
return hasNextLine();
}
catch (IOException e) {
try {
this.done = true;
this.bufferedReader.close();
}
catch (IOException e1) {
// ignored
}
throw new MessageHandlingException(this.message, "IOException while iterating", e);
}
}
private boolean hasNextLine() throws IOException {
if (!this.done && this.line == null) {
this.line = this.bufferedReader.readLine();
}
boolean ready = !this.done && this.line != null;
if (!ready) {
if (this.markers) {
this.eof = true;
if (this.sof) {
this.done = true;
}
}
this.bufferedReader.close();
}
return this.sof || ready || this.eof;
}
@Override
public Object next() {
if (!this.hasNextCalled) {
hasNext();
}
this.hasNextCalled = false;
if (this.sof) {
this.sof = false;
return markerToReturn(new FileMarker(this.filePath, Mark.START, 0));
}
if (this.eof) {
this.eof = false;
this.markers = false;
this.done = true;
return markerToReturn(new FileMarker(this.filePath, Mark.END, this.lineCount));
}
if (this.line != null) {
String payload = this.line;
this.line = null;
this.lineCount++;
AbstractIntegrationMessageBuilder<String> messageBuilder =
getMessageBuilderFactory()
.withPayload(payload);
if (this.firstLineAsHeader != null) {
messageBuilder.setHeader(FileSplitter.this.firstLineHeaderName, this.firstLineAsHeader);
}
return messageBuilder;
}
else {
this.done = true;
throw new NoSuchElementException(this.filePath + " has been consumed");
}
}
private AbstractIntegrationMessageBuilder<Object> markerToReturn(FileMarker fileMarker) {
Object payload;
if (FileSplitter.this.markersJson) {
try {
payload = OBJECT_MAPPER.toJson(fileMarker);
}
catch (Exception e) {
throw new MessageHandlingException(this.message, "Failed to convert marker to JSON", e);
}
}
else {
payload = fileMarker;
}
return getMessageBuilderFactory().withPayload(payload)
.setHeader(FileHeaders.MARKER, fileMarker.mark.name());
}
@Override
public void close() {
try {
this.done = true;
this.bufferedReader.close();
}
catch (IOException e) {
// ignored
}
}
}
public static class FileMarker implements Serializable {
private static final long serialVersionUID = 8514605438145748406L;
@@ -451,11 +479,12 @@ public class FileSplitter extends AbstractMessageSplitter {
@Override
public String toString() {
if (this.mark.equals(Mark.START)) {
if (Mark.START.equals(this.mark)) {
return "FileMarker [filePath=" + this.filePath + ", mark=" + this.mark + "]";
}
else {
return "FileMarker [filePath=" + this.filePath + ", mark=" + this.mark + ", lineCount=" + this.lineCount + "]";
return "FileMarker [filePath=" + this.filePath + ", mark=" + this.mark +
", lineCount=" + this.lineCount + "]";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2019 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.
@@ -33,6 +33,7 @@ import org.apache.commons.net.ftp.FTPFile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -49,6 +50,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.1
*
*/
@@ -63,16 +66,19 @@ public class FtpRemoteFileTemplateTests extends FtpTestSupport {
public void testINT3412AppendStatRmdir() throws IOException {
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(sessionFactory);
DefaultFileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
fileNameGenerator.setBeanFactory(mock(BeanFactory.class));
fileNameGenerator.setExpression("'foobar.txt'");
template.setFileNameGenerator(fileNameGenerator);
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
template.setUseTemporaryFileName(false);
template.setBeanFactory(mock(BeanFactory.class));
template.afterPropertiesSet();
template.execute(session -> {
session.mkdir("foo/");
return session.mkdir("foo/bar/");
});
template.append(new GenericMessage<String>("foo"));
template.append(new GenericMessage<String>("bar"));
template.append(new GenericMessage<>("foo"));
template.append(new GenericMessage<>("bar"));
assertTrue(template.exists("foo/foobar.txt"));
template.executeWithClient((ClientCallbackWithoutResult<FTPClient>) client -> {
try {
@@ -106,7 +112,7 @@ public class FtpRemoteFileTemplateTests extends FtpTestSupport {
fileOutputStream.write("foo".getBytes());
fileOutputStream.close();
try {
template.send(new GenericMessage<File>(file));
template.send(new GenericMessage<>(file));
fail("exception expected");
}
catch (MessagingException e) {