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