Skip to content

Commit 34904b3

Browse files
committed
Upgrade to commons-io-2.13.0; downgrade sshd-sftp
* The latest `sshd-sftp-2.10.0` has a bug not removing trailing `.` in the "unrooted" path when we ask to create a remote directory. It works on Windows well, but fails on UNIX file systems. * The `commons-io-2.13.0` has `Tailer` ctors deprecated and exposes a builder API. It starts a tailer process though unconditionally in its own thread. Use `setStartThread(false)` to have the tailer process managed by our own `TaskExecutor` * Add `ApacheCommonsFileTailingMessageProducer.setPollingDelayDuration(Duration)` * Deprecate a `TailerListener` impl on the `ApacheCommonsFileTailingMessageProducer` in favor of an internal instance
1 parent 0379929 commit 34904b3

File tree

3 files changed

+89
-25
lines changed

3 files changed

+89
-25
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ ext {
4747
modifiedFiles =
4848
files(grgit.status().unstaged.modified).filter { f -> f.name.endsWith('.java') || f.name.endsWith('.kt') }
4949

50-
apacheSshdVersion = '2.10.0'
50+
apacheSshdVersion = '2.9.2'
5151
artemisVersion = '2.29.0'
5252
aspectjVersion = '1.9.19'
5353
assertjVersion = '3.24.2'
@@ -56,7 +56,7 @@ ext {
5656
awaitilityVersion = '4.2.0'
5757
camelVersion = '3.20.6'
5858
commonsDbcp2Version = '2.9.0'
59-
commonsIoVersion = '2.11.0'
59+
commonsIoVersion = '2.13.0'
6060
commonsNetVersion = '3.9.0'
6161
curatorVersion = '5.5.0'
6262
debeziumVersion = '2.3.0.Final'

spring-integration-file/src/main/java/org/springframework/integration/file/tail/ApacheCommonsFileTailingMessageProducer.java

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,11 @@
1616

1717
package org.springframework.integration.file.tail;
1818

19+
import java.time.Duration;
20+
1921
import org.apache.commons.io.input.Tailer;
2022
import org.apache.commons.io.input.TailerListener;
23+
import org.apache.commons.io.input.TailerListenerAdapter;
2124

2225
/**
2326
* File tailer that delegates to the Apache Commons Tailer.
@@ -31,7 +34,9 @@
3134
public class ApacheCommonsFileTailingMessageProducer extends FileTailingMessageProducerSupport
3235
implements TailerListener {
3336

34-
private long pollingDelay = 1000; // NOSONAR magic number
37+
private final TailerListener tailerListener = new IntegrationTailerListener();
38+
39+
private Duration pollingDelay = Duration.ofSeconds(1);
3540

3641
private boolean end = true;
3742

@@ -44,6 +49,15 @@ public class ApacheCommonsFileTailingMessageProducer extends FileTailingMessageP
4449
* @param pollingDelay The delay.
4550
*/
4651
public void setPollingDelay(long pollingDelay) {
52+
setPollingDelayDuration(Duration.ofMillis(pollingDelay));
53+
}
54+
55+
/**
56+
* The delay between checks of the file for new content in {@link Duration}.
57+
* @param pollingDelay The delay duration.
58+
* @since 6.2
59+
*/
60+
public void setPollingDelayDuration(Duration pollingDelay) {
4761
this.pollingDelay = pollingDelay;
4862
}
4963

@@ -73,45 +87,96 @@ public String getComponentType() {
7387
@Override
7488
protected void doStart() {
7589
super.doStart();
76-
Tailer theTailer = new Tailer(getFile(), this, this.pollingDelay, this.end, this.reopen);
90+
Tailer theTailer =
91+
Tailer.builder()
92+
.setDelayDuration(this.pollingDelay)
93+
.setTailFromEnd(this.end)
94+
.setReOpen(this.reopen)
95+
.setFile(getFile())
96+
.setTailerListener(this.tailerListener)
97+
.setStartThread(false)
98+
.get();
7799
getTaskExecutor().execute(theTailer);
78100
this.tailer = theTailer;
79101
}
80102

81103
@Override
82104
protected void doStop() {
83105
super.doStop();
84-
this.tailer.stop();
106+
this.tailer.close();
85107
}
86108

109+
@Deprecated(since = "6.2", forRemoval = true)
87110
@Override
88111
public void init(Tailer tailer) {
112+
tailerListenerIsDeprecatedError();
89113
}
90114

115+
@Deprecated(since = "6.2", forRemoval = true)
91116
@Override
92117
public void fileNotFound() {
93-
publish("File not found: " + getFile().getAbsolutePath());
94-
try {
95-
Thread.sleep(getMissingFileDelay());
96-
}
97-
catch (InterruptedException e) {
98-
Thread.currentThread().interrupt();
99-
}
118+
tailerListenerIsDeprecatedError();
119+
this.tailerListener.fileNotFound();
100120
}
101121

122+
@Deprecated(since = "6.2", forRemoval = true)
102123
@Override
103124
public void fileRotated() {
104-
publish("File rotated: " + getFile().getAbsolutePath());
125+
tailerListenerIsDeprecatedError();
126+
this.tailerListener.fileRotated();
105127
}
106128

129+
@Deprecated(since = "6.2", forRemoval = true)
107130
@Override
108131
public void handle(String line) {
109-
send(line);
132+
tailerListenerIsDeprecatedError();
133+
this.tailerListener.handle(line);
110134
}
111135

136+
@Deprecated(since = "6.2", forRemoval = true)
112137
@Override
113138
public void handle(Exception ex) {
114-
publish(ex.getMessage());
139+
tailerListenerIsDeprecatedError();
140+
this.tailerListener.handle(ex);
141+
}
142+
143+
private void tailerListenerIsDeprecatedError() {
144+
ApacheCommonsFileTailingMessageProducer.this.logger.error(
145+
"The 'TailerListener' implementation on the 'ApacheCommonsFileTailingMessageProducer' " +
146+
"is deprecated (in favor of an internal instance) for removal in the next version.");
147+
}
148+
149+
private class IntegrationTailerListener extends TailerListenerAdapter {
150+
151+
IntegrationTailerListener() {
152+
}
153+
154+
@Override
155+
public void fileNotFound() {
156+
publish("File not found: " + getFile().getAbsolutePath());
157+
try {
158+
Thread.sleep(getMissingFileDelay());
159+
}
160+
catch (InterruptedException e) {
161+
Thread.currentThread().interrupt();
162+
}
163+
}
164+
165+
@Override
166+
public void fileRotated() {
167+
publish("File rotated: " + getFile().getAbsolutePath());
168+
}
169+
170+
@Override
171+
public void handle(String line) {
172+
send(line);
173+
}
174+
175+
@Override
176+
public void handle(Exception ex) {
177+
publish(ex.getMessage());
178+
}
179+
115180
}
116181

117182
}

spring-integration-file/src/test/java/org/springframework/integration/file/config/FileTailInboundChannelAdapterParserTests.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,9 +17,9 @@
1717
package org.springframework.integration.file.config;
1818

1919
import java.io.File;
20+
import java.time.Duration;
2021

21-
import org.junit.Test;
22-
import org.junit.runner.RunWith;
22+
import org.junit.jupiter.api.Test;
2323

2424
import org.springframework.beans.factory.annotation.Autowired;
2525
import org.springframework.beans.factory.annotation.Qualifier;
@@ -31,8 +31,7 @@
3131
import org.springframework.messaging.MessageChannel;
3232
import org.springframework.scheduling.TaskScheduler;
3333
import org.springframework.test.annotation.DirtiesContext;
34-
import org.springframework.test.context.ContextConfiguration;
35-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
34+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
3635

3736
import static org.assertj.core.api.Assertions.assertThat;
3837
import static org.mockito.Mockito.mock;
@@ -42,10 +41,10 @@
4241
* @author Artem Bilan
4342
* @author Gavin Gray
4443
* @author Ali Shahbour
44+
*
4545
* @since 3.0
4646
*/
47-
@ContextConfiguration
48-
@RunWith(SpringJUnit4ClassRunner.class)
47+
@SpringJUnitConfig
4948
@DirtiesContext
5049
public class FileTailInboundChannelAdapterParserTests {
5150

@@ -109,7 +108,7 @@ public void testApacheDefault() {
109108
String normalizedName = getNormalizedPath(fileName);
110109
assertThat(normalizedName).isEqualTo("/tmp/bar");
111110
assertThat(TestUtils.getPropertyValue(apacheDefault, "taskExecutor")).isSameAs(exec);
112-
assertThat(TestUtils.getPropertyValue(apacheDefault, "pollingDelay")).isEqualTo(2000L);
111+
assertThat(TestUtils.getPropertyValue(apacheDefault, "pollingDelay")).isEqualTo(Duration.ofSeconds(2));
113112
assertThat(TestUtils.getPropertyValue(apacheDefault, "tailAttemptsDelay")).isEqualTo(10000L);
114113
assertThat(TestUtils.getPropertyValue(apacheDefault, "idleEventInterval")).isEqualTo(10000L);
115114
assertThat(TestUtils.getPropertyValue(apacheDefault, "autoStartup", Boolean.class)).isFalse();
@@ -124,7 +123,7 @@ public void testApacheEndReopen() {
124123
String normalizedName = getNormalizedPath(fileName);
125124
assertThat(normalizedName).isEqualTo("/tmp/qux");
126125
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "taskExecutor")).isSameAs(exec);
127-
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "pollingDelay")).isEqualTo(2000L);
126+
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "pollingDelay")).isEqualTo(Duration.ofSeconds(2));
128127
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "tailAttemptsDelay")).isEqualTo(10000L);
129128
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "autoStartup", Boolean.class)).isFalse();
130129
assertThat(TestUtils.getPropertyValue(apacheEndReopen, "phase")).isEqualTo(123);

0 commit comments

Comments
 (0)