Showing posts with label opensource. Show all posts
Showing posts with label opensource. Show all posts

Tuesday, March 07, 2017

Writing plugins for Octopus

Following on from my previous post about writing plug-ins, here’s the next installment of what may or may not become an irregular feature.

I’ve been using Octopus Deploy at work for several years now, and it is a great tool for automating the deployment of software; in my case, it turned a 20-page manual checklist that took 40 minutes to work through into a five minute fire-and-forget exercise. However, there are still little steps here and there that can’t be automated out-of-the-box, and that’s where we pick up our story.

We track our work-in-progress using JIRA, and one of those tedious post-release tasks is to update all the tickets, setting their status to Released and so on. So, given that JIRA exposes an API, and Octopus supports custom steps or step templates written in Powershell, why not combine the two and cross another manual step off the list?!

Show me the code!

Actually, I’m not going to post any code snippets this time – rather, I’ve published the source code to GitHub (something else I’m learning, after 15 years of Subversion), so go ahead and fork it. There’s not a whole lot of error handling in there at the moment, but it seems to do the trick. There are two files:

  • the original Powershell file, which you can open up and run in the Powershell IDE;
  • the Octopus Deploy step template, which wraps up the Powershell in a bit and parameterises it to make the code more reusable

Tuesday, February 14, 2017

Writing plugins for Exceptionless

Exceptionless is one of those tools that no developer should be without; it integrates with your application (currently .NET and NodeJS platforms are supported, with the promise of more to come), captures your unhandled exceptions, and provides you all sorts of tools to manage these exceptions.

The project itself is open source, and you can set up your own on-premise server to run the service, or you can use their cloud service if you don’t want the hassle of maintaining YADS (Yet Another D**n Server).
Their support is also very good; I’ve been working on a number of different scenarios (e.g. creating custom reports using the API to query for data, at least until they implement custom dashboards). The scenario that prompted this post was simple – when we deploy a new version of our API, we run a suite of automated regression tests to make sure we haven’t broken anything for our customers, but these regression tests include a large number of expected failure tests, which result in a large number of false positives being reported. So time to break open the coding tools and fix that…

Show me the code

The Exceptionless client provides a number of extension points, and after talking to Blake Niemyjski, who seems to be the main point of contact for enquiries, I whipped up the following plugin:
[Priority(5)]
public class SquelchExpectedExceptionsPlugin : IEventPlugin
{
    public void Run(EventPluginContext context)
    {
        var exception = context.ContextData.GetException();
        var httpContext = context.ContextData.GetHttpContext();

        if (ApiExceptionClassifier.IsAnExpectedException(exception, httpContext.Request.Headers))
        {
            context.Cancel = true;
        }
    }
}

internal static class ExceptionlessExtensions
{
    internal static HttpActionContext GetHttpContext(this IDictionary<string, object> data)
    {
        if (!data.ContainsKey("HttpActionContext")) return null;
        return data["HttpActionContext"] as HttpActionContext;
    }

