Since Java 1.7 there is a way of watching directory without adding additional libraries. On Oracle site there is a little tutorial how to use the WatchService http://docs.oracle.com/javase/tutorial/essential/io/notification.html#try but it is highly not understandable to me. There is no consitent example how to use it.
Therefore would someone kindly show me how to to add listener to directory and invoke method for example: f() when the file is added to directory let's say :"./folder" ?
This piece of code will be called every time you create a file in your watched folder:
Path path = new File("./folder").toPath();
WatchService watchService = FileSystems.getDefault().newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
for (;;) {
try {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind.equals(StandardWatchEventKinds.ENTRY_CREATE)) {
System.out.println("file created");
}
}
} catch (InterruptedException x) {
return;
}
}
Related
I am trying to monitor a folder to get updates on a specific log file, using the Java WatchService in Java 8.
This log file is created using java.util.logging.Logger and has a regular FileHander, which rotates the log file after it reaches a specific size. It creates tracelog0.log initially and after log rotation, it creates tracelog1.log. All new entries still go to tracelog0.log.
I am getting the StandardWatchEventKinds.ENTRY_MODIFY events. But the issue is when the Log file is rotated I do not get any event and Log monitoring Thread seems to be killed. I have added a new StandardWatchEventKinds.ENTRY_DELETE event as well but still does not work.
Here is my Thread that does the monitoring. Please let me know if this is the correct approach or not. Please suggest any alternative solutions for this case.
class WatcherThread implements Runnable {
public static final String WORKING_DIRECTORY = "/var/log/data/";
Path logDir = Paths.get(WORKING_DIRECTORY);
WatchEvent.Kind<?> [] kinds = { StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY};
boolean runThread = true;
private WatchService watcher;
private boolean logRotated = false;
public WatcherThread(){
try {
watcher = logDir.getFileSystem().newWatchService();
logDir.register(watcher, kinds);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run() {
while(runThread) {
// Get next event or wait if none are yet present
try {
WatchKey key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {
// Retrieve event kind
WatchEvent.Kind<?> kind = event.kind();
if (StandardWatchEventKinds.OVERFLOW == kind) {
continue;
}else {
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path filename = ev.context();
Path child = logDir.resolve(filename);
System.out.println("New file created Filename: "+child.getFileName());
if(child.getFileName().startsWith("tracelog")) {
System.out.println("tracelog file rotated.");
//Possible tracelog0.log file is rotated.
logRotated = true;
}
}
}
boolean isValid = key.reset();
if(!isValid) {
System.out.println("Unable to reset the Key exiting!!!");
runThread = false;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public boolean isLogRotated() {
return logRotated;
}
}
I think I have figured the issue. The issue is due to the java.util.Logger framework. When the Logfile rotation happens it does not delete the actual Logging file.
The current Log is stored in a cache by the Logger. When it needs to rotate the log file based on the log size. It creates a new file and copies the content to the new file.
Example
When the server starts the below files get created
/var/log/data/tracelog0.log
/var/log/data/tracelog0.log.lck
when log file rotates it creates a new file tracelog1.log
/var/log/data/tracelog0.log
/var/log/data/tracelog0.log.lck
/var/log/data/tracelog1.log
So only MODIFY and CREATE event are triggered. The DELETE event does not get triggered.
I think this may vary based on the Logger that we use. It's better to look at how the logger rotates the log file.
I'm using inotify nio wrappers to catch file creation in a specific directory. Okay, I have this
private final String tmpDir1 = Files.createTempDirectory(null);
private final String tmpDir2 = Files.createTempDirectory(null);
WatchService watchService = FileSystems.getDefault.newWatchService()
Paths.get(tmpDir1).register(watchService, ENTRY_CREATE)
Paths.get(tmpDir2).register(watchService, ENTRY_CREATE)
public String getModifiedFilePath(){
WatchKey key = ((WatchEvent<Path>) watchService.take())
//Does p belongs to tmpDir1 or tmpDir2?
Path p = ((WatchEvent<Path>)(key.pollEvents().iterator().next())).context()
return //???
}
As specified in the documentation of the method WatchEvent#context
Returns the context for the event. In the case of ENTRY_CREATE,
ENTRY_DELETE, and ENTRY_MODIFY events the context is a Path that is
the relative path between the directory registered with the watch
service, and the entry that is created, deleted, or modified.
But sun.nio.fs.AbstractWatchKey contains the field private final Path dir. But the class is package private. Is there a way to get the directory name the file returned by the WatchEvent#context belongs to?
UPD: Creating inotify instance for each directory I want to watch sounds really wierd.
Enjoy
public Path getModifiedFilePath() throws InterruptedException {
WatchKey key = watchService.take();
return (Path) key.watchable();
}
By using the Java Reflection API you can modify the access modifiers.
Set private field value with reflection
I have bellow code to monitor a directory for any file changes.
public class DirectoryWatcherExample {
public static void main(String[] args) {
WatchService watchService
= FileSystems.getDefault().newWatchService();
Path path = Paths.get(System.getProperty("user.home"));
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println(
"Event kind:" + event.kind()
+ ". File affected: " + event.context() + ".");
}
key.reset();
}
}
}
Currently this is a stand alone and is being executed from my eclipse IDE. Now I need to automate it and I should not run this class everytime. Would you please help me as to how I can remove main() method and run it? Can I create a exeucutable jar and run it? If i run it once then will the program continuosly listen the directlroy? When will the program stops listening the directory? Is there any time limit, I mean after a few days?
I have make some code for detect changed in directory C:/java/newfolder it working good. i have given below.
import java.nio.file.*;
import java.util.List;
public class DirectoryWatchExample {
public static void testForDirectoryChange(Path myDir){
try {
WatchService watcher = myDir.getFileSystem().newWatchService();
myDir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey watckKey = watcher.take();
List<WatchEvent<?>> events = watckKey.pollEvents();
for (WatchEvent event : events) {
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("Created: " + event.context().toString());
}
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("Delete: " + event.context().toString());
}
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("Modify: " + event.context().toString());
}
}
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
}
public static void main (String[] args) {
Path myDir = Paths.get("c:/java/newfolder/");
//define a folder root
testForDirectoryChange(myDir);
}
}
now i watching only the directory. But i need to watch only the all sub directory.
for ex : c:/java/newfolder/folder1, c:/java/newfolder/folder2, c:/java/newfolder/folder3......etc
i given examples above sub directory
c:/java/newfolder/*..
i need to watch the all sub directory give me some solutions ?
What you want to do is register the WatchService recursively and continue to register it upon subsequent ENTRY_CREATE events. A complete example is provided by Oracle here: http://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java
Normally I wouldn't place a post with a single link, however I doubt Oracle will be taking down a fairly basic tutorial and the answer is far too verbose. Just in case though, examples are easily found by searching for "java watchservice recursive".
Here is what I came with..
final WatchService watcher = FileSystems.getDefault().newWatchService();
final SimpleFileVisitor<Path> fileVisitor = new SimpleFileVisitor<Path>(){
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
};
Files.walkFileTree(Paths.get("/directory/to/monitor"), fileVisitor);
I'm not familiar with the Path API, so take the following with a grain of salt.
You're registering one directory for monitoring, and will receive notifications whenever one of its direct descendants is modified / created / deleted.
The first thing you need to do is register all of its subdirectories for watching:
// Used to filter out non-directory files.
// This might need to filter '.' and '..' out, not sure whether they're returned.
public class DirectoryFilter implements FileFilter {
public boolean accept(File file) {
return file.isDirectory();
}
}
// Add this at the end of your testForDirectoryChange method
for(File dir: myDir.toFile().listFiles(new DirectoryFilter())) {
testForDirectoryChange(dir.toPath());
}
This will recursively explore your file structure and register every directory for watching.
Note that if your directory tree is too deep, recursion might not be an acceptable solution and you might need to 'iterify' it.
The second thing you need to do is, whenever you receive a directory creation event, not forget to register the new directory for monitoring.
That's how I'd do it anyway, but not having a valid Java 1.7 installation at hand, I can't test it. Do let me know if it works!
So what you want is listing the files/subdirectories within a directory?
You can use:
File dir = new File("c:/java/newfolder/");
File[] files = dir.listFiles();
listFiles() gives you the Files in a directory or null it it's not a directory.
Then you can do:
for (File file: files){
if (file.listFiles != null){
//It's a subdirectory
File [] subdirFiles = file.listfiles;
}
}
WatchService sounded like an exciting idea ... unfortunately it seems to be as low-level as warned in the tutorial/api plus doesn't really fit into the Swing event model (or I'm missing something obvious, a not-zero probability
Taking the code from WatchDir example in the tutorial (simplyfied to handle a single directory only), I basically ended up
extend SwingWorker
do the registration stuff in the constructor
put the endless loop waiting for a key in doInBackground
publish each WatchEvent when retrieved via key.pollEvents()
process the chunks by firing propertyChangeEvents with the deleted/created files as newValue
#SuppressWarnings("unchecked")
public class FileWorker extends SwingWorker<Void, WatchEvent<Path>> {
public static final String DELETED = "deletedFile";
public static final String CREATED = "createdFile";
private Path directory;
private WatchService watcher;
public FileWorker(File file) throws IOException {
directory = file.toPath();
watcher = FileSystems.getDefault().newWatchService();
directory.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
}
#Override
protected Void doInBackground() throws Exception {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return null;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
publish((WatchEvent<Path>) event);
}
// reset key return if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
break;
}
}
return null;
}
#Override
protected void process(List<WatchEvent<Path>> chunks) {
super.process(chunks);
for (WatchEvent<Path> event : chunks) {
WatchEvent.Kind<?> kind = event.kind();
Path name = event.context();
Path child = directory.resolve(name);
File file = child.toFile();
if (StandardWatchEventKinds.ENTRY_DELETE == kind) {
firePropertyChange(DELETED, null, file);
} else if (StandardWatchEventKinds.ENTRY_CREATE == kind) {
firePropertyChange(CREATED, null, file);
}
}
}
}
The basic idea is to make using code blissfully un-aware of the slimy details: it listens to the property changes and f.i. updates arbitrary models as appropriate:
String testDir = "D:\\scans\\library";
File directory = new File(testDir);
final DefaultListModel<File> model = new DefaultListModel<File>();
for (File file : directory.listFiles()) {
model.addElement(file);
}
final FileWorker worker = new FileWorker(directory);
PropertyChangeListener l = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (FileWorker.DELETED == evt.getPropertyName()) {
model.removeElement(evt.getNewValue());
} else if (FileWorker.CREATED == evt.getPropertyName()) {
model.addElement((File) evt.getNewValue());
}
}
};
worker.addPropertyChangeListener(l);
JXList list = new JXList(model);
Seems to work, but I feel uncomfortable
Outing myself as the thread agnostic I am: all example snippets I have seen so far do block the waiting thread by using watcher.take(). Why do they do it? Would expect at least some use watcher.poll() and sleep a bit.
the SwingWorker publish method doesn't quite seem to fit: for now it's okay, as I'm watching one directory only (didn't want to galopp too far into the wrong direction :) When trying to watch several directories (as in the original WatchDir example) there are several keys and the WatchEvent relative to one of those. To resolve the path, I would need both the event and the directory [A] the key is watching - but can pass on only one. Most probably got the distribution of logic wrong, though
[A] Edited (triggered by #trashgods's comment) - it's actually not the key I have to pass around along with the event, it's the directory it's reporting the changes on. Changed the question accordingly
FYI, this question is cross-posted to the OTN swing forum
Addendum
Reading the api doc of WatchKey:
Where there are several threads retrieving signalled keys from a watch
service then care should be taken to ensure that the reset method is
only invoked after the events for the object have been processed.
seems to imply that the events should
be processed on the same thread that retrieved the WatchKey
shouldn't be touched after the key is reset
Not entirely sure, but combined with the (future) requirement to recursively watching directories (more than one) decided to follow #Eels advice, kind of - will soon post the code I settled on
EDIT
just accepted my own answer - will humbly revert that if anybody has reasonable objections
Because your background thread is devoted entirely to watching, take() is the right choice. It effectively hides the platform dependent implementation, which may either forward or poll. One of the poll() methods would be appropriate if, for example, your background thread also needed to examine other queues in series with the WatchService.
Addendum: Because the WatchKey has state, it should probably not be forwarded to process(). The context() of a WatchEvent is a "relative path between the directory registered with the watch service and the entry that is created, deleted, or modified." One of the resolve() methods should work if the directories share a common root.
Actually, #Eels's comment didn't stop knocking in the back of my head - and finally registered: it's the way to go, but there is no need for any "artificial" struct, because we already have the perfect candidate - it's the PropertyChangeEvent itself :-)
Taking the overall process description from my question, the first three bullets remain the same
same: extend SwingWorker
same: do the registration stuff in the constructor
same: put the endless loop waiting for a key in doInBackground
changed: create the appropriate PropertyChangeEvent from each WatchEvent when retrieved via key.pollEvents and publish the PropertyChangeEvent
changed: fire the previously created event in process(chunks)
Revised FileWorker:
#SuppressWarnings("unchecked")
public class FileWorker extends SwingWorker<Void, PropertyChangeEvent> {
public static final String FILE_DELETED = StandardWatchEventKinds.ENTRY_DELETE.name();
public static final String FILE_CREATED = StandardWatchEventKinds.ENTRY_CREATE.name();
public static final String FILE_MODIFIED = StandardWatchEventKinds.ENTRY_MODIFY.name();
// final version will keep a map of keys/directories (just as in the tutorial example)
private Path directory;
private WatchService watcher;
public FileWorker(File file) throws IOException {
directory = file.toPath();
watcher = FileSystems.getDefault().newWatchService();
directory.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
}
#Override
protected Void doInBackground() throws Exception {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return null;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
publish(createChangeEvent((WatchEvent<Path>) event, key));
}
// reset key return if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
break;
}
}
return null;
}
/**
* Creates and returns the change notification. This method is called from the
* worker thread while looping through the events as received from the Watchkey.
*
* #param event
* #param key
*/
protected PropertyChangeEvent createChangeEvent(WatchEvent<Path> event, WatchKey key) {
Path name = event.context();
// real world will lookup the directory from the key/directory map
Path child = directory.resolve(name);
PropertyChangeEvent e = new PropertyChangeEvent(this, event.kind().name(), null, child.toFile());
return e;
}
#Override
protected void process(List<PropertyChangeEvent> chunks) {
super.process(chunks);
for (PropertyChangeEvent event : chunks) {
getPropertyChangeSupport().firePropertyChange(event);
}
}
}
With regard to your second point, couldn't you create a class that holds both WatchEvent and key and have the SwingWorker's second generic parameter be this type? Sorry, I know you've already thought of this, so I guess my question is: any down-side to doing this?