GH-3507: Fix Tail producer for proper command

Fixes https://github.com/spring-projects/spring-integration/issues/3507

The `OSDelegatingFileTailingMessageProducer` passing command string to `Runtime.getRuntime().exec()`
may cause problems if spaces (and other special characters) are used in the filename.

* Use an array for command and its options to let the target `Runtime` to parse and
execute it properly

**Cherry-pick to 5.4.x, 5.3.x & 5.2.x**
This commit is contained in:
Trung Pham
2021-03-11 16:10:30 +07:00
committed by Artem Bilan
parent 010ce8b01f
commit f30ede47e2
3 changed files with 101 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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,6 +34,7 @@ import org.springframework.util.Assert;
* @author Gavin Gray
* @author Ali Shahbour
* @author Artem Bilan
* @author Trung Pham
*
* @since 3.0
*
@@ -47,6 +48,8 @@ public class OSDelegatingFileTailingMessageProducer extends FileTailingMessagePr
private volatile String command = "ADAPTER_NOT_INITIALIZED";
private volatile String[] tailCommand;
private volatile Process nativeTailProcess;
private volatile BufferedReader stdOutReader;
@@ -89,7 +92,12 @@ public class OSDelegatingFileTailingMessageProducer extends FileTailingMessagePr
protected void doStart() {
super.doStart();
destroyProcess();
this.command = "tail " + this.options + " " + getFile().getAbsolutePath();
String[] tailOptions = this.options.split("\\s+");
this.tailCommand = new String[tailOptions.length + 2];
this.tailCommand[0] = "tail";
this.tailCommand[this.tailCommand.length - 1] = getFile().getAbsolutePath();
System.arraycopy(tailOptions, 0, this.tailCommand, 1, tailOptions.length);
this.command = String.join(" ", this.tailCommand);
getTaskExecutor().execute(this::runExec);
}
@@ -111,20 +119,18 @@ public class OSDelegatingFileTailingMessageProducer extends FileTailingMessagePr
* Exec the native tail process.
*/
private void runExec() {
this.destroyProcess();
if (logger.isInfoEnabled()) {
logger.info("Starting tail process");
}
destroyProcess();
logger.info("Starting tail process");
try {
Process process = Runtime.getRuntime().exec(this.command);
Process process = Runtime.getRuntime().exec(this.tailCommand);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
this.nativeTailProcess = process;
this.startProcessMonitor();
startProcessMonitor();
if (this.enableStatusReader) {
startStatusReader();
}
this.stdOutReader = reader;
this.getTaskExecutor().execute(this);
getTaskExecutor().execute(this);
}
catch (IOException e) {
throw new MessagingException("Failed to exec tail command: '" + this.command + "'", e);
@@ -136,33 +142,34 @@ public class OSDelegatingFileTailingMessageProducer extends FileTailingMessagePr
* Runs a thread that waits for the Process result.
*/
private void startProcessMonitor() {
this.getTaskExecutor().execute(() -> {
Process process = OSDelegatingFileTailingMessageProducer.this.nativeTailProcess;
if (process == null) {
logger.debug("Process destroyed before starting process monitor");
return;
}
getTaskExecutor()
.execute(() -> {
Process process = OSDelegatingFileTailingMessageProducer.this.nativeTailProcess;
if (process == null) {
logger.debug("Process destroyed before starting process monitor");
return;
}
int result;
try {
logger.debug(() -> "Monitoring process " + process);
result = process.waitFor();
logger.info(() -> "tail process terminated with value " + result);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
logger.error(ex, "Interrupted - stopping adapter");
stop();
}
finally {
destroyProcess();
}
if (isRunning()) {
logger.info(() -> "Restarting tail process in " + getMissingFileDelay() + " milliseconds");
getTaskScheduler()
.schedule(this::runExec, new Date(System.currentTimeMillis() + getMissingFileDelay()));
}
});
int result;
try {
logger.debug(() -> "Monitoring process " + process);
result = process.waitFor();
logger.info(() -> "tail process terminated with value " + result);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
logger.error(ex, "Interrupted - stopping adapter");
stop();
}
finally {
destroyProcess();
}
if (isRunning()) {
logger.info(() -> "Restarting tail process in " + getMissingFileDelay() + " milliseconds");
getTaskScheduler()
.schedule(this::runExec, new Date(System.currentTimeMillis() + getMissingFileDelay()));
}
});
}
/**