Automated Coder

Exploring the Code of CruiseControl.Net

Archive for August, 2008

An Overview of the WCF Extensions

Posted by Craig Sutherland on 31 August, 2008

Introduction

Recently I documented adding a WCF extension to CruiseControl.Net. Due to mono-requirements I had to do it in a way that I wouldn’t force an upgrade of the CruiseControl.Net source code to .Net 3.0 or 3.5. Here is the complete set of posts:

This will be my last post in this series as I’m preparing to move onto some more work (adding some security extensions to CruiseControl.Net). However I thought I ought to add this final post just to wrap up what I have done.

Before WCF

Before I started adding the extensions CruiseControl.Net consisted of three main components:

  • Server (whether as a service or running as the console app)
  • Dash board
  • CCTray (the client application)

These communicated in the following ways:

Initial CC.Net communcations
Initial CC.Net communcations

As you see .Net Remoting was the only way to communicate with the server, thus the client either had to use .Net Remoting or go via the dashboard.

Extension Points

The first change was to add some extension points to both the server and CCTray. This would allow me to add the WCF code without converting the rest of the application. With the extension points the system now looked like this:

Extension points to CruiseControl.Net
Extension points to CruiseControl.Net

I didn’t add any extension points to the dashboard as I’m planning on going directly from the server (the worker of the system) to CCTray.

WCF Extensions

WCF Extensions
WCF Extensions

Now that I had some extension points for communications, I added the actual WCF components. These consisted of a server-side WCF extension, plus a custom transport protocol for CCTray.

The server side extension used the new hooks in CruiseServer to start and stop/abort a WCF ServiceHost. I added a service contract, ICruiseControlContract, plus its implementation and a number of data contracts to allow data extract. The only thing that a server administrator now needs to do is configure the server to use WCF – which uses the standard WCF configuration for the following service/contract combination:

  • Service: ThoughtWorks.CruiseControl.WcfExtension.CruiseControlImplementation
  • Contract: ThoughtWorks.CruiseControl.WcfExtension.ICruiseControlContract

CCTray is a custom transport extension. I added transport extensions so other people can also build their own transport mechanisms if desired. The main points to a transport extension is they must provide implementations of ICruiseProjectManager and ICruiseServerManager. These implementations do most of the actual work in CCTray, and they are returned from factory methods in ITransportExtension. For the WCF extension I implemented these classes and passed on all the calls to the client code generated by svcutil.

There is no need to modify the app.config for CCTray as the bindings and endpoints are generated in the code from user input – making it even simplier for end-users to set it up on their machines.

Routing Messages

The final piece of the puzzle is a router to sit in IIS:

WCF Extensions plus Router
WCF Extensions plus Router

This is required for installations that already have IIS listening on port 80 and want to use HTTP transport within WCF. Without the router we would have to use a different port to listen for requests.

The router merely needs to be added to IIS (either in an existing .Net 3.0 application or in its own application) and then its web.config modified to point to the listening WCF extension (which can listen on any port or protocol it likes).

What’s Next?

That, in a nutshell, is the work I did to allow WCF to be used in CruiseControl.Net. The extension points are general and can be used for other, non-WCF extensions.

Now I haven’t quite finished the work on the WCF extensions – they are missing one important component. Currently I am not passing any user information around! This is a major issue as ForceBuild(), AbortBuild() and FixBuild() all need this information. However I haven’t figured out an easy, secure way of passing this information, so I’m going to leave it for the moment.

At the moment I’m working on fixing a few issues that have been raised in Jira to try and get a better understanding of the code. Once I have finished these I’m going to look into adding a security layer to CruiseControl.Net that is easy to configure, flexible to expand and yet powerful enough to handle any security scenarios.

Posted in CruiseControl.Net, WCF | Tagged: , , | Leave a Comment »

A Challenging Bug

Posted by Craig Sutherland on 30 August, 2008

Welcome to Issue #1179, and #1096, and #730, …

I was looking through Jira trying to find another nice and easy issue that I could resolve when I came across issue #1179. And then I came across issue #1096 – both describing the same problem. A bit of digging brought me to #730 which appears to be the root cause for all of this.

