source: josm/trunk/src/org/openstreetmap/josm/io/FileWatcher.java@ 7267

Last change on this file since 7267 was 7248, checked in by bastiK, 10 years ago

reworked MirroredInputStream (renamed to CachedFile):

  • no more awkwardly open and close InputStream if you just want the underlying file (e.g. to get file inside zip file)
  • make it easier to add configuration parameters, without having endless list of parameters for the constructor (Factory style, similar to ImageProvider)

breaks plugins; see #10139

File size: 5.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import java.io.File;
5import java.io.IOException;
6import java.nio.file.FileSystems;
7import java.nio.file.Path;
8import java.nio.file.StandardWatchEventKinds;
9import java.nio.file.WatchEvent;
10import java.nio.file.WatchEvent.Kind;
11import java.nio.file.WatchKey;
12import java.nio.file.WatchService;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.Map;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintStyleLoader;
19import org.openstreetmap.josm.gui.mappaint.StyleSource;
20import org.openstreetmap.josm.tools.CheckParameterUtil;
21
22/**
23 * Background thread that monitors certain files and perform relevant actions when they change.
24 * @since 7185
25 */
26public class FileWatcher {
27
28 private WatchService watcher;
29
30 private final Map<Path, StyleSource> styleMap = new HashMap<>();
31
32 /**
33 * Constructs a new {@code FileWatcher}.
34 */
35 public FileWatcher() {
36 try {
37 watcher = FileSystems.getDefault().newWatchService();
38 new Thread(new Runnable() {
39 @Override
40 public void run() {
41 processEvents();
42 }
43 }, "File Watcher").start();
44 } catch (IOException e) {
45 Main.error(e);
46 }
47 }
48
49 /**
50 * Registers a map paint style for local file changes, allowing dynamic reloading.
51 * @param style The style to watch
52 * @throws IllegalArgumentException if {@code style} is null or if it does not provide a local file
53 * @throws IllegalStateException if the watcher service failed to start
54 * @throws IOException if an I/O error occurs
55 */
56 public void registerStyleSource(StyleSource style) throws IOException {
57 CheckParameterUtil.ensureParameterNotNull(style, "style");
58 if (watcher == null) {
59 throw new IllegalStateException("File watcher is not available");
60 }
61 CachedFile cf = style.getCachedFile();
62 // Get underlying file
63 File file = cf.getFile();
64 if (file == null) {
65 throw new IllegalArgumentException("Style "+style+" does not have a local file");
66 }
67 // Get parent directory as WatchService allows only to monitor directories, not single files
68 File dir = file.getParentFile();
69 if (dir == null) {
70 throw new IllegalArgumentException("Style "+style+" does not have a parent directory");
71 }
72 synchronized(this) {
73 // Register directory. Can be called several times for a same directory without problem
74 // (it returns the same key so it should not send events several times)
75 dir.toPath().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);
76 styleMap.put(file.toPath(), style);
77 }
78 }
79
80 /**
81 * Process all events for the key queued to the watcher.
82 */
83 private void processEvents() {
84 if (Main.isDebugEnabled()) {
85 Main.debug("File watcher thread started");
86 }
87 while (true) {
88
89 // wait for key to be signaled
90 WatchKey key;
91 try {
92 key = watcher.take();
93 } catch (InterruptedException x) {
94 return;
95 }
96
97 for (WatchEvent<?> event: key.pollEvents()) {
98 Kind<?> kind = event.kind();
99
100 if (StandardWatchEventKinds.OVERFLOW.equals(kind)) {
101 continue;
102 }
103
104 // The filename is the context of the event.
105 @SuppressWarnings("unchecked")
106 WatchEvent<Path> ev = (WatchEvent<Path>)event;
107 Path filename = ev.context();
108 if (filename == null) {
109 continue;
110 }
111
112 // Only way to get full path (http://stackoverflow.com/a/7802029/2257172)
113 Path fullPath = ((Path)key.watchable()).resolve(filename);
114
115 synchronized(this) {
116 StyleSource style = styleMap.get(fullPath);
117 if (style != null) {
118 Main.info("Map style "+style.getDisplayString()+" has been modified. Reloading style...");
119 //style.loadStyleSource();
120 Main.worker.submit(new MapPaintStyleLoader(Collections.singleton(style)));
121 } else if (Main.isDebugEnabled()) {
122 Main.debug("Received "+kind.name()+" event for unregistered file: "+fullPath);
123 }
124 }
125 }
126
127 // Reset the key -- this step is critical to receive
128 // further watch events. If the key is no longer valid, the directory
129 // is inaccessible so exit the loop.
130 if (!key.reset()) {
131 break;
132 }
133 }
134 }
135}
Note: See TracBrowser for help on using the repository browser.