1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.resourceloader.file;
35
36 import static info.magnolia.resourceloader.ResourceOriginChange.Type.*;
37 import static info.magnolia.resourceloader.ResourceOriginChange.resourceChange;
38
39 import info.magnolia.dirwatch.WatcherCallback;
40 import info.magnolia.resourceloader.ResourceOriginChange;
41
42 import java.io.IOException;
43 import java.nio.file.FileVisitOption;
44 import java.nio.file.FileVisitResult;
45 import java.nio.file.Files;
46 import java.nio.file.Path;
47 import java.nio.file.SimpleFileVisitor;
48 import java.nio.file.attribute.BasicFileAttributes;
49 import java.util.Collections;
50 import java.util.function.Predicate;
51
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58 class FileWatcherCallback implements WatcherCallback {
59
60 private static final Logger log = LoggerFactory.getLogger(FileWatcherCallback.class);
61
62 private final FileSystemResourceOrigin origin;
63 private final Predicate<Path> watchedPathFilter;
64
65 FileWatcherCallback(FileSystemResourceOrigin origin, Predicate<Path> watchedPathFilter) {
66 this.origin = origin;
67 this.watchedPathFilter = watchedPathFilter;
68 }
69
70
71
72
73
74
75
76 @Override
77 public void added(Path path) {
78 dispatchResourceChange(path, ADDED, true);
79 }
80
81
82
83
84
85
86
87 @Override
88 public void modified(Path path) {
89 dispatchResourceChange(path, MODIFIED, false);
90 }
91
92
93
94
95
96
97 @Override
98 public void removed(Path path) {
99 dispatchResourceChange(path, REMOVED, false);
100 }
101
102
103
104
105
106
107 private void dispatchResourceChange(Path changedResourcePath, ResourceOriginChange.Type type, boolean handleDirectoryContents) {
108 if (!watchedPathFilter.test(changedResourcePath)) {
109 return;
110 }
111
112 final ResourceOriginChange.Builder resourceChange =
113 resourceChange().
114 ofType(type).
115 at(origin.parseResourcePath(changedResourcePath)).
116 inOrigin(origin);
117
118 origin.dispatchResourceChange(resourceChange.build());
119
120 if (handleDirectoryContents && Files.isDirectory(changedResourcePath)) {
121 try {
122 Files.walkFileTree(changedResourcePath, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
123 @Override
124 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
125 if (watchedPathFilter.test(file)) {
126 origin.dispatchResourceChange(resourceChange.at(origin.parseResourcePath(file)).build());
127 }
128 return FileVisitResult.CONTINUE;
129 }
130
131 @Override
132 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
133 if (!watchedPathFilter.test(dir)) {
134 return FileVisitResult.SKIP_SUBTREE;
135 }
136 return FileVisitResult.CONTINUE;
137 }
138
139 @Override
140 public FileVisitResult visitFileFailed(Path file, IOException e) throws IOException {
141 log.warn("Visiting failed for {}", file);
142 return FileVisitResult.SKIP_SUBTREE;
143 }
144
145 });
146 } catch (IOException e) {
147 log.error("Failed to communicate file system resource changes recursively: {}", e.getMessage(), e);
148 }
149 }
150 }
151 }