Now I was interested in this problem because I had come across the same problem when I was adding my custom transport protocols to CCTray. At first I had thought it was a problem with my code, so I had wiped the config file, changed some code around the persistence and tried to reproduce it. As I couldn’t I thought I had fixed it. Silly me :(

It turns out the problem wasn’t caused by my code, but instead it had been added at an earlier point. And reading the issue logs it is now very easy to reproduce. First we need two servers with a project that has the same name (in my case I attached to the same server by via two different protocols). Add both these servers to CCTray and add the project that has the same name. Accept all the changes and go back to the main window. You’ll now see that the projects with the same name come from the same server! (it appears the last project in the list wins – all previous projects with the same name use the last project’s server.)

Great, now I can reproduce the error – let’s see if I can resolve it.

Delving into the Code

The first challenge was to find out where the server name was set – and that turned out to be a challenge! Initially I tried stepping through the code. I set a break point on BindToListView() in MainFormController and looked to see what happened. I got a ListViewItem alright, but it didn’t contain the server name :| Next I tried searching for “ListViewItem” in the code – that brought up a large list which I slowly went through (occasionally stopping to step through some occurrences to understand them). Eventually I came back to ProjectStatusListViewItemAdaptor and found a method called DisplayProjectStateInListViewItem(). I say came back as I had stepped through it in my initial debugging – but as I hadn’t read the entire class I hadn’t found the method.

This method attempts to find the relevant project from the configuration and extract the server name. Now this works if there are all unique project names – but fails with duplicates. The reason why is: DisplayProjectStateInListViewItem() searches through the project configurations to find a project that has the same name. As soon as it finds a matching project it stops searching and uses the server for that project. Obviously when CCTray was initially written they had only envisaged it looking at one server (I’m not blaming anyone here, it’s easy to make assumptions like this!)

Fixing the Problem

Now that I know where the problem occurs I can attempt to fix it. The search loop goes through the list of CCTrayProject entities in the configuration. This contains both a project name and a server URL. Next, it checks each entity’s project name against the project name of the Detail property in the selected IProjectMonitor. The Detail property is a ISingleProjectDetail entity – and voila, the problem is found. ISingleProjectDetail does not store the server URL and it doesn’t store anything that could be used to identify the server (well it does have WebURL, but I don’t think we can rely on using this property). Time to delve into the code some more.

First let’s find out who implements ISingleProjectDetail. It turns out that StubProjectMonitor and ProjectMonitor both implement it. StubProjectMonitor holds the project name in a private field, while ProjectMonitor gets it from an instance of ICruiseProjectManager that it holds. Still nowhere we can get the server URL from.

Next step – find out where these classes are instantiated. StubProjectMonitor is not instantiated anywhere – so for the moment I will assume that this is an unused class. ProjectMonitor is instantiated from CCTrayMultiConfiguration, and it uses a CCTrayProject to initialise the new instance. I’ve now arrived at a point I have the server URL and so can start to fix the problem.

Storing the URL

Since I’m comparing a CCTrayProject against an ISingleProjectDetail I decided to modify ISingleProjectDetail to include the actual CCTrayProject entity (not sure how this will work with StubProjectMonitor but since it’s not used I’m not going to worry about it).

Next I modified ProjectMonitor to store a CCTrayProject instance (I also modified StubProjectMonitor since I changed the interface, but all it returns is null). I then expanded the constructors to require a CCTrayProject instance, which is then stored in a private field.

Finally I modified GetProjectStatusMonitors() in CCTrayMultiConfiguration to include the CCTrayProject instance in the constructor. Now I can properly check to see whether DisplayProjectStateInListViewItem() has the correct project.

Since I don’t want to run into a similar problem in the future when more stuff is added to CCTrayProject I added an Equals() override to CCTrayProject (and also the GetHashCode() override to get rid of the warning). This then compares all the necessary properties (currently project name & server URL – when the extensions patch goes in I’ll need to expand it to include the extension name).

The End of the Fix

And that’s end. I updated the unit tests to make sure they all ran, plus added some new unit tests for my additions. I also actually viewed the results of the changes – the correct server is now being displayed in the list :) Now all that’s left is to generate a patch file and send it in to the admins to get it added to the trunk. I’ll do that tomorrow when I’m less tired…

Posted in CruiseControl.Net | Tagged: | 1 Comment »

Exploring CCTray

Posted by Craig Sutherland on 30 August, 2008

An Introduction to CCTray

CCTray is a little application that can be installed on a developer’s (or anyone else’s) machine that allows someone to remotely monitor the status of CruiseControl.Net. You can choose which projects on which servers to monitor. It even has a raft of functionality to try and meet everyone’s needs, some of which are:

  • Icon in the tray bar
  • Balloon notifications on project changes
  • Sounds for build events
  • Ability to change the icons
  • Speech
  • Etc.

Below is a screenshot of what CCTray looks like (note: I’ve changed the build icons so they are more obvious – the default just have different colours of the same icon):

Standard appearance of CCTray
Standard appearance of CCTray

However, like any application it has it short-comings. A quick look at the issues log in Jira show 43 outstanding issues (although I know some have been completed, but not marked as resolved).

Since I’m new to developing on the CruiseControl.Net project, I thought I’d try my hand at resolving an issue or two. Of course, I’m going to start off easy and move to the harder stuff later :)

Persisting the Settings

CCNET-754 mentions that the list view type isn’t being persisted (e.g. details, list or large icons). A quick test proves that this is still the case, so this will be my first issue to investigate (I’m not particularly interested in it myself, but I’ve got to start somewhere in the code).