    internal static Exception GetException(this IDictionary<string, object> data)
    {
        if (!data.ContainsKey("@@_Exception")) return null;
        return data["@@Exception"] as Exception;
    }

I had to add the extension methods as these are currently internal to the Exceptionless.Net library, while the ApiExceptionClassifier is a bit of business logic which checks for a specific header the the request to determine what sort of HTTP status code a given exception should result in (e.g. we want all ValidationExceptions to result in a 400 Bad Request). The plugin priority needs to be set to a low value so that it gets executed as early as possible – there’s not much point in gathering all sorts of data if we’re just going to throw it all away when the Cancel method gets called.
Once that was done, all that is left is to register the plugin in the API’s startup routine:
ExceptionlessClient.Default.Configuration.AddPlugin<SquelchExpectedExceptionsPlugin>();
And now, my regression tests don’t fill the Exceptionless reports with noise, unless an actual exception occurs during the process.

Friday, August 17, 2012

NDoc3 - a modern-day Lazarus

Back in the early days, the nDoc utility was the go-to solution for auto-generating API documentation for .NET applications. The build engine would parse the XML comments that the developer decorated class members with, and nDoc would take those results and produce MSDN-style web pages based on it. As an open source product, it attracted quite a following.
However, with the release of .NET Framework v2, nDoc failed to make the jump and instead imploded, being at the time quite a high-profile example of the risks of running an open source project:
  • the community might love your product, but if they don’t contribute something back (whether code, time or money) then it may not be possible for you to continue to develop it;
  • the vendor whose environment you are working in (in this case, Microsoft, but it could easily be another company. Apple, anyone?) can move into your space and deliver something that is good enough to draw mindshare away from your target audience. Actually, Sandcastle has never really been as good as, nor as easy to use as, nDoc, but the damage was done;
Fast-forward to the near-present time, and while Sandcastle continues to be unwieldy, the open source community is once again picking up the nDoc baton, this time in the form of nDoc3. In its current state, nDoc3 brings to the table support for .NET 3.5, but if you’re prepared to download the source code and build it yourself, you can get .NET 4 support as well. Of course, Microsoft moved the goalposts slightly this week with the release of .NET 4.5, so the cycle continues!
If you do want to try getting .NET 4 support, these are the steps that I had to go through (and a few hoops that I needed to jump through):
  1. Download the source code from Sourceforge, and extract the tarball to the file system;
  2. Rather than opening in VS2008 as the README file directs, I fired up VS2010;
  3. Building threw up a whole host of warnings, so I decided to try the provided NAnt build scripts;
  4. Building threw up a whole host of warnings, mostly about missing NAnt targets; in the end, I had to hack the NAnt.build file to pieces, first commenting out the references to the JavaDoc, Latex and LinearHtml documenters which, though references in the build file, don’t exist in the source code; I also had to comment out all the code related to testing, as again it referred to folders and/or files that don’t exist;
  5. I also needed to remove a duplicate AssemblyVersion attribute from Core\AssemblyInfo.cs;
  6. This at least allowed me to compile and run the application; however, when I tried to get it to document one of my own pieces of code, it quickly threw an exception which I traced back to an already-reported issue; fortunately, the user who raised the issue also submitted a patch, which while it has not been applied to the source tree, at least fixes the problem.
So now I have nDoc3 happily spitting out documentation for me; perhaps if time allows, I will look into writing a Wiki-format documenter.

Friday, January 27, 2012

A nice little trick with FluentAssertions

I love using FluentAssertions when writing tests for my code, it just makes the code so expressive in plain language. Recently I’ve been working on tests which involve the testing of lots of properties on a test result, and was looking for a way to remove a lot of the repetition involved.

Usually when asserting the properties on a test result, you can do it in three different ways. If I have my test class defined as below:

[TestFixture]
public class MyTestFixture
{
    private MyClass _subject; 
    private MyOtherClass _result;
 
    [SetUp]
    public void ExecuteTest() 
    { 
        _subject = new MyClass(); 
        _subject.Initialise(/*.. Some parameters here ..*/); 
        _result = _subject.SomeOperation();
    }
}

Then I can test the results as follows:

A. Assert all the properties in a test method

    [Test]
    public void ThePropertiesAreSet()
    {
        _result.First.Should().Be("Paula Bean");
        _result.Second.Should().Be("Brillant!");
    }

This is probably the usual way people do this. However, if the first test fails, the second test will never run, and we'll never know if our test method set the second result correctly or not, so we are lacking information as to the overall state of the test. Secondly, as the number of properties to test increases, you end up with a lot of visual noise, although Intellisense will at least take some of the effort out of creating the noise.

B. Assert the properties in a test method per property

    [Test]
    public void TheFirstPropertyIsSet()
    {
        _result.First.Should().Be("Paula Bean");
    }
 
    [Test]
    public void TheSecondPropertyIsSet()
    {
        _result.Second.Should().Be("Brillant!");
    }

With this approach, we now know if the setting of _result.Second was successful, even if _result.First failed. This extra information does come at the expense of more visual noise (the addition of another test method). Also, it allows you to really game the system if you get rewarded for the number of tests in your project :-)

C. Implement IEquatable

As the number of properties on MyOtherClass go up, you'll find a lot of code appearing in the test method. You can remove this by implemeting IEquatable<MyOtherClass> on MyOtherClass:

public class MyOtherClass : IEquatable<MyOtherClass>
{
    public override bool Equals(MyOtherClass other)
    {
        return this.First == other.First && this.Second == other.Second;
    }
}

