Java SE 7 Tutorial adds an example of Directory change monitoring to introduce the newly released WatchService API.
However, for users who are used to. NET FileWatcher, if it is used for projects, I think it has two shortcomings:
1. An independent background thread running mechanism should be provided so that the monitoring process can be switched on the background without affecting front-end processing.
2. Java does not have a built-in source event mechanism like. NET, but it can use its built-in Observer/Observable object to implement quasi-events in Observer mode.
The following describes how to delete irrelevant content in the Java SE Tutorial example and add the implementation after the two extensions. This API is relatively new and I hope to discuss with you more:
1. Refer to. NET to define event parameter objects.
Package marvellousworks. practicalpattern. concept. unittest;
Import java. nio. file. WatchEvent. Kind;
/**
* File System Event Type
* @ Author wangxiang
*
*/
Public final class FileSystemEventArgs {
Private final String fileName;
Private final Kind <?> Kind;
Public FileSystemEventArgs (String fileName, Kind <?> Kind ){
This. fileName = fileName;
This. kind = kind;
}
/**
* File path
*/
Public String getFileName () {return fileName ;}
/**
* Operation type: Change, create, and delete
*/
@ SuppressWarnings ("rawtypes ")
Public Kind getKind () {return kind ;}
}
2. Define DirectoryWatcher to monitor a folder. As for how to extend FileWatcher, you can limit the file name and operation type.
Package marvellousworks. practicalpattern. concept. unittest;
Import java. io. IOException;
Import java. nio. file. FileSystems;
Import java. nio. file. Path;
Import java. nio. file. Paths;
Import java. nio. file. WatchEvent;
Import java. nio. file. WatchEvent. Kind;
Import java. nio. file. WatchKey;
Import java. nio. file. WatchService;
Import java. util. Observable;
Import java. util. concurrent. Callable;
Import java. util. concurrent. Executor;
Import java. util. concurrent. Executors;
Import java. util. concurrent. FutureTask;
Import static java. nio. file. StandardWatchEventKinds .*;
/**
* Monitors the update, creation, and deletion of files in a directory (excluding subdirectories)
*
* Modified http://download.oracle.com/javase/tutorial/esstial/io/icationication.html.
* Make it closer to. NET's DirectoryWatcher usage habits
*
* Java does not have an event mechanism similar to. NET source.
* Therefore, Java SE's Observer/Observable object is used to throw a "false" event.
*
* Applicable to Java SE 7
*
* @ Author wangxiang
*
*/
Public class DirectoryWatcher extends Observable {
Private WatchService watcher;
Private Path path;
Private WatchKey key;
Private Executor executor = Executors. newSingleThreadExecutor ();
FutureTask <Integer> task = new FutureTask <Integer> (
New Callable <Integer> (){
Public Integer call () throws InterruptedException {
ProcessEvents ();
Return Integer. valueOf (0 );}});
@ SuppressWarnings ("unchecked ")
Static <T> WatchEvent <T> cast (WatchEvent <?> Event ){
Return (WatchEvent <T>) event;
}
Public DirectoryWatcher (String dir) throws IOException {
Watcher = FileSystems. getDefault (). newWatchService ();
Path = Paths. get (dir );
// Monitors file update, creation, and deletion events in the directory
Key = path. register (watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE );
}
/**
* Start the monitoring process
*/
Public void execute (){
// Start an additional thread to load the Watching process through the thread pool
Executor.exe cute (task );
}
/**
* The closed object cannot be restarted.
* @ Throws IOException
*/
Public void shutdown () throws IOException {
Watcher. close ();
Executor = null;
}
/**
* Monitors File System Events
*/
Void processEvents (){
While (true ){
// Wait until the event signal is obtained
WatchKey signal;
Try {
Signal = watcher. take ();
} Catch (InterruptedException x ){
Return;
}
For (WatchEvent <?> Event: signal. pollEvents ()){
Kind <?> Kind = event. kind ();
// TBD-provide example of how OVERFLOW event is handled
If (kind = OVERFLOW ){
Continue;
}
// Context for directory entry event is the file name of entry
WatchEvent <Path> ev = cast (event );
Path name = ev. context ();
Notifiy (name. getFileName (). toString (), kind );
}
// Prepare for the next monitoring notification
Key. reset ();
}
}
/**
* Notifies the external Observer directories of new event updates.
*/
Void notifiy (String fileName, Kind <?> Kind ){
// The annotation directory has been changed.
SetChanged ();
// Actively notify the observer of changes to the target object status
// Here we use the "push" method of the observer mode.
NotifyObservers (new FileSystemEventArgs (fileName, kind ));
}
}
3. Unit Test
Package marvellousworks. practicalpattern. concept. unittest;
Import static org. junit. Assert .*;
Import java. io. File;
Import java. io. IOException;
Import java. util. ArrayList;
Import java. util. List;
Import java. util. Observable;
Import java. util. Observer;
Import org. junit. Test;
Import static java. nio. file. StandardWatchEventKinds .*;
Public class DirectoryWatcherFixture {
Private static final String DIR_PATH = System. getProperty ("user. dir ");
Private static final File DIR = new File (DIR_PATH );
Private static final String SUFFIX = ". txt ";
Private static final String PREFIX = "test ";
Private static final int ADD_TIMES = 3;
/**
* Observer
* @ Author wangxiang
*
*/
Public class Logger implements Observer {
@ Override
Public void update (Observable observable, Object eventArgs ){
FileSystemEventArgs args = (FileSystemEventArgs) eventArgs;
System. out. printf ("% s has been % s \ n", args. getFileName (), args. getKind ());
AssertTrue (args. getFileName (). startsWith (PREFIX ));
AssertEquals (ENTRY_CREATE, args. getKind ());
}
}
@ Test
Public void testWatchFile () throws IOException, InterruptedException {
DirectoryWatcher watcher = new DirectoryWatcher (DIR_PATH );
Logger l1 = new Logger ();
Watcher. addObserver (l1 );
Watcher.exe cute ();
// Create a series of temporary files
List <String> files = new ArrayList <> ();
For (int I = 0; I <ADD_TIMES; I ++ ){
Files. add (File. createTempFile (PREFIX, SUFFIX, DIR). toString ());
}
// Wait for the execution of background tasks.
Thread. sleep (4000 );
Watcher. shutdown ();
System. out. println ("finished ");
}
}
Test content displayed in the Console window
Test5769907807190550725.txt has been ENTRY_CREATE
Test100007672246246330348.txt has been ENTRY_CREATE
Test1823102943601166149.txt has been ENTRY_CREATE
Finished