To explain my findings, I’m going to divide the settings for CCTray into two general groups: appearance and functionality.

The functional settings (which projects to monitor, audio configuration, start-up settings, etc) are handled by the CCTrayMultiConfiguration class. This in turn uses PersistentConfiguration, CCTrayProject and a few other classes to handle the actual serialisation of the data. Pretty much all we have to do is add our properties to one of these classes, make sure our code gets/sets these properties and CCTrayMultiConfiguration takes care of the rest. As I’ve already looked at this functionality in an earlier post I’m not going to cover any more.

The appearance settings are handled in a different way altogether. Instead they revolve around a class called PersistWindowState. According to the code this was originally written by Joel Matthias (article here) and modified for CCTray by Grant Drake. This class handles the position of the window, plus its visibility. Additional properties are handled by hooking the events LoadState and SaveState. In contract to CCTrayMultiConfiguration this class persists the settings in the registry.

In CCTray these events are handled by the OnLoadState() and OnSaveState() methods in MainForm. These methods basically handle all the extra appearance properties that need to be persisted. To add the list view type was a simple matter of persisting the View property from the list view and loading it back again.

To load the setting:

lvProjects.View = (View)Enum.Parse(typeof(View), (string)e.Key.GetValue(“ProjectViewMode”, View.Details.ToString()));

And to persist it:

e.Key.SetValue(“ProjectViewMode”, lvProjects.View.ToString());

And that was it! A nice easy start to the world of CCTray development.

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

WCF Custom Protocol

Posted by Craig Sutherland on 29 August, 2008

The Story So Far…

This is just a quick recap of what we’ve done so far. If you’ve been following along you can probably skip down to the next heading.

This series has been based on the task of adding Windows Communications Foundation (WCF) to CruiseControl.Net. However there was one condition that made it a bit harder – the additions must not force an upgrade to .Net 3.0/3.5 or add code that can’t be compiled on Mono (e.g. for Linux users).

To get around this condition I first extended the CruiseControl.Net server to allow developers to write their own custom extensions that hook into the server process. With this in place I then developed a custom extension that added a WCF service host to the server, designed a contract of what would be provided and implemented the interface.

The next step was to add a router to the dashboard to automatically forward requests onto the server.

With these steps in place we were ready to tackle the client – CCTray. The next two posts covered adding the ability to add custom protocols to CCTray and to be able to configure them. This post now handles the final step – implementing a custom protocol to handle using WCF.

A Simple Task

In order to add a custom transport protocol, there are three interfaces that we need to implement:

  • ITransportExtension
  • ICruiseServerManager
  • ICruiseProjectManager

ITransportExtension is the new interface we added to allow custom protocols, while ICruiseServerManager and ICruiseProjectManager already existed in CCTray. We need to implement these as they provide the glue between our transport and CCTray’s inner workings. They are created by calling RetrieveProjectManager() and RetrieveServerManager() on the ITransportExtension interface.

I started a new project called CCTrayExtensions and implemented the interfaces as follows:

  • WcfExtension implements ITransportExtension
  • WcfServerManager implements ICruiseServerManager
  • WcfProjectManager implements ICruiseProjectManager

The last two classes open a WCF client and call the appropriate method on the client. Yes, all they are is pass-through classes to send the actual requests onto the server. They use auto-generated code – all I did is call svcutil on the WCF endpoint.

WcfExtension handles instantiating WcfServerManager and WcfProjectManager, plus it also displays a configuration dialog and converts between Settings (the property on the interface) and the internal settings (e.g. the transport binding to use). The configuration dialog is a class called WcfConfigurationWindow - this looks like the picture below:

WCF Extension Configuration Dialog
WCF Extension Configuration Dialog

The binding is stored in an enumeration – TransportBinding. As endpoint is stored in the Url property the only value Settings needs to store is this binding, so all the extension does is convert the binding to a string and back.

The final important task of WcfExtension is to open and close the clients. WcfExtension handles the open so it can retrieve the binding and endpoint and correctly configure the client. Close is just to make sure everything is tidied up correctly – plus later on I plan on looking at caching the client so we don’t need to open a new client everytime (which can become expensive for some bindings).

And that’s all there is to the extension – most of the hard work has already been done in adding the custom transport protocol to CCTray.

And In Conclusion

This concludes adding WCF to CruiseControl.Net – both on the server side and the client side. This involves a set of changes to the core libraries, to allow extensions, plus some WCF-specific extensions, so we don’t break our Mono compilation requirement. I’ll add the code to JIRA under two patches – extension-enabling code, plus the actual WCF extensions.

However, I still have a few things to tidy up, so I won’t stop this series just yet. Here are some of the other items I’m thinking of covering:

  • Unit testing the code (yes I did write unit tests, but for these posts I wanted to focus on the actual functionality)
  • Modifying the installers – it would be nice to get the installers to also install the WCF extensions for those who want it
  • Additional WCF functionality – security, logging, dual-channel communications
  • How to write a server extension, and also how to write a custom transport – these will be step-by-step guides (if anyone wants to try their own extensions)
  • And hopefully an overview of the whole series

