You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by mw...@apache.org on 2006/04/06 07:32:41 UTC

svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/...

Author: mwomack
Date: Wed Apr  5 22:32:38 2006
New Revision: 391905

URL: http://svn.apache.org/viewcvs?rev=391905&view=rev
Log:
Cleaned up FileWatchdog implementation, added back configureAndWatch methods for DOMConfigurator and PropertyConfigurator, added more FileWatchdog related tests.

Added:
    logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java   (with props)
    logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml   (with props)
    logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml   (with props)
    logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml   (with props)
    logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties   (with props)
    logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties   (with props)
    logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt   (with props)
    logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt   (with props)
Removed:
    logging/log4j/trunk/src/java/org/apache/log4j/watchdog/TimedLocationWatchdog.java
Modified:
    logging/log4j/trunk/docs/HISTORY.txt
    logging/log4j/trunk/src/java/org/apache/log4j/PropertyConfigurator.java
    logging/log4j/trunk/src/java/org/apache/log4j/joran/JoranConfigurator.java
    logging/log4j/trunk/src/java/org/apache/log4j/watchdog/FileWatchdog.java
    logging/log4j/trunk/src/java/org/apache/log4j/watchdog/Watchdog.java
    logging/log4j/trunk/src/java/org/apache/log4j/watchdog/WatchdogSkeleton.java
    logging/log4j/trunk/src/java/org/apache/log4j/xml/DOMConfigurator.java
    logging/log4j/trunk/tests/src/java/org/apache/log4j/watchdog/FileWatchdogTestCase.java

Modified: logging/log4j/trunk/docs/HISTORY.txt
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/docs/HISTORY.txt?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/docs/HISTORY.txt (original)
+++ logging/log4j/trunk/docs/HISTORY.txt Wed Apr  5 22:32:38 2006
@@ -11,6 +11,8 @@
  - Release of version 1.3-alpha-9
    Release date: TBD
 
+ - Cleaned up FileWatchdog implementation.
+
    January 30th, 2006
  - Release of version 1.3alpha-8
    

Modified: logging/log4j/trunk/src/java/org/apache/log4j/PropertyConfigurator.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/PropertyConfigurator.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/PropertyConfigurator.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/PropertyConfigurator.java Wed Apr  5 22:32:38 2006
@@ -22,16 +22,13 @@
 import org.apache.log4j.config.ConfiguratorBase;
 import org.apache.log4j.config.PropertySetter;
 import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.helpers.Constants;
 import org.apache.log4j.or.RendererMap;
-import org.apache.log4j.spi.Configurator;
-import org.apache.log4j.spi.ErrorItem;
-import org.apache.log4j.spi.LoggerFactory;
-import org.apache.log4j.spi.LoggerRepository;
-import org.apache.log4j.spi.LoggerRepositoryEx;
 
 //import org.apache.log4j.config.PropertySetterException;
-import org.apache.log4j.spi.OptionHandler;
-import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.*;
+import org.apache.log4j.watchdog.FileWatchdog;
+import org.apache.log4j.plugins.PluginRegistry;
 
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -43,6 +40,8 @@
 import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.Vector;
+import java.net.URL;
+import java.net.MalformedURLException;
 
 
 /**
@@ -85,7 +84,8 @@
    @author Ceki Gülcü
    @author Anders Kristensen
    @since 0.8.1 */
