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:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -53,6 +53,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
* @author Gavin Gray
|
||||
* @author Artem Bilan
|
||||
* @author Ali Shahbour
|
||||
* @author Trung Pham
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@@ -183,7 +184,7 @@ public class FileTailingMessageProducerTests {
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.afterPropertiesSet();
|
||||
adapter.setTaskScheduler(taskScheduler);
|
||||
final List<FileTailingEvent> events = new ArrayList<FileTailingEvent>();
|
||||
final List<FileTailingEvent> events = new ArrayList<>();
|
||||
adapter.setApplicationEventPublisher(event -> {
|
||||
FileTailingEvent tailEvent = (FileTailingEvent) event;
|
||||
logger.debug(event);
|
||||
@@ -232,6 +233,8 @@ public class FileTailingMessageProducerTests {
|
||||
}
|
||||
|
||||
assertThat(events.size()).isGreaterThanOrEqualTo(1);
|
||||
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
private void waitForField(FileTailingMessageProducerSupport adapter, String field) throws Exception {
|
||||
@@ -248,4 +251,38 @@ public class FileTailingMessageProducerTests {
|
||||
fail("adapter failed to start");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TailAvailable
|
||||
public void canHandleFilenameHavingSpecialCharacters() throws Exception {
|
||||
File file = File.createTempFile("foo bar", " -c 1");
|
||||
file.delete();
|
||||
|
||||
OSDelegatingFileTailingMessageProducer adapter = new OSDelegatingFileTailingMessageProducer();
|
||||
adapter.setOptions(TAIL_OPTIONS_FOLLOW_NAME_ALL_LINES);
|
||||
adapter.setFile(file);
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.afterPropertiesSet();
|
||||
adapter.setTaskScheduler(taskScheduler);
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
adapter.setOutputChannel(outputChannel);
|
||||
adapter.setTailAttemptsDelay(500);
|
||||
adapter.setBeanFactory(mock(BeanFactory.class));
|
||||
adapter.afterPropertiesSet();
|
||||
|
||||
adapter.start();
|
||||
waitForField(adapter, "stdOutReader");
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(("hello foobar\n").getBytes());
|
||||
fos.close();
|
||||
|
||||
Message<?> message = outputChannel.receive(10000);
|
||||
assertThat(message).as("expected a non-null message").isNotNull();
|
||||
assertThat(message.getPayload()).isEqualTo("hello foobar");
|
||||
|
||||
adapter.stop();
|
||||
file.delete();
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -75,9 +76,6 @@ public class TailRule extends TestWatcher {
|
||||
}
|
||||
|
||||
private boolean tailWorksOnThisMachine() {
|
||||
if (tmpDir.contains(":")) {
|
||||
return false;
|
||||
}
|
||||
File testDir = new File(tmpDir, "FileTailingMessageProducerTests");
|
||||
testDir.mkdir();
|
||||
final File file = new File(testDir, "foo");
|
||||
@@ -88,19 +86,24 @@ public class TailRule extends TestWatcher {
|
||||
fos.close();
|
||||
final AtomicReference<Integer> c = new AtomicReference<>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Future<Process> future = Executors.newSingleThreadExecutor().submit(() -> {
|
||||
final Process process = Runtime.getRuntime().exec(commandToTest + " " + file.getAbsolutePath());
|
||||
Executors.newSingleThreadExecutor().execute(() -> {
|
||||
try {
|
||||
c.set(process.getInputStream().read());
|
||||
latch.countDown();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Error reading test stream", e);
|
||||
}
|
||||
});
|
||||
return process;
|
||||
});
|
||||
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
|
||||
Future<Process> future =
|
||||
newSingleThreadExecutor.submit(() -> {
|
||||
final Process process = Runtime.getRuntime().exec(commandToTest + " " + file.getAbsolutePath());
|
||||
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
c.set(process.getInputStream().read());
|
||||
latch.countDown();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Error reading test stream", e);
|
||||
}
|
||||
});
|
||||
executorService.shutdown();
|
||||
return process;
|
||||
});
|
||||
newSingleThreadExecutor.shutdown();
|
||||
try {
|
||||
Process process = future.get(10, TimeUnit.SECONDS);
|
||||
if (latch.await(10, TimeUnit.SECONDS)) {
|
||||
|
||||
Reference in New Issue
Block a user