As you can see, I still have plenty of ideas on what to write! However, now that I’ve finished documating most of my work to date it takes to get back into some development, so the documentation will probably slow down :) (I’ve been focusing on the documentation over the past week so I don’t forget anything.)

Hope you enjoy what I’ve done so far. And if anyone has any questions on what I’ve done, or suggestions on improvements, please let me know.

Posted in CruiseControl.Net, WCF | Tagged: , | Leave a Comment »

Configuring a CCTray Custom Protocol

Posted by Craig Sutherland on 29 August, 2008

Previously

In my previous post in this series (here), I looked at modifying CCTray to add in custom protocols. While I showed how to extend CCTray I didn’t go into the UI. As any user will tell you, the UI is king!

In this post I’ll look at modifying the UI for CCTray so we can include custom protocols and configure them.

Where To Start

If you have used CCTray you’ll know the sequence for adding a new server is:

  1. Open CCTray
  2. Goto Settings to open the CruiseControl.Net Tray Settings window
  3. Choose the Build Projects tab and click on Add… to open the Project window
  4. Click on Add Server to open the Build Server window

It is in this last window that we select both the transport protocol and the URL. Currently the three options are:

  • Via CruiseControl.Net dashboard – this is an HTTP protocol that contains to the dashboard.
  • Directly using .Net remoting – this uses .Net remoting via a custom port (by default 21234, but can be any).
  • Via a custom HTTP URL – internally this also uses HTTP protocol, but it is for non-.Net CC servers.

I need to add a fourth option to the list – in this case the ability for a user to select their own transport protocol. This part is fairly easy – I just opened the window in the form designer, added my controls and then wrote some code to wire up the events (enabling/disabling the input controls). In the end I had a result similar to below:

Extended Build Server Dialog
Extended Build Server Dialog

As you can see I added the radio button for the extension, plus some text describing it. Underneath I added a combo box to contain the name of the extension, plus a button to allow the user to configure it. Clicking on the radio button marks it as checked, disables all the other controls and enables the combo box. The button is not enabled until the user enters a name for the extension.

Making it Easy – Auto-detection

I wanted to make things easier for the users so I added an auto-detection feature to the form. When the form is loaded it opens all the DLLs in the extensions folder and adds any classes that implement ITransportExtension to the dropdown list. The extensions folder is just a folder under the bin directly that we can load any extensions into.

At the moment this is displaying the assembly-qualified type name, but I’m thinking about adding some attributes to the classes to allow us to specify a “friendly” name.

If the user does not like the list of extensions they can still enter the name of their own extension. Again it has to be a fully qualified type name, and they also have to make sure the assembly is either in the bin or extensions folder.

Custom Configurations

One of the main differences between the transport extension option and the other transport mechanism is there is no text box for entering the URL. Initially I had started with a text box for the URL, but as I progressed I realised that there could be more options than just the URL. For example, my WCF extension has the following dialog:

WCF Extension Configuration Dialog
WCF Extension Configuration Dialog

In here I just prompt the user for the type of binding (e.g. HTTP, TCP, WS-*, etc) and the endpoint (WCF’s name for the URL). A different custom transport might have a different set of requirements (e.g. a secure transport protocol might require the user name and password, etc.)

This dialog comes from the extension class itself. ITransportExtension has a method called Configure(). When this method is called the extension knows to display a dialog box for the user to enter the requirements. I did it this way as each transport will have different needs. It is up to the extension to then store these settings in the URL property and the Settings property in ITransportExtension. Whatever is set for the URL is then displayed where the Build Server dialog says “Please configure this extension”.

Wrapping Up

This shows how I have implemented the configuration of a custom transport. The persistance of the settings is handled by CCTrayMultiConfiguration – the extensions don’t have to worry about it.

Now that I’ve covered how I extended CCTray to allow WCF communications, and shown how the configuration is handled, the only remaining step is to cover the actual WCF extension itself! We already done all the hard work – now it’s just one last step to go…

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Monitoring CC.Net

Posted by Craig Sutherland on 27 August, 2008

Are You Sure CC.Net is Running?

With our modern 24/7 world we often expect things to be constantly up and running. However, as any sysadmin would tell us, expectation doesn’t always match reality.

Recently I had a problem where my CC.Net server crashed (well, it was my WCF code that crashed it, but it was still a crash). Now I assumed it was all running happily, made several check-ins to our code base and carried on working knowing that CC.Net would alert me to any problems. However since CC.Net was down I didn’t get any warnings – it wasn’t until I went to get the deployment files that I realised it was down.

