1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.impl;
18
19 import static org.apache.commons.vfs2.VfsTestUtils.getTestDirectoryFile;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.util.ArrayDeque;
30 import java.util.Deque;
31 import java.util.Objects;
32 import java.util.concurrent.atomic.AtomicLong;
33
34 import org.apache.commons.vfs2.FileChangeEvent;
35 import org.apache.commons.vfs2.FileListener;
36 import org.apache.commons.vfs2.FileObject;
37 import org.apache.commons.vfs2.FileSystemManager;
38 import org.apache.commons.vfs2.VFS;
39 import org.junit.jupiter.api.AfterEach;
40 import org.junit.jupiter.api.BeforeEach;
41 import org.junit.jupiter.api.Disabled;
42 import org.junit.jupiter.api.Test;
43
44
45
46
47 public class DefaultFileMonitorTest {
48
49 private static class CountingListener implements FileListener {
50 private final AtomicLong changed = new AtomicLong();
51 private final AtomicLong created = new AtomicLong();
52 private final AtomicLong deleted = new AtomicLong();
53
54 @Override
55 public void fileChanged(final FileChangeEvent event) {
56 changed.incrementAndGet();
57 }
58
59 @Override
60 public void fileCreated(final FileChangeEvent event) {
61 created.incrementAndGet();
62 }
63
64 @Override
65 public void fileDeleted(final FileChangeEvent event) {
66 deleted.incrementAndGet();
67 }
68 }
69
70 private enum PeekLocation {
71 FIRST, LAST
72 }
73
74 private enum Status {
75 CHANGED, CREATED, DELETED
76 }
77
78 private class TestFileListener implements FileListener {
79
80 @Override
81 public void fileChanged(final FileChangeEvent event) throws Exception {
82 status.add(Status.CHANGED);
83 }
84
85 @Override
86 public void fileCreated(final FileChangeEvent event) throws Exception {
87 status.add(Status.CREATED);
88 }
89
90 @Override
91 public void fileDeleted(final FileChangeEvent event) throws Exception {
92 status.add(Status.DELETED);
93 }
94 }
95
96 private static final int DELAY_MILLIS = 100;
97
98 private FileSystemManager fileSystemManager;
99
100 private final Deque<Status> status = new ArrayDeque<>();
101
102 private File testDir;
103
104 private File testFile;
105
106 private void deleteTestFileIfPresent() {
107 if (testFile != null && testFile.exists()) {
108 final boolean deleted = testFile.delete();
109 assertTrue(deleted, testFile.toString());
110 }
111 }
112
113 private Status getStatus(final PeekLocation peekLocation) {
114 switch (Objects.requireNonNull(peekLocation, "peekLocation")) {
115 case FIRST:
116 return status.peekFirst();
117 case LAST:
118 return status.peekLast();
119 }
120 throw new IllegalStateException();
121 }
122
123 private void resetStatus() {
124 status.clear();
125 }
126
127 @BeforeEach
128 public void setUp() throws Exception {
129 fileSystemManager = VFS.getManager();
130 testDir = getTestDirectoryFile();
131 resetStatus();
132 testFile = new File(testDir, "testReload.properties");
133 deleteTestFileIfPresent();
134 }
135
136 @AfterEach
137 public void tearDown() {
138 deleteTestFileIfPresent();
139 }
140
141 @Test
142 public void testChildFileDeletedWithoutRecursiveChecking() throws Exception {
143 writeToFile(testFile);
144 try (FileObject fileObject = fileSystemManager.resolveFile(testDir.toURI().toURL().toString())) {
145 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
146 monitor.setDelay(2000);
147 monitor.setRecursive(false);
148 monitor.addFile(fileObject);
149 monitor.start();
150 resetStatus();
151 Thread.sleep(DELAY_MILLIS * 5);
152 testFile.delete();
153 Thread.sleep(DELAY_MILLIS * 30);
154 assertNull(getStatus(PeekLocation.LAST), "Event should not have occurred");
155 }
156 }
157 }
158
159 @Test
160 public void testChildFileRecreated() throws Exception {
161 writeToFile(testFile);
162 try (FileObject fileObj = fileSystemManager.resolveFile(testDir.toURI().toURL().toString())) {
163 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
164 monitor.setDelay(2000);
165 monitor.setRecursive(true);
166 monitor.addFile(fileObj);
167 monitor.start();
168 resetStatus();
169 Thread.sleep(DELAY_MILLIS * 5);
170 testFile.delete();
171 waitFor(Status.DELETED, DELAY_MILLIS * 30, PeekLocation.LAST);
172 resetStatus();
173 Thread.sleep(DELAY_MILLIS * 5);
174 writeToFile(testFile);
175 waitFor(Status.CREATED, DELAY_MILLIS * 30, PeekLocation.LAST);
176 }
177 }
178 }
179
180 @Test
181 public void testFileCreated() throws Exception {
182 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toURL().toString())) {
183 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
184
185 monitor.setDelay(DELAY_MILLIS);
186 monitor.addFile(fileObject);
187 monitor.start();
188 writeToFile(testFile);
189 Thread.sleep(DELAY_MILLIS * 5);
190 waitFor(Status.CREATED, DELAY_MILLIS * 5, PeekLocation.FIRST);
191 }
192 }
193 }
194
195 @Test
196 public void testFileDeleted() throws Exception {
197 writeToFile(testFile);
198 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toString())) {
199 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
200
201 monitor.setDelay(DELAY_MILLIS);
202 monitor.addFile(fileObject);
203 monitor.start();
204 testFile.delete();
205 waitFor(Status.DELETED, DELAY_MILLIS * 5, PeekLocation.LAST);
206 }
207 }
208 }
209
210 @Test
211 public void testFileModified() throws Exception {
212 writeToFile(testFile);
213 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toURL().toString())) {
214 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
215
216 monitor.setDelay(DELAY_MILLIS);
217 monitor.addFile(fileObject);
218 monitor.start();
219
220
221 Thread.sleep(DELAY_MILLIS * 10);
222 final long valueMillis = System.currentTimeMillis();
223 final boolean rcMillis = testFile.setLastModified(valueMillis);
224 assertTrue(rcMillis, "setLastModified succeeded");
225 waitFor(Status.CHANGED, DELAY_MILLIS * 5, PeekLocation.LAST);
226 }
227 }
228 }
229
230 @Test
231 public void testFileMonitorRestarted() throws Exception {
232 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toString())) {
233 final DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener());
234 try {
235
236 monitor.setDelay(DELAY_MILLIS);
237 monitor.addFile(fileObject);
238
239 monitor.start();
240 writeToFile(testFile);
241 Thread.sleep(DELAY_MILLIS * 5);
242 } finally {
243 monitor.stop();
244 }
245
246 monitor.start();
247 try {
248 testFile.delete();
249 waitFor(Status.DELETED, DELAY_MILLIS * 5, PeekLocation.LAST);
250 } finally {
251 monitor.stop();
252 }
253 }
254 }
255
256 @Test
257 public void testFileRecreated() throws Exception {
258 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI())) {
259 try (DefaultFileMonitor monitor = new DefaultFileMonitor(new TestFileListener())) {
260
261 monitor.setDelay(DELAY_MILLIS);
262 monitor.addFile(fileObject);
263 monitor.start();
264 writeToFile(testFile);
265 waitFor(Status.CREATED, DELAY_MILLIS * 10, PeekLocation.LAST);
266 resetStatus();
267 testFile.delete();
268 waitFor(Status.DELETED, DELAY_MILLIS * 10, PeekLocation.LAST);
269 resetStatus();
270 Thread.sleep(DELAY_MILLIS * 5);
271 monitor.addFile(fileObject);
272 writeToFile(testFile);
273 waitFor(Status.CREATED, DELAY_MILLIS * 10, PeekLocation.LAST);
274 }
275 }
276 }
277
278
279
280
281
282
283 @Disabled("VFS-299")
284 @Test
285 public void testIgnoreTestAddRemove() throws Exception {
286 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toString())) {
287 final CountingListener listener = new CountingListener();
288 try (DefaultFileMonitor monitor = new DefaultFileMonitor(listener)) {
289 monitor.setDelay(DELAY_MILLIS);
290 monitor.addFile(fileObject);
291 monitor.removeFile(fileObject);
292 monitor.addFile(fileObject);
293 monitor.start();
294 writeToFile(testFile);
295 Thread.sleep(DELAY_MILLIS * 3);
296 assertEquals(1, listener.created.get(), "Created event is only fired once");
297 }
298 }
299 }
300
301
302
303
304
305
306 @Disabled("VFS-299")
307 @Test
308 public void testIgnoreTestStartStop() throws Exception {
309 try (FileObject fileObject = fileSystemManager.resolveFile(testFile.toURI().toString())) {
310 final CountingListener stoppedListener = new CountingListener();
311 try (DefaultFileMonitor stoppedMonitor = new DefaultFileMonitor(stoppedListener)) {
312 stoppedMonitor.start();
313 stoppedMonitor.addFile(fileObject);
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 final CountingListener activeListener = new CountingListener();
331 try (DefaultFileMonitor activeMonitor = new DefaultFileMonitor(activeListener)) {
332 activeMonitor.setDelay(DELAY_MILLIS);
333 activeMonitor.addFile(fileObject);
334 activeMonitor.start();
335 writeToFile(testFile);
336 Thread.sleep(DELAY_MILLIS * 10);
337
338 assertEquals(1, activeListener.created.get(), "The listener of the active monitor received one created event");
339 assertEquals(0, stoppedListener.created.get(), "The listener of the stopped monitor received no events");
340 }
341 }
342 }
343
344 private void waitFor(final Status expected, final long timeoutMillis, final PeekLocation peekLocation) throws InterruptedException {
345 if (expected == getStatus(peekLocation)) {
346 return;
347 }
348 long remaining = timeoutMillis;
349 final long interval = timeoutMillis / 10;
350 while (remaining > 0) {
351 Thread.sleep(interval);
352 remaining -= interval;
353 if (expected == getStatus(peekLocation)) {
354 return;
355 }
356 }
357 assertNotNull(getStatus(peekLocation), "No event occurred");
358 assertEquals(expected, getStatus(peekLocation), "Incorrect event " + getStatus(peekLocation));
359 }
360
361 private void writeToFile(final File file) throws IOException {
362 Files.write(file.toPath(), "string=value1".getBytes(StandardCharsets.UTF_8));
363 }
364
365 }