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.ArrayList;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.ServiceLoader;
26 import java.util.UUID;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.ScheduledFuture;
30 import java.util.concurrent.TimeUnit;
31
32 import org.apache.logging.log4j.Logger;
33 import org.apache.logging.log4j.core.AbstractLifeCycle;
34 import org.apache.logging.log4j.core.config.ConfigurationFileWatcher;
35 import org.apache.logging.log4j.core.config.ConfigurationScheduler;
36 import org.apache.logging.log4j.status.StatusLogger;
37 import org.apache.logging.log4j.util.LoaderUtil;
38
39
40
41
42
43
44
45 public class WatchManager extends AbstractLifeCycle {
46
47 private static Logger logger = StatusLogger.getLogger();
48 private final ConcurrentMap<Source, ConfigurationMonitor> watchers = new ConcurrentHashMap<>();
49 private int intervalSeconds = 0;
50 private ScheduledFuture<?> future;
51 private final ConfigurationScheduler scheduler;
52 private final List<WatchEventService> eventServiceList;
53 private final UUID id = UuidUtil.getTimeBasedUuid();
54
55 public WatchManager(final ConfigurationScheduler scheduler) {
56 this.scheduler = scheduler;
57 eventServiceList = getEventServices();
58 }
59
60 public UUID getId() {
61 return this.id;
62 }
63
64 public boolean hasEventListeners() {
65 return eventServiceList.size() > 0;
66 }
67
68
69
70
71
72
73
74
75
76
77
78 public void reset() {
79 logger.debug("Resetting {}", this);
80 for (final Source source : watchers.keySet()) {
81 reset(source);
82 }
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96 public void reset(final File file) {
97 if (file == null) {
98 return;
99 }
100 Source source = new Source(file);
101 reset(source);
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115 public void reset(final Source source) {
116 if (source == null) {
117 return;
118 }
119 final ConfigurationMonitor monitor = watchers.get(source);
120 if (monitor != null) {
121 Watcher watcher = monitor.getWatcher();
122 if (watcher.isModified()) {
123 final long lastModifiedMillis = watcher.getLastModified();
124 if (logger.isDebugEnabled()) {
125 logger.debug("Resetting file monitor for '{}' from {} ({}) to {} ({})", source.getLocation(),
126 millisToString(monitor.lastModifiedMillis), monitor.lastModifiedMillis,
127 millisToString(lastModifiedMillis), lastModifiedMillis);
128 }
129 monitor.setLastModifiedMillis(lastModifiedMillis);
130 }
131 }
132 }
133
134 public void setIntervalSeconds(final int intervalSeconds) {
135 if (!isStarted()) {
136 if (this.intervalSeconds > 0 && intervalSeconds == 0) {
137 scheduler.decrementScheduledItems();
138 } else {
139 if (this.intervalSeconds == 0 && intervalSeconds > 0) {
140 scheduler.incrementScheduledItems();
141 }
142 }
143 this.intervalSeconds = intervalSeconds;
144 }
145 }
146
147
148
149
150
151
152 public int getIntervalSeconds() {
153 return this.intervalSeconds;
154 }
155
156 @Override
157 public void start() {
158 super.start();
159
160 if (intervalSeconds > 0) {
161 future = scheduler
162 .scheduleWithFixedDelay(new WatchRunnable(), intervalSeconds, intervalSeconds, TimeUnit.SECONDS);
163 }
164 for (WatchEventService service : eventServiceList) {
165 service.subscribe(this);
166 }
167 }
168
169 @Override
170 public boolean stop(final long timeout, final TimeUnit timeUnit) {
171 setStopping();
172 for (WatchEventService service : eventServiceList) {
173 service.unsubscribe(this);
174 }
175 final boolean stopped = stop(future);
176 setStopped();
177 return stopped;
178 }
179
180
181
182
183
184
185
186 public void unwatchFile(final File file) {
187 Source source = new Source(file);
188 unwatch(source);
189 }
190
191
192
193
194
195
196
197
198 public void unwatch(final Source source) {
199 logger.debug("Unwatching configuration {}", source);
200 watchers.remove(source);
201 }
202
203 public void checkFiles() {
204 new WatchRunnable().run();
205 }
206
207
208
209
210
211
212
213 public void watchFile(final File file, final FileWatcher fileWatcher) {
214 Watcher watcher;
215 if (fileWatcher instanceof Watcher) {
216 watcher = (Watcher) fileWatcher;
217 } else {
218 watcher = new WrappedFileWatcher(fileWatcher);
219 }
220 Source source = new Source(file);
221 watch(source, watcher);
222 }
223
224
225
226
227
228
229
230 public void watch(final Source source, final Watcher watcher) {
231 watcher.watching(source);
232 final long lastModified = watcher.getLastModified();
233 if (logger.isDebugEnabled()) {
234 logger.debug("Watching configuration '{}' for lastModified {} ({})", source, millisToString(lastModified),
235 lastModified);
236 }
237 watchers.put(source, new ConfigurationMonitor(lastModified, watcher));
238 }
239
240
241
242
243
244
245
246 public Map<File, FileWatcher> getWatchers() {
247 final Map<File, FileWatcher> map = new HashMap<>(watchers.size());
248 for (Map.Entry<Source, ConfigurationMonitor> entry : watchers.entrySet()) {
249 if (entry.getValue().getWatcher() instanceof ConfigurationFileWatcher) {
250 map.put(entry.getKey().getFile(), (FileWatcher) entry.getValue().getWatcher());
251 } else {
252 map.put(entry.getKey().getFile(), new WrappedFileWatcher((FileWatcher) entry.getValue().getWatcher()));
253 }
254 }
255 return map;
256 }
257
258
259
260
261
262
263
264 public Map<Source, Watcher> getConfigurationWatchers() {
265 final Map<Source, Watcher> map = new HashMap<>(watchers.size());
266 for (final Map.Entry<Source, ConfigurationMonitor> entry : watchers.entrySet()) {
267 map.put(entry.getKey(), entry.getValue().getWatcher());
268 }
269 return map;
270 }
271
272 private String millisToString(final long millis) {
273 return new Date(millis).toString();
274 }
275
276 private List<WatchEventService> getEventServices() {
277 List<WatchEventService> list = new ArrayList<>();
278 for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
279 try {
280 final ServiceLoader<WatchEventService> serviceLoader = ServiceLoader
281 .load(WatchEventService.class, classLoader);
282 for (final WatchEventService service : serviceLoader) {
283 list.add(service);
284 }
285 } catch (final Throwable ex) {
286 LOGGER.debug("Unable to retrieve WatchEventService from ClassLoader {}", classLoader, ex);
287 }
288 }
289 return list;
290 }
291
292 private final class WatchRunnable implements Runnable {
293
294
295 private final String SIMPLE_NAME = WatchRunnable.class.getSimpleName();
296
297 @Override
298 public void run() {
299 logger.trace("{} run triggered.", SIMPLE_NAME);
300 for (final Map.Entry<Source, ConfigurationMonitor> entry : watchers.entrySet()) {
301 final Source source = entry.getKey();
302 final ConfigurationMonitor monitor = entry.getValue();
303 if (monitor.getWatcher().isModified()) {
304 final long lastModified = monitor.getWatcher().getLastModified();
305 if (logger.isInfoEnabled()) {
306 logger.info("Source '{}' was modified on {} ({}), previous modification was on {} ({})", source,
307 millisToString(lastModified), lastModified, millisToString(monitor.lastModifiedMillis),
308 monitor.lastModifiedMillis);
309 }
310 monitor.lastModifiedMillis = lastModified;
311 monitor.getWatcher().modified();
312 }
313 }
314 logger.trace("{} run ended.", SIMPLE_NAME);
315 }
316 }
317
318 private final class ConfigurationMonitor {
319 private final Watcher watcher;
320 private volatile long lastModifiedMillis;
321
322 public Watcher getWatcher() {
323 return watcher;
324 }
325
326 public ConfigurationMonitor(final long lastModifiedMillis, final Watcher watcher) {
327 this.watcher = watcher;
328 this.lastModifiedMillis = lastModifiedMillis;
329 }
330
331 private void setLastModifiedMillis(final long lastModifiedMillis) {
332 this.lastModifiedMillis = lastModifiedMillis;
333 }
334
335 @Override
336 public String toString() {
337 return "ConfigurationMonitor [watcher=" + watcher + ", lastModifiedMillis=" + lastModifiedMillis + "]";
338 }
339
340 }
341
342 @Override
343 public String toString() {
344 return "WatchManager [intervalSeconds=" + intervalSeconds + ", watchers=" + watchers + ", scheduler="
345 + scheduler + ", future=" + future + "]";
346 }
347 }