(please note you should also really implement Object.Equals and Object.GetHashCode if you're going to use IEquatable, and for brevity, I've missed out a lot of the basic implementation of IEquatable<T>)

The result of this is that our test method now becomes:

    [Test]
    public void TheFirstPropertyIsSet()
    {
        MyOtherClass expected = new MyOtherClass { First = "Paula Bean", Second = "Brillant!" };
        _result.Should().Be(expected);
    }

Much cleaner and very expressive, and you'll probably find your code benefits elsewhere from having implementations of IEquatable<T>; but you do have to write that implementation (and perhaps unit test it too!). Yet more code...

This finally leads me to my point:

D. Use anonymous classes and FluentAssertion's AllProperties

How's this for simplicity?

    [Test]
    public void TheFirstPropertyIsSet()
    {
        _result.ShouldHave().AllProperties().EqualTo(new { First = "Paula Bean", Second = "Brillant!" });
    }

ShouldHave().AllProperties() does all the hard work of checking the properties. You can also use SharedProperties() if you only need to test a subset of the properties, and add IncludeNestedObjects if you have a more complex object model.

P.S. I love the story of the Brillant Paula Bean. It confirms all your prejudices about contractors.

Tuesday, March 22, 2011

Unity and log4net

In my current project at TouchStar, I’ve had the opportunity to play with Unity, Microsoft’s Inversion-of-Control container. Previously I’ve used StructureMap, which is very popular, but I wanted to broaden my experience of different containers, and given that my project involved a WPF client, Unity seemed to be a good choice.

One of the first problems was how to configure log4net using Unity. Usually, log4net is called from within a class thusly:

   1: public class MyClassWithLogger()
   2: {
   3:     private static readonly ILog Logger = LogManager.GetLogger(typeof(this));
   4:  
   5:     public void DoAction()
   6:     {
   7:         Logger.Debug("Hello world");
   8:         DoSomeOtherAction();
   9:     }
  10:  
  11:     private void DoSomeOtherAction()
  12:     {
  13:         Logger.Debug("Still here");
  14:     }
  15: }

Which, depending on how you’ve configured your appenders, would produce an output something like this:

   1: MyClassWithLogger [DEBUG] - Hello world
   2: MyClassWithLogger [DEBUG] - Still here

So far, so vanilla, and the sort of code that anyone whose used log4net will have written thousands of times; but what happens if we want to apply good SOLID design principles and inject the log4net dependency into the class, rather than have the class create the instance itself. I prefer constructor injection for this, as it makes it very explicit what the dependencies are, and you can’t accidentally forget to set one without getting a compile-time error. The class would then become:

   1: public class MyClassWithLoggerDependency()
   2: {
   3:     private static readonly ILog Logger;
   4:  
   5:     public MyClassWithLoggerDependency(ILog logger)
   6:     {
   7:         Logger = logger;
   8:     }
   9:  
  10:     public void DoAction()
  11:     {
  12:         Logger.Debug("Hello world");
  13:         DoSomeOtherAction();
  14:     }
  15:  
  16:     private void DoSomeOtherAction()
  17:     {
  18:         Logger.Debug("Still here");
  19:     }
  20: }

We’re now ready to apply some IoC magic with Unity to create the instance of ILog that our class will use. Since log4net uses its own factory class to instantiate itself, we have to jump through a hoop or two to get Unity to set things up properly:

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         var container = new UnityContainer()
   6:             .Register<ILog>(new InjectionFactory(x => LogManager.GetLogger(typeof(this))));
   7:             .Register<MyClassWithLoggerDependency>()
   8:  
   9:         var myClass = new container.Resolve<MyClassWithLoggerDependency>();
  10:         myClass.DoStuff();
  11:     }
  12: }

Which produces an output like this:

   1: Program [DEBUG] - Hello world
   2: Program [DEBUG] - Still here

See the problem? Now the logger is using the context of the code where Unity was configured to log from, rather than the context that the logger is being called from. I’ve seen plenty of questions asked about this, but it was only today that I stumbled upon the answer to this problem, hidden in a discussion on the Unity forums. A developer named Marco posted his solution, which was to create two extensions – the first being a BuildTracking extension, and the second being a LogCreation extension. The build tracking extension keeps tabs on what types are being built up, and then calls out to the log creation extension to create the logger component in the context of the class which is asking for it. Another developer, Scott, then posted his amendments that took care of some breaking API changes from the earlier version of Unity that Marco was using, together with an enhancement to improve detection of the calling class name when log4net is not being used with dependency injection.

