/*
* This software is distributed under the terms of the FSF
* Gnu Lesser General Public License (see lgpl.txt).
*
* This program is distributed WITHOUT ANY WARRANTY. See the
* GNU General Public License for more details.
*/
package com.scooterframework.admin;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Timer;
import java.util.TimerTask;
import com.scooterframework.common.logging.LogUtil;
/**
* <p>
* PropertyFileChangeMonitor class monitors property file changes and also
* notifies the changes to associated observers.
* </p>
*
* <p>
* The default monitor interval is 2000 milliseconds. This can be changed by
* System property <tt>property.load.interval</tt>.
* </p>
*
* <p>
* <code>ApplicationConfig.getInstance()</code> must be called before this class
* can be used.
* </p>
*
* @author (Fei) John Chen
*/
public class PropertyFileChangeMonitor {
private LogUtil log = LogUtil.getLogger(this.getClass().getName());
private Map<File, Observable> observables = new HashMap<File, Observable>();
private static long oneHundredDays = 8640000;
private boolean periodicReading = false;
private Date oldDate = null;
private Timer timer = null;
/**
* Time interval in milliseconds between successive task executions
*/
private long loadInterval = 2000L;
private String propertyFilePath = "";
private static final PropertyFileChangeMonitor fcm = new PropertyFileChangeMonitor();
private PropertyFileChangeMonitor() {
propertyFilePath = ApplicationConfig.getInstance().getPropertyFileLocationPath();
String loadDelay = System.getProperty("property.load.interval");
if (loadDelay != null) {
try {
loadInterval = (new Integer(loadDelay)).intValue();
}
catch(NumberFormatException ex) {
log.warn("System property property.load.interval has wrong integer format. Use default value " + loadInterval + " milliseconds.");
}
}
if (ApplicationConfig.getInstance().isWebApp() &&
ApplicationConfig.getInstance().isInDevelopmentEnvironment() &&
loadInterval > 0) {
periodicReading = true;
}
}
public static PropertyFileChangeMonitor getInstance() {
return fcm;
}
public void start() {
if (periodicReading) {
oldDate = new Date();
oldDate.setTime(oldDate.getTime()-oneHundredDays);
timer = new Timer();
PropertyFileChangeMonitorTimerTask task =
new PropertyFileChangeMonitorTimerTask();
schedule(task, loadInterval);
}
}
public void stop() {
if (timer != null) {
timer.cancel();
log.debug("Property file change monitor stopped.");
}
}
public void registerObserver(Observer observer, String fileName) {
registerObserver(observer, new File(getFullFileName(fileName)));
}
public void registerObserver(Observer observer, File file) {
if (!(ApplicationConfig.getInstance().isWebApp() &&
ApplicationConfig.getInstance().isInDevelopmentEnvironment())) return;
if (file == null) {
log.error("Can not watch the file, because the input file is null.");
return;
}
else {
if (!file.exists()) {
log.error("Can not watch the file " + file.getName() + ", because it does not exist.");
return;
}
}
log.debug("monitoring file: " + file.getName());
Observable observable = observables.get(file);
if (observable == null) {
observable = new FileObservable(file);
observables.put(file, observable);
}
observable.addObserver(observer);
}
public String getFullFileName(String fileName) {
if (fileName.startsWith("/") || fileName.startsWith("\\")) {
fileName = fileName.substring(1);
}
return (propertyFilePath != null && !"".equals(propertyFilePath))?
(propertyFilePath + File.separatorChar + fileName):
(fileName);
}
private void schedule(TimerTask task, long period) {
if (period > 0) {
timer.schedule(task, oldDate, period);
}
else {
timer.schedule(task, oldDate);
}
}
/**
* FileChangeMonitorTimerTask is responsible for scanning files.
*/
public class PropertyFileChangeMonitorTimerTask extends TimerTask {
public PropertyFileChangeMonitorTimerTask() {
super();
}
public void run() {
for (Map.Entry<File, Observable> entry : observables.entrySet()) {
FileObservable observable = (FileObservable)observables.get(entry.getKey());
observable.checkChange();
}
}
}
}