That was when I thought wouldn’t it be nice to know when CC.Net isn’t running. Now at my work we have a monitor server that will send out notifications if a server (or service) goes down. One of the mechanisms for this is the server sends out a message telling the monitor it’s still happy (or that it’s unhappy but still running). If the monitor doesn’t receive a message within a certain timeframe then it sends out notifications.

This monitor server also tries to make things easy for us developers. We can tell it our service is running by passing in a simple HTTP request. So I thought this would be a fun mini-project using my new extension points in CC.Net.

ServiceMonitor

I quickly started up a new project and added a class that implemented ICruiseServerExtension. Since all I wanted to do is send out a message every time period (I choose one minute) I added a Timer (from System.Timers) to the class and attached the Elapsed event to a handler. Then all I had to do was get the Start()/Stop()/Abort() methods to start/stop the timer and I was done!

I initialised the new timer in the Initialise() method (including attaching the handler and hard-coding the interval). All the handler does is send a request off to the monitor server (again hard-coded, I was feeling lazy this morning).

The End

Once I compiled the code I deployed it to my test CC.Net and left it running there. It seems to be working quite happily. I then configured our monitor server and did some simple testing. I tried killing the service and the monitor sent me a notification. I left the service running for a while – no notifications. Now I’ll just leave it running for a week or so and if there are no issues I’ll deploy to our prod CC.Net.

Nice and easy!

The Code

Since I have no immediate plans to add this to the CC.Net code base (since the values are hard-coded), here is the complete source for my extension:

using System;
using System.Timers;
using System.Net;
using ThoughtWorks.CruiseControl.Core.Util;
using ThoughtWorks.CruiseControl.Remote;
namespace ThoughtWorks.CruiseControl.ServiceMonitor
{
   /// <summary>
   /// Adds an extension to send the server status to a monitor application using HTTP.
   /// </summary>
   public class ServiceMonitorExtension
      : ICruiseServerExtension, IDisposable
   {
      #region Private fields
      private ICruiseServer _cruiseServer;
      private Timer _monitorTimer;
      #endregion
      #region Public properties
      #region IsRunning
      /// <summary>
      /// Gets whether this extension is currently active.
      /// </summary>
      public bool IsRunning
      {
         get { return _monitorTimer.Enabled; }
      }
      #endregion
      #endregion
 
      #region Public methods
      #region Initialise()
      /// <summary>
      /// Initialises the service host to use.
      /// </summary>
      /// <param name="server">The CruiseServer that is initialising this extension.</param>
      /// <param name="extensionConfig">The configuration for the extension.</param>
      public void Initialise(ICruiseServer server, ExtensionConfiguration extensionConfig)
      {
         // Validate the input parameters
         server.ValidateNotNull("Cannot pass in a null server");
         // Store the parameters that we need for later
         _cruiseServer = server;
         // Initialise the timer
         _monitorTimer = new Timer(60000);
         _monitorTimer.Elapsed += new ElapsedEventHandler(CheckServerStatus);
      }
      #endregion
 
      #region Start()
      /// <summary>
      /// Starts the monitor timer.
      /// </summary>
      public void Start()
      {
         CheckServerStatus();
         _monitorTimer.Start();
      }
      #endregion
      #region Stop()
      /// <summary>
      /// Stops the monitor timer.
      /// </summary>
      public void Stop()
      {
         _monitorTimer.Stop();
      }
      #endregion
      #region Abort()
      /// <summary>
      /// Stops the monitor timer.
      /// </summary>
      public void Abort()
      {
         _monitorTimer.Stop();
      }
      #endregion
      #region Dispose()
      /// <summary>
      /// Make sure everything is closed.
      /// </summary>
      public void Dispose()
      {
         if (_monitorTimer != null)
         {
            _monitorTimer.Stop();
            _monitorTimer.Dispose();
            _monitorTimer = null;
         }
      }
      #endregion
      #region CheckServerStatus()
      /// <summary>
      /// Checks the status of the ICruiseServer and tells a listening server (event handler signature).
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void CheckServerStatus(object sender, ElapsedEventArgs e)
      {
         CheckServerStatus();
      }
      /// <summary>
      /// Checks the status of the ICruiseServer and tells a listening server (event handler signature).
      /// </summary>
      private void CheckServerStatus()
      {
         Log.Info("Sending server status");

         // TODO: Figure out how to check whether the ICruiseServer is running
 
         // Tell the remote server we're still running
         WebRequest request = HttpWebRequest.Create("http://monitorserver/monitor.aspx?app=ccnet&status=active");
         try
         {
            WebResponse response = request.GetResponse();
            response.Close();
         }
         // If the remote server is down we don't want to crash CC.Net as well!
         catch (Exception) { }
      }
      #endregion
      #endregion
   }

}

Posted in CruiseControl.Net, Tools | Leave a Comment »

Extending CCTray

Posted by Craig Sutherland on 26 August, 2008

Introducing CCTray