My small contribution to the discussion was to provide the actual implementation code, which is as follows:

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         var container = new UnityContainer()
   6:             .AddNewExtension<BuildTracking>()
   7:             .AddNewExtension<LogCreation>()
   8:             .Register<MyClassWithLoggerDependency>()
   9:  
  10:         var myClass = new container.Resolve<MyClassWithLoggerDependency>();
  11:         myClass.DoStuff();
  12:     }
  13: }
Now that in my eyes is a much tidier piece of code. Gone is the explicit registration of the ILog type with its rather ungainly use of the InjectionFactory to create the appropriate implementation, and instead we have the two new extensions registered.

This is a great little bit of code, and big thanks go to Marco and Scott for sharing their contributions with us. I’m sure that many other developers will find this useful.

Monday, March 21, 2011

Watching Ideas Take Flight

In my last entry, I mentioned an idea I’d had before on how to make it easier to package .NET applications when run as part of an automated build. It solved a problem for me, and I thought it worth sharing, firstly as an aide mémoire for myself, and secondly because it seems the sort of thing that other people would find useful.

Rob Reynolds came across my post, and he obviously found it useful. In fact, he liked it so much that he has put together a NuGet package to make the set-up of _PublishedApplications even easier, together with a demo of how easy it is to use.

Demo of _PublishedApplications at work

I’m thrilled to see this getting out there – a big thanks to Rob for really getting this off the ground, as well as giving it more exposure.

Wednesday, May 20, 2009

SvnRevisionLabeller makes its v2 release

Recently Thoughtworks started to make available release candidates for version 1.4.4 of CruiseControl.NET. Tucked away inside there though are some changes to some of the core components, that break compatibility with SvnRevisionLabeller.

This gave me the proper motivation to find some time to finish off the packaging of a fairly major change to the way the labeller constructs build labels. The real work was actually done by another developer, fezguy - I just had to integrate his patch, tweak it to the way I like things (hey, it is my project after all!), and finally start work on some automated tests. I like automated tests.

Since the two changes combined do break backwards compatibility, it's good to take a step back and see where we are now.

Before, SvnRevisionLabeller was quite opinionated about how it thought build labels should be structured. With version 1.1, it was {major}.{minor}.{svnRevision}.{build} - {build} would start at 0, then if you forced another build without another commit having taken place, it would be incremented by one. A commit would then reset {build} back to 0, so you had a good idea how often the build was being forced. I later added the ability to specify prefix and suffix text for the label.

Version 1.3 saw me rethink things a bit, with the label now defaulting to {major}.{minor}.{build}.{svnRevision}; now, successive forced builds made no difference to the label. What it did give me was that when I deployed a hotfix to production (usually designated by the incremented {build} number), I could tell immediately which Subversion revision it was built with.

So it obviously works the way I wanted it to - but what about other people? Version 2.0 makes the label scheme very flexible - some tokens are defined, and you just position the tokens wherever you want them in a string pattern. So for instance, if I set my configuration thusly:

<labeller type="svnRevisionLabeller">
<major>1</major>
<minor>4</minor>
<build>2</build>
<pattern>Release v{major}.{minor}.{build} from {revision}</pattern>
</labeller>

then when revision 42 is committed, the build label will become Release v1.4.2 from 42. At work, I use the {build} number to represent the sprint number since we began working on the {minor} release.

Finally, I noticed that my project was not the only one to be caught out by CC.NET's upgrade - Codeplex's CC.NET Community Plugins are also broken by the change; unfortunately, they have a little more work to do, as it's not just a question of updating their external libraries and recompiling. I'm rather underwhelmed by the amount of activity on the site: I raised an issue earlier this month, and then submitted a patch to fix the issue, and it still hasn't been acknowledged yet, let alone added to the repository. For the time being, I won't be upgrading to v1.4.4 at work, since we need that library for our deployments.

Tuesday, March 31, 2009

MassTransit

Earlier this month, I mentioned the MassTransit project, an open-source .NET Enterprise Service Bus implementation, a topic sure to break the ice at a many a party.

