2009-07-14 Marek Habersack <mhabersack@novell.com>
authorMarek Habersack <grendel@twistedcode.net>
Tue, 14 Jul 2009 11:24:36 +0000 (11:24 -0000)
committerMarek Habersack <grendel@twistedcode.net>
Tue, 14 Jul 2009 11:24:36 +0000 (11:24 -0000)
* WebConfigurationManager.cs: added support for suppressing
application reload when the main config file is written to from
application.

* WebConfigurationHost.cs: added minimal implementation of
WriteCompleted, which checks if there's need to suppress
application reload.

2009-07-14  Marek Habersack  <mhabersack@novell.com>

* ConfigurationSaveEventArgs.cs, ConfigurationSaveEventHandler.cs:
added

* Configuration.cs: added two internal events - SaveStart and
SaveEnd. They are used by System.Web's configuration system to
suppress application reloads when configuration is modified and
saved from within a web application. It is necessary to use events
since there is no guarantee the web application will use
WebConfigurationManager (and thus WebConfigurationHost) for
writing.

2009-07-14  Marek Habersack  <mhabersack@novell.com>

* System.Configuration.dll.sources: added
System.Configuration/ConfigurationSaveEventArgs.cs
System.Configuration/ConfigurationSaveEventHandler.cs

2009-07-14  Marek Habersack  <mhabersack@novell.com>

* AssemblyInfo.cs: added InternalsVisibleTo for System.Web

2009-07-14  Marek Habersack  <mhabersack@novell.com>

* HttpApplicationFactory.cs: OnFileChanged doesn't reload
application if reload suppression is active.

svn path=/trunk/mcs/; revision=137847

13 files changed:
mcs/class/System.Configuration/Assembly/AssemblyInfo.cs
mcs/class/System.Configuration/Assembly/ChangeLog
mcs/class/System.Configuration/ChangeLog
mcs/class/System.Configuration/System.Configuration.dll.sources
mcs/class/System.Configuration/System.Configuration/ChangeLog
mcs/class/System.Configuration/System.Configuration/Configuration.cs
mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventArgs.cs [new file with mode: 0644]
mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventHandler.cs [new file with mode: 0644]
mcs/class/System.Web/System.Web.Configuration_2.0/ChangeLog
mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationHost.cs
mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationManager.cs
mcs/class/System.Web/System.Web/ChangeLog
mcs/class/System.Web/System.Web/HttpApplicationFactory.cs

index 75f7aa7aada86dd714dcd0ea94adfac898ab3c47..de97cae8a55070a164c6bd451da4b0b9f52e3633 100644 (file)
@@ -62,6 +62,7 @@ using System.Runtime.InteropServices;
 #endif
 
 #if NET_2_0
