Assistindo a um diretório para alterações em Java

Eu quero assistir a um diretório para alterações de arquivos. E usei o WatchService no java.nio. Eu posso ouvir com sucesso o evento criado por arquivo. Mas não posso ouvir o evento de modificação de arquivo. Eu verifiquei o tutorial java oficial , mas ainda está lutando.

Aqui está o código fonte.

import static java.nio.file.LinkOption.NOFOLLOW_LINKS; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchEvent.Kind; import java.nio.file.WatchKey; import java.nio.file.WatchService; public class MainWatch { public static void watchDirectoryPath(Path path) { // Sanity check - Check if path is a folder try { Boolean isFolder = (Boolean) Files.getAttribute(path, "basic:isDirectory", NOFOLLOW_LINKS); if (!isFolder) { throw new IllegalArgumentException("Path: " + path + " is not a folder"); } } catch (IOException ioe) { // Folder does not exists ioe.printStackTrace(); } System.out.println("Watching path: " + path); // We obtain the file system of the Path FileSystem fs = path.getFileSystem(); // We create the new WatchService using the new try() block try (WatchService service = fs.newWatchService()) { // We register the path to the service // We watch for creation events path.register(service, ENTRY_CREATE); path.register(service, ENTRY_MODIFY); path.register(service, ENTRY_DELETE); // Start the infinite polling loop WatchKey key = null; while (true) { key = service.take(); // Dequeueing events Kind kind = null; for (WatchEvent watchEvent : key.pollEvents()) { // Get the type of the event kind = watchEvent.kind(); if (OVERFLOW == kind) { continue; // loop } else if (ENTRY_CREATE == kind) { // A new Path was created Path newPath = ((WatchEvent) watchEvent) .context(); // Output System.out.println("New path created: " + newPath); } else if (ENTRY_MODIFY == kind) { // modified Path newPath = ((WatchEvent) watchEvent) .context(); // Output System.out.println("New path modified: " + newPath); } } if (!key.reset()) { break; // loop } } } catch (IOException ioe) { ioe.printStackTrace(); } catch (InterruptedException ie) { ie.printStackTrace(); } } public static void main(String[] args) throws IOException, InterruptedException { // Folder we are going to watch // Path folder = // Paths.get(System.getProperty("C:\\Users\\Isuru\\Downloads")); File dir = new File("C:\\Users\\Isuru\\Downloads"); watchDirectoryPath(dir.toPath()); } } 

Na verdade, você se inscreveu incorretamente em events. Apenas o último ouvinte foi registrado com o tipo de evento ENTRY_DELETE.

Para se inscrever para todos os tipos de events de uma só vez você deve usar:

  path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE); 

Atenção! Auto promoção desavergonhada!

Eu criei um wrapper em torno do WatchService do Java 1.7 que permite registrar um diretório e qualquer número de padrões glob . Essa class cuidará da filtragem e emitirá apenas events nos quais você está interessado.

 DirectoryWatchService watchService = new SimpleDirectoryWatchService(); // May throw watchService.register( // May throw new DirectoryWatchService.OnFileChangeListener() { @Override public void onFileCreate(String filePath) { // File created } @Override public void onFileModify(String filePath) { // File modified } @Override public void onFileDelete(String filePath) { // File deleted } }, , // Directory to watch , // Eg "*.log" , // Eg "input-?.txt" ... // As many patterns as you like ); watchService.start(); 

O código completo está neste repo .

Eu fiz algumas aulas para isso.

 public interface FileAvailableListener { public void fileAvailable(File file) throws IOException; } 

e

 public class FileChange { private long lastModified; private long size; private long lastCheck; public FileChange(File file) { this.lastModified=file.lastModified(); this.size=file.length(); this.lastCheck = System.currentTimeMillis(); } public long getLastModified() { return lastModified; } public long getSize() { return size; } public long getLastCheck() { return lastCheck; } public boolean isStable(FileChange other,long stableTime) { boolean b1 = (getLastModified()==other.getLastModified()); boolean b2 = (getSize()==other.getSize()); boolean b3 = ((other.getLastCheck()-getLastCheck())>stableTime); return b1 && b2 && b3; } } 

e

 public class DirectoryWatcher { private Timer timer; private List tasks = new ArrayList(); public DirectoryWatcher() throws URISyntaxException, IOException, InterruptedException { super(); timer = new Timer(true); } public void addDirectoryMonitoringTask(DirectoryMonitorTask task,long period) { tasks.add(task); timer.scheduleAtFixedRate(task, 5000, period); } public List getTasks() { return Collections.unmodifiableList(tasks); } public Timer getTimer() { return timer; } } 

e

 class DirectoryMonitorTask extends TimerTask { public final static String DIRECTORY_NAME_ARCHIVE="archive"; public final static String DIRECTORY_NAME_ERROR="error"; public final static String LOCK_FILE_EXTENSION=".lock"; public final static String ERROR_FILE_EXTENSION=".error"; public final static String FILE_DATE_FORMAT="yyyyMMddHHmmssSSS"; private String name; private FileAvailableListener listener; private Path directory; private File directoryArchive; private File directoryError; private long stableTime; private FileFilter filter; private WatchService watchService; private SimpleDateFormat dateFormatter = new SimpleDateFormat(FILE_DATE_FORMAT); private Hashtable fileMonitor = new Hashtable(); public DirectoryMonitorTask(String name,FileAvailableListener listener,Path directory,long stableTime,FileFilter filter) throws IOException { super(); this.name=name; this.listener=listener; this.directory=directory; this.stableTime=stableTime; if (stableTime<1) { stableTime=1000; } this.filter=filter; validateNotNull("Name",name); validateNotNull("Listener",listener); validateNotNull("Directory",directory); validate(directory); directoryArchive = new File(directory.toFile(),DIRECTORY_NAME_ARCHIVE); directoryError = new File(directory.toFile(),DIRECTORY_NAME_ERROR); directoryArchive.mkdir(); directoryError.mkdir(); // log("Constructed for "+getDirectory().toFile().getAbsolutePath()); initialize(); // watchService = FileSystems.getDefault().newWatchService(); directory.register(watchService,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_MODIFY); log("Started"); } private void initialize() { File[] files = getDirectory().toFile().listFiles(); for (File file : files) { if (isLockFile(file)) { file.delete(); } else if (acceptFile(file)) { fileMonitor.put(file,new FileChange(file)); log("Init file added -"+file.getName()); } } } public SimpleDateFormat getDateFormatter() { return dateFormatter; } public Path getDirectory() { return directory; } public FileAvailableListener getListener() { return listener; } public String getName() { return name; } public WatchService getWatchService() { return watchService; } public long getStableTime() { return stableTime; } public File getDirectoryArchive() { return directoryArchive; } public File getDirectoryError() { return directoryError; } public FileFilter getFilter() { return filter; } public Iterator getMonitoredFiles() { return fileMonitor.keySet().iterator(); } @Override public void run() { WatchKey key; try { key = getWatchService().take(); // Poll all the events queued for the key for (WatchEvent event : key.pollEvents()) { @SuppressWarnings("unchecked") Path filePath = ((WatchEvent) event).context(); File file = filePath.toFile(); if ((!isLockFile(file)) && (acceptFile(file))) { switch (event.kind().name()) { case "ENTRY_CREATE": // fileMonitor.put(file,new FileChange(file)); log("File created ["+file.getName()+"]"); break; // case "ENTRY_MODIFY": // fileMonitor.put(file,new FileChange(file)); log("File modified ["+file.getName()+"]"); break; // case "ENTRY_DELETE": // log("File deleted ["+file.getName()+"]"); createLockFile(file).delete(); fileMonitor.remove(file); break; // } } } // reset is invoked to put the key back to ready state key.reset(); } catch (InterruptedException e) { e.printStackTrace(); } Iterator it = fileMonitor.keySet().iterator(); while (it.hasNext()) { File file = it.next(); FileChange fileChange = fileMonitor.get(file); FileChange fileChangeCurrent = new FileChange(file); if (fileChange.isStable(fileChangeCurrent, getStableTime())) { log("File is stable ["+file.getName()+"]"); String filename = getDateFormatter().format(new Date())+"_"+file.getName(); File lockFile = createLockFile(file); if (!lockFile.exists()) { log("File do not has lock file ["+file.getName()+"]"); try { Files.createFile(lockFile.toPath()); log("Processing file ["+file.getName()+"]"); getListener().fileAvailable(file); file.renameTo(new File(getDirectoryArchive(),filename)); log("Moved to archive file ["+file.getName()+"]"); } catch (IOException e) { file.renameTo(new File(getDirectoryError(),filename)); createErrorFile(file,e); log("Moved to error file ["+file.getName()+"]"); } finally { lockFile.delete(); } } else { log("File do has lock file ["+file.getName()+"]"); fileMonitor.remove(file); } } else { log("File is unstable ["+file.getName()+"]"); fileMonitor.put(file,fileChangeCurrent); } } } public boolean acceptFile(File file) { if (getFilter()!=null) { return getFilter().accept(file); } else { return true; } } public boolean isLockFile(File file) { int pos = file.getName().lastIndexOf('.'); String extension=""; if (pos!=-1) { extension = file.getName().substring(pos).trim().toLowerCase(); } return(extension.equalsIgnoreCase(LOCK_FILE_EXTENSION)); } private File createLockFile(File file) { return new File(file.getParentFile(),file.getName()+LOCK_FILE_EXTENSION); } private void createErrorFile(File file,IOException exception) { File errorFile = new File(file.getParentFile(),file.getName()+ERROR_FILE_EXTENSION); StringWriter sw = null; PrintWriter pw = null; FileWriter fileWriter = null; try { // fileWriter = new FileWriter(errorFile); if (exception!=null) { sw = new StringWriter(); pw = new PrintWriter(sw); exception.printStackTrace(pw); fileWriter.write(sw.toString()); } else { fileWriter.write("Exception is null."); } // fileWriter.flush(); // } catch (IOException e) { } finally { if (sw!=null) { try { sw.close(); } catch (IOException e1) { } } if (pw!=null) { pw.close(); } if (fileWriter!=null) { try { fileWriter.close(); } catch (IOException e) { } } } } private void validateNotNull(String name,Object obj) { if (obj==null) { throw new NullPointerException(name+" is null."); } } private void validate(Path directory) throws IOException { File file = directory.toFile(); if (!file.exists()) { throw new IOException("Directory ["+file.getAbsolutePath()+"] do not exists."); } else if (!file.isDirectory()) { throw new IOException("Directory ["+file.getAbsolutePath()+"] is not a directory."); } else if (!file.canRead()) { throw new IOException("Can not read from directory ["+file.getAbsolutePath()+"]."); } else if (!file.canWrite()) { throw new IOException("Can not write to directory ["+file.getAbsolutePath()+"] ."); } } private void log(String msg) { //TODO System.out.println("Task ["+getName()+"] "+msg); } } 
 package p1; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import static java.nio.file.LinkOption.NOFOLLOW_LINKS; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.List; public class WatchForFile { public void WatchMyFolder(String path ) { File dir = new File(path); Path myDir= dir.toPath(); try { Boolean isFolder = (Boolean) Files.getAttribute(myDir,"basic:isDirectory", NOFOLLOW_LINKS); if (!isFolder) { throw new IllegalArgumentException("Path: " + myDir + " is not a folder"); } } catch (IOException ioe) { ioe.printStackTrace(); } System.out.println("Watching 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> events = watckKey.pollEvents(); for (WatchEvent event : events) { if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { System.out.println("Created: " + event.kind().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()); } } }