We've been getting stuck into MassTransit at work for a major project, and had all sorts of problems along the way, mainly due to the youth of the project and the general lack of documentation. Where there were code samples, sometimes the code just plain didn't work.

Blog postings need images, apparently; so here's one that's distantly related to the topic under discussion - it's a restaurant tram in MelbourneThe two main developers, Dru and Chris, have been a fountain of knowledge and general helpfulness, responding quickly to my sometimes continuous barrage of questions, not helped of course by our being on opposite sides of the planet. I've been able to chip in with the occasional patch, nothing to fancy at this stage, but at least enough for me to feel like it's not a completely one-way street.

Anyway, congratulations to MassTransit for shipping their v0.6 release candidate. I've been running it for a few days now, although without using some of the cooler pieces of new technology, such as the saga state machines. Hopefully with their help, I'll be able to get our project back on track and heading in the right direction.

Thursday, March 19, 2009

Topshelf

At work, I'm currently working on a project which requires the integration of several different systems, some of them hosted on the internet, and some on different sides of the corporate firewalls. This means that we can expect to lose connections between systems, which naturally pointed us in the direction of message queues, which are designed for such scenarios. A chat with a former colleague pointed me in the direction of enterprise service buses as the core technology to take care of all the plumbing; and after comparing feature sets with our requirements, I downloaded MassTransit.

What interested me was a spin-off from the MassTransit project, called Topshelf, which has a single and simple aim - to get rid of all the cruft associated with actually launching .NET programs, especially all the boilerplate code you have to come up with to run services.

I ran into a few issues though: the first was that before, I had been using the WiX's ServiceInstall code to handle all the service registration, such as setting the service name, description and credentials. However, Topshelf provides its own code to do that, and the two aren't compatible. In the end, the solution I came up with was to remove the WiX code and use Topshelf's, which would be invoked by a custom action in the WiX script:

<CustomAction Id="InstallService" FileKey="ServiceExe" ExeCommand="/install" />
<InstallExecuteSequence>
    <Custom Action="InstallService" After="InstallFinalize" />
</InstallExecuteSequence>

FileKey is just a reference to the Id of the executable for the service, and ExeCommand contains the parameter(s) to pass to it.

All well and good, but unfortunately, Topshelf's implementation currently launches the same dialog box that installutil does, and if you're trying to produce an unattended install file, a dialog box is the last thing you want. I've had a little play around with the source code and came up with something which would technically work, but was nowhere near as elegant as the rest of the code, so I'm not planning on submitting it.

I did add a few refactorings and documentation into the codebase, nothing clever or anything; I then submitted the changes as a patch to Dru, and within the hour, they had been committed to trunk. This really reminds me why I love open-source programming so much - if the application doesn't do quite what you want it to do, you can download the source code and modify it, and if it works well, you can contribute that change back to the community. Talk about a win-win situation. Non-open source companies have to think about every way that people will use their software, and then program all that functionality in, thus proving the 80-20 rule - 80% of your users are only using 20% of your application's functionality. Did you know that Microsoft Word has a Japanese Greeting dialog box, which allows you to select a greeting specific to the month of the year?! I wonder even how many Japanese people know it exists?

Japanese Greeting dialog box from Microsoft Word 2003

Speaking of contributing to open source, I've accepted a couple of patches to my SvnRevisionLabeller project, and should be releasing a new build soon, once I've figured how to get some good tests in there. You are testing your software, right?!

Friday, February 13, 2009

Catching Up

It's been a while since my last release of SvnRevisionLabeller, and I've not paid the project pages too much attention, seeing as everything was very quiet. Unfortunately, I hadn't set up the project to notify me whenever issues were raised, so I'd missed the raising of several bug or change requests.

Well, after stumbling upon one such change request during an unrelated Google session, I thought I'd better catch up on my project maintenance; it doesn't look very professional when your projects have bug reports unloved, unacknowledged, and unresolved.

The biggest change is that build number will be configurable; it was one of the first feature requests, and for a current project at work, it's something I wanted to do anyway.

BTW if you have downloaded and installed the plugin, I'd be interested to know. Google reports 822 downloads; the person who filed that request has linked to their site, so that's one, and of course I use it at Fairfax Digital, so that's two. Anyone care to offer up any of the other 820 downloads?!

Wednesday, February 11, 2009