CCTray is a multi-server monitoring tool that can be installed on a client’s (i.e. developer’s) machine to allow real-time monitoring of projects. As I stated in an earlier post I didn’t particularly want to write a new client application, so instead I need to modify the existing application. Also remember one of the goals of this development is to not force an upgrade of the code base to .Net 3.0 or later.

Extension Points

As with the server, the first question was were to add in the extension points. After looking at the code the most obvious place was to add them as a transport protocol. Currently CCTray has two protocols – HTTP and .Net remoting (HTTP is used for web dashboard checking and also checking non-.Net CruiseControl servers).

Since I want to add a generic extension point, rather than hard-coding it, I first expanded the first of possible protocols to include an option “Extension”. That was where the fun began!

Extending BuildServer

BuildServer is the class that stores information about a server that CCTray connects to. Any extension would need to modify this class as a starting point. However looking at this class it only stores one value – the URL! The transport was deduced by looking at the URL (i.e. the scheme portion of the URL). Since my extension could possibly use HTTP as its scheme I couldn’t continue to use this option.

First step, I added a field to store the transport protocol. This then required modifying the URL property to store and return the transport protocol. Then, to maintain backwards compatibility, I modified the constructor to deduce the transport protocol from the URL.

Next step, I added another field to store the name of the extension. To simplify matters I just used the assembly qualified type name (i.e. the name of the class, including the namespace, plus the assembly name, version number, etc.) I then added a property to get and set the extension name and a new constructor to set the extension name. Finally I modified the static method BuildFromRemotingDisplayName() to use the new constructor (this constructor also accepted the protocol so it saved having to deduce it).

That was enough to get started on the journey. I’m going to fast-forward a bit here because later on I also realised I needed to store some extension-specific settings. To achieve this I just added a new field and getter/setter property for the settings. This field was just a string – it’s up to the extension to serialise/deserialise the actual settings. The final appearance of BuildServer is shown below:

Final modified version of BuildServer
Final modified version of BuildServer

Persisting the Settings

Now that the settings have been added to BuildServer we need to persist them so the user doesn’t have to re-add them everytime. The class responsible for saving and loading the user’s settings is CCTrayMultiConfiguration. This in turn uses XML serialisation on PersistentConfiguration, which in turn holds an array of CCTrayProject instances. I added two properties to CCTrayProject to hold the extension name and settings. The transport protocol is set in the following manner:

  1. If there is no extension name, then revert to the original deduction method (i.e. based on scheme)
  2. If there is an extension name, then the transport protocol will always be Extension.

These were the only changes I needed to make, the current code handled all the rest of the persistance.

Using the New Settings

Now that we can save and load the server settings we need to be able to use them. The two classes that check the transport protocol are CruiseServerManagerFactory and CruiseProjectManagerFactory. Between these two classes they have three methods that check the transport protocol and return the correct objects. Inside these methods they just have a switch statement to figure out which transport protocol is being used and then instantiate the correct class to handle it.

All I did to extend these is add in a case statement to check for my new transport protocol and then instantiate my protocol handler.

Introducing ITransportExtension

The actual hard work of instantiating the protocol handler is handled by a class that implements ITransportExtension. This is a new interface I added to allow external extensions. This has two getter/setter properties – Settings and URL (these come from BuildServer), one getter property – DisplayName (this is a user-friendly name to display in CCTray) and four methods.

  • Configure() – called by the UI to configure the settings for the extension.
  • GetProjectList() – used in CruiseProjectManagerFactory.
  • RetrieveProjectManager() – CruiseProjectManagerFactory.
  • RetrieveServerManager() – CruiseServerManagerFactory.

The last three methods are called from the switch statements within the factory classes to retrieve the specific instances of either an ICruiseProjectManager or an ICruiseServerManager. These instances are then used in other parts of CCTray to perform the actual work. I’ll talk about Configure() in a later post.

Next Steps

That concludes this post on adding the infrastructure for a custom transport extension. I’ve tried to make this as open and friendly as possible so other people could write their own custom extensions if they want.

In my next post I’ll look at how do we allow the client to configure the extension? This will involve modifying the UI for CCTray, and I’ll also expand where the Configure() method on ITransportExtension comes into things.

Once we’ve looked at configuring the extension the only thing left is to actually write an extension. Once I’ve looked at the UI I’ll then look at finally writing an extension protocol for CCTray that implements WCF.

Posted in CruiseControl.Net | Tagged: | 1 Comment »

WCF Router

Posted by Craig Sutherland on 24 August, 2008

A Small Detour

Before I move on to the client side extensions for WCF, I realised I have to make a small detour. In my previous posts I talked about how I added extensions to CruiseServer and then built a WCF extension. This worked beautifully – until I tried to deploy the server to a machine where I already had IIS running. What was the problem? The port for HTTP (port 80) was already being monitored by IIS. Therefore all requests were being processed by IIS instead of my WCF service!

