package org.grails.io.watch; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** * Implementation of a {@link AbstractDirectoryWatcher} that uses polling. * This implementation is used where {@link java.nio.WatchService} isn't available (pre Java 7). * @author Craig Andrews * @since 2.4 * @see WatchServiceDirectoryWatcher * @see DirectoryWatcher */ class PollingDirectoryWatcher extends AbstractDirectoryWatcher { private Collection<String> extensions = new ConcurrentLinkedQueue<String>(); private Map<File, Long> lastModifiedMap = new ConcurrentHashMap<File, Long>(); private Map<File, Collection<String>> directoryToExtensionsMap = new ConcurrentHashMap<File, Collection<String>>(); private Map<File, Long> directoryWatch = new ConcurrentHashMap<File, Long>(); @Override public void run() { int count = 0; while (active) { Set<File> files = lastModifiedMap.keySet(); for (File file : files) { long currentLastModified = file.lastModified(); Long cachedTime = lastModifiedMap.get(file); if (currentLastModified > cachedTime) { lastModifiedMap.put(file, currentLastModified); fireOnChange(file); } } try { count++; if (count > 2) { count = 0; checkForNewFiles(); } Thread.sleep(sleepTime); } catch (InterruptedException e) { // ignore } } } @Override public void addWatchFile(File fileToWatch) { lastModifiedMap.put(fileToWatch, fileToWatch.lastModified()); } @Override public void addWatchDirectory(File dir, List<String> fileExtensions) { if(!isValidDirectoryToMonitor(dir)){ return; } trackDirectoryExtensions(dir, fileExtensions); cacheFilesForDirectory(dir, fileExtensions, false); } private void trackDirectoryExtensions(File dir, List<String> fileExtensions) { Collection<String> existingExtensions = directoryToExtensionsMap.get(dir); if(existingExtensions == null) { directoryToExtensionsMap.put(dir, new ArrayList<String>(fileExtensions)); } else { existingExtensions.addAll(fileExtensions); } } private void checkForNewFiles() { for (File directory : directoryWatch.keySet()) { final Long currentTimestamp = directoryWatch.get(directory); if (currentTimestamp < directory.lastModified()) { Collection<String> extensions = directoryToExtensionsMap.get(directory); if(extensions == null) { extensions = this.extensions; } cacheFilesForDirectory(directory, extensions, true); } } } private void cacheFilesForDirectory(File directory, Collection<String> fileExtensions, boolean fireEvent) { addExtensions(fileExtensions); directoryWatch.put(directory, directory.lastModified()); File[] files = directory.listFiles(); if (files == null) { return; } for (File file : files) { if(isValidDirectoryToMonitor(file)) { cacheFilesForDirectory(file, fileExtensions, fireEvent); } else if (isValidFileToMonitor(file, fileExtensions)) { if (!lastModifiedMap.containsKey(file) && fireEvent) { fireOnNew(file); } lastModifiedMap.put(file, file.lastModified()); } } } private void addExtensions(Collection<String> toAdd) { for (String extension : toAdd) { extension = removeStartingDotIfPresent(extension); if (!extensions.contains(extension)) { extensions.add(extension); } } } private String removeStartingDotIfPresent(String extension) { if (extension.startsWith(".")) { extension = extension.substring(1); } return extension; } }