Using Sandcastle with CruiseControl.NET

For a new project at work, I'm adding automatically-generated API documentation as part of my nightly build. Back in the days of .NET 1.1, we had the NDoc project, and explicit support for it in NAnt. Unfortunately, the project never made the transition to .NET 2.0, instead coming to a rather bitter and public end due to predominantly a lack of community support (despite the large numbers of downloads). I guess it's one the main failing of open-source projects - many are happy to take, but not so happy to give (whether money or time/effort).

Sandcastle is Microsoft's official documentation generator, but even three years on, it lacks some of the user-friendliness that any NDoc user had come to expect. The Sandcastle Help File Builder tool goes a long way to rectifying this, but most of the focus is on the GUI tool. Until the current v1.8 release, command line builds were taken care of by the SandcastleBuilderConsole.exe tool; however, the latest release drops this, and instead uses MSBuild, which ships with the .NET Framework. This is a rather nice touch, in my opinion - a clever use of existing tooling, rather than writing another new tool.

However, the documentation about using it in a continuous integration environment is still a little lacking, so I'm documenting here the problems I came up against.

Installation

So far, it seems like neither Sandcastle nor SHFB have been designed with easy deployment in mind. I like to include my build tools within my source repository; this saves a lot of tedious installation and configuration whenever setting up a new developer PC. Both tools come as MSI packages, with Sandcastle deploying as a 220Mb install, so maybe that one you can do without in your repository! SHFB on the other hand is relatively lightweight, at around 3Mb for the files required by the build tasks. You will need to set an environment variable for it to run properly, though:

<setenv name="SHFBROOT" 
   value="${Sandcastle.dir}" 
   unless="${environment::variable-exists('SHFBROOT')}" 
   verbose="true" />

The SHFB files that you seem to require are:

  • ColorizerLibrary.dll;
  • ICSharpCode.TextEditor.dll;
  • SandcastleBuilder.Components.Config;
  • SandcastleBuilder.*.dll;
  • SandcastleHelpFileBuilder.targets;

You'll also need the Help 2.0 Compiler tool hxcomp.exe, which ships as part of Visual Studio 2008 SDK; and of course, the SDK requires that Visual Studio 2008 is installed before it itself will install. Fortunately, you can pull out the contents of C:\Program Files\Common Files\Microsoft Shared\Help 2.0 Compiler\ and add that to your repository as well, bypassing that little pain-in-the-neck.

Configuration

To use SHFB within CC.NET with NAnt, you'll need the <msbuild> task that comes with NAntContrib. The syntax is as follows:

<msbuild project="${source.basedir}\Documentation.shfbproj">
   <arg value="/property:${HxComp.dir}" />
   <arg value="/property:SandcastlePath=C:\Program%20Files\Sandcastle\" />
</msbuild>

All the properties listed in the SHFB GUI tool can be set as above - just watch out for the need to escape spaces when hard-coding paths, which had me stumbled for an hour or two, until I found a post asking the same question.

Still To-Do

The one thing I really want to get done is programatically set the assemblies which will be documented; at the moment, you either use the GUI tool and relative paths, or edit the SHFB project file by hand. You could probably also use NAnt's <xmlpoke /> task to edit it, but I would prefer to be able to set it via parameters to the <msbuild /> task, so watch this space while I work it out.

Thursday, June 26, 2008

Implementing a .NET SortedKeyedCollection

I've been looking for a solution for this one for a while. The .NET Framework v2 provides a generic KeyedCollection<TKey, TItem>, that stores objects using a key which is a property on the object being stored, which is a fantastic concept. However, the objects are stored in a standard dictionary, in the order in which they were inserted, and there's no provision in the class for sorting the collection.

So I turned to the web for a solution - surely, someone somewhere must have tried to do the same thing as me. As it turns out, they had. This wasn't the only such example, but unfortunately, the advice given was always the same - add a Sort method, which is clunky, and forces the implementor to make an extra step to get their collection sorted; or use SortedList<TKey, TItem> or SortedDictionary<TKey, TItem>. These collections are full featured, containing all sorts of helpful methods and properties. I see similar arguments made when suggesting people use List<T> for pretty much any collection. All well and good for 90% of developers, but what do you do about the 10% who are API developers?