+       [assembly: InternalsVisibleTo ("System.Web, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
        [assembly: AssemblyFileVersion (Consts.FxFileVersion)]
        [assembly: SecurityPermission (SecurityAction.RequestMinimum, SkipVerification = true)]
        [assembly: Debuggable (DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
index e9742aa27c0c0496800f4b738fcb6e13af40e56b..fce50bca895e3315208cabd820833f0fbfac44f7 100644 (file)
@@ -1,3 +1,7 @@
+2009-07-14  Marek Habersack  <mhabersack@novell.com>
+
+       * AssemblyInfo.cs: added InternalsVisibleTo for System.Web
+
 2008-04-15  Andreas Nahr <ClassDevelopment@A-SoftTech.com>
 
        * AssemblyInfo.cs: Added missing attributes
index 7226e698ee167c9d78d80eb18e0bdfe48e61f178..03c2dacfc02c1d2ffb76ef4af71648ed7533ad95 100644 (file)
@@ -1,3 +1,9 @@
+2009-07-14  Marek Habersack  <mhabersack@novell.com>
+
+       * System.Configuration.dll.sources: added
+       System.Configuration/ConfigurationSaveEventArgs.cs
+       System.Configuration/ConfigurationSaveEventHandler.cs
+
 2008-09-14  Gert Driesen  <drieseng@users.sourceforge.net>
 
        * System.Configuration_test.dll.sources: added
index 311f19ce1fddc2ad08da95768b6dcaebca1a415f..dc7d3f83cc8ffea6eb9c38ead573a0486ca4c77e 100644 (file)
@@ -50,6 +50,8 @@ System.Configuration/ConfigurationPropertyCollection.cs
 System.Configuration/ConfigurationPropertyOptions.cs
 System.Configuration/ConfigurationRemoveElement.cs
 System.Configuration/ConfigurationSaveMode.cs
+System.Configuration/ConfigurationSaveEventArgs.cs
+System.Configuration/ConfigurationSaveEventHandler.cs
 System.Configuration/ConfigurationSection.cs
 System.Configuration/ConfigurationSectionCollection.cs
 System.Configuration/ConfigurationSectionGroup.cs
index 6ae39cd0868cca8e31855057a40e35543c351351..f96ecf18c06d210e2a5a2be786341cfb34a122fc 100644 (file)
@@ -1,3 +1,16 @@
+2009-07-14  Marek Habersack  <mhabersack@novell.com>
+
+       * ConfigurationSaveEventArgs.cs, ConfigurationSaveEventHandler.cs:
+       added
+
+       * Configuration.cs: added two internal events - SaveStart and
+       SaveEnd. They are used by System.Web's configuration system to
+       suppress application reloads when configuration is modified and
+       saved from within a web application. It is necessary to use events
+       since there is no guarantee the web application will use
+       WebConfigurationManager (and thus WebConfigurationHost) for
+       writing.
+
 2009-06-08  Marek Habersack  <mhabersack@novell.com>
 
        * ConfigurationLocation.cs: if the path passed to constructor
index 3ecd0cf87ade7d720bda2f6955a42ab6a2fa7d97..7db5d8360d581a2c30a2cd481976a1c59ea14fc5 100644 (file)
 using System;
 using System.Collections;
 using System.Collections.Specialized;
+using System.Configuration.Internal;
+using System.ComponentModel;
 using System.Reflection;
 using System.Xml;
 using System.IO;
-using System.Configuration.Internal;
 
 namespace System.Configuration {
 
        public sealed class Configuration
-       {
+       {               
                Configuration parent;
                Hashtable elementData = new Hashtable ();
                string streamName;
@@ -53,6 +54,9 @@ namespace System.Configuration {
                string locationConfigPath;
                string locationSubPath;
 
+               internal static event ConfigurationSaveEventHandler SaveStart;
+               internal static event ConfigurationSaveEventHandler SaveEnd;
+               
                internal Configuration (Configuration parent, string locationSubPath)
                {
                        this.parent = parent;
@@ -370,16 +374,26 @@ namespace System.Configuration {
                
                public void Save (ConfigurationSaveMode mode, bool forceUpdateAll)
                {
+                       ConfigurationSaveEventHandler saveStart = SaveStart;
+                       ConfigurationSaveEventHandler saveEnd = SaveEnd;
+                       
                        object ctx = null;
+                       Exception saveEx = null;
                        Stream stream = system.Host.OpenStreamForWrite (streamName, null, ref ctx);
                        try {
+                               if (saveStart != null)
+                                       saveStart (this, new ConfigurationSaveEventArgs (streamName, true, null, ctx));
+                               
                                Save (stream, mode, forceUpdateAll);
                                system.Host.WriteCompleted (streamName, true, ctx);
-                       } catch (Exception) {
+                       } catch (Exception ex) {
+                               saveEx = ex;
                                system.Host.WriteCompleted (streamName, false, ctx);
                                throw;
                        } finally {
                                stream.Close ();
+                               if (saveEnd != null)
+                                       saveEnd (this, new ConfigurationSaveEventArgs (streamName, false, saveEx, ctx));
                        }
                }
                
diff --git a/mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventArgs.cs b/mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventArgs.cs
new file mode 100644 (file)
index 0000000..a226764
--- /dev/null
@@ -0,0 +1,52 @@
+//
+// System.Configuration.ConfigurationSaveEventArgs.cs
+//
+// Authors:
+//     Marek Habersack (mhabersack@novell.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+#if NET_2_0
+using System;
+
+namespace System.Configuration 
+{
+       class ConfigurationSaveEventArgs : EventArgs
+       {
+               public string StreamPath { get; private set; }
+               public bool Start { get; private set; }
+               public object Context { get; private set; }
+               public bool Failed { get; private set; }
+               public Exception Exception { get; private set; }
+               
+               public ConfigurationSaveEventArgs (string streamPath, bool start, Exception ex, object context)
+               {
+                       this.StreamPath = streamPath;
+                       this.Start = start;
+                       this.Failed = ex != null;
+                       this.Exception = ex;
+                       this.Context = context;
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventHandler.cs b/mcs/class/System.Configuration/System.Configuration/ConfigurationSaveEventHandler.cs
new file mode 100644 (file)
index 0000000..2a8b8ef
--- /dev/null
@@ -0,0 +1,37 @@
+//
+// System.Configuration.ConfigurationSaveEventHandler.cs
+//
+// Authors:
+//     Marek Habersack (mhabersack@novell.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+#if NET_2_0
+using System;
+
+namespace System.Configuration 
+{
+       internal delegate void ConfigurationSaveEventHandler (Configuration sender, ConfigurationSaveEventArgs args);
+}
+#endif
+       
\ No newline at end of file
index 165a8dd22a6eb82d14d33420dd6c67e0b04f412a..5a771f05014bc18f8b0c424288f2d0d37d367b8d 100644 (file)
@@ -1,3 +1,13 @@
+2009-07-14  Marek Habersack  <mhabersack@novell.com>
+
+       * WebConfigurationManager.cs: added support for suppressing
+       application reload when the main config file is written to from
+       application.
+
+       * WebConfigurationHost.cs: added minimal implementation of
+       WriteCompleted, which checks if there's need to suppress
+       application reload.
+
 2009-07-13  Marek Habersack  <mhabersack@novell.com>
 
        * ProvidersHelper.cs: InstantiateProvider doesn't have to
index 6bfe659a0297343804ccb6582763da0618a19641..cf1c750dd5f396e4c4242652436da220f637b11d 100644 (file)
@@ -411,6 +411,9 @@ namespace System.Web.Configuration
 
                public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
                {
+                       string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
+                       if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
+                               WebConfigurationManager.SuppressAppReload (true);
                        return new FileStream (streamName, FileMode.Create, FileAccess.Write);
                }
 
@@ -438,7 +441,7 @@ namespace System.Web.Configuration
                }
 
                public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
-               {
+               {                       
                        throw new NotImplementedException ();
                }
                
@@ -455,14 +458,22 @@ namespace System.Web.Configuration
                                throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
                }
                
-               [MonoTODO("Does nothing")]
                public virtual void WriteCompleted (string streamName, bool success, object writeContext)
                {
-               }
-               
-               [MonoTODO("Does nothing")]
+                       WriteCompleted (streamName, success, writeContext, false);
+               }               
+
                public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
                {
+                       // There are probably other things to be done here, but for the moment we
+                       // just mark the completed write as one that should not cause application
+                       // reload. Note that it might already be too late for suppression, since the
+                       // FileSystemWatcher monitor might have already delivered the
+                       // notification. If the stream has been open using OpenStreamForWrite then
+                       // we're safe, though.
+                       string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
+                       if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
+                               WebConfigurationManager.SuppressAppReload (true);
                }
 
                public virtual bool SupportsChangeNotifications {
index 76b1af6cda904cb58d20ed69a2941e4a978cf342..4c7c6c4314be63461f62b5d5428a8bbe1dc9f9c6 100644 (file)
@@ -48,10 +48,13 @@ namespace System.Web.Configuration {
        public static class WebConfigurationManager
        {
 #if !TARGET_J2EE
+               static readonly object suppressAppReloadLock = new object ();
+               
                static IInternalConfigConfigurationFactory configFactory;
                static Hashtable configurations = Hashtable.Synchronized (new Hashtable ());
                static Hashtable sectionCache = new Hashtable ();
                static Hashtable configPaths = Hashtable.Synchronized (new Hashtable ());
+               static bool suppressAppReload;
 #else
                const string AppSettingsKey = "WebConfigurationManager.AppSettings";
                static internal IInternalConfigConfigurationFactory configFactory
@@ -139,7 +142,6 @@ namespace System.Web.Configuration {
                        }
                }
 #endif
-
                static ArrayList extra_assemblies = null;
                static internal ArrayList ExtraAssemblies {
                        get {
@@ -161,10 +163,10 @@ namespace System.Web.Configuration {
                
                static WebConfigurationManager ()
                {
-                       PropertyInfo prop = typeof(ConfigurationManager).GetProperty ("ConfigurationFactory", BindingFlags.Static | BindingFlags.NonPublic);
-                       if (prop != null)
-                               configFactory = prop.GetValue (null, null) as IInternalConfigConfigurationFactory;
-
+                       configFactory = ConfigurationManager.ConfigurationFactory;
+                       _Configuration.SaveStart += ConfigurationSaveHandler;
+                       _Configuration.SaveEnd += ConfigurationSaveHandler;
+                       
                        // Part of fix for bug #491531
                        Type type = Type.GetType ("System.Configuration.CustomizableFileSettingsProvider, System", false);
                        if (type != null) {
@@ -174,6 +176,13 @@ namespace System.Web.Configuration {
                        }
                }
 
+               static void ConfigurationSaveHandler (_Configuration sender, ConfigurationSaveEventArgs args)
+               {
+                       string rootConfigPath = WebConfigurationHost.GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
+                       if (String.Compare (args.StreamPath, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
+                               SuppressAppReload (args.Start);
+               }
+               
                public static _Configuration OpenMachineConfiguration ()
                {
                        return ConfigurationManager.OpenMachineConfiguration ();
@@ -460,7 +469,19 @@ namespace System.Web.Configuration {
                        HttpRequest req = ctx != null ? ctx.Request : null;
                        return req != null ? req.Path : HttpRuntime.AppDomainAppVirtualPath;
                }
+               
+               internal static bool SuppressAppReload (bool newValue)
+               {
+                       bool ret;
+                       
+                       lock (suppressAppReloadLock) {
+                               ret = suppressAppReload;
+                               suppressAppReload = newValue;
+                       }
 
+                       return ret;
+               }
+               
                internal static void RemoveConfigurationFromCache (HttpContext ctx)
                {
                        configurations.Remove (GetCurrentPath (ctx));
@@ -642,7 +663,6 @@ namespace System.Web.Configuration {
                        // nothing. We need a context.
                }
        }
-
 #endregion
 }
 
index a8373bfc03070e90e346dd724fbde675302b2811..9621c73ceb3ee556fd6bd9cb27b08c938659b2a7 100644 (file)
@@ -1,3 +1,8 @@
+2009-07-14  Marek Habersack  <mhabersack@novell.com>
+
+       * HttpApplicationFactory.cs: OnFileChanged doesn't reload
+       application if reload suppression is active.
+
 2009-07-13  Marek Habersack  <mhabersack@novell.com>
 
        * HttpApplication.cs: LoadType - wrap call to LoadTypeFromBin in
index bb8802439d726bb7b236d7ff07b7ff6822e39599..039e0e068ebcaa511eb78763a8131d817e642a21 100644 (file)
@@ -717,9 +717,12 @@ namespace System.Web {
                static void OnFileChanged(object sender, FileSystemEventArgs args)
                {
                        string name = args.Name;
+                       bool isConfig = false;
+                       
                        if (StrUtils.EndsWith (name, "onfig", true)) {
                                if (String.Compare (Path.GetFileName (name), "web.config", true) != 0)
                                        return;
+                               isConfig = true;
                        } else if (StrUtils.EndsWith (name, "lobal.asax", true) && String.Compare (name, "global.asax", true) != 0)
                                return;
 
@@ -731,6 +734,17 @@ namespace System.Web {
                        FileSystemWatcher watcher = sender as FileSystemWatcher;
                        if (watcher != null && String.Compare (watcher.Filter, "?eb.?onfig", true) == 0 && Directory.Exists (name))
                                return;
+
+#if NET_2_0
+                       // We re-enable suppression here since WebConfigurationManager will disable
+                       // it after save is done. WebConfigurationManager is called twice by
+                       // Configuration - just after opening the target file and just after closing
+                       // it. For that reason we will receive two change notifications and if we
+                       // disabled suppression here, it would reload the application on the second
+                       // change notification.
+                       if (isConfig && WebConfigurationManager.SuppressAppReload (true))
+                               return;
+#endif
                        
                        lock (watchers_lock) {
                                if(app_shutdown)