JPathwatch
JPathwatch
Példaprogram a következő:
1public class JPathwatchSample { 2 3 public static void main(String[] args) throws Exception { 4 WatchService watchService = FileSystems.getDefault().newWatchService(); 5 Path watchedPath = Paths.get("c:/temp"); 6 WatchEvent.Kind[] eventsToWatch = new WatchEvent.Kind[] { 7 StandardWatchEventKind.ENTRY_CREATE, 8 StandardWatchEventKind.ENTRY_DELETE, 9 StandardWatchEventKind.ENTRY_MODIFY, 10 StandardWatchEventKind.OVERFLOW, 11 ExtendedWatchEventKind.ENTRY_RENAME_FROM, 12 ExtendedWatchEventKind.ENTRY_RENAME_TO, 13 }; 14 WatchKey key = null; 15 try { 16 key = watchedPath.register(watchService, eventsToWatch, ExtendedWatchEventModifier.FILE_TREE); 17 } 18 catch (UnsupportedOperationException uox) { 19 System.err.println("file watching not supported!"); 20 } 21 catch (IOException iox) { 22 System.err.println("I/O errors"); 23 } 24 25 for (;;) { 26 // take() will block until a file has been created/deleted 27 WatchKey signalledKey; 28 try { 29 signalledKey = watchService.take(); 30 } 31 catch (InterruptedException ix) { 32 continue; 33 } 34 catch (ClosedWatchServiceException cwse) { 35 System.out.println("watch service closed, terminating."); 36 break; 37 } 38 39 // get list of events from key 40 List<WatchEvent> list = signalledKey.pollEvents(); 41 42 // VERY IMPORTANT! call reset() AFTER pollEvents() to allow the 43 // key to be reported again by the watch service 44 signalledKey.reset(); 45 46 System.out.println("--------------------------------"); 47 // we'll simply print what has happened; real applications 48 // will do something more sensible here 49 for (WatchEvent e : list) { 50 String message = ""; 51 if (e.kind() == StandardWatchEventKind.ENTRY_CREATE) { 52 Path context = (Path) e.context(); 53 message = context.toString() + " created"; 54 } 55 else if (e.kind() == StandardWatchEventKind.ENTRY_DELETE) { 56 Path context = (Path) e.context(); 57 message = context.toString() + " deleted"; 58 } 59 else if (e.kind() == StandardWatchEventKind.ENTRY_MODIFY) { 60 Path context = (Path) e.context(); 61 message = context.toString() + " modified"; 62 } 63 else if (e.kind() == ExtendedWatchEventKind.ENTRY_RENAME_FROM) { 64 Path context = (Path) e.context(); 65 message = context.toString() + " renamed from"; 66 } 67 else if (e.kind() == ExtendedWatchEventKind.ENTRY_RENAME_TO) { 68 Path context = (Path) e.context(); 69 message = context.toString() + " renamed to"; 70 } 71 else if (e.kind() == StandardWatchEventKind.OVERFLOW) { 72 message = "OVERFLOW: more changes happened than we could retreive"; 73 } 74 System.out.println(message); 75 } 76 } 77 } 78}
Jellemzői:
- java 5
- java 7 api kompatibilis, ami jó. Könnyen át lehet állni java7-re
- java 7 api kompatibilis, ami rosz. Nem szép az API. pl:
-
StandardWatchEventKinds
: Milyen elbaszott egy név -
ENTRY_CREATE
: Minden esemény ENTRY-vel kezdődik. De mivel egy konstans család tagjai teljesen felesleges. Persze a CREATE, MODIFY, stb szavak könnyen ütközhetnek más API-val, de akkor miért pont az ENTRY-vel prefixelt. Prefix sem kell, ha nem az elbaszottStandardWatchEventKinds
osztályba tartozik. - Kicsit kibővíti a java7-es API-t. Maguk a kibővítések jók (például a rekurzív figyelés). De emiatt lesz teljesen kompatibilis.
-
- rekurzívan is tud figyelni könyvtárszerkezetet
- szinkron API.
- szálat blokkolva (
take()
) és nem blokkolva (poll()
) is használható. - egy
take()
hívásra egyszerre több eventet is kapunk, amiből lehet következtetni a végrehajtandó műveletre. - Könyvtár létrehozás:
-
dir
created
-
- könyvtár törlése:
-
dir
deleted
-
- file létrehozása
-
file.txt
created
-
- file módosítása
-
file.txt
modified
-
- file átnevezése
-
file.txt
renamed from -
file2.txt
renamed to
-
- file törlés
-
file2.txt
deleted
-
- kicsi file másolása a figyelt könvtárba
-
stylesheet.css
created -
stylesheet.css
modified -
stylesheet.css
modified
-
- nagy file másolása
-
alcatraz.101.hdtv-lol.avi
created -
alcatraz.101.hdtv-lol.avi
modified - újjabb event handling ciklus
-
alcatraz.101.hdtv-lol.avi
modified
-
- alkönyvtár létrehozás
-
dir\subdiir
created -
dir
modified
-
- alkönyvtárban file ltrehozás
-
dir\subdiir\subfile.txt
created -
dir\subdiir
modified
-
- alkönyvtárban file módosulás
-
dir\subdiir\subfile.txt
modified
-
File másolás kezelésére itt készítő tanácsa (FAQ):
Can I use jpathwatch to find out when a program finished writing to a file?
This is a bit tricky, but doable. First of all the problem is that most operating systems do not provide events for that, all they report is if a file has been created, modified or deleted in a directory, which is what jpathwatch passes back to your application code.
So effectively, you can find out when a program writes to a file, but you can’t find out when it’s done with it.
What I recommend in this case is to use heuristics: Assume that a process is done writing to a file when the file isn’t modified for a while (say, ten seconds or so).
Assume you watch a directory on an FTP server, and you want to pick up files after they have finished uploading to that directory.
In a loop, you
poll()
on the WatchService for events on that directory. For each file that is created or modified (ENTRY_CREATE/ENTRY_MODIFY
), you store the time of the last event on that file. Maintain a sorted list of these times; the oldest file in the list is the one that is going to expire first. Remove files that have expired from your sorted list (calculate if their last event time plus the timeout is less than the current time) and flag them as ‘done’.
For the now oldest file in your list that hasn’t expired yet, calculate the time until it will expire, and call
poll()
with that duration. If you have no more files in your list, calltake()
instead ofpoll()
to wait for new files to appear.
Az én kis problémámra a következő implementációt hoztam össze.
Másodjára implementálva a megoldást kiderül, hogy a JNotify esetén mely kódrészleteket tudom újra felhasználni. A JNotify-ban megismert BatchExecutor nagyon jól ki lehet emelni. A szemafor váltogatást interfész mögé teszem és már egy csomó mindent nem kell újra megírnom.
1public interface IWorkerSemafor { 2 public void working(); 3 public void notWorking(); 4} 5 6class BatchExecutor implements Runnable { 7 private final IWorkerSemafor semafor; 8 9 BatchExecutor(IWorkerSemafor semafor) { 10 this.semafor = semafor; 11 } 12 13 @Override 14 public void run() { 15 this.semafor.working(); 16 try { 17 Thread.sleep(1 * 1000); // 1 sec 18 } 19 catch (InterruptedException e) { 20 return; 21 } 22 finally { 23 this.semafor.notWorking(); 24 } 25 try { 26 System.out.println("executing command... "+new Date()); 27 Runtime.getRuntime().exec("cmd /c build.bat "); 28 } 29 catch (IOException e) { 30 e.printStackTrace(); 31 } 32 } 33}
És a következő egyszerű kódot írtam, hogy meghajthassam a funkcionalitást:
1public class JPathwatchSample2 { 2 3 public static void main(String[] args) throws Exception { 4 Thread watcherT = new Thread(watcher); 5 watcherT.start(); 6 System.out.println("JPathwatch: Press CTR+C to stop"); 7 for (;;) { 8 Thread.sleep(1000); 9 } 10 } 11 12 static volatile boolean workerRunning = false; 13 14 static Runnable watcher = new BackgroundWatcher(); 15 16 static class BackgroundWatcher implements Runnable, IWorkerSemafor { 17 public void run() { 18 WatchService watchService = FileSystems.getDefault().newWatchService(); 19 Path watchedPath = Paths.get("c:/temp"); 20 setup(watchService, watchedPath); 21 for (;;) { 22 WatchKey signalledKey; 23 try { 24 signalledKey = watchService.take(); 25 } 26 catch (InterruptedException ix) { 27 continue; 28 } 29 catch (ClosedWatchServiceException cwse) { 30 System.out.println("watch service closed, pls exit"); 31 break; 32 } 33 List<WatchEvent<?>> list = signalledKey.pollEvents(); 34 signalledKey.reset(); 35 if (workerRunning) { 36 continue; 37 } 38 if (null == list || 0 == list.size()) { 39 continue; 40 } 41 Thread worker = new Thread(new BatchExecutor(this)); 42 worker.start(); 43 } 44 } 45 private void setup(WatchService watchService, Path watchedPath) { 46 WatchEvent.Kind<?>[] eventsToWatch = new WatchEvent.Kind[] { 47 StandardWatchEventKind.ENTRY_CREATE, 48 StandardWatchEventKind.ENTRY_DELETE, 49 StandardWatchEventKind.ENTRY_MODIFY, 50 StandardWatchEventKind.OVERFLOW, 51 ExtendedWatchEventKind.ENTRY_RENAME_FROM, 52 ExtendedWatchEventKind.ENTRY_RENAME_TO, 53 }; 54 WatchKey key = null; 55 try { 56 key = watchedPath.register(watchService, eventsToWatch, ExtendedWatchEventModifier.FILE_TREE); 57 } 58 catch (UnsupportedOperationException uox) { 59 uox.printStackTrace(); 60 } 61 catch (IOException iox) { 62 iox.printStackTrace(); 63 } 64 } 65 @Override 66 public void working() { 67 workerRunning = true; 68 } 69 @Override 70 public void notWorking() { 71 workerRunning = false; 72 } 73 }; 74}
Directory Watcher
- Mi az a Directory Watcher?
- Java directory watcher
- JNotify
- JPathwatch
- Java 7 NIO notification
- Pollozó megoldás
- Groovy directory diff
- Groovy pollozó megoldás