Now I could turn off IIS on that machine, but then I’d lose all my nice web sites (including the dashboard for CruiseControl.Net). I could map either IIS or the WCF extension to use a different port, but the entire aim of this exercise is to use port 80 so we can get through our firewall. Therefore I needed to come up with a different solution.

Introducing the Router

Thankfully WCF came to the rescue here. WCF can be hosted natively in IIS (or at least natively via an ISAPI handler), therefore all I needed to do was add a .svc file to the web site and use that instead. However I didn’t want to redo all my work with adding WCF into the service (besides which, as far as I understand I’d then have to use remoting between the .svc file and the CruiseControl.Net server). So I had a look around to see what I could do to pass on messages.

It turns out that this is a very common scenario – something called routing (see the articles by Michele Leroux Bustamante part I and part II). So using the example code I quickly whipped up a WCF router to be part of CruiseControl.Net.

Again this is a separate project (see this post for the reasons why). This is a very simple project – it only contains one interface (IRouterService), one class (RouterService), an .svc file and the web.config. The interface defines the contract or in this case merely tells WCF that we’re going to handle every single message ourselves. The class is the implementation of the contract. Here we merely receive the message, generate a client to the “real” service and then pass on the message.

The web.config needs a bit more work than just the service or the client requires. Instead it needs both a service definition (to handle incoming messages) plus a client definition (so it knows where to send messages to). Visual Studio added the service definition for me automatically and I just copied and pasted the client definition from what svcutil generated for me. I needed to modify it a bit (had to change the contract attribute to use our interface) and then it worked beautifully!

To finish things off all we need to do is compile the project and then add the web.config, .svc file and DLL to the web site. When I get around to finding out how the installer for CruiseControl.Net works I can see if it’s possible to do it in there.

It’s Not All Bad News

Now that I’ve started on a router it does open up a few new possibilities:

  • I could possibility use the router to handle security (i.e. integrated security in IIS and pass on security headers to the actual service).
  • The router could also be used in the same way as the dashboard – receive messages from a client and route them to multiple CruiseControl.Net servers.
  • I could even use it for switching protocols (i.e. accept incoming requests over HTTP and use named pipes or TCP to connect with the server)

It’ll take a bit more investigation, but the list goes on…

Where to Next?

Now that I have the CruiseControl.Net server exposed as a WCF service and I can access it over http, we need to look at the client-side. In my next few posts I’ll look at what I did to allow custom transport mechanisms in CCTray, then my WCF transport and hopefully finish things off with an overview of the entire mechanism.

Posted in CruiseControl.Net, WCF | Tagged: | Leave a Comment »

Transporting Data via Data Contracts

Posted by Craig Sutherland on 24 August, 2008

Catching Up

In my last post (here) I showed how I implemented the web service for CruiseControl.Net using WCF. However I stated that I couldn’t transfer data across the wire using the standard CruiseControl.Net data entities (e.g. CruiseServerSnapshot, ProjectStatus, etc.) In this post we’ll look at what I did to get around this issue.

Data Contracts

WCF automatically serialises/deserialises any basic types (e.g. integers, reals, strings, booleans, etc.) However it doesn’t handle complex types (e.g. classes) by default. I had thought that if a class was marked serializable then it would handle it, but I was wrong.

