You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by rg...@apache.org on 2008/08/24 19:00:03 UTC

svn commit: r688527 - /logging/log4net/trunk/src/Config/XmlConfigurator.cs

Author: rgrabowski
Date: Sun Aug 24 10:00:02 2008
New Revision: 688527

URL: http://svn.apache.org/viewvc?rev=688527&view=rev
Log:
Fix for LOG4NET-158. XMLConfigurator.ConfigureAndWatch() won't leak resources when called multiple times. Internal FileWatcher is properly disposed when a change is detected.

Modified:
    logging/log4net/trunk/src/Config/XmlConfigurator.cs

Modified: logging/log4net/trunk/src/Config/XmlConfigurator.cs
URL: http://svn.apache.org/viewvc/logging/log4net/trunk/src/Config/XmlConfigurator.cs?rev=688527&r1=688526&r2=688527&view=diff
==============================================================================
--- logging/log4net/trunk/src/Config/XmlConfigurator.cs (original)
+++ logging/log4net/trunk/src/Config/XmlConfigurator.cs Sun Aug 24 10:00:02 2008
@@ -870,9 +870,23 @@
 
 				try
 				{
-					// Create a watch handler that will reload the
-					// configuration whenever the config file is modified.
-					ConfigureAndWatchHandler.StartWatching(repository, configFile);
+                    lock (m_repositoryName2ConfigAndWatchHandler)
+                    {
+                        // support multiple repositories each having their own watcher
+                        ConfigureAndWatchHandler handler =
+                            (ConfigureAndWatchHandler)m_repositoryName2ConfigAndWatchHandler[repository.Name];
+
+                        if (handler != null)
+                        {
+                            m_repositoryName2ConfigAndWatchHandler.Remove(repository.Name);
+                            handler.Dispose();
+                        }
+
+                        // Create and start a watch handler that will reload the
+                        // configuration whenever the config file is modified.
+                        handler = new ConfigureAndWatchHandler(repository, configFile);
+                        m_repositoryName2ConfigAndWatchHandler[repository.Name] = handler;
+                    }
 				}
 				catch(Exception ex)
 				{
@@ -903,24 +917,9 @@
 		/// elapse.
 		/// </para>
 		/// </remarks>
-		private sealed class ConfigureAndWatchHandler
+		private sealed class ConfigureAndWatchHandler : IDisposable
 		{
 			/// <summary>
-			/// Watch a specified config file used to configure a repository
-			/// </summary>
-			/// <param name="repository">The repository to configure.</param>
-			/// <param name="configFile">The configuration file to watch.</param>
-			/// <remarks>
-			/// <para>
-			/// Watch a specified config file used to configure a repository
-			/// </para>
-			/// </remarks>
-			internal static void StartWatching(ILoggerRepository repository, FileInfo configFile)
-			{
-				new ConfigureAndWatchHandler(repository, configFile);
-			}
-
-			/// <summary>
 			/// Holds the FileInfo used to configure the XmlConfigurator
 			/// </summary>
 			private FileInfo m_configFile;
@@ -941,8 +940,15 @@
 			/// </summary>
 			private const int TimeoutMillis = 500;
 
+            /// <summary>
+            /// Watches file for changes. This object should be disposed when no longer
+            /// needed to free system handles on the watched resources.
+            /// </summary>
+            private FileSystemWatcher m_watcher;
+
 			/// <summary>
-			/// Initializes a new instance of the <see cref="ConfigureAndWatchHandler" /> class.
+			/// Initializes a new instance of the <see cref="ConfigureAndWatchHandler" /> class to
+            /// watch a specified config file used to configure a repository.
 			/// </summary>
 			/// <param name="repository">The repository to configure.</param>
 			/// <param name="configFile">The configuration file to watch.</param>
@@ -951,31 +957,31 @@
 			/// Initializes a new instance of the <see cref="ConfigureAndWatchHandler" /> class.
 			/// </para>
 			/// </remarks>
-			private ConfigureAndWatchHandler(ILoggerRepository repository, FileInfo configFile)
+			public ConfigureAndWatchHandler(ILoggerRepository repository, FileInfo configFile)
 			{
 				m_repository = repository;
 				m_configFile = configFile;
 
 				// Create a new FileSystemWatcher and set its properties.
-				FileSystemWatcher watcher = new FileSystemWatcher();
+				m_watcher = new FileSystemWatcher();
 
-				watcher.Path = m_configFile.DirectoryName;
-				watcher.Filter = m_configFile.Name;
+				m_watcher.Path = m_configFile.DirectoryName;
+				m_watcher.Filter = m_configFile.Name;
 
 				// Set the notification filters
-				watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;
+				m_watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;
 
 				// Add event handlers. OnChanged will do for all event handlers that fire a FileSystemEventArgs
-				watcher.Changed += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
-				watcher.Created += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
-				watcher.Deleted += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
-				watcher.Renamed += new RenamedEventHandler(ConfigureAndWatchHandler_OnRenamed);
+				m_watcher.Changed += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
+				m_watcher.Created += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
+				m_watcher.Deleted += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
+				m_watcher.Renamed += new RenamedEventHandler(ConfigureAndWatchHandler_OnRenamed);
 
 				// Begin watching.
-				watcher.EnableRaisingEvents = true;
+				m_watcher.EnableRaisingEvents = true;
 
 				// Create the timer that will be used to deliver events. Set as disabled
-				m_timer = new Timer(new TimerCallback(OnWatchedFileChange), null, Timeout.Infinite, Timeout.Infinite);
+                m_timer = new Timer(new TimerCallback(OnWatchedFileChange), null, Timeout.Infinite, Timeout.Infinite);
 			}
 
 			/// <summary>
@@ -1024,6 +1030,16 @@
 			{
 				XmlConfigurator.InternalConfigure(m_repository, m_configFile);
 			}
+
+            /// <summary>
+            /// Release the handles held by the watcher and timer.
+            /// </summary>
+            public void Dispose()
+            {
+                m_watcher.EnableRaisingEvents = false;
+                m_watcher.Dispose();
+                m_timer.Dispose();
+            }
 		}
 #endif
 
@@ -1083,6 +1099,13 @@
 
 	    #region Private Static Fields
 
+        /// <summary>
+        /// Maps repository names to ConfigAndWatchHandler instances to allow a particular
+        /// ConfigAndWatchHandler to dispose of its FileSystemWatcher when a repository is 
+        /// reconfigured.
+        /// </summary>
+        private readonly static Hashtable m_repositoryName2ConfigAndWatchHandler = new Hashtable();
+
 	    /// <summary>
 	    /// The fully qualified type of the XmlConfigurator class.
 	    /// </summary>