/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.file; import java.io.File; import java.text.SimpleDateFormat; import java.util.logging.Logger; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.Resource.Type; import org.geoserver.platform.resource.Files; import org.geoserver.platform.resource.ResourceListener; import org.geoserver.platform.resource.ResourceNotification; /** * This class is based on the concept from the FileWatchDog * class in log4j. * * Objects of this class watch for modifications of files * periodically. If a file has changed an action is triggered, * this action has to be implemented in a concrete subclass. * * @author christian * */ public abstract class FileWatcher implements ResourceListener { static protected Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.security"); /** The default delay between every file modification check, set to 10 seconds. */ //static final public long DEFAULT_DELAY = 10000; /** The name of the file to observe for changes. */ protected String path; /** The delay to observe between every check. By default set {@link #DEFAULT_DELAY}. */ //protected long delay = DEFAULT_DELAY; Resource resource; long lastModified = 0; boolean warnedAlready = false; boolean terminate = false; Object terminateLock= new Object(); Object lastModifiedLock= new Object(); /** * Check if FileWatcher has been terminated. * <p> * A terminated FileWatcher no longer listens for resource notification, and will * ignore any last minuet notifications that sneak in. * * @return true if file watcher has been terminated */ public boolean isTerminated() { synchronized (terminateLock) { return terminate; } } /** * Use this method for stopping the thread * @param terminated */ public void setTerminate(boolean terminated) { resource.removeListener( this ); synchronized (terminateLock) { this.terminate = terminated; // will ignore any last minuet events } } /** * @param file * * @deprecated Use Resource instead of File */ @Deprecated protected FileWatcher(File file) { this.resource = Files.asResource(file); this.path = resource.path(); } protected FileWatcher(Resource resource) { this.resource = resource; this.path = resource.path(); } /** * Used to register FileWatcher as a resource notification listener. */ public void start(){ resource.addListener( this ); } /** * Set the delay to observe between each check of the file changes. * Use values > 1000, most file systems have a time granularity * of seconds * @param delay * @deprecated No longer used as resource notifications handle checking the file system */ public void setDelay(long delay) { //this.delay = delay; } @Override public void changed(ResourceNotification notify) { if(isTerminated()){ return; // ignore this event } doOnChange(); //checkAndConfigure(); } /** * Subclasses must override */ abstract protected void doOnChange(); /** * Test constellation and call * {@link #doOnChange()} if necessary */ protected void checkAndConfigure() { boolean fileExists; try { fileExists = resource.getType() == Type.RESOURCE; } catch(SecurityException e) { LOGGER.warning("Was not allowed to read check file existance, file:["+ path+"]."); setTerminate(true); // there is no point in continuing return; } if(fileExists) { long l = resource.lastmodified(); // this can also throw a SecurityException if(testAndSetLastModified(l)) { // however, if we reached this point this doOnChange(); // is very unlikely. warnedAlready = false; } } else { if(!warnedAlready) { LOGGER.warning("["+path+"] does not exist."); warnedAlready = true; } } } /* (non-Javadoc) * @see java.lang.Thread#run() */ // public void run() { // while(!isTerminated()) { // try { // Thread.sleep(delay); // } catch(InterruptedException e) { // // no interruption expected // } // checkAndConfigure(); // } // } /** * @return info about the watched file */ public String getFileInfo() { SimpleDateFormat sdf = new SimpleDateFormat(); StringBuffer buff = new StringBuffer(path); buff.append( " last modified: "); buff.append(sdf.format(resource.lastmodified())); return buff.toString(); } @Override public String toString() { return "FileWatcher: " + getFileInfo(); } /** * Test if l > last modified * * This extra check is used in conjunction with {@link #setLastModified(long)} to allow * the FileWatcher to ignore an event that has been caused by a file update. * * @param l * @return true if file was modified */ public boolean testAndSetLastModified(long l) { synchronized (lastModifiedLock) { if (l > lastModified) { lastModified=l; return true; } return false; } } /** * Method intended to set last modified from a client which is up to date. This avoids unnecessary reloads * * @param lastModified */ public void setLastModified(long lastModified) { synchronized (lastModifiedLock) { this.lastModified = lastModified; } } }