Thursday, June 14, 2007

Dynamic.web.config

I don't know about you, but I absolutely hate it when I change something in my web.config and the application restarts. Maybe I just misspelled something in the <appSettings> or changed some custom cache configuration. The app should be able to survive some configuration settings changes without resetting. So I built my own ConfigurationSettings-like class and named it DynamicConfigurationSettings. It reads a web.config-like file (which I call dynamic.web.config, but could be anything) and whenever the file changes, its values are automatically reloaded (courtesy of FileSystemWatcher and Bjarne Lindberg's FileWaiter). FileWaiter was necessary because FileSystemWatcher raises multiple events when a file is changed, a problem described in this thread. DynamicConfigurationSettings is meant to be used as a drop-in replacement for ConfigurationSettings. It was written for 1.1 but it should be fairly easy to port it to 2.0. Like I said, dynamic.web.config has a schema similar to that of a basic web.config:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="configuration">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="configSections">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="section">
                <xs:complexType>
                  <xs:attribute name="name" type="xs:string" use="required" />
                  <xs:attribute name="type" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="appSettings">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="add">
                <xs:complexType>
                  <xs:attribute name="key" type="xs:string" use="required" />
                  <xs:attribute name="value" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

(extracted with Visual Studio). Basically, it supports <appSettings> and <configSections>.
Example:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
  <section name="customSection" type="MyApp.CustomSectionHandler, MyAssembly"/>
 </configSections>
  <appSettings>
  <add key="MaxResults" value="300"/>
  </appSettings>
 <customSection>
  <default>hello</default>
  <rules>
   <rule source="something" destination="something else"/>
  </rules>
 </customSection>
</configuration>

To use it, just call DynamicConfigurationSettings.load(@"path\to\dynamic.web.config") in your Application_Start and that's it.

Additionally, whenever it can't find some requested config item, it tries to fetch it from ConfigurationSettings. So, if your code uses DynamicConfigurationSettings, you can override your web.config settings with your dynamic.web.config settings. Example: suppose you have defined MaxResults as 200 in <appSettings> in web.config, and you have the following code:

DynamicConfigurationSettings.load("dynamic.web.config");
int maxResults = Convert.ToInt32(DynamicConfigurationSettings.AppSettings["MaxResults"]);
You'd get 300, as it's defined in dynamic.web.config. BUT, if we remove the definition from dynamic.web.config, you get 200, as defined in web.config.
Before going to the code, a piece of advice/disclaimer:

