Skip to content

Commit 73ed3ee

Browse files
AdamaSorhoartembilan
authored andcommitted
GH-8691: Add (S)FTP, SMB aged file filters
Fixes #8691 * Remove setAge with TimeUnit Turned out we don't need anymore since we're using Duration for age in FtpLastModifiedFileListFilter and SftpLastModifiedFileListFilter. * Add changes to the docs * Introduce AbstractLastModifiedFileListFilter * Some code readability improvements * Make language in the docs more official
1 parent f3d0441 commit 73ed3ee

File tree

14 files changed

+540
-90
lines changed

14 files changed

+540
-90
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,8 @@ project('spring-integration-sftp') {
930930

931931
testImplementation project(':spring-integration-event')
932932
testImplementation project(':spring-integration-file').sourceSets.test.output
933+
934+
testRuntimeOnly 'net.i2p.crypto:eddsa:0.3.0'
933935
}
934936
}
935937

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.file.filters;
18+
19+
import java.time.Duration;
20+
import java.time.Instant;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.function.Consumer;
24+
25+
import org.springframework.lang.Nullable;
26+
27+
/**
28+
* The {@link FileListFilter} implementation to filter those files which
29+
* lastModified is less than the {@link #age} in comparison
30+
* with the {@link Instant#now()}.
31+
* When {@link #discardCallback} is provided, it called for all the rejected files.
32+
*
33+
* @param <F> the file
34+
*
35+
* @author Adama Sorho
36+
* @author Artem Bilan
37+
*
38+
* @since 6.2
39+
*/
40+
public abstract class AbstractLastModifiedFileListFilter<F> implements DiscardAwareFileListFilter<F> {
41+
42+
protected static final long ONE_SECOND = 1000;
43+
44+
private static final long DEFAULT_AGE = 60;
45+
46+
private Duration age = Duration.ofSeconds(DEFAULT_AGE);
47+
48+
@Nullable
49+
private Consumer<F> discardCallback;
50+
51+
public AbstractLastModifiedFileListFilter() {
52+
}
53+
54+
public AbstractLastModifiedFileListFilter(Duration age) {
55+
this.age = age;
56+
}
57+
58+
/**
59+
* Set the age that files have to be before being passed by this filter.
60+
* If lastModified plus {@link #age} is before the {@link Instant#now()}, the file
61+
* is filtered.
62+
* Defaults to 60 seconds.
63+
* @param age the Duration.
64+
*/
65+
public void setAge(Duration age) {
66+
this.age = age;
67+
}
68+
69+
/**
70+
* Set the age that files have to be before being passed by this filter.
71+
* If lastModified plus {@link #age} is before the {@link Instant#now()}, the file
72+
* is filtered.
73+
* Defaults to 60 seconds.
74+
* @param age the age in seconds.
75+
*/
76+
public void setAge(long age) {
77+
setAge(Duration.ofSeconds(age));
78+
}
79+
80+
@Override
81+
public void addDiscardCallback(@Nullable Consumer<F> discardCallback) {
82+
this.discardCallback = discardCallback;
83+
}
84+
85+
@Override
86+
public List<F> filterFiles(F[] files) {
87+
List<F> list = new ArrayList<>();
88+
Instant now = Instant.now();
89+
for (F file: files) {
90+
if (fileIsAged(file, now)) {
91+
list.add(file);
92+
}
93+
else if (this.discardCallback != null) {
94+
this.discardCallback.accept(file);
95+
}
96+
}
97+
98+
return list;
99+
}
100+
101+
@Override
102+
public boolean accept(F file) {
103+
if (fileIsAged(file, Instant.now())) {
104+
return true;
105+
}
106+
else if (this.discardCallback != null) {
107+
this.discardCallback.accept(file);
108+
}
109+
110+
return false;
111+
}
112+
113+
private boolean fileIsAged(F file, Instant now) {
114+
return getLastModified(file).plus(this.age).isBefore(now);
115+
}
116+
117+
@Override
118+
public boolean supportsSingleFileFiltering() {
119+
return true;
120+
}
121+
122+
protected Duration getAgeDuration() {
123+
return this.age;
124+
}
125+
126+
protected abstract Instant getLastModified(F remoteFile);
127+
128+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2022 the original author or authors.
2+
* Copyright 2015-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.
@@ -18,51 +18,41 @@
1818

1919
import java.io.File;
2020
import java.time.Duration;
21-
import java.util.ArrayList;
22-
import java.util.List;
21+
import java.time.Instant;
2322
import java.util.concurrent.TimeUnit;
2423
import java.util.function.Consumer;
2524

26-
import org.springframework.lang.Nullable;
27-
2825
/**
2926
* The {@link FileListFilter} implementation to filter those files which
30-
* {@link File#lastModified()} is less than the {@link #age} in comparison
27+
* {@link File#lastModified()} is less than the age in comparison
3128
* with the current time.
3229
* <p>
3330
* The resolution is done in seconds.
3431
* <p>
35-
* When {@link #discardCallback} is provided, it called for all the
32+
* When discardCallback {@link #addDiscardCallback(Consumer)} is provided, it called for all the
3633
* rejected files.
3734
*
3835
* @author Gary Russell
3936
* @author Artem Bilan
37+
* @author Adama Sorho
4038
*
4139
* @since 4.2
4240
*
4341
*/
44-
public class LastModifiedFileListFilter implements DiscardAwareFileListFilter<File> {
45-
46-
private static final long ONE_SECOND = 1000;
47-
48-
private static final long DEFAULT_AGE = 60;
49-
50-
private volatile long age = DEFAULT_AGE;
51-
52-
@Nullable
53-
private Consumer<File> discardCallback;
42+
public class LastModifiedFileListFilter extends AbstractLastModifiedFileListFilter<File> {
5443

5544
public LastModifiedFileListFilter() {
45+
super();
5646
}
5747

5848
/**
59-
* Construct a {@link LastModifiedFileListFilter} instance with provided {@link #age}.
49+
* Construct a {@link LastModifiedFileListFilter} instance with provided age.
6050
* Defaults to 60 seconds.
6151
* @param age the age in seconds.
6252
* @since 5.0
6353
*/
6454
public LastModifiedFileListFilter(long age) {
65-
this.age = age;
55+
super(Duration.ofSeconds(age));
6656
}
6757

6858
/**
@@ -72,76 +62,25 @@ public LastModifiedFileListFilter(long age) {
7262
* Defaults to 60 seconds.
7363
* @param age the age
7464
* @param unit the timeUnit.
65+
* @deprecated since 6.2 in favor of {@link #setAge(Duration)}
7566
*/
67+
@Deprecated(since = "6.2", forRemoval = true)
7668
public void setAge(long age, TimeUnit unit) {
77-
this.age = unit.toSeconds(age);
78-
}
79-
80-
/**
81-
* Set the age that files have to be before being passed by this filter.
82-
* If {@link File#lastModified()} plus age is greater than the current time, the file
83-
* is filtered. The resolution is seconds.
84-
* Defaults to 60 seconds.
85-
* @param age the age
86-
* @since 5.1.3
87-
*/
88-
public void setAge(Duration age) {
89-
setAge(age.getSeconds());
69+
setAge(unit.toSeconds(age));
9070
}
9171

9272
/**
93-
* Set the age that files have to be before being passed by this filter.
94-
* If {@link File#lastModified()} plus age is greater than the current time, the file
95-
* is filtered. The resolution is seconds.
96-
* Defaults to 60 seconds.
97-
* @param age the age
73+
* @return the age in seconds.
74+
* @deprecated since 6.2 in favor of {@link #getAgeDuration()}
9875
*/
99-
public void setAge(long age) {
100-
setAge(age, TimeUnit.SECONDS);
101-
}
102-
76+
@Deprecated(since = "6.2", forRemoval = true)
10377
public long getAge() {
104-
return this.age;
105-
}
106-
107-
@Override
108-
public void addDiscardCallback(@Nullable Consumer<File> discardCallbackToSet) {
109-
this.discardCallback = discardCallbackToSet;
110-
}
111-
112-
@Override
113-
public List<File> filterFiles(File[] files) {
114-
List<File> list = new ArrayList<>();
115-
long now = System.currentTimeMillis() / ONE_SECOND;
116-
for (File file : files) {
117-
if (fileIsAged(file, now)) {
118-
list.add(file);
119-
}
120-
else if (this.discardCallback != null) {
121-
this.discardCallback.accept(file);
122-
}
123-
}
124-
return list;
125-
}
126-
127-
@Override
128-
public boolean accept(File file) {
129-
if (fileIsAged(file, System.currentTimeMillis() / ONE_SECOND)) {
130-
return true;
131-
}
132-
else if (this.discardCallback != null) {
133-
this.discardCallback.accept(file);
134-
}
135-
return false;
136-
}
137-
138-
private boolean fileIsAged(File file, long now) {
139-
return file.lastModified() / ONE_SECOND + this.age <= now;
78+
return getAgeDuration().getSeconds();
14079
}
14180

14281
@Override
143-
public boolean supportsSingleFileFiltering() {
144-
return true;
82+
protected Instant getLastModified(File file) {
83+
return Instant.ofEpochSecond(file.lastModified() / ONE_SECOND);
14584
}
14685

14786
}

spring-integration-file/src/test/java/org/springframework/integration/file/filters/LastModifiedFileListFilterTests.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2022 the original author or authors.
2+
* Copyright 2015-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.
@@ -18,37 +18,38 @@
1818

1919
import java.io.File;
2020
import java.io.FileOutputStream;
21-
import java.util.concurrent.TimeUnit;
21+
import java.time.Duration;
22+
import java.time.Instant;
2223

23-
import org.junit.Rule;
24-
import org.junit.Test;
25-
import org.junit.rules.TemporaryFolder;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.io.TempDir;
2626

2727
import static org.assertj.core.api.Assertions.assertThat;
2828

2929
/**
3030
* @author Gary Russell
3131
* @author Artem Bilan
32+
* @author Adama Sorho
33+
*
3234
* @since 4.2
3335
*
3436
*/
3537
public class LastModifiedFileListFilterTests {
3638

37-
@Rule
38-
public TemporaryFolder folder = new TemporaryFolder();
39+
@TempDir
40+
public File folder;
3941

4042
@Test
4143
public void testAge() throws Exception {
4244
LastModifiedFileListFilter filter = new LastModifiedFileListFilter();
43-
filter.setAge(60, TimeUnit.SECONDS);
44-
File foo = this.folder.newFile();
45+
filter.setAge(60);
46+
File foo = new File(folder, "test.tmp");
4547
FileOutputStream fileOutputStream = new FileOutputStream(foo);
4648
fileOutputStream.write("x".getBytes());
4749
fileOutputStream.close();
4850
assertThat(filter.filterFiles(new File[] {foo})).hasSize(0);
4951
assertThat(filter.accept(foo)).isFalse();
50-
// Make a file as of yesterday's
51-
foo.setLastModified(System.currentTimeMillis() - 1000 * 60 * 60 * 24);
52+
foo.setLastModified(Instant.now().minus(Duration.ofDays(1)).toEpochMilli());
5253
assertThat(filter.filterFiles(new File[] {foo})).hasSize(1);
5354
assertThat(filter.accept(foo)).isTrue();
5455
}

0 commit comments

Comments
 (0)