/*
* File Monitor - Watches a folder and notify files changes
* Copyright (C) 2007 Federico Fissore
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.fridrik.filemonitor;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* FileMonitor (the name says it all) monitors a folder and its subfolders for
* file changes (added, removed and modified). Only one file extension can be
* monitored for each instance of FileMonitor. For each change found, an event
* is raised. File renames are notified as a file removal and a file addition,
* in this order. FileMonitor implements Runnable and expects you to start it
* through a ScheduledExecutorService
*
* @author Federico Fissore (federico@fissore.org)
* @since 1.0
*/
public class FileMonitor implements Runnable {
private final File folder;
private final ExtFilenameFilter filenameFilter;
private final String fileExtension;
private final Map<String, Long> fileMap;
private final List<FileAddedListener> fileAddedListeners;
private final List<FileDeletedListener> fileDeletedListeners;
private final List<FileModifiedListener> fileModifiedListeners;
class ExtFilenameFilter implements FilenameFilter {
@SuppressWarnings("synthetic-access")
public boolean accept(File folder, String name) {
return name.endsWith(fileExtension)
|| new File(folder.getAbsolutePath() + File.separator + name)
.isDirectory();
}
}
/**
* Creates a new instance of FileMonitor
*
* @param absoluteFolderPath
* the absolute folder path to monitor
* @param fileExtension
* the file extension to monitor
*/
public FileMonitor(String absoluteFolderPath, String fileExtension) {
this.fileExtension = fileExtension;
this.filenameFilter = new ExtFilenameFilter();
this.fileMap = new HashMap<String, Long>();
this.fileAddedListeners = new LinkedList<FileAddedListener>();
this.fileDeletedListeners = new LinkedList<FileDeletedListener>();
this.fileModifiedListeners = new LinkedList<FileModifiedListener>();
this.folder = new File(absoluteFolderPath);
if (!folder.isAbsolute() || !folder.isDirectory()) {
throw new IllegalArgumentException("The parameter with value "
+ absoluteFolderPath + " MUST be a folder");
}
}
public void run() {
checkDeletion();
checkAddAndModify(folder);
}
/**
* Checks for files deletion
*/
protected void checkDeletion() {
List<String> pathsToDelete = new LinkedList<String>();
for (String path : fileMap.keySet()) {
if (!new File(path).exists()) {
pathsToDelete.add(path);
notifyDeletedListeners(new FileEvent(path, folder.getAbsolutePath()));
}
}
for (String path : pathsToDelete) {
fileMap.remove(path);
}
}
/**
* Checks for file addition and modification
*
* @param currentFolder
* the folder to monitor
*/
protected void checkAddAndModify(File currentFolder) {
for (File file : getFiles(currentFolder)) {
if (file.isDirectory()) {
checkAddAndModify(file);
} else {
if (fileMap.containsKey(file.getAbsolutePath())) {
if (fileMap.get(file.getAbsolutePath()).longValue() != file
.lastModified()) {
fileMap.put(file.getAbsolutePath(), Long.valueOf(file
.lastModified()));
notifyModifiedListeners(new FileEvent(file.getAbsolutePath(),
folder.getAbsolutePath()));
}
} else {
fileMap
.put(file.getAbsolutePath(), Long.valueOf(file.lastModified()));
notifyAddedListeners(new FileEvent(file.getAbsolutePath(), folder
.getAbsolutePath()));
}
}
}
}
public File[] getFiles(File folder) {
return folder.listFiles(filenameFilter);
}
/**
* Adds a file modified listener
*
* @param listener
* the listener
*/
public void addModifiedListener(FileModifiedListener listener) {
fileModifiedListeners.add(listener);
}
/**
* Adds a file deleted listener
*
* @param listener
* the listener
*/
public void addDeletedListener(FileDeletedListener listener) {
fileDeletedListeners.add(listener);
}
/**
* Adds a file added listener
*
* @param listener
* the listener
*/
public void addAddedListener(FileAddedListener listener) {
fileAddedListeners.add(listener);
}
private void notifyModifiedListeners(FileEvent event) {
for (FileModifiedListener listener : fileModifiedListeners) {
listener.fileModified(event);
}
}
private void notifyAddedListeners(FileEvent event) {
for (FileAddedListener listener : fileAddedListeners) {
listener.fileAdded(event);
}
}
private void notifyDeletedListeners(FileEvent event) {
for (FileDeletedListener listener : fileDeletedListeners) {
listener.fileDeleted(event);
}
}
}