package net.mlvaplus.xmlwatcher; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Collections; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import net.mlvaplus.log.DefaultLog; import net.mlvaplus.tools.XMLUtils; /** * Watches the given <code>path</code>. Checks before each getList() after <code>checkIntervall</code> milliseconds * if the files with <code>extension</code> have changed. If yes, rebuilds the <code>list</code> that is returned * in getList(). * A {@link PropertyChangeListener} can be used to listen to updates of the * list (will fire with property name PROPERTY_UPDATE). The method pauseUpdates() and resumeUpdates() can * be used to temporary deactivate the update checks. * * Example: * <pre> * * private init(){ * ... * this.watcherACL = new RefreshingXMLDirLister(path, EXTENSION, CHECK_INTERVALL, AccessXML.class); * watcherACL.addPropertyChangeListener(this); * ... * } * * private List getAllAcls() { * return (List)watcherACL.getList(); * } * <pre> */ public class RefreshingXMLDirLister<T> { public static final String PROPERTY_UPDATE = "update"; private String extension; @SuppressWarnings("unchecked") private Class clazz; private String path; private volatile int checkIntervall; private volatile long lastWatchedTime; private long lastCheckSum; private List<T> list; private ReadWriteLock lock = new ReentrantReadWriteLock(); private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private volatile boolean doUpdateChecks = true; /** last time readFiles() was called. Used for debug */ private long lastUpdateTime; public RefreshingXMLDirLister(String aPath, String anExtension, int anCheckIntervall, Class<T> aClazz){ this.extension = anExtension; this.clazz = aClazz; this.path = aPath; this.checkIntervall = anCheckIntervall; } public List<T> getList(){ checkAndUpdate(); lock.readLock().lock(); try{ return list; } finally{ lock.readLock().unlock(); } } private long calcCheckSum(){ return XMLUtils.calcCheckSum(path, extension); } private void checkAndUpdate(){ if (!doUpdateChecks){ return; } boolean updated = false; if (System.currentTimeMillis() - lastWatchedTime > checkIntervall){ lock.writeLock().lock(); try{ if (System.currentTimeMillis() - lastWatchedTime > checkIntervall){ long checkSum = calcCheckSum(); if (checkSum != lastCheckSum){ readFiles(); updated = true; } } } catch (Exception e){ DefaultLog.getInstance().err(e); } finally { lastWatchedTime = System.currentTimeMillis(); lock.writeLock().unlock(); } } if (updated){ propertyChangeSupport.firePropertyChange(PROPERTY_UPDATE, false, true); } } @SuppressWarnings("unchecked") private void readFiles(){ lock.writeLock().lock(); try{ List<T> listNew = (List)XMLUtils.readFiles(path, extension, clazz); list = Collections.unmodifiableList(listNew); lastCheckSum = calcCheckSum(); lastUpdateTime = System.currentTimeMillis(); } finally { lock.writeLock().unlock(); } } /** Use this method for testing only! */ @Deprecated public void clearLastWatchedTime(){ lock.writeLock().lock(); lastWatchedTime = 0; lock.writeLock().unlock(); } /** Use this method for testing only! * @return last time readFiles() was called. */ @Deprecated public long getLastUpdateTime() { return lastUpdateTime; } /** * Fires a {@link PropertyChangeEvent} with PROPERTY_UPDATE as property when files are read. */ public void addPropertyChangeListener(PropertyChangeListener listener){ propertyChangeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener){ propertyChangeSupport.removePropertyChangeListener(listener); } /** disables updates check for the given directory. * Users should call this method prior to update the directory */ public void pauseUpdates(){ lock.writeLock().lock(); doUpdateChecks = false; lock.writeLock().unlock(); } /** * re-enabled updates check for the given directory. * Users should call this method after calling pauseUpdates() and updating the directory */ public void resumeUpdates(){ lock.writeLock().lock(); doUpdateChecks = true; lastWatchedTime = 0; lock.writeLock().unlock(); } /** * forces the listener to update all files the next time getList() is called. */ public void forceUpdate(){ lock.writeLock().lock(); lastCheckSum = 0; lastWatchedTime = 0; lock.writeLock().unlock(); } }