1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.util;
18
19 import java.io.File;
20 import java.util.Date;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
27
28 import org.apache.logging.log4j.Logger;
29 import org.apache.logging.log4j.core.AbstractLifeCycle;
30 import org.apache.logging.log4j.core.config.ConfigurationScheduler;
31 import org.apache.logging.log4j.status.StatusLogger;
32
33
34
35
36
37
38
39 public class WatchManager extends AbstractLifeCycle {
40
41 private static Logger logger = StatusLogger.getLogger();
42 private final ConcurrentMap<File, FileMonitor> watchers = new ConcurrentHashMap<>();
43 private int intervalSeconds = 0;
44 private ScheduledFuture<?> future;
45 private final ConfigurationScheduler scheduler;
46
47 public WatchManager(final ConfigurationScheduler scheduler) {
48 this.scheduler = scheduler;
49 }
50
51
52
53
54
55
56
57
58
59
60
61 public void reset() {
62 logger.debug("Resetting {}", this);
63 for (final File file : watchers.keySet()) {
64 reset(file);
65 }
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public void reset(final File file) {
81 if (file == null) {
82 return;
83 }
84 final FileMonitor fileMonitor = watchers.get(file);
85 if (fileMonitor != null) {
86 final long lastModifiedMillis = file.lastModified();
87 if (lastModifiedMillis != fileMonitor.lastModifiedMillis) {
88 if (logger.isDebugEnabled()) {
89 logger.debug("Resetting file monitor for '{}' from {} ({}) to {} ({})", file,
90 millisToString(fileMonitor.lastModifiedMillis), fileMonitor.lastModifiedMillis,
91 millisToString(lastModifiedMillis), lastModifiedMillis);
92 }
93 fileMonitor.setLastModifiedMillis(lastModifiedMillis);
94 }
95 }
96 }
97
98 public void setIntervalSeconds(final int intervalSeconds) {
99 if (!isStarted()) {
100 if (this.intervalSeconds > 0 && intervalSeconds == 0) {
101 scheduler.decrementScheduledItems();
102 } else if (this.intervalSeconds == 0 && intervalSeconds > 0) {
103 scheduler.incrementScheduledItems();
104 }
105 this.intervalSeconds = intervalSeconds;
106 }
107 }
108
109
110
111
112
113
114 public int getIntervalSeconds() {
115 return this.intervalSeconds;
116 }
117
118 @Override
119 public void start() {
120 super.start();
121 if (intervalSeconds > 0) {
122 future = scheduler.scheduleWithFixedDelay(new WatchRunnable(), intervalSeconds, intervalSeconds,
123 TimeUnit.SECONDS);
124 }
125 }
126
127 @Override
128 public boolean stop(final long timeout, final TimeUnit timeUnit) {
129 setStopping();
130 final boolean stopped = stop(future);
131 setStopped();
132 return stopped;
133 }
134
135
136
137
138
139
140
141
142 public void unwatchFile(final File file) {
143 logger.debug("Unwatching file '{}'", file);
144 watchers.remove(file);
145 }
146
147
148
149
150
151
152
153
154
155 public void watchFile(final File file, final FileWatcher watcher) {
156 final long lastModified = file.lastModified();
157 if (logger.isDebugEnabled()) {
158 logger.debug("Watching file '{}' for lastModified {} ({})", file, millisToString(lastModified), lastModified);
159 }
160 watchers.put(file, new FileMonitor(lastModified, watcher));
161 }
162
163 public Map<File, FileWatcher> getWatchers() {
164 final Map<File, FileWatcher> map = new HashMap<>(watchers.size());
165 for (final Map.Entry<File, FileMonitor> entry : watchers.entrySet()) {
166 map.put(entry.getKey(), entry.getValue().fileWatcher);
167 }
168 return map;
169 }
170
171 private String millisToString(final long millis) {
172 return new Date(millis).toString();
173 }
174
175 private final class WatchRunnable implements Runnable {
176
177
178 private final String SIMPLE_NAME = WatchRunnable.class.getSimpleName();
179
180 @Override
181 public void run() {
182 logger.trace("{} run triggered.", SIMPLE_NAME);
183 for (final Map.Entry<File, FileMonitor> entry : watchers.entrySet()) {
184 final File file = entry.getKey();
185 final FileMonitor fileMonitor = entry.getValue();
186 final long lastModfied = file.lastModified();
187 if (fileModified(fileMonitor, lastModfied)) {
188 if (logger.isInfoEnabled()) {
189 logger.info("File '{}' was modified on {} ({}), previous modification was on {} ({})", file,
190 millisToString(lastModfied), lastModfied, millisToString(fileMonitor.lastModifiedMillis),
191 fileMonitor.lastModifiedMillis);
192 }
193 fileMonitor.lastModifiedMillis = lastModfied;
194 fileMonitor.fileWatcher.fileModified(file);
195 }
196 }
197 logger.trace("{} run ended.", SIMPLE_NAME);
198 }
199
200 private boolean fileModified(final FileMonitor fileMonitor, final long lastModifiedMillis) {
201 return lastModifiedMillis != fileMonitor.lastModifiedMillis;
202 }
203 }
204
205 private final class FileMonitor {
206 private final FileWatcher fileWatcher;
207 private volatile long lastModifiedMillis;
208
209 public FileMonitor(final long lastModifiedMillis, final FileWatcher fileWatcher) {
210 this.fileWatcher = fileWatcher;
211 this.lastModifiedMillis = lastModifiedMillis;
212 }
213
214 private void setLastModifiedMillis(final long lastModifiedMillis) {
215 this.lastModifiedMillis = lastModifiedMillis;
216 }
217
218 @Override
219 public String toString() {
220 return "FileMonitor [fileWatcher=" + fileWatcher + ", lastModifiedMillis=" + lastModifiedMillis + "]";
221 }
222
223 }
224
225 @Override
226 public String toString() {
227 return "WatchManager [intervalSeconds=" + intervalSeconds + ", watchers=" + watchers + ", scheduler="
228 + scheduler + ", future=" + future + "]";
229 }
230 }