Merge commit 'b809a171b6dfb81ed9ef9e84870dcbcbc5912f0e'
[simantics/platform.git] / bundles / org.simantics.fileimport / src / org / simantics / fileimport / dropins / FileImportDropins.java
1 package org.simantics.fileimport.dropins;\r
2 \r
3 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;\r
4 import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;\r
5 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;\r
6 import static java.nio.file.StandardWatchEventKinds.OVERFLOW;\r
7 \r
8 import java.io.IOException;\r
9 import java.io.RandomAccessFile;\r
10 import java.nio.file.FileSystem;\r
11 import java.nio.file.FileSystemException;\r
12 import java.nio.file.FileVisitResult;\r
13 import java.nio.file.Files;\r
14 import java.nio.file.Path;\r
15 import java.nio.file.SimpleFileVisitor;\r
16 import java.nio.file.WatchEvent;\r
17 import java.nio.file.WatchEvent.Kind;\r
18 import java.nio.file.WatchKey;\r
19 import java.nio.file.WatchService;\r
20 import java.nio.file.attribute.BasicFileAttributes;\r
21 import java.util.HashMap;\r
22 import java.util.Map;\r
23 import java.util.Optional;\r
24 import java.util.concurrent.atomic.AtomicBoolean;\r
25 \r
26 import org.simantics.fileimport.Activator;\r
27 import org.simantics.fileimport.FileImportService;\r
28 \r
29 /**\r
30  * Directory watcher based on {@link java.nio.file.WatchService} which will listen to file changes inside the dropins directory\r
31  * ~/workspace/.metadata/plugins/org.simantics.fileimport/dropins\r
32  * \r
33  * @author Jani Simomaa\r
34  *\r
35  */\r
36 public class FileImportDropins {\r
37     \r
38     private static Thread watcherThread = null;\r
39     private static DropinsFolderWatcher watcher = null;\r
40 \r
41     /**\r
42      * Start watching the dropins folder which are located in\r
43      * ~/workspace/.metadata/plugins/org.simantics.fileimport/dropins\r
44      */\r
45     public static void watchDropinsFolder() {\r
46         if (watcher == null && watcherThread == null) {\r
47             try {\r
48                 watcher = new DropinsFolderWatcher(Activator.getDropinsFolder());\r
49                 watcherThread = new Thread(watcher, "Simantics Dropins Folder watcher thread");\r
50                 watcherThread.setDaemon(true);\r
51                 watcherThread.start();\r
52             } catch (IOException e) {\r
53                 e.printStackTrace();\r
54             }\r
55         }\r
56     }\r
57     \r
58     /**\r
59      * Stop watching the dropins folder\r
60      */\r
61     public static void unwatchDropinsFolder() {\r
62         watcher.stop();\r
63         try {\r
64             watcherThread.join(500);\r
65             if (watcherThread.isAlive())\r
66                 watcherThread.interrupt();\r
67         } catch (InterruptedException e) {\r
68             e.printStackTrace();\r
69         }\r
70         watcherThread = null;\r
71         watcher = null;\r
72     }\r
73     \r
74     private static class DropinsFolderWatcher implements Runnable {\r
75 \r
76         private final Path dropinsFolder;\r
77         private final WatchService ws;\r
78         private final AtomicBoolean stopped = new AtomicBoolean(true);\r
79         \r
80         private final Map<WatchKey, Path> keys = new HashMap<>();\r
81         \r
82         public DropinsFolderWatcher(Path dropinsFolder) throws IOException {\r
83             this.dropinsFolder = dropinsFolder;\r
84             FileSystem fs = dropinsFolder.getFileSystem();\r
85             this.ws = fs.newWatchService();\r
86             registerAll(this.dropinsFolder);\r
87         }\r
88         \r
89         private static void syncPath(Path f) throws IOException {\r
90             // Does not seem to need 's' according to unit test in Windows\r
91             boolean synced = false;\r
92             int count = 0;\r
93             while (!synced) {\r
94                 try (RandomAccessFile raf = new RandomAccessFile(f.toFile(), "rw")) {\r
95                     raf.getFD().sync();\r
96                     synced = true;\r
97                 } catch (IOException e) {\r
98                     if (count == 3) {\r
99                         throw e;\r
100                     } else {\r
101                         try {\r
102                             Thread.sleep(50);\r
103                         } catch (InterruptedException e1) {\r
104                             e1.printStackTrace();\r
105                         }\r
106                         count++;\r
107                     }\r
108                 }\r
109             }\r
110         }\r
111         \r
112         @Override\r
113         public void run() {\r
114             stopped.set(false);\r
115 \r
116             while (!stopped.get()) {\r
117                 try {\r
118                     WatchKey key = ws.take();\r
119                     for (WatchEvent<?> watchEvent : key.pollEvents()) {\r
120                         if (OVERFLOW == watchEvent.kind())\r
121                             continue; // loop\r
122                         \r
123                         @SuppressWarnings("unchecked")\r
124                         WatchEvent<Path> pathEvent = (WatchEvent<Path>) watchEvent;\r
125                         Kind<Path> kind = pathEvent.kind();\r
126                         \r
127                         Path parent = keys.get(key);\r
128                         Path newPath = parent.resolve(pathEvent.context());\r
129                         if (FileImportService.DB_FILE.equals(newPath.getFileName().toString()))\r
130                             continue;\r
131                         if (ENTRY_CREATE == kind) {\r
132                             System.out.println("New path created: " + newPath);\r
133                             int current = 0;\r
134                             \r
135                             while (!Files.isWritable(newPath) && current <= 10) {\r
136                                 System.out.println("Sleeping for file import (current=" + current +")");\r
137                                 Thread.sleep(200);\r
138                                 current++;\r
139                             }\r
140                             \r
141                             FileImportService.performFileImport(newPath, Optional.of(t -> {\r
142                                 if (t instanceof FileSystemException) {\r
143                                     try {\r
144                                         syncPath(newPath);\r
145                                     } catch (IOException e) {\r
146                                         e.printStackTrace();\r
147                                     }\r
148                                     FileImportService.performFileImport(newPath, Optional.empty());\r
149                                 } else {\r
150                                     t.printStackTrace();\r
151                                 }\r
152                             }));\r
153                             register(newPath);\r
154                             \r
155                         } else if (ENTRY_MODIFY == kind) {\r
156                             System.out.println("New path modified: " + newPath);\r
157                         } else if (ENTRY_DELETE == kind) {\r
158                             System.out.println("New path deleted: " + newPath);\r
159                             FileImportService.removeResourceForFile(newPath.toAbsolutePath(), Optional.empty());\r
160                         }\r
161                     }\r
162                     if (!key.reset()) {\r
163                         keys.remove(key);\r
164 //                        break; // loop\r
165                     }\r
166                 } catch (InterruptedException e) {\r
167                     if (!stopped.get())\r
168                         e.printStackTrace();\r
169                 } catch (Throwable t) {\r
170                     t.printStackTrace();\r
171                 }\r
172             }\r
173         }\r
174         \r
175         public void stop() {\r
176             stopped.set(true);\r
177         }\r
178         \r
179         private void registerAll(Path path) throws IOException {\r
180             Files.walkFileTree(path, new SimpleFileVisitor<Path>() {\r
181                 \r
182                 @Override\r
183                 public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) throws IOException {\r
184                     register(file);\r
185                     return FileVisitResult.CONTINUE;\r
186                 }\r
187             });\r
188         }\r
189 \r
190         private void register(Path path) throws IOException {\r
191             if (Files.isDirectory(path)) {\r
192                 WatchKey key = path.toAbsolutePath().register(ws, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);\r
193                 keys.put(key, path);\r
194             }\r
195         }\r
196     }\r
197 }\r