-public class PropertyConfigurator extends ConfiguratorBase {
+public class PropertyConfigurator extends ConfiguratorBase
+    implements ConfiguratorEx {
   static final String CATEGORY_PREFIX = "log4j.category.";
   static final String LOGGER_PREFIX = "log4j.logger.";
   static final String FACTORY_PREFIX = "log4j.factory";
@@ -101,6 +101,9 @@
   public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
   private static final String INTERNAL_ROOT_NAME = "root";
 
+  private static Object watchdogLock = new Object();
+  private static FileWatchdog fileWatchdog = null;
+
   /**
      Used internally to keep track of configured appenders.
    */
@@ -348,6 +351,57 @@
   }
 
   /**
+    Like {@link #configureAndWatch(String, long)} except that the
+    default delay of 60 seconds is used.
+
+    @deprecated Use org.apache.log4j.watchdog.FileWatchdog directly.
+
+    @param configFilename A log4j configuration file in XML format.
+
+  */
+  static public void configureAndWatch(String configFilename) {
+    configureAndWatch(configFilename, 60000);
+  }
+
+  /**
+    Read the configuration file <code>configFilename</code> if it
+    exists. Moreover, a thread will be created that will periodically
+    check if <code>configFilename</code> has been created or
+    modified. The period is determined by the <code>delay</code>
+    argument. If a change or file creation is detected, then
+    <code>configFilename</code> is read to configure log4j.
+
+    @deprecated Use org.apache.log4j.watchdog.FileWatchdog directly.
+
+    @param configFilename A log4j configuration file in XML format.
+    @param delay The delay in milliseconds to wait between each check.
+  */
+  static public void configureAndWatch(String configFilename, long delay) {
+    synchronized(watchdogLock) {
+      PluginRegistry pluginRegistry =
+        ((LoggerRepositoryEx)LogManager.getLoggerRepository()).getPluginRegistry();
+
+      // stop existing watchdog
+      if (fileWatchdog != null) {
+        pluginRegistry.stopPlugin(fileWatchdog.getName());
+        fileWatchdog = null;
+      }
+
+      // create the watchdog
+      fileWatchdog = new FileWatchdog();
+      fileWatchdog.setName("PropertyConfigurator.FileWatchdog");
+      fileWatchdog.setConfigurator(PropertyConfigurator.class.getName());
+      fileWatchdog.setFile(configFilename);
+      fileWatchdog.setInterval(delay);
+      fileWatchdog.setInitialConfigure(true);
+
+      // register and start the watchdog
+      pluginRegistry.addPlugin(fileWatchdog);
+      fileWatchdog.activateOptions();
+    }
+  }
+
+  /**
      Read configuration options from <code>properties</code>.
 
      See {@link #doConfigure(String, LoggerRepository)} for the expected format.
@@ -358,20 +412,20 @@
     try {
       // we start by attaching a temporary list appender
       attachListAppender(repository);
-      
+
       boolean attachedConsoleApepnder = false;
       if ((value != null) && OptionConverter.toBoolean(value, true)) {
         attachTemporaryConsoleAppender(repository);
         attachedConsoleApepnder = true;
       }
-
+      
       // As soon as we start configuration process, the pristine flag is set to 
       // false.
       if(repository instanceof LoggerRepositoryEx) {
         ((LoggerRepositoryEx) repository).setPristine(false);
       }
 
-      
+
       String thresholdStr =
         OptionConverter.findAndSubst(THRESHOLD_PREFIX, properties);
 
@@ -430,7 +484,7 @@
 
     doConfigure(props, repository);
   }
-  
+
   /**
    * Read configuration options from input stream <code>configStream</code>.
    * @since 1.3
@@ -438,7 +492,7 @@
    * @param repository
    */
   public void doConfigure(InputStream configStream,
-  LoggerRepository repository) {
+                          LoggerRepository repository) {
     Properties props = new Properties();
     getLogger(repository).debug(
       "Reading configuration from input stream");
@@ -679,7 +733,7 @@
           PropertySetter layoutPS = new PropertySetter(layout);
           layoutPS.setLoggerRepository(repository);
           layoutPS.setProperties(props, layoutPrefix + ".");
-          
+
           activateOptions(layout);
           getLogger(repository).debug(
             "End of parsing for \"" + appenderName + "\".");

Modified: logging/log4j/trunk/src/java/org/apache/log4j/joran/JoranConfigurator.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/joran/JoranConfigurator.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/joran/JoranConfigurator.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/joran/JoranConfigurator.java Wed Apr  5 22:32:38 2006
@@ -42,6 +42,7 @@
 import org.apache.log4j.joran.spi.SimpleRuleStore;
 import org.apache.log4j.spi.ErrorItem;
 import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.ConfiguratorEx;
 
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -68,7 +69,8 @@
  * @author Curt Arnold
  * @author <a href="http://www.qos.ch/log4j/">Ceki G&uuml;lc&uuml;</a>
  */
-public class JoranConfigurator extends ConfiguratorBase {
+public class JoranConfigurator extends ConfiguratorBase
+    implements ConfiguratorEx {
   private Interpreter joranInterpreter;
   private LoggerRepository repository;
 

Added: logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java (added)
+++ logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java Wed Apr  5 22:32:38 2006
@@ -0,0 +1,19 @@
+package org.apache.log4j.spi;
+
+import java.io.InputStream;
+
+/**
+ * Defines extended methods for Configurators to implement.
+ *
+ * @author Mark Womack
+ * @since 1.3
+ */
+public interface ConfiguratorEx {
+  /**
+   * Configures using an InputStream for input.
+   * 
+   * @param stream
+   * @param repository
+   */
+  public void doConfigure(InputStream stream, LoggerRepository repository);
+}

Propchange: logging/log4j/trunk/src/java/org/apache/log4j/spi/ConfiguratorEx.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: logging/log4j/trunk/src/java/org/apache/log4j/watchdog/FileWatchdog.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/watchdog/FileWatchdog.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/watchdog/FileWatchdog.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/watchdog/FileWatchdog.java Wed Apr  5 22:32:38 2006
@@ -16,21 +16,28 @@
 
 package org.apache.log4j.watchdog;
 
-import java.io.File;
-import java.net.URL;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggerRepositoryEx;
+import org.apache.log4j.scheduler.Job;
+
+import java.io.*;
 
 /**
  * Implements a watchdog to watch a file.  When the file changes, determined by
  * a change in the file's modification date, the contents of the file are use to
  * reconfigure the log4j environment.
  */
-public class FileWatchdog extends TimedLocationWatchdog {
+public class FileWatchdog extends WatchdogSkeleton implements Job {
+
+  public static final long DEFAULT_INTERVAL = 60000;
 
   private String filePath;
+  private long interval = DEFAULT_INTERVAL;
+  private boolean initialConfigure = false;
 
   /** The file being watched. */
   private File watchedFile;
-  private URL watchedURL;
+  private long lastModTime = -1;
 
   /**
    * Sets the path of the file to use and watch for configuration changes.
@@ -49,11 +56,60 @@
   public String getFile() {
     return filePath;
   }
+
+  /**
+   * Sets the interval of time between checks for file modifications.
+   *
+   * @param interval
+   */
+  public void setInterval(long interval) {
+    this.interval = interval;
+  }
+
+  /**
+   * Returns the interval of time between checks for file modifications.
+   *
+   * @return interval of time
+   */
+  public long getInterval() {
+    return interval;
+  }
+
+  /**
+   * Set to true if watchdog should configure with file before starting watch
+   * in activateOptions.
+   *
+   * @param initialConfigure
+   */
+  public void setInitialConfigure(boolean initialConfigure) {
+    this.initialConfigure = initialConfigure;
+  }
+
+  /**
+   * Returns true if watchdog will configure before starting watch in
+   * activateOptions.
+   *
+   * @return
+   */
+  public boolean getInitialConfigure() {
+    return initialConfigure;
+  }
+
+  /**
+   * Returns the last modification time of the watched file.
+   * 
+   * @return
+   */
+  public long getLastModTime() {
+    return lastModTime;
+  }
+
   /**
    * Sets up the reference to the file being watched, then calls the version
    * in the super class.
    */
   public void activateOptions() {
+    getLogger().debug("activateOptions called for watchdog " + this.getName());
 
     if (filePath == null) {
       getLogger().error("watchdog \"{}\" not configured with path to watch",
@@ -62,42 +118,83 @@
     }
 
     watchedFile = new File(filePath);
-    try {
-        //
-        //   attempt to invoke JDK 1.4's File.toURI and URI.toURL methods
-        //       which do a better job of escaping file names than File.toURL.
-        Object uri = File.class.getMethod("toURI", null).invoke(watchedFile, null);
-        watchedURL = (URL) uri.getClass().getMethod("toURL", null).invoke(uri, null);
-    } catch(Exception ex) {
-        try {
-            watchedURL = watchedFile.toURL();
-        } catch(java.net.MalformedURLException ex2) {
-            this.getLogger().error("Watchdog {} unable to express filename {} as a URL",
-              this.getName(), watchedFile.getName());
-        }
+
+    // do an initial configure or record the current mod time
+    if (initialConfigure) {
+      execute();
+    } else if (watchedFile.exists()) {
+      lastModTime = watchedFile.lastModified();
+    }
+
+    LoggerRepository repo = getLoggerRepository();
+    if (repo instanceof LoggerRepositoryEx) {
+        ((LoggerRepositoryEx) repo).getScheduler().schedule(this,
+      System.currentTimeMillis() + interval, interval);
+    } else {
+        this.getLogger().error("{} watchdog requires repository that supports LoggerRepositoryEx",
+          this.getName());
     }
-    super.activateOptions();
   }
 
+    /**
+   * Implements the Job interface for the Scheduler.  When this method is called
+   * by the Scheduler it checks the current modification time of the watched
+   * source with the last recorded modification time.  If the modification times
+   * are different, then the log4j environment is reconfigured using the
+   * watched source for the configuration data.
+   */
+  public void execute() {
+    getLogger().debug("FileWatchdog \"{}\" executing", this.getName());
+    if (watchedFile.exists()) {
+      long curModTime = watchedFile.lastModified();
+      getLogger().debug("Checking times for watchdog " + this.getName() +
+        " :(lastModTime - " + lastModTime + ") ?? (curModTime - " +
+        curModTime + ")");
+      if (curModTime != lastModTime) {
+        if (reconfigure()) {
+          lastModTime = curModTime;
+          getLogger().debug("Reconfiguration successful for watchdog " +
+            this.getName());
+        } else {
+          getLogger().debug("Reconfiguration not successful for watchdog " +
+            this.getName() + ", not updating mod time");
+        }
+      } else {
+        getLogger().debug("Times matched, doing nothing");
+      }
+    } else {
+      getLogger().debug("File does not exist, doing nothing");
+    }
+  }
   /**
-   * Returns the modification of the file being watched.
-   * 
-   * @return The modification time of the file.
+   * Shutdown this watchdog.  Since implemented as a scheduled Job, this method
+   * simply removes the watchdog from the Scheduler.
    */
-  public long getModificationTime() {
-    return watchedFile.lastModified();
+  public void shutdown() {
+    LoggerRepository repo = getLoggerRepository();
+    if (repo instanceof LoggerRepositoryEx) {
+        ((LoggerRepositoryEx) repo).getScheduler().delete(this);
+    }
   }
-
   /**
    * Reconfigures the log4j environment using the file as the source of the
    * configuration data.
    */
-  public void reconfigure() {
-    if (watchedFile.exists() && watchedURL != null) {
-        reconfigureByURL(watchedURL);
-    } else {
-        this.getLogger().warn("{} watchdog cannot find file {}",
-          this.getName(), watchedFile.getName());
+  public boolean reconfigure() {
+    InputStream stream = null;
+    try {
+      stream = new FileInputStream(watchedFile);
+      return reconfigureByStream(stream);
+    } catch (FileNotFoundException e) {
+      return false;
+    } finally {
+      if (stream != null) {
+        try {
+          stream.close();
+        } catch (IOException e2) {
+          // ignore
+        }
+      }
     }
   }
 }

Modified: logging/log4j/trunk/src/java/org/apache/log4j/watchdog/Watchdog.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/watchdog/Watchdog.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/watchdog/Watchdog.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/watchdog/Watchdog.java Wed Apr  5 22:32:38 2006
@@ -62,7 +62,9 @@
   /**
    * Called to reconfigure the log4j environment when the monitored data
    * source has been updated.
+   *
+   * @return True if reconfiguration was without errors
    */
-  public void reconfigure();
+  public boolean reconfigure();
   
 }

Modified: logging/log4j/trunk/src/java/org/apache/log4j/watchdog/WatchdogSkeleton.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/watchdog/WatchdogSkeleton.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/watchdog/WatchdogSkeleton.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/watchdog/WatchdogSkeleton.java Wed Apr  5 22:32:38 2006
@@ -18,12 +18,15 @@
 
 
 import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.config.ConfiguratorBase;
 import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ConfiguratorEx;
 import org.apache.log4j.plugins.PluginSkeleton;
 import org.apache.log4j.helpers.OptionConverter;
 
-import java.io.InputStream;
 import java.net.URL;
+import java.io.InputStream;
+import java.util.List;
 
 /**
   Implements the required interface for Watchdog objects.
@@ -34,7 +37,7 @@
   required.  Developers may choose to implement their own version of the
   Watchdog interface (which extends the base Plugin interface) if they so 
   choose.
-  
+
   <p>This implementation provides two helper methods, reconfigureByURL and
   reconfigureByInputStream.  Either of these methods can be called from the
   implemented reconfigure method, as required by the specific type of data
@@ -42,18 +45,18 @@
   that can be described by a URL (ie file, url).  The rconfigureByInputStream
   method is meant for sources where onl a stream of data is available
   (ie socket).
-  
+
   <p>Subclasses of this implementation are required to implement their own
   version of the abstract reconfigure method.
 
   @author Mark Womack <mw...@apache.org>
   @since 1.3
 */
-public abstract class WatchdogSkeleton 
+public abstract class WatchdogSkeleton
 extends PluginSkeleton implements Watchdog {
-  
+
   protected String configuratorClassName;
-  
+
   /**
    * Sets the Configurator class used for reconfiguration.
    *
@@ -63,7 +66,7 @@
    public void setConfigurator(String configuratorClassName) {
      this.configuratorClassName = configuratorClassName;
    }
-  
+
   /**
    * Returns the configurator class used for reconfiguration.
    *
@@ -77,8 +80,8 @@
    * Called to reconfigure the log4j environment when the monitored data
    * source has been updated.  Must be implemented by subclasses.
    */
-  public abstract void reconfigure();
-  
+  public abstract boolean reconfigure();
+
   /**
    * Helper method to get an instance of the configurator class.
    *
@@ -87,21 +90,21 @@
    */
   protected Configurator getConfiguratorInstance() {
     // create an instance of the configurator class
-    Configurator configurator = null;
-    
+    Configurator configurator;
+
     // if we were configured with a configurator class name, use it
     if (configuratorClassName != null) {
       configurator = (Configurator) OptionConverter.instantiateByClassName(
-      	configuratorClassName, Configurator.class, null);
+        configuratorClassName, Configurator.class, null);
     }
     // otherwise, default to PropertyConfigurator
     else {
       configurator = new PropertyConfigurator();
     }
-    
+
     return configurator;
   }
-  
+
   /**
    * Helper method to reconfigure using a URL.
    * The input parameter, configurationURL, should be a URL pointing to
@@ -109,24 +112,88 @@
    *
    * @param srcURL The url that contains the data to be used for
    *   reconfiguration.
+   * @return True if the reconfiguration was without error
    */
-  protected void reconfigureByURL(URL srcURL) {
+  protected boolean reconfigureByURL(URL srcURL) {
     if (this.getLogger().isDebugEnabled()) {
       this.getLogger().debug("watchdog \"{}\" reconfiguring from url: {}",
         this.getName(), srcURL);
     }
-    
+
     // create an instance of the configurator class
     Configurator configurator = getConfiguratorInstance();
-    
+
     // if able to create configurator, then reconfigure using input stream
     if (configurator != null) {
       configurator.doConfigure(srcURL, this.getLoggerRepository());
+      if (configurator instanceof ConfiguratorBase) {
+        ConfiguratorBase baseConfigurator = (ConfiguratorBase)configurator;
+        List errorList = baseConfigurator.getErrorList();
+        getLogger().error("errors reported during reconfiguration: ");
+        if (errorList.size() != 0) {
+          for (int x = 0; x < errorList.size(); x++) {
+            getLogger().debug("error " + x + ": " + errorList.get(x));
+          }
+          return false;
+        }
+      }
+    }
+    else {
+      getLogger().error(
+        "watchdog \"{}\" could not create configurator, ignoring new configuration settings",
+        this.getName());
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Helper method to reconfigure using an InputStream.
+   *
+   * @param stream The stream of data to be used for reconfiguration.
+   * @return True if the reconfiguration was without error
+   */
+  protected boolean reconfigureByStream(InputStream stream) {
+    if (this.getLogger().isDebugEnabled()) {
+      this.getLogger().debug("watchdog \"{}\" reconfiguring by stream",
+        this.getName());
     }
-	  else {
-	    getLogger().error(
+
+    // create an instance of the configurator class
+    Configurator configurator = getConfiguratorInstance();
+    ConfiguratorEx configuratorEx = null;
+
+    if (configurator instanceof ConfiguratorEx) {
+      configuratorEx = (ConfiguratorEx)configurator;
+    } else {
+      getLogger().error(
+        "watchdog \"{}\" could not create configurator, configurator class is not of type ConfiguratorEx",
+        this.getName());
+    }
+
+    // if able to create configurator, then reconfigure using input stream
+    if (configuratorEx != null) {
+      configuratorEx.doConfigure(stream, this.getLoggerRepository());
+      if (configuratorEx instanceof ConfiguratorBase) {
+        ConfiguratorBase baseConfigurator = (ConfiguratorBase)configuratorEx;
+        List errorList = baseConfigurator.getErrorList();
+        getLogger().error("errors reported during reconfiguration: ");
+        if (errorList.size() != 0) {
+          for (int x = 0; x < errorList.size(); x++) {
+            getLogger().debug("error " + x + ": " + errorList.get(x));
+          }
+          return false;
+        }
+      }
+    }
+    else {
+      getLogger().error(
         "watchdog \"{}\" could not create configurator, ignoring new configuration settings",
         this.getName());
-	  }
+      return false;
+    }
+
+    return true;
   }
 }

Modified: logging/log4j/trunk/src/java/org/apache/log4j/xml/DOMConfigurator.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/apache/log4j/xml/DOMConfigurator.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/src/java/org/apache/log4j/xml/DOMConfigurator.java (original)
+++ logging/log4j/trunk/src/java/org/apache/log4j/xml/DOMConfigurator.java Wed Apr  5 22:32:38 2006
@@ -19,6 +19,9 @@
 import org.apache.log4j.LogManager;
 import org.apache.log4j.joran.JoranConfigurator;
 import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggerRepositoryEx;
+import org.apache.log4j.plugins.PluginRegistry;
+import org.apache.log4j.watchdog.FileWatchdog;
 
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
@@ -29,6 +32,7 @@
 import org.xml.sax.helpers.DefaultHandler;
 
 import java.net.URL;
+import java.net.MalformedURLException;
 
 import javax.xml.parsers.SAXParser;
 
@@ -62,6 +66,10 @@
    @deprecated Replaced by the much more flexible {@link org.apache.log4j.joran.JoranConfigurator}.
    @since 0.8.3 */
 public class DOMConfigurator extends JoranConfigurator {
+
+  private static Object watchdogLock = new Object();
+  private static FileWatchdog fileWatchdog = null;
+  
   public static void configure(String file) {
     JoranConfigurator joran = new JoranConfigurator();
     joran.doConfigure(file, LogManager.getLoggerRepository());
@@ -92,6 +100,57 @@
     doConfigure(action, repository);
   }
 
+  /**
+    Like {@link #configureAndWatch(String, long)} except that the
+    default delay of 60 seconds is used.
+    
+    @deprecated Use org.apache.log4j.watchdog.FileWatchdog directly.
+    
+    @param configFilename A log4j configuration file in XML format.
+    
+  */
+  static public void configureAndWatch(String configFilename) {
+    configureAndWatch(configFilename, 60000);
+  }
+
+  /**
+    Read the configuration file <code>configFilename</code> if it
+    exists. Moreover, a thread will be created that will periodically
+    check if <code>configFilename</code> has been created or
+    modified. The period is determined by the <code>delay</code>
+    argument. If a change or file creation is detected, then
+    <code>configFilename</code> is read to configure log4j.
+
+    @deprecated Use org.apache.log4j.watchdog.FileWatchdog directly.
+    
+    @param configFilename A log4j configuration file in XML format.
+    @param delay The delay in milliseconds to wait between each check.
+  */
+  static public void configureAndWatch(String configFilename, long delay) {
+    synchronized(watchdogLock) {
+      PluginRegistry pluginRegistry = 
+        ((LoggerRepositoryEx)LogManager.getLoggerRepository()).getPluginRegistry();
+          
+      // stop existing watchdog
+      if (fileWatchdog != null) {
+        pluginRegistry.stopPlugin(fileWatchdog.getName());
+        fileWatchdog = null;
+      }
+      
+      // create the watchdog
+      fileWatchdog = new FileWatchdog();
+      fileWatchdog.setName("DOMConfigurator.FileWatchdog");
+      fileWatchdog.setConfigurator(DOMConfigurator.class.getName());
+      fileWatchdog.setFile(configFilename);
+      fileWatchdog.setInterval(delay);
+      fileWatchdog.setInitialConfigure(true);
+      
+      // register and start the watchdog
+      pluginRegistry.addPlugin(fileWatchdog);
+      fileWatchdog.activateOptions();
+    }
+  }
+  
   /**
    *  Class that "parses" a DOM element by replaying the
    * corresponding SAX events.

Added: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml (added)
+++ logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml Wed Apr  5 22:32:38 2006
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration >
+
+<log4j:configuration xmlns:log4j="http://logging.apache.org/">
+
+<!-- we just want a badly configured file here -->
+  
+<!--/log4j:configuration-->

Propchange: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test3_1.xml
------------------------------------------------------------------------------
    svn:executable = *

Added: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml (added)
+++ logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml Wed Apr  5 22:32:38 2006
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration >
+
+<log4j:configuration xmlns:log4j="http://logging.apache.org/">
+
+  <appender name="A1" class="org.apache.log4j.FileAppender">
+  
+    <param name="Append" value="false" />
+    <param name="File"   value="output/watchdog.FileWatchdog.test5.txt" />
+    
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p - %m%n"/>
+    </layout>
+  </appender>
+
+  <appender name="A2" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{ABSOLUTE} %c [%p] - %m%n"/>
+    </layout>
+  </appender>
+  
+  <logger name="org.apache.log4j">
+    <level value="info"/>
+  </logger>
+  
+  <logger name="test.FileWatchdogTestCase">
+    <appender-ref ref="A1" />	
+    <level value="debug"/>
+  </logger>
+
+  <logger name="org.apache.log4j.watchdog">
+    <level value="debug"/>
+  </logger>
+  
+  <root>
+    <appender-ref ref="A2"/>
+    <level value="debug" />
+  </root>
+  
+</log4j:configuration>

Propchange: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_1.xml
------------------------------------------------------------------------------
    svn:executable = *

Added: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml (added)
+++ logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml Wed Apr  5 22:32:38 2006
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration >
+
+<log4j:configuration xmlns:log4j="http://logging.apache.org/">
+  
+  <logger name="test.FileWatchdogTestCase">
+    <level value="info"/>
+  </logger>
+  
+</log4j:configuration>

Propchange: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test5_2.xml
------------------------------------------------------------------------------
    svn:executable = *

Added: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties (added)
+++ logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties Wed Apr  5 22:32:38 2006
@@ -0,0 +1,20 @@
+log4j.debug=TRUE
+log4j.threshold=ON
+
+log4j.rootLogger=DEBUG,A2
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=output/watchdog.FileWatchdog.test6.txt
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%p - %m%n
+
+log4j.appender.A2=org.apache.log4j.ConsoleAppender
+log4j.appender.A2.layout=org.apache.log4j.PatternLayout
+log4j.appender.A2.layout.ConversionPattern=%d{ABSOLUTE} %c [%p] - %m%n
+
+#log4j.logger.org.apache.log4j=WARN
+log4j.logger.test.FileWatchdogTestCase=DEBUG,A1
+log4j.logger.org.apache.log4j.watchdog.FileWatchdogTestCase=DEBUG
+log4j.logger.org.apache.log4j=INFO
+log4j.logger.org.apache.log4j.watchdog=DEBUG

Propchange: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_1.properties
------------------------------------------------------------------------------
    svn:executable = *

Added: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties (added)
+++ logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties Wed Apr  5 22:32:38 2006
@@ -0,0 +1,10 @@
+log4j.debug=TRUE
+log4j.threshold=ON
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=output/watchdog.FileWatchdog.test6.txt
+log4j.appender.A1.Append=true
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%p - %m%n
+
+log4j.logger.test.FileWatchdogTestCase=INFO,A1

Propchange: logging/log4j/trunk/tests/input/watchdog/watchdog.FileWatchdog.test6_2.properties
------------------------------------------------------------------------------
    svn:executable = *

Modified: logging/log4j/trunk/tests/src/java/org/apache/log4j/watchdog/FileWatchdogTestCase.java
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/src/java/org/apache/log4j/watchdog/FileWatchdogTestCase.java?rev=391905&r1=391904&r2=391905&view=diff
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/watchdog/FileWatchdogTestCase.java (original)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/watchdog/FileWatchdogTestCase.java Wed Apr  5 22:32:38 2006
@@ -29,7 +29,7 @@
 import org.apache.log4j.spi.LoggerRepositoryEx;
 import org.apache.log4j.joran.JoranConfigurator;
 import org.apache.log4j.Level;
-
+import org.apache.log4j.xml.DOMConfigurator;
 
 public class FileWatchdogTestCase extends TestCase {
 
@@ -46,8 +46,16 @@
 
     private void copyFile(File src, File dst) throws Exception {
       if (dst.exists()) {
-          assertTrue(dst.delete());
+        for (int x = 0; x < 5; x++) {
+          if (x == 4) {
+            assertTrue("File " + dst.getAbsolutePath() +
+            " not deleted", false);
+          }
+          if (dst.delete()) break;
+          Thread.sleep(750);
+        }
       }
+
       FileOutputStream out = new FileOutputStream(dst);
       FileInputStream in = new FileInputStream(src);
       byte[] buffer = new byte[1024];
@@ -101,9 +109,10 @@
     }
 
     // basic test of plugin in standalone mode
-    public void test1() throws Exception {
+    public void testJoranConfigurator() throws Exception {
       LogManager.getLoggerRepository().resetConfiguration();
-      
+      logger.setLevel(Level.DEBUG);
+
       File outFile = new File(getOutputFile("test1"));
       if (outFile.exists()) {
           assertTrue(outFile.delete());
@@ -120,14 +129,14 @@
       // move the first config file into place
       copyFile(sourceFile1, configFile);
       assertTrue(configFile.exists());
-      
+
       testLogger.debug("first config file in place: " + configFile.getAbsolutePath());
 
       // configure environment to first config file
       JoranConfigurator configurator = new JoranConfigurator();
       configurator.doConfigure(configFile.getAbsolutePath(),
           LogManager.getLoggerRepository());
-      
+
       testLogger.debug("log4j configured with configFile");
 
       // now watch the file for changes
@@ -138,7 +147,7 @@
       watchdog.setConfigurator(JoranConfigurator.class.getName());
       ((LoggerRepositoryEx) LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(watchdog);
       watchdog.activateOptions();
-      
+
       testLogger.debug("watchdog activated");
 
       // output some test messages
@@ -147,16 +156,16 @@
       logger.warn("warn message");
       logger.error("error message");
       logger.fatal("fatal message");
-      
+
       testLogger.debug("first set of test messages output");
 
       Thread.sleep(2000);
-      
+
       testLogger.debug("about to copy second config file");
-      
+
       // copy over a new version of the config file
       copyFile(sourceFile2, configFile);
-      
+
       testLogger.debug("second config file copied");
 
       // wait a few seconds for the watchdog to react
@@ -171,7 +180,7 @@
               logger.warn("warn message");
               logger.error("error message");
               logger.fatal("fatal message");
-              
+
               testLogger.debug("second set of test messages output");
 
               assertTrue(Compare.compare(getOutputFile("test1"),
@@ -181,88 +190,398 @@
           testLogger.debug("looping for level check");
       }
       fail("Expected change in level did not occur within 20 seconds.");
+  }
+
+  // basic test of plugin in standalone mode with PropertyConfigurator
+  public void testPropertyConfigurator() throws Exception {
+    LogManager.getLoggerRepository().resetConfiguration();
+    logger.setLevel(Level.DEBUG);
+
+    File outFile = new File(getOutputFile("test2"));
+    if (outFile.exists()) {
+          assertTrue(outFile.delete());
     }
 
-    // basic test of plugin in standalone mode with PropertyConfigurator
-    public void test2() throws Exception {
-      LogManager.getLoggerRepository().resetConfiguration();
-      
-      File outFile = new File(getOutputFile("test2"));
-      if (outFile.exists()) {
-            assertTrue(outFile.delete());
+    // set up the needed file references
+    File sourceFile1 = new File(getSourceConfigFile("test2", 1));
+    File sourceFile2 = new File(getSourceConfigFile("test2", 2));
+    assertTrue(sourceFile1.exists());
+    assertTrue(sourceFile2.exists());
+
+    File configFile = new File(getConfigFile("test2"));
+
+    // move the first config file into place
+    copyFile(sourceFile1, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("first config file in place: " + configFile.getAbsolutePath());
+
+    // configure environment to first config file
+    PropertyConfigurator configurator = new PropertyConfigurator();
+    configurator.doConfigure(configFile.getAbsolutePath(),
+        LogManager.getLoggerRepository());
+
+    testLogger.debug("log4j configured with configFile");
+
+    // now watch the file for changes
+    FileWatchdog watchdog = new FileWatchdog();
+    watchdog.setName("test2");
+    watchdog.setFile(configFile.getAbsolutePath());
+    watchdog.setInterval(1000);
+    watchdog.setConfigurator(PropertyConfigurator.class.getName());
+    ((LoggerRepositoryEx) LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(watchdog);
+    watchdog.activateOptions();
+
+    testLogger.debug("watchdog activated");
+
+    // output some test messages
+    logger.debug("debug message");
+    logger.info("info message");
+    logger.warn("warn message");
+    logger.error("error message");
+    logger.fatal("fatal message");
+
+    testLogger.debug("first set of test messages output");
+
+    Thread.sleep(2000);
+
+    testLogger.debug("about to copy second config file");
+
+    // copy over a new version of the config file
+    copyFile(sourceFile2, configFile);
+
+    testLogger.debug("second config file copied");
+
+    // wait a few seconds for the watchdog to react
+    for (int i = 0; i < 40; i++) {
+        testLogger.debug("sleeping for 500 ms");
+        Thread.sleep(500);
+        testLogger.debug("level for logger " + logger.getName() + " is " + logger.getLevel());
+        if (logger.getLevel() == Level.INFO) {
+            // output some test messages
+            logger.debug("debug message");
+            logger.info("info message");
+            logger.warn("warn message");
+            logger.error("error message");
+            logger.fatal("fatal message");
+
+            testLogger.debug("second set of test messages output");
+            Thread.sleep(500);
+
+            assertTrue("output does not match", Compare.compare(getOutputFile("test2"),
+              getWitnessFile("test2")));
+            return;
+        }
+        testLogger.debug("looping for level check");
+    }
+    fail("Expected change in level did not occur within 20 seconds.");
+  }
+
+  public void testJoranConfigurationError() throws Exception {
+    LogManager.getLoggerRepository().resetConfiguration();
+    logger.setLevel(Level.DEBUG);
+
+    File outFile = new File(getOutputFile("test3"));
+    if (outFile.exists()) {
+        assertTrue(outFile.delete());
+    }
+
+    // set up the needed file references
+    File sourceFile1 = new File(getSourceXMLConfigFile("test3", 1));
+    File sourceFile2 = new File(getSourceXMLConfigFile("test1", 2));
+    assertTrue(sourceFile1.exists());
+    assertTrue(sourceFile2.exists());
+
+    // config file should not exist yet
+    File configFile = new File(getXMLConfigFile("test3"));
+    assertFalse(configFile.exists());
+
+    // now watch the nonexistent file for changes
+    FileWatchdog watchdog = new FileWatchdog();
+    watchdog.setName("test3");
+    watchdog.setFile(configFile.getAbsolutePath());
+    watchdog.setInterval(1000);
+    watchdog.setConfigurator(JoranConfigurator.class.getName());
+    ((LoggerRepositoryEx) LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(watchdog);
+    watchdog.activateOptions();
+
+    testLogger.debug("watchdog activated");
+
+    // the file does not exist, so the modification time should never change
+    long modTime = watchdog.getLastModTime();
+    for (int count = 0; count < 5; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue("watchdog mod time changed when no file", false);
       }
+      Thread.sleep(500);
+    }
 
-      // set up the needed file references
-      File sourceFile1 = new File(getSourceConfigFile("test2", 1));
-      File sourceFile2 = new File(getSourceConfigFile("test2", 2));
-      assertTrue(sourceFile1.exists());
-      assertTrue(sourceFile2.exists());
+    testLogger.debug("no file, mod time not changed: " + modTime);
 
-      File configFile = new File(getConfigFile("test2"));
+    // move the bad config file into place
+    copyFile(sourceFile1, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("bad config file put into place");
+
+    // the file is "bad", so the modification time should never change
+    for (int count = 0; count < 7; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue("watchdog mod time changed for bad file", false);
+      }
+      Thread.sleep(500);
+    }
 
-      // move the first config file into place
-      copyFile(sourceFile1, configFile);
-      assertTrue(configFile.exists());
-      
-      testLogger.debug("first config file in place: " + configFile.getAbsolutePath());
+    testLogger.debug("bad file, mod time not changed: " + modTime);
 
-      // configure environment to first config file
-      PropertyConfigurator configurator = new PropertyConfigurator();
-      configurator.doConfigure(configFile.getAbsolutePath(),
-          LogManager.getLoggerRepository());
-      
-      testLogger.debug("log4j configured with configFile");
+    // move the good config file into place
+    copyFile(sourceFile2, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("moved good config file into place");
+
+    // the file is good, so the modification time and level should change
+    for (int count = 0; count < 7; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue(logger.getLevel() == Level.INFO);
+        break;
+      }
 
-      // now watch the file for changes
-      FileWatchdog watchdog = new FileWatchdog();
-      watchdog.setName("test2");
-      watchdog.setFile(configFile.getAbsolutePath());
-      watchdog.setInterval(1000);
-      watchdog.setConfigurator(PropertyConfigurator.class.getName());
-      ((LoggerRepositoryEx) LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(watchdog);
-      watchdog.activateOptions();
-      
-      testLogger.debug("watchdog activated");
+      if (count == 6) {
+        assertTrue("mod time for good file never changed", false);
+      }
 
-      // output some test messages
-      logger.debug("debug message");
-      logger.info("info message");
-      logger.warn("warn message");
-      logger.error("error message");
-      logger.fatal("fatal message");
-      
-      testLogger.debug("first set of test messages output");
+      Thread.sleep(500);
+    }
 
-      Thread.sleep(2000);
-      
-      testLogger.debug("about to copy second config file");
-      
-      // copy over a new version of the config file
-      copyFile(sourceFile2, configFile);
-      
-      testLogger.debug("second config file copied");
+    testLogger.debug("good file, modTime changed: " + modTime);
+  }
 
-      // wait a few seconds for the watchdog to react
-      for (int i = 0; i < 40; i++) {
-          testLogger.debug("sleeping for 500 ms");
-          Thread.sleep(500);
-          testLogger.debug("level for logger " + logger.getName() + " is " + logger.getLevel());
-          if (logger.getLevel() == Level.INFO) {
-              // output some test messages
-              logger.debug("debug message");
-              logger.info("info message");
-              logger.warn("warn message");
-              logger.error("error message");
-              logger.fatal("fatal message");
-              
-              testLogger.debug("second set of test messages output");
+  public void testPropertyConfigurationError() throws Exception {
+    LogManager.getLoggerRepository().resetConfiguration();
+    logger.setLevel(Level.DEBUG);
+
+    File outFile = new File(getOutputFile("test4"));
+    if (outFile.exists()) {
+        assertTrue(outFile.delete());
+    }
 
-              assertTrue(Compare.compare(getOutputFile("test2"),
-                getWitnessFile("test2")));
-              return;
-          }
-          testLogger.debug("looping for level check");
+    // set up the needed file references
+    // need a "bad" property file
+    //File sourceFile1 = new File(getSourceConfigFile("test4", 1));
+    File sourceFile2 = new File(getSourceConfigFile("test2", 2));
+    //assertTrue(sourceFile1.exists());
+    assertTrue(sourceFile2.exists());
+
+    // config file should not exist yet
+    File configFile = new File(getConfigFile("test4"));
+    assertFalse(configFile.exists());
+
+    // now watch the nonexistent file for changes
+    FileWatchdog watchdog = new FileWatchdog();
+    watchdog.setName("test4");
+    watchdog.setFile(configFile.getAbsolutePath());
+    watchdog.setInterval(1000);
+    watchdog.setConfigurator(PropertyConfigurator.class.getName());
+    ((LoggerRepositoryEx) LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(watchdog);
+    watchdog.activateOptions();
+
+    testLogger.debug("watchdog activated");
+
+    // the file does not exist, so the modification time should never change
+    long modTime = watchdog.getLastModTime();
+    for (int count = 0; count < 5; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue("watchdog mod time changed when no file", false);
       }
-      fail("Expected change in level did not occur within 20 seconds.");
+      Thread.sleep(500);
+    }
+
+    testLogger.debug("no file, mod time not changed: " + modTime);
+
+/* need a "bad" property file
+    // move the bad config file into place
+    copyFile(sourceFile1, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("bad config file put into place");
+
+    // the file is "bad", so the modification time should never change
+    for (int count = 0; count < 7; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue("watchdog mod time changed for bad file", false);
+      }
+      Thread.sleep(500);
+    }
+
+    testLogger.debug("bad file, mod time not changed: " + modTime);
+*/
+
+    // move the good config file into place
+    copyFile(sourceFile2, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("moved good config file into place");
+
+    // the file is good, so the modification time and level should change
+    for (int count = 0; count < 7; count++) {
+      if (modTime != watchdog.getLastModTime()) {
+        assertTrue(logger.getLevel() == Level.INFO);
+        break;
+      }
+
+      if (count == 6) {
+        assertTrue("mod time for good file never changed", false);
+      }
+
+      Thread.sleep(500);
+    }
+
+    testLogger.debug("good file, modTime changed: " + modTime);
+  }
+
+  public void testDOMConfigureAndWatch() throws Exception {
+    LogManager.getLoggerRepository().resetConfiguration();
+    logger.setLevel(Level.DEBUG);
+
+    File outFile = new File(getOutputFile("test5"));
+    if (outFile.exists()) {
+        assertTrue(outFile.delete());
+    }
+
+    // set up the needed file references
+    File sourceFile1 = new File(getSourceXMLConfigFile("test5", 1));
+    File sourceFile2 = new File(getSourceXMLConfigFile("test5", 2));
+    assertTrue(sourceFile1.exists());
+    assertTrue(sourceFile2.exists());
+
+    File configFile = new File(getXMLConfigFile("test5"));
+
+    // move the first config file into place
+    copyFile(sourceFile1, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("first config file in place: " + configFile.getAbsolutePath());
+
+    // now watch the file for changes
+    DOMConfigurator.configureAndWatch(configFile.getAbsolutePath(), 1000);
+
+    testLogger.debug("configureAndWatch activated");
+
+    // output some test messages
+    logger.debug("debug message");
+    logger.info("info message");
+    logger.warn("warn message");
+    logger.error("error message");
+    logger.fatal("fatal message");
+
+    testLogger.debug("first set of test messages output");
+
+    Thread.sleep(2000);
+
+    testLogger.debug("about to copy second config file");
+
+    // copy over a new version of the config file
+    copyFile(sourceFile2, configFile);
+
+    testLogger.debug("second config file copied");
+
+    // wait a few seconds for the watchdog to react
+    for (int i = 0; i < 40; i++) {
+        testLogger.debug("sleeping for 500 ms");
+        Thread.sleep(500);
+        testLogger.debug("level for logger " + logger.getName() + " is " + logger.getLevel());
+        if (logger.getLevel() == Level.INFO) {
+            // output some test messages
+            logger.debug("debug message");
+            logger.info("info message");
+            logger.warn("warn message");
+            logger.error("error message");
+            logger.fatal("fatal message");
+
+            testLogger.debug("second set of test messages output");
+            Thread.sleep(500);
+
+            assertTrue("output does not match", Compare.compare(getOutputFile("test5"),
+              getWitnessFile("test5")));
+            return;
+        }
+        testLogger.debug("looping for level check");
+    }
+    fail("Expected change in level did not occur within 20 seconds.");
+  }
+
+  /* there is a bug in property configurator where it will not work a second
+     time, so commenting this out for now
+  public void testPropertyConfigureAndWatch() throws Exception {
+    LogManager.getLoggerRepository().resetConfiguration();
+    logger.setLevel(Level.DEBUG);
+
+    File outFile = new File(getOutputFile("test6"));
+    if (outFile.exists()) {
+          assertTrue(outFile.delete());
+    }
+
+    // set up the needed file references
+    File sourceFile1 = new File(getSourceConfigFile("test6", 1));
+    File sourceFile2 = new File(getSourceConfigFile("test6", 2));
+    assertTrue(sourceFile1.exists());
+    assertTrue(sourceFile2.exists());
+
+    File configFile = new File(getConfigFile("test6"));
+
+    // move the first config file into place
+    copyFile(sourceFile1, configFile);
+    assertTrue(configFile.exists());
+
+    testLogger.debug("first config file in place: " + configFile.getAbsolutePath());
+
+    // now watch the file for changes
+    PropertyConfigurator.configureAndWatch(configFile.getAbsolutePath(), 1000);
+
+    testLogger.debug("configureAndWatch activated");
+
+    // output some test messages
+    logger.debug("debug message");
+    logger.info("info message");
+    logger.warn("warn message");
+    logger.error("error message");
+    logger.fatal("fatal message");
+
+    testLogger.debug("first set of test messages output");
+
+    Thread.sleep(2000);
+
+    testLogger.debug("about to copy second config file");
+
+    // copy over a new version of the config file
+    copyFile(sourceFile2, configFile);
+
+    testLogger.debug("second config file copied");
+
+    // wait a few seconds for the watchdog to react
+    for (int i = 0; i < 40; i++) {
+        testLogger.debug("sleeping for 500 ms");
+        Thread.sleep(500);
+        testLogger.debug("level for logger " + logger.getName() + " is " + logger.getLevel());
+        if (logger.getLevel() == Level.INFO) {
+            // output some test messages
+            logger.debug("debug message");
+            logger.info("info message");
+            logger.warn("warn message");
+            logger.error("error message");
+            logger.fatal("fatal message");
+
+            testLogger.debug("second set of test messages output");
+            Thread.sleep(1000);
+
+            assertTrue("output does not match", Compare.compare(getOutputFile("test6"),
+              getWitnessFile("test6")));
+            return;
+        }
+        testLogger.debug("looping for level check");
     }
+    fail("Expected change in level did not occur within 20 seconds.");
+  }
+  */
 }

Added: logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt (added)
+++ logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt Wed Apr  5 22:32:38 2006
@@ -0,0 +1,9 @@
+DEBUG - debug message
+INFO - info message
+WARN - warn message
+ERROR - error message
+FATAL - fatal message
+INFO - info message
+WARN - warn message
+ERROR - error message
+FATAL - fatal message

Propchange: logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test5.txt
------------------------------------------------------------------------------
    svn:executable = *

Added: logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt
URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt?rev=391905&view=auto
==============================================================================
--- logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt (added)
+++ logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt Wed Apr  5 22:32:38 2006
@@ -0,0 +1,9 @@
+DEBUG - debug message
+INFO - info message
+WARN - warn message
+ERROR - error message
+FATAL - fatal message
+INFO - info message
+WARN - warn message
+ERROR - error message
+FATAL - fatal message

Propchange: logging/log4j/trunk/tests/witness/watchdog/watchdog.FileWatchdog.test6.txt
------------------------------------------------------------------------------
    svn:executable = *



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/

Posted by Curt Arnold <ca...@apache.org>.
On Apr 7, 2006, at 1:10 AM, Curt Arnold wrote:

>
> On Apr 6, 2006, at 11:12 PM, Mark Womack wrote:
>> Hm.  That implies that the Watchdog will need to be configured  
>> with what kind of input type to give to the configurator, since  
>> InputSource will not generically work with all configurators (like  
>> PropertyConfigurator).  Or I could add doConfigure(File) to  
>> ConfiguratorEx.  That is probably better.
>>
>
> I don't see any reason why PropertyConfigurator could not have a  
> doConfigure(InputSource) method.  ConfiguratorEx is a new  
> interface, so there is no compatibility issue.  If a configurator  
> wants to support ConfiguratorEx, it can add a doConfigure 
> (InputSource, LoggerRepository).
>
>
>> But another question is what is going to happen when I add  
>> HttpWatchdog and SocketWatchdog this weekend?  How would they  
>> resolve the location of the filespec?  Or would it need to specify  
>> the ENTITY in a different way, maybe as a url?
>
> An HttpWatchdog (or any watchdog that is representing a resource  
> that can be addressed with a URL) would just call  
> configurator.doConfigure(new InputSource("http://www.example.org/ 
> foo/log4j.xml"), repository) which would provide the base URL for  
> any entities (so in the example the external entity would be  
> resolved as http://www.example.org/foo/stdAppenders.xml).  Don't  
> know about the SocketAppender, I assume that you'd only have a  
> Reader or an InputStream (both of which can be represented in an  
> InputSource).  You couldn't use a configuration file with external  
> entities with relative URL's since there'd be no base URL, however  
> that should be acceptable.  It wouldn't be like the file scenario  
> where it would successfully parse on the initial pass and then  
> start failing on the watchdog.
>

And forget what I just said about InputSource.  Unfortunately, there  
is not a reliable mechanism to convert File paths to URL on earlier  
JDK's.  You may have to resort to using an approach like I did with  
the DOMConfigurator (in log4j 1.2) and JoranConfigurator where you  
create a distinct object that packages all the necessary  
information.  It may be as simple as having Watchdog take a Runnable  
and invoking run() every time the monitored resource changes. 

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/

Posted by Curt Arnold <ca...@apache.org>.
On Apr 6, 2006, at 11:12 PM, Mark Womack wrote:
> Hm.  That implies that the Watchdog will need to be configured with  
> what kind of input type to give to the configurator, since  
> InputSource will not generically work with all configurators (like  
> PropertyConfigurator).  Or I could add doConfigure(File) to  
> ConfiguratorEx.  That is probably better.
>

I don't see any reason why PropertyConfigurator could not have a  
doConfigure(InputSource) method.  ConfiguratorEx is a new interface,  
so there is no compatibility issue.  If a configurator wants to  
support ConfiguratorEx, it can add a doConfigure(InputSource,  
LoggerRepository).


> But another question is what is going to happen when I add  
> HttpWatchdog and SocketWatchdog this weekend?  How would they  
> resolve the location of the filespec?  Or would it need to specify  
> the ENTITY in a different way, maybe as a url?

An HttpWatchdog (or any watchdog that is representing a resource that  
can be addressed with a URL) would just call configurator.doConfigure 
(new InputSource("http://www.example.org/foo/log4j.xml"), repository)  
which would provide the base URL for any entities (so in the example  
the external entity would be resolved as http://www.example.org/foo/ 
stdAppenders.xml).  Don't know about the SocketAppender, I assume  
that you'd only have a Reader or an InputStream (both of which can be  
represented in an InputSource).  You couldn't use a configuration  
file with external entities with relative URL's since there'd be no  
base URL, however that should be acceptable.  It wouldn't be like the  
file scenario where it would successfully parse on the initial pass  
and then start failing on the watchdog.






---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/

Posted by Mark Womack <mw...@apache.org>.
> Using an InputStream is a problem since it would cause any XML 
> configuration that contained external entities to fail to parse.  For 
> example, if you have a document like:
>
> <!DOCTYPE log4j:configuration [
> <!ENTITY stdAppenders SYSTEM 'stdAppenders.xml'>
> ]>
> <log4j:configuration>
>      <!-- include standard appenders -->
>      &stdAppenders;
> ...
> </log4j:configuration>
>
> This document would successfully parse when 
> DOMConfigurator.configurator(File) is called but would fail on 
> DOMConfigurator(InputStream) since the base path needed to resolve  the 
> relative file spec stdAppenders.xml is not provided.  From  reading the 
> code, I believe that configureAndWatch would likely  succeed on the 
> initial configuration but would fail on any subsequent  configuration.
>
> Using org.xml.sax.InputSource instead of java.io.InputStream may be  one 
> way to address the problem.

Hm.  That implies that the Watchdog will need to be configured with what 
kind of input type to give to the configurator, since InputSource will not 
generically work with all configurators (like PropertyConfigurator).  Or I 
could add doConfigure(File) to ConfiguratorEx.  That is probably better.

But another question is what is going to happen when I add HttpWatchdog and 
SocketWatchdog this weekend?  How would they resolve the location of the 
filespec?  Or would it need to specify the ENTITY in a different way, maybe 
as a url?

-Mark 



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/...

Posted by Curt Arnold <ca...@apache.org>.
On Apr 6, 2006, at 12:32 AM, mwomack@apache.org wrote:

> Author: mwomack
> Date: Wed Apr  5 22:32:38 2006
> New Revision: 391905
>
> URL: http://svn.apache.org/viewcvs?rev=391905&view=rev
> Log:

> Cleaned up FileWatchdog implementation, added back  
> configureAndWatch methods for DOMConfigurator and  
> PropertyConfigurator, added more FileWatchdog related tests.
>
....

> URL: http://svn.apache.org/viewcvs/logging/log4j/trunk/src/java/org/ 
> apache/log4j/spi/ConfiguratorEx.java?rev=391905&view=auto
> ====================================================================== 
> ========
> --- logging/log4j/trunk/src/java/org/apache/log4j/spi/ 
> ConfiguratorEx.java (added)
> +++ logging/log4j/trunk/src/java/org/apache/log4j/spi/ 
> ConfiguratorEx.java Wed Apr  5 22:32:38 2006
> @@ -0,0 +1,19 @@
> +package org.apache.log4j.spi;
> +
> +import java.io.InputStream;
> +
> +/**
> + * Defines extended methods for Configurators to implement.
> + *
> + * @author Mark Womack
> + * @since 1.3
> + */
> +public interface ConfiguratorEx {
> +  /**
> +   * Configures using an InputStream for input.
> +   *
> +   * @param stream
> +   * @param repository
> +   */
> +  public void doConfigure(InputStream stream, LoggerRepository  
> repository);
> +}
>


Using an InputStream is a problem since it would cause any XML  
configuration that contained external entities to fail to parse.  For  
example, if you have a document like:

<!DOCTYPE log4j:configuration [
<!ENTITY stdAppenders SYSTEM 'stdAppenders.xml'>
]>
<log4j:configuration>
      <!-- include standard appenders -->
      &stdAppenders;
...
</log4j:configuration>

This document would successfully parse when  
DOMConfigurator.configurator(File) is called but would fail on  
DOMConfigurator(InputStream) since the base path needed to resolve  
the relative file spec stdAppenders.xml is not provided.  From  
reading the code, I believe that configureAndWatch would likely  
succeed on the initial configuration but would fail on any subsequent  
configuration.

Using org.xml.sax.InputSource instead of java.io.InputStream may be  
one way to address the problem.




---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/

Posted by Curt Arnold <ca...@apache.org>.
On Apr 6, 2006, at 12:36 AM, Mark Womack wrote:

> I checked in a bunch of changes.  Hopefully it isn't more broken  
> than before.  The tests for FileWatchdog are just too intricate.   
> The config file could be getting read by watchdog, so can't be  
> deleted as required in the tests, so I had to add some code to loop  
> on deleting.  Then it seems that it can take some time for the  
> appender buffer to flush to the file, etc.  Most of this intricacy  
> is because of the nature of the test.  In real world usage I don't  
> think it would be this complex.
>
> We'll see if Gump complains tonight.
>
> -Mark


Also, I'm not sure how the FileWatchdog stuff would interact with  
restoring DOMConfigurator for log4j 1.2 compatibility (http:// 
issues.apache.org/bugzilla/show_bug.cgi?id=39024).

configureAndWatch has some very peculiar behavior and I don't think  
it will carry over into log4j 2.0.  So perhaps, restoring the 1.2  
DOMConfigurator and omitting configureAndWatch from JoranConfigurator  
would make all the FileWatchdog stuff moot.

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Re: svn commit: r391905 - in /logging/log4j/trunk: docs/ src/java/org/apache/log4j/ src/java/org/apache/log4j/joran/ src/java/org/apache/log4j/spi/ src/java/org/apache/log4j/watchdog/ src/java/org/apache/log4j/xml/ tests/input/watchdog/ tests/src/java/org/

Posted by Mark Womack <mw...@apache.org>.
I checked in a bunch of changes.  Hopefully it isn't more broken than 
before.  The tests for FileWatchdog are just too intricate.  The config file 
could be getting read by watchdog, so can't be deleted as required in the 
tests, so I had to add some code to loop on deleting.  Then it seems that it 
can take some time for the appender buffer to flush to the file, etc.  Most 
of this intricacy is because of the nature of the test.  In real world usage 
I don't think it would be this complex.

We'll see if Gump complains tonight.

-Mark 



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org