net.sf.eBus.util.Properties Maven / Gradle / Ivy
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with this library; if not, write to the
//
// Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330,
// Boston, MA
// 02111-1307 USA
//
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright (C) 2001 - 2010, 2012. Charles W. Rapp.
// All Rights Reserved.
//
package net.sf.eBus.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* {@code Properties} extends
* {@code java.util.Properties} to include:
*
* -
* Loading properties from a named file and returning
* a {@code net.sf.eBus.util.Properties} object.
*
* -
* Storing a {@link net.sf.eBus.util.Properties} object
* into a named file.
*
* -
* Returning property values as a boolean, int, double and
* String array.
*
* -
* Setting property values as a boolean, int, double and
* String array.
*
* -
* Informs registered {@link PropertiesListener}s when the
* underlying properties file has changed and been
* automatically reloaded.
*
* Note: automatic properties file reloading is done only if
* there is a registered properties listener. If there are
* no registered listeners, then Properties does not watch
* the underlying file for changes and so does not
* automatically reload the properties file if it should
* change.
*
*
*
* @author Charles Rapp
*/
public final class Properties
extends java.util.Properties
{
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Constants.
//
/**
* This is eBus version 2.1.0.
*/
private static final long serialVersionUID = 0x050200L;
/**
* Append this suffix to the file name.
*/
private static final String THREAD_SUFFIX = "WatchThread";
//-----------------------------------------------------------
// Statics.
//
/**
* Once a property file is loaded, store the Properties
* object in this map under its file name.
*/
private static Map sPropertiesMap =
new HashMap<>();
/**
* Properties map synchronizer.
*/
private static Lock sPropertiesMutex = new ReentrantLock();
/**
* Logging subsystem interface.
*/
private static final Logger sLogger =
Logger.getLogger(Properties.class.getName());
//-----------------------------------------------------------
// Locals.
//
/**
* This properties file is associated with this file.
*/
private final String mFileName;
/**
* Inform these listeners of property file changes.
*/
private transient final List mListeners;
/**
* This thread polls the watch key for file updates.
*/
private transient WatchThread mWatchThread;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Creates an empty property list with no default values.
* Because there is no associated file, property listening
* is not allowed.
*/
public Properties()
{
super ();
mFileName = null;
mListeners = new LinkedList<>();
mWatchThread = null;
} // end of Properties()
/**
* Creates an empty property list with default values.
* Because there is no associated file, property listening
* is not allowed.
* @param defaults the default property values.
*/
public Properties(final java.util.Properties defaults)
{
super (defaults);
mFileName = null;
mListeners = new LinkedList<>();
mWatchThread = null;
} // end of Properties(java.util.Properties)
// This properties object cannot be directly instantiated
// by an application but only through the loadProperties()
// static methods.
private Properties(final String fileName)
{
super ();
mFileName = fileName;
mListeners = new LinkedList<>();
mWatchThread = null;
} // end of Properties(String)
//
// end of Constructors.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Object Method Overrides.
//
@Override
public boolean equals(final Object o)
{
return (super.equals(o));
} // equals(Object)
@Override
public int hashCode()
{
return (super.hashCode());
} // end of hashCode()
//
// end of Object Method Overrides.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Get Methods.
//
//
// Get methods, no default.
//
/**
* Returns the named property value as a
* {@code boolean}. If {@code key} is an unknown
* property or is neither {@code "true"} nor
* {@code "false"} then returns {@code false}.
* @param key The property key.
* @return the named property value as a
* {@code boolean}. If the value is {@code "true"},
* then {@code true} is returned, otherwise returns
* {@code false}.
* @see #getBooleanProperty(String, boolean)
* @see #setBooleanProperty(String, boolean)
*/
public boolean getBooleanProperty(final String key)
{
final String value = getProperty(key);
boolean retval = false;
if (value != null)
{
final Boolean bool = Boolean.valueOf(value.trim());
retval = bool;
}
return (retval);
} // end of getBooleanProperty(String)
/**
* Returns the named property value as an {@code int}.
* If {@code key} is an unknown property or is not a
* valid integer, then returns {@code 0}.
* @param key The property key.
* @return the named property value as an {@code int}.
* @see #getIntProperty(String, int)
* @see #setIntProperty(String, int)
*/
public int getIntProperty(final String key)
{
final String value = getProperty(key);
int retval = 0;
if (value != null && value.length() > 0)
{
try
{
retval = Integer.parseInt(value.trim());
}
catch (NumberFormatException numberex)
{
// Ignore - return value already set to 0.
}
}
return (retval);
} // end of getIntProperty(String)
/**
* Returns the named property value as a {@code double}.
* If {@code key} is an unknown property or is not a
* value double, then returns {@code 0.0}.
* @param key The property key.
* @return the named property value as a {@code double}.
* @exception NumberFormatException
* if the property value is not a valid double.
* @see #getDoubleProperty(String, double)
* @see #setDoubleProperty(String, double)
*/
public double getDoubleProperty(final String key)
{
final String value = getProperty(key);
double retval = 0.0;
if (value != null && value.length() > 0)
{
try
{
retval = Double.parseDouble(value.trim());
}
catch (NumberFormatException numberex)
{
// Ignore - return value already set to 0.0.
}
}
return (retval);
} // end of getDoubleProperty(String)
/**
* Returns the named property value as a
* {@code String[]}. {@code ifs} is used as
* the interfield separator character.
* If the property value does not exist, then returns
* an empty array.
* @param key The property key.
* @param ifs The interfield separator.
* @return the named property value as a
* {@code String[]}. If the property value does not
* exist, then returns an empty array.
* @see #setArrayProperty(String, String[], char)
*/
public String[] getArrayProperty(final String key,
final char ifs)
{
final String value = getProperty(key);
String[] retval;
if (value == null || value.length() == 0)
{
retval = new String[0];
}
else
{
// Place the ifs in a regular expression [] block
// just in case the ifs is also a regular expression
// character.
retval = value.split(String.format("[%c]", ifs));
}
return (retval);
} // end of getArrayProperty(String, char)
//
// Get methods with default.
//
/**
* Returns the named property value as a
* {@code boolean}. If {@code key} is an unknown
* property or an invalid boolean string, then returns the
* default value.
* @param key The property key.
* @param defaultValue The default value.
* @return the named property value as a
* {@code boolean}. If {@code key} is an unknown
* property or an invalid boolean string, then returns
* {@code defaultValue}.
* @see #getBooleanProperty(String)
* @see #setBooleanProperty(String, boolean)
*/
public boolean getBooleanProperty(final String key,
final boolean defaultValue)
{
final String value = getProperty(key);
boolean retval;
if (value == null)
{
retval = defaultValue;
}
else
{
final Boolean bool = Boolean.valueOf(value.trim());
retval = bool;
}
return (retval);
} // end of getBooleanProperties(String, boolean)
/**
* Returns the named property value as an {@code int}.
* If either the property does not exist or does exist but
* is not an integer, then returns the default value.
* @param key The property key.
* @param defaultValue The default value.
* @return the named property value as an {@code int}
* or {@code defaultValue}.
* @see #getIntProperty(String)
* @see #setIntProperty(String, int)
*/
public int getIntProperty(final String key,
final int defaultValue)
{
final String value = getProperty(key);
int retval = defaultValue;
if (value != null && value.length() > 0)
{
try
{
retval = Integer.parseInt(value.trim());
}
catch (NumberFormatException formex)
{
retval = defaultValue;
}
}
return (retval);
} // end of getIntProperty(String, int)
/**
* Returns the named property value as a {@code double}.
* If the property value does not exist or is not a valid
* {@code double}, then returns the default value.
* @param key The property key.
* @param defaultValue The default value.
* @return the named property value as a {@code double}.
* If the property value does not exist or is not a valid
* {@code double}, then returns
* {@code defaultValue}.
* @see #getDoubleProperty(String)
* @see #setDoubleProperty(String, double)
*/
public double getDoubleProperty(final String key,
final double defaultValue)
{
final String value = getProperty(key);
double retval;
if (value == null || value.length() == 0)
{
retval = defaultValue;
}
else
{
try
{
retval = Double.parseDouble(value);
}
catch (NumberFormatException formex)
{
retval = defaultValue;
}
}
return (retval);
} // end of getDoubleProperties(String, double)
/**
* Returns a set of keys in this property list whose key
* matches the given regular expression pattern {@code p}
* and the corresponding values are strings.
* Includes distinct keys in the default property list if a
* key of the same name is not in the main properties list.
* Properties whose key or value is not of type
* {@code String} are omitted.
*
* The returned set is not backed by the {@code Properties}
* object. Changes to {@code this Properties} are not
* reflected in the set or vice versa.
*
* @param p match property keys against this pattern.
* @return String property keys matching the regular expression
* pattern.
* @see #stringPropertyNames()
* @see #defaults
*/
public Set stringPropertyNames(final Pattern p)
{
final Iterator it;
Matcher m;
final Set retval = this.stringPropertyNames();
for (it = retval.iterator(); it.hasNext() == true;)
{
m = p.matcher(it.next());
// If the property does *not* match the pattern,
// then remove it from the set.
if (m.matches() == false)
{
it.remove();
}
}
return (retval);
} // end of stringPropertyNames(Pattern)
//
// end of Get Methods.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Set Methods.
//
/**
* Sets the named property value to the specified boolean.
* @param key The property key.
* @param value The boolean value.
* @see #getBooleanProperty(String)
* @see #getBooleanProperty(String, boolean)
*/
public void setBooleanProperty(final String key,
final boolean value)
{
final Boolean obj = value;
setProperty(key, obj.toString());
return;
}
/**
* Sets the named property value to the specified integer.
* @param key The property key.
* @param value The integer value.
* @see #getIntProperty(String)
* @see #getIntProperty(String, int)
*/
public void setIntProperty(final String key, final int value)
{
setProperty(key, Integer.toString(value));
return;
} // end of setIntProperty(String, int)
/**
* Sets the named property value to the specified double.
* @param key The property key.
* @param value The double value.
* @see #getDoubleProperty(String)
* @see #getDoubleProperty(String, double)
*/
public void setDoubleProperty(final String key,
final double value)
{
setProperty(key, Double.toString(value));
return;
} // end of setDoubleProperty(String, double)
/**
* Sets the named property value to the string array.
* The array is converted into a single string by
* concatenating the strings together separated by
* {@code ifs}.
* @param key The property key.
* @param value The string array.
* @param ifs The interfield separator.
* @see #getArrayProperty
*/
public void setArrayProperty(final String key,
final String[] value,
final char ifs)
{
StringBuffer buffer;
int bufferLen;
int i;
String newValue = "";
if (value.length > 0)
{
// Figure out the buffer size first by adding up
// the string length.
for (i = 0, bufferLen = 0; i < value.length; ++i)
{
bufferLen += value[i].length();
}
// Add in the IFS.
bufferLen += value.length - 1;
// Allocate the buffer and start filling it in.
buffer = new StringBuffer(bufferLen);
for (i = 0; i < value.length; ++i)
{
if (i > 0)
{
buffer.append(ifs);
}
buffer.append(value[i]);
}
newValue = buffer.toString();
}
setProperty(key, newValue);
return;
} // end of setArrayProperty(String, String[], char)
//
// end of Set Methods.
//-----------------------------------------------------------
/**
* Reloads properties from the properties file.
* @exception IOException
* if there are errors reading in the properties file.
* @see #store(String)
* @see #loadProperties(String)
* @see #loadProperties(File)
*/
public void load()
throws IOException
{
if (mFileName == null)
{
throw (
new IOException("no property file to load"));
}
try (final FileInputStream fis =
new FileInputStream(mFileName))
{
load(fis);
}
catch (FileNotFoundException nofilex)
{
// Convert this exception to an IOException.
throw (
new IOException(mFileName + " does not exist"));
}
return;
} // end of load()
/**
* Stores properties in properties file using the provided
* header comment. The header comment is placed at the
* property file's start.
* @param header File header comment. May be
* {@code null}.
* @exception FileNotFoundException
* if the properties file could not be created.
* @exception IOException
* if there is an error storing the properties into the
* file.
* @see #load
* @see #loadProperties(String)
* @see #loadProperties(File)
*/
public void store(final String header)
throws FileNotFoundException,
IOException
{
if (mFileName == null)
{
throw (new IOException("no property file"));
}
else
{
try (final FileOutputStream fos =
new FileOutputStream(mFileName))
{
store(fos, header);
}
}
return;
} // end of store(String)
/**
* Stores properties in the named properties file using the
* provided header comment. The header comment is placed at
* the property file's start.
* @param fileName Property file's name.
* @param header File header comment. May be
* {@code null}.
* @exception IllegalArgumentException
* if {@code fileName} is either {@code null} or an empty
* string.
* @exception FileNotFoundException
* if the properties file could not be created.
* @exception IOException
* if there is an error storing the properties into the
* file.
* @see #store(String)
* @see #store(File, String)
* @see #load
* @see #loadProperties(String)
* @see #loadProperties(File)
*/
public void store(final String fileName, final String header)
throws IllegalArgumentException,
FileNotFoundException,
IOException
{
if (fileName == null || fileName.isEmpty() == true)
{
throw (
new IllegalArgumentException(
"null or empty fileName"));
}
else
{
store(new File(fileName), header);
}
return;
} // end of store(String, String)
/**
* Stores properties in the named properties file using the
* provided header comment. The header comment is placed at
* the property file's start.
* @param file Property file.
* @param header File header comment. May be
* {@code null}.
* @exception IllegalArgumentException
* if {@code file} is {@code null}.
* @exception FileNotFoundException
* if the properties file could not be created.
* @exception IOException
* if there is an error storing the properties into the
* file.
* @see #store(String)
* @see #store(String, String)
* @see #load
* @see #loadProperties(String)
* @see #loadProperties(File)
*/
public void store(final File file, final String header)
throws IllegalArgumentException,
FileNotFoundException,
IOException
{
boolean existsFlag;
if (file == null)
{
throw (new IllegalArgumentException("null file"));
}
// Only non-directory, writeable files may be property
// files.
else if ((existsFlag = file.exists()) == true &&
file.isDirectory() == true)
{
throw (new IOException(file.getName() +
" is a directory"));
}
else if (existsFlag == true && file.canWrite() == false)
{
throw (new IOException(file.getName() +
" is unwriteable"));
}
else
{
try (final FileOutputStream fos =
new FileOutputStream(file))
{
store(fos, header);
}
}
return;
} // end of store(File, String)
/**
* Adds a properties listener. If this is the first listener
* and the watchRate > zero, then starts the watch timer.
*
* Note: when the underlying properties file changes, it will
* be automatically reloaded prior to calling back to the
* registered {@link PropertiesListener}s.
* @param listener Add this properties listener.
* @exception IllegalArgumentException
* if {@code listener} is {@code null}.
* @exception IllegalStateException
* if this is no underlying property file.
*/
public void addListener(final PropertiesListener listener)
throws IllegalArgumentException,
IllegalStateException
{
boolean addFlag = false;
int listenerSize = 0;
if (mFileName == null)
{
throw (
new IllegalStateException(
"no underlying property file"));
}
else if (listener == null)
{
throw (
new IllegalArgumentException("null listener"));
}
synchronized (mListeners)
{
if (mListeners.contains(listener) == false)
{
mListeners.add(listener);
addFlag = true;
listenerSize = mListeners.size();
}
}
// If this is the first listener, then
// start watching the properties file.
if (addFlag == true && listenerSize == 1)
{
try
{
final File f =
(new File(mFileName)).getAbsoluteFile();
final FileSystem fs = FileSystems.getDefault();
final Path p = fs.getPath(f.getParent());
final Path fp = fs.getPath(f.getName());
final WatchService watcher;
final WatchKey key;
if (sLogger.isLoggable(Level.FINE) == true)
{
sLogger.fine(
String.format(
"Watching %s properties file.",
mFileName));
}
watcher = fs.newWatchService();
key = p.register(watcher,
ENTRY_CREATE,
ENTRY_MODIFY,
ENTRY_DELETE);
mWatchThread =
new WatchThread(fp,
key,
watcher,
String.format("%s%s",
f.getName(),
THREAD_SUFFIX),
this);
mWatchThread.start();
}
catch (IOException ioex)
{
sLogger.log(Level.WARNING,
"File watch failed.",
ioex);
}
}
return;
} // end of addListener(PropertiesListener)
/**
* Removes a properties listener. If there are no more
* listeners and the watch timer is running, then the
* timer is canceled.
*
* Note: when the watch timer is canceled, Properties
* will no longer determine if the underlying properties file
* has changed and so will not automatically reload said file
* if it should change.
* @param listener Remove this listener.
* @exception IllegalStateException
* if this is no underlying property file.
*/
public void removeListener(final PropertiesListener listener)
{
if (mFileName == null)
{
throw (
new IllegalStateException(
"no underlying property file"));
}
if (listener != null)
{
boolean removeFlag = false;
boolean emptyFlag = false;
synchronized (mListeners)
{
if (mListeners.contains(listener) == true)
{
mListeners.remove(listener);
removeFlag = true;
emptyFlag = mListeners.isEmpty();
}
}
// If there are no more listeners, then
// stop watching the properties file.
if (removeFlag == true && emptyFlag == true)
{
mWatchThread.cancel();
mWatchThread = null;
if (sLogger.isLoggable(Level.FINE) == true)
{
sLogger.fine(
String.format(
"Stopped watching %s properties file.",
mFileName));
}
}
}
return;
} // end of removeListener(PropertiesListener)
/**
* Returns a properties list loaded with the values found
* in the named properties file. If the file does not exist,
* an empty properties object is returned. This allows new
* properties to be created and stored.
* @param fileName the properties file name.
* @return A properties list.
* @exception IllegalArgumentException
* if {@code fileName} is either {@code null} or an empty
* string.
* @exception IOException
* if {@code fileName} is not a valid properties
* file.
* @see #loadProperties(File)
* @see #load
* @see #store(String)
*/
public static Properties loadProperties(
final String fileName)
throws IllegalArgumentException,
IOException
{
Properties retval = null;
if (fileName == null || fileName.isEmpty() == true)
{
throw (
new IllegalArgumentException(
"null or empty fileName"));
}
else
{
retval = loadProperties(new File(fileName));
}
return (retval);
} // end of loadProperties(String)
/**
* Returns a properties list loaded with the values found
* in the properties file. If the file does not exist,
* an empty properties object is returned. This allows new
* properties to be created and stored.
* @param file the properties file object.
* @return A properties list.
* @exception IllegalArgumentException
* if {@code file} is {@code null}.
* @exception IOException
* if {@code file} is not a valid properties file.
* @see #loadProperties(String)
* @see #load
* @see #store(String)
*/
public static Properties loadProperties(final File file)
throws IllegalArgumentException,
IOException
{
boolean existsFlag;
Properties retval = null;
if (file == null)
{
throw (new IllegalArgumentException("null file"));
}
// Only non-directory, readable files may be property
// files.
else if ((existsFlag = file.exists()) == true &&
file.isDirectory() == true)
{
throw (new IOException(file.getName() +
" is a directory"));
}
else if (existsFlag == true && file.canRead() == false)
{
throw (new IOException(file.getName() +
" is unreadable"));
}
else
{
final String fileName = file.getPath();
sPropertiesMutex.lock();
try
{
retval = sPropertiesMap.get(fileName);
// Check if this properties file has already been
// loaded.
if (retval == null)
{
if (sLogger.isLoggable(Level.FINE) == true)
{
sLogger.fine(
String.format(
"Loading %s properties file.",
fileName));
}
// No. Create a new properties object and
// load it up.
retval =
createInstance(
file, fileName, existsFlag);
}
}
catch (IOException ioex)
{
throw (ioex);
}
finally
{
sPropertiesMutex.unlock();
}
}
return (retval);
} // end of loadProperties(File)
/**
* Does the actual work of creating and filling a properties
* instance.
* @param file load the properties from this file.
* @param fileName the properties file name.
* @param existsFlag if {@code true} then read in the
* properties file; otherwise do not.
* @return the properties instance.
* @throws IOException
* if there is an error loading the properties file.
*/
private static Properties createInstance(
final File file,
final String fileName,
final boolean existsFlag)
throws IOException
{
final Properties retval = new Properties(fileName);
sPropertiesMap.put(fileName, retval);
if (existsFlag == true)
{
try (final FileInputStream fis =
new FileInputStream(file))
{
retval.load(fis);
}
catch (IOException ioex)
{
// Remove the properties object from the
// map and re-throw the exception.
sPropertiesMap.remove(fileName);
throw (ioex);
}
}
return (retval);
} // end of createInstance(File, String, boolean)
private void handleFileEvent(final WatchEvent> event)
{
if (sLogger.isLoggable(Level.FINE) == true)
{
sLogger.fine(
String.format("Properties event: %s.",
(event.kind()).name()));
}
// Whether the properties file was created, modified or
// deleted, they are all "updates".
// Reload the properties and inform the listeners.
try
{
final PropertiesEvent propEvent =
new PropertiesEvent(this);
List listeners =
new LinkedList<>();
load();
// Copy the listeners list and use that. That way
// _listeners can be modified without affecting
// the loop below.
synchronized (mListeners)
{
listeners.addAll(mListeners);
}
// Tell the listeners about the changes but only
// if the modified properties are successfully
// loaded.
listeners.stream().
forEach((listener) ->
{
try
{
listener.propertiesUpdate(propEvent);
}
catch (Exception jex)
{
sLogger.log(Level.WARNING,
"Properties listener exception.",
jex);
}
});
}
catch (IOException ioex)
{
// Ignore.
}
return;
} // end of handleFileEvent(WatchEvent>)
//---------------------------------------------------------------
// Inner classes.
//
private static final class WatchThread
extends Thread
{
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
public WatchThread(final Path propFile,
final WatchKey key,
final WatchService watcher,
final String name,
final Properties owner)
{
super (name);
_propertiesFile = propFile;
_key = key;
_watcher = watcher;
_owner = owner;
} // end of WatchThread(...)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Thread Method Overrides.
//
/**
* This thread waits for watch events to occur on the
* registered watch key and reports them to the owning
* properties instance. This thread terminates when
* the watch key is no longer registered with the watch
* service.
*/
@Override
public void run()
{
boolean runFlag = true;
// Continue taking watch events until the no longer
// registered.
while (runFlag == true)
{
// Wait for the watch key to be signalled.
// Why?
// Because pollEvents() does not block.
try
{
// Don't bother with the returned key -
// it will be the one we already have.
_watcher.take();
// Get the latest events and pass them to the
// properties instance.
_key.pollEvents().stream().
filter((event) -> (event.kind()!= OVERFLOW &&
(event.context()).equals(
_propertiesFile) == true)).
forEach((event) ->
{
_owner.handleFileEvent(event);
}); // Ignore overflows.
}
catch (InterruptedException interrupt)
{
// Ignore.
}
// Reset the key before checking for new events.
// If reset() returns false, then we are no
// longer registered with the watch service.
runFlag = _key.reset();
}
return;
} // end of run()
//
// end of Thread Method Overrides.
//-------------------------------------------------------
/**
* Cancels the registration with the watch service.
* This will cause this thread to stop running.
*/
public void cancel()
{
_key.cancel();
this.interrupt();
return;
} // end of cancel()
//-----------------------------------------------------------
// Member data.
//
/**
* Look for changes to this file only.
*/
private final Path _propertiesFile;
/**
* Use to key to watch for changes to this directory.
*/
private final WatchKey _key;
/**
* The key is registered with this watch service.
*/
private final WatchService _watcher;
/**
* When a watch event occurs, inform this properties
* instance.
*/
private final Properties _owner;
} // end of class WatchThread
} // end of class Properties
//
// CHANGE LOG
// $Log: Properties.java,v $
// Revision 1.6 2008/01/19 14:02:29 charlesr
// Added net.sf.eBus.io imports.
//
// Revision 1.5 2005/07/21 00:06:43 charlesr
// Moved to Java 5:
// + Using generics in collection declarations.
// + Using for-each syntax.
//
// Revision 1.4 2004/12/26 13:35:51 charlesr
// Correct critical section synchronization.
//
// Revision 1.3 2004/07/25 16:02:05 charlesr
// Corrected javadoc comments.
//
// Revision 1.2 2004/07/19 14:43:35 charlesr
// Added constructors matching java.util.Properties. Fixed adding
// or removing listener from within properties update callback.
//
// Revision 1.1 2004/03/02 23:19:54 charlesr
// Added PropertiesListener support.
//
// Revision 1.0 2003/11/20 01:46:48 charlesr
// Initial revision
//