DON'T use this to store critical data! I only use it to store cache sizes and other non-critical stuff. I tried hard to make it as thread-safe and fault-tolerant as possible, but be aware that it's very dangerous to change configuration at runtime. If you edit you dynamic.web.config and save it while it's not well-formed, and your application crashes, it's not my fault (it shouldn't happen, but still).
I recommend that you wrap every access to a strongly-typed value in DynamicConfigurationSettings (like the maxResults example above) with try..catch and provide sensible defaults, so you won't crash everything if you write something like <add key="MaxResults" value="Eoo"/>

You have been warned. Now here's the code, go have fun :-)
As usual, comments and bugfixes are very welcome.

FileWaiter.cs
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace Utils
{
 /// <summary>
 /// Waits for a file to close
 /// From Bjarne Lindberg (http://www.thescripts.com/forum/post1453548-8.html)
 /// </summary>
 public class FileWaiter
 {
  public string fileName;

  public FileWaiter(string fileName) {
   this.fileName = fileName;
  }

  public void DoWait(int timeout) {
   DoWait(new TimeSpan(0, 0, 0, 0, timeout));
  }

  public void DoWait() {
   DoWait(TimeSpan.MinValue);
  }

  public void DoWait(TimeSpan timeout) {
   Debug.Assert(fileName != null);
   Thread t = new Thread(new ThreadStart(ThreadWait));
   t.Start();
   if (timeout > TimeSpan.MinValue)
    t.Join(timeout);
   else
    t.Join();
  }

  private void ThreadWait() {
   do {
    //Start waiting..
    Thread.Sleep(500);
   } while (File.Exists(fileName) && isFileOpen(fileName));
  }

  protected static bool isFileOpen(string fileName) {
   FileStream s = null;
   try {
    s = File.Open(fileName, FileMode.Open,
                  FileAccess.ReadWrite,
                  FileShare.None);
    return false;
   }
   catch (IOException) {
    return true;
   }
   finally {
    if (s != null)
     s.Close();
   }
  }
 }
}

DynamicConfigurationSettings.cs
using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Xml;
using System.Collections.Specialized;
using log4net;

namespace Utils
{
 /// <summary>
 /// Similar to <see cref="System.Configuration.ConfigurationSettings"/>, but accepts changes in configuration file at runtime without restarting the application
 /// </summary>
 public class DynamicConfigurationSettings
 {
  private static readonly ILog log = LogManager.GetLogger(typeof(DynamicConfigurationSettings));
  
  /// <summary>
  /// Gets configuration settings in the configuration section.
  /// </summary>
  public static NameValueCollection AppSettings {
   get {
    lock (padLock) {
     if (_appSettings != null)
      return _appSettings;
     return ConfigurationSettings.AppSettings;     
    }
   }
  }
  
  private static Hashtable configSectionsCache = new Hashtable();

  /// <summary>
  /// Returns configuration settings for a user-defined configuration section.
  /// </summary>
  /// <param name="sectionName">The configuration section to read.</param>
  /// <returns>The configuration settings for sectionName.</returns>
  public static object GetConfig(string sectionName) {
   if (configSectionsCache[sectionName] == null)
    try {
     SectionConfig config = (SectionConfig) sections[sectionName];
     IConfigurationSectionHandler sectionHandler = (IConfigurationSectionHandler) Activator.CreateInstance(Type.GetType(config.handlerName));
     configSectionsCache[sectionName] = sectionHandler.Create(null, null, config.node);
    } catch (Exception) {
     configSectionsCache[sectionName] = ConfigurationSettings.GetConfig(sectionName);
    }
   return configSectionsCache[sectionName];
  }

  /// <summary>
  /// Loads configuration from xml document
  /// </summary>
  /// <param name="config">Document to load</param>
  public static void load(XmlDocument config) {
   XmlNodeList configSections = config.SelectNodes("/configuration/configSections/section");
   
   lock (padLock) {
    sections = new Hashtable();
    
    foreach (XmlNode section in configSections) {
     SectionConfig sc = new SectionConfig();
     string sectionName = section.Attributes["name"].InnerText;
     sc.node = config.SelectSingleNode("/configuration/" + sectionName);
     sc.handlerName = section.Attributes["type"].InnerText;
     sections[sectionName] = sc;
    }
   
    XmlNode nodeAppSettings = config.SelectSingleNode("/configuration/appSettings");
    NameValueSectionHandler nvsh = new NameValueSectionHandler();   
    NameValueCollection nvc = (NameValueCollection) nvsh.Create(null, null, nodeAppSettings);
    _appSettings = new NameValueCollection();
    _appSettings.Add(ConfigurationSettings.AppSettings);   
    foreach (string k in nvc.Keys)
     _appSettings[k] = nvc[k];    
    configSectionsCache = new Hashtable();
   }

  }
  
  /// <summary>
  /// Loads configuration from xml file and reloads when it changes
  /// </summary>
  /// <param name="filename"></param>
  public static void load(string filename) {
   _filename = filename;
   XmlDocument config = new XmlDocument();
   try {
    config.Load(filename);
    load(config);
    if (fsw != null) {
     fsw.Dispose();
    }
    fsw = new FileSystemWatcher(Path.GetDirectoryName(filename));
    fsw.Changed += new FileSystemEventHandler(fsw_Changed);
    fsw.EnableRaisingEvents = true;       
   } catch (Exception e) {
    log.ErrorFormat("Error loading dynamic.web.config: {0}\n{1}", filename, e.ToString());
   }
  }
  
  /// <summary>
  /// Loads configuration from xml stream
  /// </summary>
  /// <param name="s"></param>
  public static void load(Stream s) {
   XmlDocument config = new XmlDocument();
   StreamReader sr = new StreamReader(s);
   try {
    config.LoadXml(sr.ReadToEnd());
    load(config);
   } catch (Exception e) {
    log.Error("Error loading dynamic.web.config:", e);
   } finally {
    sr.Close();
   }
  }
  
  /// <summary>
  /// Loads configuration from xml string
  /// </summary>
  /// <param name="s"></param>
  public static void loadString(string s) {
   XmlDocument config = new XmlDocument();
   try {
    config.LoadXml(s);
    load(config);          
   } catch (Exception e) {
    log.Error("Error loading dynamic.web.config:", e);
   }
  }

  /// <summary>
  /// Handles changes in config file
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private static void fsw_Changed(object sender, FileSystemEventArgs e) {
   if (e.Name.ToLower() == Path.GetFileName(_filename.ToLower()) && e.ChangeType == WatcherChangeTypes.Changed) {
    FileWaiter fw = new FileWaiter(e.FullPath);
    fw.DoWait();
    load(e.FullPath);
   }
  }

  private static NameValueCollection _appSettings;  
  private static FileSystemWatcher fsw;
  private static string _filename;
  private static Hashtable sections;
  private static object padLock = new object();
  
  private class SectionConfig {
   public string handlerName;
   public XmlNode node;
  }

 }
}

Sunday, June 10, 2007

Exception Translation with Castle Windsor

Warning: the following code has become obsolete since the release of Castle RC3. I'll update as soon as I can. The concepts still apply, though.

Recently I've been writing an app using Castle Windsor. While I was writing the service classes, the following problem came up: I don't want my services' consumers catching Dao exceptions. I'm using ActiveRecord to persist domain objects, but it could have been directly NHibernate, or iBatis. Either way, whoever uses the service classes should be ignorant about persistance details. Suppose the service interface looks like this:

public interface ISomeService {
 void ProvideService();
}
and its implementation lets an internal exception pass, which we could model like this to make things simple:
public class SomeService : ISomeService {
 public void ProvideService() { 
   throw new InternalException();
  }
}

public class InternalException : ApplicationException {}
Now we'd like to translate InternalException into an ExternalException, which is a service-level exception. We could manually catch InternalException in every method of SomeService, but that's annoying.
Let's use Windsor instead. We need three components: the exception translator (which says what external exception corresponds to each internal exception), the interceptor (which actually does the try..catch) and a contributor (which installs the interceptor and translator).
First, let's write a test that should pass when we finish installing all three components:
[TestFixture]
public class ExceptionTranslatorTests {
  [Test]
  [ExpectedException(typeof (ExternalException), "1")]
  public void ExceptionIsTranslated() {
    IWindsorContainer container = new WindsorContainer();
    container.AddComponent("service", typeof (ISomeService), typeof (SomeService));
    ISomeService svc = container.Resolve<ISomeService>();
    svc.ProvideService();
  }
}

public class ExternalException : ApplicationException {
  public ExternalException(string message) : base(message) {}
  public ExternalException() {}
  public ExternalException(string message, Exception innerException) : base(message, innerException) {}
}
If we run this test now, it will fail, since the exception thrown by ProvideService() is still InternalException instead of the expected ExternalException.
Now let's build the translator:
public interface IExceptionTranslator {
 Exception translate(Exception e);
}

public class SomeExceptionTranslator : IExceptionTranslator {
  public Exception translate(Exception e) {
    return new ExternalException("1", e);
  }
}
Simple, no? Just make sure you wrap the original exception, so you don't lose stack information and stuff.
Now for the generic interceptor:
public class ExceptionTranslationInterceptor<T> : IMethodInterceptor where T : IExceptionTranslator {
  private T translator;

  public ExceptionTranslationInterceptor(T translator) {
   this.translator = translator;
  }

  public object Intercept(IMethodInvocation invocation, params object[] args) {
    try {
      return invocation.Proceed(args);
    } catch (Exception e) {
      throw translator.translate(e);
    }
  }
}
Notice that the actual translation logic will be injected by Windsor into the interceptor. Oh, and make sure theIMethodInterceptor you implement is the one in Castle.Core.Interceptor, not the one in AopAlliance.Intercept.
You may wonder why the interceptor takes <T>, you'll see why in a second when we build the contributor:
public class ExceptionTranslatorContributor<T, T2> : IContributeComponentModelConstruction
 where T2 : IExceptionTranslator {
  public void ProcessModel(IKernel kernel, ComponentModel model) {
    if (typeof (T).IsAssignableFrom(model.Implementation)) {
      kernel.AddComponent(typeof (T2).ToString(), typeof (T2));
      kernel.AddComponent(string.Format("ExceptionTranslationInterceptor<{0}>", typeof(T2)), typeof(ExceptionTranslationInterceptor<T2>));
      model.Interceptors.Add(new InterceptorReference(typeof (ExceptionTranslationInterceptor<T2>)));
    }
  }
}

This contributor takes two types: T, which is the service to intercept, and T2, which is the IExceptionTranslator. It installs the translator and the corresponding interceptor as components, then it installs the interceptor as such. By making the interceptor and the contributor generic, we can easily associate different translators for different services, just by setting up the contributor in the container, like this:
[TestFixture]
public class ExceptionTranslatorTests {
  [Test]
  [ExpectedException(typeof (ExternalException), "1")]
  public void ExceptionIsTranslated() {
    IWindsorContainer container = new WindsorContainer();
    container.Kernel.ComponentModelBuilder.AddContributor(new ExceptionTranslatorContributor<SomeService, SomeExceptionTranslator>());
    container.AddComponent("service", typeof (ISomeService), typeof (SomeService));
    ISomeService svc = container.Resolve<ISomeService>();
    svc.ProvideService();
  }
}
And now the test passes!
Full code shown here is available here.

Monday, May 28, 2007

Xml serialization of enums - System.InvalidOperationException

A few days ago I was writing a web service when all of a sudden a test failed on me with:
System.InvalidOperationException: Instance validation error: '3' is not a valid value for SomeEnumInMyApp
which turned out to be quite a google-unfriendly exception. The exception is thrown when trying to serialize an enum with a value that isn't explicitly in the declaration of the enum. A couple of people dealt with this, but I don't really want to manually edit the WSDL, and I don't care right now about breaking a future version of the web service (not likely to happen in my case)
In case nothing of this makes any sense to you, let's see some code:
Given this enum declaration:
public enum Epopo {
 Uno = 1,
 Dos = 2
}
"Epopo?" Yes, Epopo. Just because.
This test passes:
[Test]
public void SerializeEnumToXml1() {
 Epopo p = Epopo.Uno;
 using (MemoryStream ms = new MemoryStream()) {
  XmlSerializer xml = new XmlSerializer(typeof(Epopo));
  xml.Serialize(ms, p); 
 }
}
But this one fails with the mentioned exception:
[Test]
public void SerializeEnumToXml2() {
 Epopo p = Epopo.Uno | Epopo.Dos;
 using (MemoryStream ms = new MemoryStream()) {
  XmlSerializer xml = new XmlSerializer(typeof(Epopo));
  xml.Serialize(ms, p); 
 }
}
Hmm. What if we try with a LosFormatter instead of XmlSerializer?
[Test]
public void SerializeEnumToLosFormatter() {
 Epopo p = Epopo.Uno | Epopo.Dos;
 LosFormatter los = new LosFormatter();
 using (MemoryStream ms = new MemoryStream()) {
  los.Serialize(ms, p); 
 }
}
Test passes. Damn you, XmlSerializer. What about XmlEnum?
public enum XmlEpopo {
 [XmlEnum("Uno")]
 Uno = 1,
 [XmlEnum("Dos")]
 Dos = 2
}

[Test] 
public void SerializeEnumWithXmlEnumToXml() { 
 XmlEpopo p = XmlEpopo.Uno | XmlEpopo.Dos; 
 using (MemoryStream ms = new MemoryStream()) { 
  XmlSerializer xml = new XmlSerializer(typeof(XmlEpopo));
  xml.Serialize(ms, p); 
 }
}
Doesn't work either... Well, after that I despaired a bit and started trying everything: XmlElement, PowerCollections.Set, Iesi.Collections.ListSet, Dictionary<Epopo, object>, but nothing worked. Ultimately, I used a List<Epopo>, like this:
[Test]
public void SerializeListOfEnum() { 
 List<Epopo> l = new List<Epopo>();
 l.Add(Epopo.Uno); 
 l.Add(Epopo.Dos); 
 using (MemoryStream ms = new MemoryStream()) { 
  XmlSerializer xml = new XmlSerializer(typeof(List<Epopo>));
  xml.Serialize(ms, l); 
 } 
}
It sucks, I know, but at least it works. If any knows a better solution, feel free to post a comment!
By the way, it seems that Microsoft is having this same problem as well...
Full code for these tests is here.

Sunday, May 20, 2007

Introduction 1.1

A good way to know a programmer is to see what tools he uses. If I read "NUnit" on a résumé, I can safely assume he/she at least heard of unit testing (that doesn't mean he's doing it right, though). If I see "MbUnit", it's likely that he did use NUnit at first, but needed more or wasn't satisfied (that was my case) So, here are some of the tools/frameworks I use (in no particular order) Another good way is to see what he's reading. I must confess I'm not much of a book reader (technical books are very expensive here) but I read a lot of blogs (part of my blogroll is in the column on the right). I think the best ideas, patterns, practices, like TDD, DDD, Continuous Integration, etc, are out there in the net anyway. I try not to stay with .NET blogs only, I also read some Java, Javascript and even Lisp blogs (although Lisp is a pending subject)

Wednesday, May 16, 2007

Introduction

Hi, my name is Mauricio Scheffer, and this is yet-another-programming-blog. I live in Argentina and I'm currently working as a developer for a fairly popular US website (around 150000 unique visitors per month) using ASP.NET (still 1.1, we don't have time to migrate yet :-( ) Since I'm pretty much the only developer here (my boss knows her .NET and SQL but she's too busy administering the site) I spend most of the day squashing bugs (hence the blog title). But I'm not complaining, I'm getting a new teammate next week and I'm constantly interviewing new people. Plus, there's zero bureaucracy, so I'm free to implement things the way I want, as long as it doesn't take forever. Well, that should be enough for a first post. :-)