The reason why these classes are not so good for API developers is that these helpful classes are not easily extended - few, if any, of the methods or properties are virtual. Microsoft's own recommendation for API developers and developers of third-party libraries is that they use the Collection<T>, KeyedCollection<TKey, TItem>, and ReadOnlyCollection<T>, as these classes deliberately provide virtual methods which can be overridden to provide customisation.

So, here is my implementation of a SortedKeyedCollection<TKey, TItem>, which extends the KeyedCollection and sorts the objects at the point at which they are inserted. I'm adding this code to Google Code, more as a safe repository than to start an open source project - the project's just a shell at the moment, seeing as I can't get an SVN connection to it from work.

   1:  using System.Collections.Generic;
   2:  using System.Collections.ObjectModel;
   3:   
   4:  public abstract class SortedKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
   5:  {
   6:      protected virtual IComparer<TKey> KeyComparer
   7:      {
   8:          get 
   9:          {
  10:              return Comparer<TKey>.Default;
  11:          }
  12:      }
  13:   
  14:      protected override void InsertItem(int index, TItem item)
  15:      {
  16:          int insertIndex = index;
  17:   
  18:          for (int i = 0; i < Count; i++)
  19:          {
  20:              TItem retrievedItem = this[i];
  21:              if (KeyComparer.Compare(GetKeyForItem(item), GetKeyForItem(retrievedItem)) < 0)
  22:              {
  23:                  insertIndex = i;
  24:                  break;
  25:              }
  26:          }
  27:   
  28:          base.InsertItem(insertIndex, item);
  29:      }
  30:  }

The InsertItem override does most of the work - if you're adding a new object, it intercepts that call, and works out the index that you should be inserting the object, based on its key. By default, it uses the Comparer<TKey>.Default, so string keys are compared using the default (case-insensitive) comparer, and other primitives such as ints and decimals are compared in a similar fashion. By providing a virtual property KeyComparer, I've allowed the user of the class to provide their own IComparer<TKey>, should they want to use a specific comparer, for instance if the key is a custom class.

Monday, March 17, 2008

Updates to SvnRevisionLabeller

It's a been a while since I uploaded my SvnRevisionLabeller code to the CruiseControl.NET community site. If you don't know what it does, it allows you to label your Continuous Integration builds with the Subversion revision that they were built from. If you still don't know what it does, I suggest you stop reading now...

I wrote this plugin because it was useful for me, and seeing as CC.NET is a useful piece of Open Source software, I decided to contribute back to the community. I have no idea how many people use it, but I know the number must be greater than four, because three other people have emailed me with bug fixes and enhancements.

With these changes coming through, I decided that it was time to move away from the traditional source control known as a zip file buried somewhere in my email archives. Seeing as I'm currently going through a Google love-in, I uploaded it to Google Code, which appropriately enough means I have my code under SVN revision control.

On a similar note, my other contributions to the Open Source community, at least that part of the community that develops using .NET, is a GuidTask for NAntContrib, but the process for adding code there seems to be a lot more obtuse than Thoughtworks', so at the moment it doesn't appear in the code anyway; but at least I have it on my servers.

Sunday, June 17, 2007

Safari vs Firefox