Instead to allow passing complex types WCF provides a set of attributes for marking up classes. The two basic attributes are DataContract and DataMember. DataContract tells WCF that the class can be passed over the wire, while DataMember tell WCF which properties/fields to include. Unlike XML serialisation, WCF uses an opt-in model – in other words we have to tell WCF what to include. Like service contracts we can also include a namespace – which I have done. This is the same namespace as the service (http://ccnet.thoughtworks.com/1/5/wcf).

Now I could have modified the entities in the Remote project to add the WCF attributes, but that would break one of the requirements for my code and force a migration to .Net 3.0. So, instead I added some new entities to the WcfExtension project.

What to Send

I decided that I was going to change the structure a bit for the data that I send using WCF. Mainly it’s because I wanted to use the generic List<> rather than writing my own collections (yes, I’m a lazy coder). The following shows the entities that I decided to implement:

WCF Data Objects for CruiseControl.Net
WCF Data Objects for CruiseControl.Net

These map to the standard CruiseControl.Net entities in the following way:

  • Snapshot maps to CruiseServerSnapshot.
  • Project maps to ProjectStatus
  • Queue maps to QueueSnapshot.
  • QueueRequest maps to QueuedRequestSnapshot.

Each of the WCF entities has a constructor that accepts the standard CruiseControl.Net entity. The WCF entities have the same properties as the standard entities.

With this in place, all I had to do was modify the service contract to use the new entities. Then I modified the methods that returned data (RetrieveSnapshot(), RetrieveProject() and RetrieveProjects()) to use the new entities. And then the WcfExtension is already to roll!

Coming Soon: The Client

Now that I’ve finished the server-side of the WCF extension it’s time to start looking at the client-side. Initially for testing I had written my own client – but this had only a subset of the full CCTray functionality. Obviously I don’t want to redo all the work that’s already done, so it’s time to start looking at modifying CCTray to handle WCF protocols.

Posted in CruiseControl.Net, WCF | Tagged: | 2 Comments »

Cruise Control Contract

Posted by Craig Sutherland on 24 August, 2008

Previously

In my previous two posts I covered the basics of what I am planning to do (here) and my initial work around adding a WCF extension to CruiseControl.Net (here). In this post I’ve delve into ICruiseControlContract and its implementation – CruiseControlImplementation.

Why a Contract?

The purpose of a web service is to allow two systems to communicate. And just like we have a contract on how we communicate (most of us use a contract called English) so web services need one. In technical terms a contract lists the methods it exposes to the external world, what information it expects in order to perform those methods and what data it will return. If you’re thinking this sounds very much like an interface, you right on! Contracts in WCF are basically a sub-set of the interface functionality (properties and events aren’t part of a contract). In addition there are various attributes we can apply to the contract to affect the definition of the contract (namespaces are a piece of metadata from an attribute).

While the contract attributes can be applied just as easier to a class, it is recommended practise that we use an interface as a contract. So we’ll stick with this practise and define an interface called ICruiseControlContract. The purpose of this interface is list all the methods we’ll expose for working with CruiseServer.

The very first thing I did was add an attribute to tell WCF that this interface is a contract. At the same time I told it the namespace that this contract would use:

[ServiceContract(Namespace = "http://ccnet.thoughtworks.com/1/5/wcf")]

I’ve given our contract a multi-part namespace. Most of this should be obvious, but we’ll break it down:

  • ccnet.thoughtworks.com: this is the base domain to use, in our case we’re on ccnet which belongs to thoughtworks.com.
  • 1/5: this is the current development version of CruiseControl.Net. As I’m hoping to have the work finished by the next release I’m using 1.5 as the version number.
  • wcf: this is the extension name – in our case WCF. I might change this later on to something more meaningful, but I can’t think of anything at the moment.

Designing the Contract

Now that we have started on the contract the question arises as to what should we put in it? In my initial development I added the following methods:

  • RetrieveVersion()
  • RetrieveSnapshot()
  • ForceBuild()

I added these for testing purposes – mainly could I could find them easily in CruiseServer. The diagram on the right has the full list of method that I expose. Basically I’ve marked all of the methods in this interface as web methods, since that’s the sole purpose of this interface. The additional methods I added were:

  • AbortBuild()
  • CancelPendingRequest()
  • FixBuild()
  • RetrieveProject()
  • RetrieveProjects()
  • StartProject()
  • StopProject()

I got this full list by mainly looking at CCTray (ICruiseProjectManager and ICruiseServerManager). This should be the full functionality to handle all of CCTray’s requests. RetrieveProject() was added as an extra to allow me to view a single project – rather than getting the full list (which could be a bit of an overkill, especially when we serialise/de-serialise).

The final note on the contract, in order to expose these methods I needed to add an OperationContract attribute to each of the methods. This is just how WCF works – it uses an opt-in model for the contract. The attribute I added to each method was:

[OperationContract]

I haven’t bothered setting any metadata yet. Perhaps if we need it later on I’ll add it in. I should mention that these methods will use the same namespace as the interface (i.e. “http://ccnet.thoughtworks.com/1/5/wcf”).

Now that we have our contract, let’s move onto making this contract work.

The Implementation

Finally, we’re ready to actually make the web service do something. The diagram on the right shows my CruiseControlImplementation class. You’ll notice it implements all the methods from ICruiseControlContract. These methods are very simple in their implementation.

If the method receives any parameters (e.g. ForceBuild(), RetrieveProject(), etc.) then the parameters are validated. The validation is first checking they are not null (if we expect a value). Then for project name parameters we check that the project actually exists. Once the parameters are validated the method call gets passed onto the CruiseServer instance (which is being held in _cruiseServer). Any outputs from the instance are passed back to the client.

To help with the validation I added a couple of private helper methods:

  • CheckProjectExists()
  • ValidateProjectNameInput()

These do exactly what their names are.

The final method is a helper method for getting the name of the current user: RetrieveCurrentUserName(). At the moment this isn’t working, but when I get around to adding security to the web service I’ll get it up and running.

Next Step

Now that the web service is hopefully finished it was time to test it. I built a quick test client that just called the initial three methods and tried to send and retrieve data. However it failed – badly :( The problem was CruiseControl.Net uses some nice custom classes for passing around its data. Since these were marked as Serializable I had hoped they would work as is. The problem is they didn’t. So for my next, and hopefully last post, on the service side of WCF I’ll talk about the data contracts I added.

Posted in CruiseControl.Net, WCF | Tagged: | 1 Comment »