Apple has now released a Windows version of the beta of version 3 of their Safari browser. In the first 24 hours of availability, it has been downloaded one million times, a feat roughly on par with Firefox. One question I've seen asked on such luminary sites as Slashdot is, "Will Safari steal market share from Firefox?". This is obviously a concern for the rabid anti-Microsoft crowd at Slashdot, but I really don't see it that way. One reason that I believe Firefox spread so quickly is because it was a browser that web developers could really use to enhance their productivity. Where I work, it seems most, if not all, developers use Firefox to develop the web pages, and then use IE to check that the site renders properly there too. These developers would then encourage friends and relatives to install Firefox on their PCs at home, not so much because that they could then be good developers too, but because Firefox blew IE6 out of the water on almost all counts. In the absence of a conventional marketing machine, Mozilla used word-of-mouth to "ignite the web". In my opinion, Safari is aimed at a different audience - people who have bought an iPod, and use it with their Windows PC. Apple already bundle in their atrocious QuickTime player in with iTunes, and adding a decent web browser to the bundle is a no-brainer for them. There's not too much denying that it performs better than IE when it comes to outright speed, and it appears to focus on simplicity (although many complain that it is missing too much (such as the use of the left thumb button on the mouse to navigate back a page, as IE and Firefox do. Of course, since Mac users barely get a right mouse button, it's no surprise there's no support for further buttons!)), but that is also a good thing if you don't do anything complex when browsing. What Apple are doing is playing Microsoft's monopoly game; if Microsoft can use a monopoly on the OS market to drive sales of the Office suite (or, more relevantly, to completely crush Netscape's dire Navigator 4 browser), then Apple can turn a monopoly on the MP3-player market into browser market share (by the way, see how I manage to avoid use of the most pointless and hideous word to be shoe-horned into the English language - "leveraged". Guys, the word is "used" - just because it has three times as many syllables, it doesn't make it three times as clever to use it). Even better, there is a good proportion of the market that only uses their Windows PC for music, email and web browsing. With iTunes and Safari, Apple now has a good chance of hooking those users, and converting them into Mac owners. And that's where the real money for Apple still is, selling expensive white boxes.

Wednesday, March 28, 2007

CruiseControl.NET and Subversion Revision Labeller plug-in

Last year, I finally managed to get into automated builds at work. We write a lot of platform code for the other business units in our organisation, so any bugs/defects have quite a high visibility; what could be a better way of improving the transparency of our processes than having an easily accessible automated build system, which would not only compile and test our code, but create release/deployment packages, all before we had even arrived at the office? It didn't take too long to get CruiseControl.NET up and running, using a fairly typical open-source blend of Subversion, NAnt, NAntContrib, NUnit etc etc, whicb should all be familiar to anyone who has read Open Source .NET Development, which should really be required reading for all .NET programmers. Using NAntContrib's <version> task, it's easy enough to automatically set the version number in an assembly. What it didn't allow us to do was pass that information back up to CruiseControl for display in the web dashboard or the system tray application for more immediate feedback. So I decided to knock up a plug-in to do just that. I'd already played about with plug-ins for open-source applications, so it wasn't quite so much of a big step. So in October 2006, I posted to the ccnet-user group, announcing the creation of the plug-in, inspired by a blog entry by Jonathan Malek. I got a response from Richard Hensley, who was putting together a library of plug-ins, suggesting I might want to add my code to his library, but unfortunately I haven't had any response to my emails. So I thought I'd take matters into my own hands, and post directly to the CC.NET community site; you can imagine my surprise when I saw that someone else has, fairly recently, also uploaded a Subversion revision labeller! Fortunately, there are enough differences between the two to make it worth uploading; so I now have two contributions to the open-source community, both small and specific to my job, but hopefully useful enough for others to use. Now I just have to get the NAntContrib guys to actually include mine in the builds...

Tuesday, September 12, 2006

CruiseControl.NET - Using the Multi SourceControl block with filters

As some have been led to observe, the documentation that goes with CruiseControl.NET is, charitably speaking, a little sparse. This is particularly true of the Multi SourceControl Block, which, unsurprisingly, allows the use of multiple source control blocks in a project. In the project that I am currently working on, I have some five separate CVS projects to check, and to add to the fun, I wanted to add some path filters to the mix. So, after a little trial and error, and a quick peek at the source code, here's a code sample for using path filters with multiple source control providers:
<sourcecontrol type="multi">     <sourceControls>         <filtered>             <sourceControlProvider type="cvs">                 <executable>C:\Program Files\cvsnt\cvs.exe</executable>                 <workingDirectory>D:\Cvs\Services</workingDirectory>             </sourceControlProvider>             <exclusionFilters>                 <pathFilter>                     <pattern>**/build.number</pattern>                 </pathFilter>                 <pathFilter>                     <pattern>**/CommonAssemblyInfo.cs</pattern>                 </pathFilter>             </exclusionFilters>         </filtered>         <filtered>             <sourceControlProvider type="cvs">                 <executable>C:\Program Files\cvsnt\cvs.exe</executable>                 <workingDirectory>D:\Cvs\Console</workingDirectory>             </sourceControlProvider>             <exclusionFilters>                 <pathFilter>                     <pattern>**/build.number</pattern>                 </pathFilter>                 <pathFilter>                     <pattern>**/CommonAssemblyInfo.cs</pattern>                 </pathFilter>             </exclusionFilters>         </filtered>     </sourceControls> </sourcecontrol>